foreign key locks, 2nd attempt

Started by Alvaro Herreraabout 14 years ago137 messages
#1Alvaro Herrera
alvherre@alvh.no-ip.org
1 attachment(s)

Hello,

After some rather extensive rewriting, I submit the patch to improve
foreign key locks.

To recap, the point of this patch is to introduce a new lock tuple mode,
that lets the RI code obtain a lighter lock on tuples, which doesn't
conflict with updates that do not modify the key columns.

So Noah Misch proposed using the FOR KEY SHARE syntax, and that's what I
have implemented here. (There was some discussion that instead of
inventing new SQL syntax we could pass the necessary lock mode
internally in the ri_triggers code. That can still be done of course,
though I haven't done so in the current version of the patch.)

The other user-visible pending item is that it was said that instead of
simply using "columns used by unique indexes" as the key columns
considered by this patch, we should do some ALTER TABLE command. This
would be a comparatively trivial undertaking, I think, but I would like
there to be consensus on that this is really the way to go before I
implement it.

There are three places that have been extensively touched for this to be
possible:

- multixact.c stores two flag bits for each member transaction of a
MultiXactId. With those two flags we can tell whether each member
transaction is a key-share locker, a Share locker, an Exclusive
locker, or an updater. This also needed some new logic to implement
new truncation logic: previously we could truncate multixact as soon
as the member xacts went below the oldest multi generated by current
transactions. The new code cannot do this, because some multis can
contain updates, which means that they need to persist beyond that.
The new design is to truncate multixact segments when the
corresponding Xid is frozen by vacuum -- to this end, we keep track
of RecentGlobalXmin (and corresponding Xid epoch) on each multixact
SLRU segment, and remove previous segments when that Xid is frozen.
This RecentGlobalXmin and epoch values are stored in the first two
multixact/offset values in the first page of each segment.
(AFAICT there are serious bugs in the implementation of this, but I
believe the basic idea to be sound.)

- heapam.c needs some new logic to keep closer track of multixacts
after updates and locks.

- tqual needed to be touched extensively too, mainly so that we
understand that some multixacts can contain updates -- and this needs
to show as HeapTupleBeingUpdated (or equivalent) when consulted.

The new code mostly works fine but I'm pretty sure there must be serious
bugs somewhere. Also, in places, heap_update and heap_lock_tuple have
become spaguettish, though I'm not sure I see better ways to write them.

I would like some opinions on the ideas on this patch, and on the patch
itself. If someone wants more discussion on implementation details of
each part of the patch, I'm happy to provide a textual description --
please just ask.

--
Álvaro Herrera <alvherre@alvh.no-ip.org>

Attachments:

fklocks-4.patchapplication/octet-stream; name=fklocks-4.patchDownload
diff --git a/contrib/pgrowlocks/Makefile b/contrib/pgrowlocks/Makefile
index f56389b..fe80423 100644
--- a/contrib/pgrowlocks/Makefile
+++ b/contrib/pgrowlocks/Makefile
@@ -4,7 +4,7 @@ MODULE_big	= pgrowlocks
 OBJS		= pgrowlocks.o
 
 EXTENSION = pgrowlocks
-DATA = pgrowlocks--1.0.sql pgrowlocks--unpackaged--1.0.sql
+DATA = pgrowlocks--1.1.sql pgrowlocks--1.0--1.1.sql pgrowlocks--unpackaged--1.0.sql
 
 ifdef USE_PGXS
 PG_CONFIG = pg_config
diff --git a/contrib/pgrowlocks/pgrowlocks--1.0--1.1.sql b/contrib/pgrowlocks/pgrowlocks--1.0--1.1.sql
new file mode 100644
index 0000000..70f20c7
--- /dev/null
+++ b/contrib/pgrowlocks/pgrowlocks--1.0--1.1.sql
@@ -0,0 +1,18 @@
+/* contrib/pgrowlocks/pgrowlocks--1.0--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION pgrowlocks" to load this file. \quit
+
+ALTER EXTENSION pgrowlocks DROP FUNCTION pgrowlocks(text);
+DROP FUNCTION pgrowlocks(text);
+CREATE FUNCTION pgrowlocks(IN relname text,
+    OUT locked_row TID,		-- row TID
+    OUT lock_type TEXT,		-- lock type
+    OUT locker XID,		-- locking XID
+    OUT multi bool,		-- multi XID?
+    OUT xids xid[],		-- multi XIDs
+    OUT modes text[],		-- multi XID statuses
+    OUT pids INTEGER[])		-- locker's process id
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pgrowlocks'
+LANGUAGE C STRICT;
diff --git a/contrib/pgrowlocks/pgrowlocks--1.0.sql b/contrib/pgrowlocks/pgrowlocks--1.0.sql
deleted file mode 100644
index a909b74..0000000
--- a/contrib/pgrowlocks/pgrowlocks--1.0.sql
+++ /dev/null
@@ -1,15 +0,0 @@
-/* contrib/pgrowlocks/pgrowlocks--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION pgrowlocks" to load this file. \quit
-
-CREATE FUNCTION pgrowlocks(IN relname text,
-    OUT locked_row TID,		-- row TID
-    OUT lock_type TEXT,		-- lock type
-    OUT locker XID,		-- locking XID
-    OUT multi bool,		-- multi XID?
-    OUT xids xid[],		-- multi XIDs
-    OUT pids INTEGER[])		-- locker's process id
-RETURNS SETOF record
-AS 'MODULE_PATHNAME', 'pgrowlocks'
-LANGUAGE C STRICT;
diff --git a/contrib/pgrowlocks/pgrowlocks--1.1.sql b/contrib/pgrowlocks/pgrowlocks--1.1.sql
new file mode 100644
index 0000000..924d80f
--- /dev/null
+++ b/contrib/pgrowlocks/pgrowlocks--1.1.sql
@@ -0,0 +1,16 @@
+/* contrib/pgrowlocks/pgrowlocks--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION pgrowlocks" to load this file. \quit
+
+CREATE FUNCTION pgrowlocks(IN relname text,
+    OUT locked_row TID,		-- row TID
+    OUT lock_type TEXT,		-- lock type
+    OUT locker XID,		-- locking XID
+    OUT multi bool,		-- multi XID?
+    OUT xids xid[],		-- multi XIDs
+    OUT modes text[],		-- multi XID statuses
+    OUT pids INTEGER[])		-- locker's process id
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pgrowlocks'
+LANGUAGE C STRICT;
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index 20beed2..170547a 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -59,6 +59,14 @@ typedef struct
 	int			ncolumns;
 } MyData;
 
+#define		Atnum_tid		0
+#define		Atnum_type		1
+#define		Atnum_xmax		2
+#define		Atnum_ismulti	3
+#define		Atnum_xids		4
+#define		Atnum_modes		5
+#define		Atnum_pids		6
+
 Datum
 pgrowlocks(PG_FUNCTION_ARGS)
 {
@@ -124,72 +132,96 @@ pgrowlocks(PG_FUNCTION_ARGS)
 									 GetCurrentCommandId(false),
 									 scan->rs_cbuf) == HeapTupleBeingUpdated)
 		{
-
 			char	  **values;
-			int			i;
 
 			values = (char **) palloc(mydata->ncolumns * sizeof(char *));
 
-			i = 0;
-			values[i++] = (char *) DirectFunctionCall1(tidout, PointerGetDatum(&tuple->t_self));
+			values[Atnum_tid] = (char *) DirectFunctionCall1(tidout, PointerGetDatum(&tuple->t_self));
 
-			if (tuple->t_data->t_infomask & HEAP_XMAX_SHARED_LOCK)
-				values[i++] = pstrdup("Shared");
-			else
-				values[i++] = pstrdup("Exclusive");
-			values[i] = palloc(NCHARS * sizeof(char));
-			snprintf(values[i++], NCHARS, "%d", HeapTupleHeaderGetXmax(tuple->t_data));
+			values[Atnum_type] = palloc(36);
+			values[Atnum_type][0] = '\0';
+			if (tuple->t_data->t_infomask & HEAP_XMAX_KEYSHR_LOCK)
+				strcat(values[Atnum_type], "KeyShare ");
+			if (tuple->t_data->t_infomask & HEAP_XMAX_EXCL_LOCK)
+				strcat(values[Atnum_type], "Exclusive ");
+			if (tuple->t_data->t_infomask & HEAP_XMAX_IS_NOT_UPDATE)
+				strcat(values[Atnum_type], "IsNotUpdate ");
+
+			values[Atnum_xmax] = palloc(NCHARS * sizeof(char));
+			snprintf(values[Atnum_xmax], NCHARS, "%d", HeapTupleHeaderGetXmax(tuple->t_data));
 			if (tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI)
 			{
-				TransactionId *xids;
-				int			nxids;
+				MultiXactMember *members;
+				int			nmembers;
 				int			j;
-				int			isValidXid = 0;		/* any valid xid ever exists? */
+				bool		isValidXid = false;		/* any valid xid ever exists? */
 
-				values[i++] = pstrdup("true");
-				nxids = GetMultiXactIdMembers(HeapTupleHeaderGetXmax(tuple->t_data), &xids);
-				if (nxids == -1)
-				{
+				values[Atnum_ismulti] = pstrdup("true");
+
+				nmembers = GetMultiXactIdMembers(HeapTupleHeaderGetXmax(tuple->t_data), &members);
+				if (nmembers == -1)
 					elog(ERROR, "GetMultiXactIdMembers returns error");
-				}
 
-				values[i] = palloc(NCHARS * nxids);
-				values[i + 1] = palloc(NCHARS * nxids);
-				strcpy(values[i], "{");
-				strcpy(values[i + 1], "{");
+				values[Atnum_xids] = palloc(NCHARS * nmembers);
+				values[Atnum_modes] = palloc(NCHARS * nmembers);
+				values[Atnum_pids] = palloc(NCHARS * nmembers);
 
-				for (j = 0; j < nxids; j++)
+				strcpy(values[Atnum_xids], "{");
+				strcpy(values[Atnum_modes], "{");
+				strcpy(values[Atnum_pids], "{");
+
+				for (j = 0; j < nmembers; j++)
 				{
 					char		buf[NCHARS];
 
-					if (TransactionIdIsInProgress(xids[j]))
+					if (isValidXid)
 					{
-						if (isValidXid)
-						{
-							strcat(values[i], ",");
-							strcat(values[i + 1], ",");
-						}
-						snprintf(buf, NCHARS, "%d", xids[j]);
-						strcat(values[i], buf);
-						snprintf(buf, NCHARS, "%d", BackendXidGetPid(xids[j]));
-						strcat(values[i + 1], buf);
-
-						isValidXid = 1;
+						strcat(values[Atnum_xids], ",");
+						strcat(values[Atnum_modes], ",");
+						strcat(values[Atnum_pids], ",");
+					}
+					snprintf(buf, NCHARS, "%d", members[j].xid);
+					strcat(values[Atnum_xids], buf);
+					switch (members[j].status)
+					{
+						case MultiXactStatusKeyUpdate:
+							snprintf(buf, NCHARS, "keyupd");
+							break;
+						case MultiXactStatusUpdate:
+							snprintf(buf, NCHARS, "upd");
+							break;
+						case MultiXactStatusForUpdate:
+							snprintf(buf, NCHARS, "forupd");
+							break;
+						case MultiXactStatusForShare:
+							snprintf(buf, NCHARS, "shr");
+							break;
+						case MultiXactStatusForKeyShare:
+							snprintf(buf, NCHARS, "keyshr");
+							break;
 					}
+					strcat(values[Atnum_modes], buf);
+					snprintf(buf, NCHARS, "%d", BackendXidGetPid(members[j].xid));
+					strcat(values[Atnum_pids], buf);
+
+					isValidXid = true;
 				}
 
-				strcat(values[i], "}");
-				strcat(values[i + 1], "}");
-				i++;
+				strcat(values[Atnum_xids], "}");
+				strcat(values[Atnum_modes], "}");
+				strcat(values[Atnum_pids], "}");
 			}
 			else
 			{
-				values[i++] = pstrdup("false");
-				values[i] = palloc(NCHARS * sizeof(char));
-				snprintf(values[i++], NCHARS, "{%d}", HeapTupleHeaderGetXmax(tuple->t_data));
+				values[Atnum_ismulti] = pstrdup("false");
+
+				values[Atnum_xids] = palloc(NCHARS * sizeof(char));
+				snprintf(values[Atnum_xids], NCHARS, "{%d}", HeapTupleHeaderGetXmax(tuple->t_data));
+
+				values[Atnum_modes] = NULL;
 
-				values[i] = palloc(NCHARS * sizeof(char));
-				snprintf(values[i++], NCHARS, "{%d}", BackendXidGetPid(HeapTupleHeaderGetXmax(tuple->t_data)));
+				values[Atnum_pids] = palloc(NCHARS * sizeof(char));
+				snprintf(values[Atnum_pids], NCHARS, "{%d}", BackendXidGetPid(HeapTupleHeaderGetXmax(tuple->t_data)));
 			}
 
 			LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
@@ -200,10 +232,10 @@ pgrowlocks(PG_FUNCTION_ARGS)
 			/* make the tuple into a datum */
 			result = HeapTupleGetDatum(tuple);
 
-			/* Clean up */
-			for (i = 0; i < mydata->ncolumns; i++)
-				pfree(values[i]);
-			pfree(values);
+			/*
+			 * no need to pfree what we allocated; it's on a short-lived memory
+			 * context anyway
+			 */
 
 			SRF_RETURN_NEXT(funcctx, result);
 		}
diff --git a/contrib/pgrowlocks/pgrowlocks.control b/contrib/pgrowlocks/pgrowlocks.control
index a6ba164..dfa587d 100644
--- a/contrib/pgrowlocks/pgrowlocks.control
+++ b/contrib/pgrowlocks/pgrowlocks.control
@@ -1,5 +1,5 @@
 # pgrowlocks extension
 comment = 'show row-level locking information'
-default_version = '1.0'
+default_version = '1.1'
 module_pathname = '$libdir/pgrowlocks'
 relocatable = true
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index b2d1901..42d14a2 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -74,6 +74,7 @@
 bool		synchronize_seqscans = true;
 
 
+static LOCKMODE get_lockmode_for_tuplelock(LockTupleMode mode);
 static HeapScanDesc heap_beginscan_internal(Relation relation,
 						Snapshot snapshot,
 						int nkeys, ScanKey key,
@@ -84,6 +85,7 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
 				bool all_visible_cleared, bool new_all_visible_cleared);
 static bool HeapSatisfiesHOTUpdate(Relation relation, Bitmapset *hot_attrs,
 					   HeapTuple oldtup, HeapTuple newtup);
+static uint16 GetMultiXactIdHintBits(MultiXactId multi);
 
 
 /* ----------------------------------------------------------------
@@ -1620,7 +1622,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
 				   ItemPointerGetBlockNumber(tid));
 			offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
 			at_chain_start = false;
-			prev_xmax = HeapTupleHeaderGetXmax(heapTuple->t_data);
+			prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
 		}
 		else
 			break;				/* end of chain */
@@ -1743,7 +1745,7 @@ heap_get_latest_tid(Relation relation,
 		 * tuple.  Check for XMIN match.
 		 */
 		if (TransactionIdIsValid(priorXmax) &&
-		  !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
+			!TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
 		{
 			UnlockReleaseBuffer(buffer);
 			break;
@@ -1761,7 +1763,8 @@ heap_get_latest_tid(Relation relation,
 		/*
 		 * If there's a valid t_ctid link, follow it, else we're done.
 		 */
-		if ((tp.t_data->t_infomask & (HEAP_XMAX_INVALID | HEAP_IS_LOCKED)) ||
+		if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+			HeapTupleHeaderIsLocked(tp.t_data) ||
 			ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
 		{
 			UnlockReleaseBuffer(buffer);
@@ -1769,7 +1772,7 @@ heap_get_latest_tid(Relation relation,
 		}
 
 		ctid = tp.t_data->t_ctid;
-		priorXmax = HeapTupleHeaderGetXmax(tp.t_data);
+		priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
 		UnlockReleaseBuffer(buffer);
 	}							/* end of loop */
 }
@@ -2085,10 +2088,11 @@ simple_heap_insert(Relation relation, HeapTuple tup)
  * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated
  * (the last only possible if wait == false).
  *
- * In the failure cases, the routine returns the tuple's t_ctid and t_xmax.
+ * In the failure cases, the routine returns the tuple's t_ctid and the
+ * updating Xid (resolving a possible MultiXact, if necessary).
  * If t_ctid is the same as tid, the tuple was deleted; if different, the
  * tuple was updated, and t_ctid is the location of the replacement tuple.
- * (t_xmax is needed to verify that the replacement tuple matches.)
+ * (xmax is needed to verify that the replacement tuple matches.)
  */
 HTSU_Result
 heap_delete(Relation relation, ItemPointer tid,
@@ -2174,20 +2178,22 @@ l1:
 		 */
 		if (!have_tuple_lock)
 		{
-			LockTuple(relation, &(tp.t_self), ExclusiveLock);
+			LockTuple(relation, &(tp.t_self),
+					  get_lockmode_for_tuplelock(LockTupleKeyUpdate));
 			have_tuple_lock = true;
 		}
 
 		/*
 		 * Sleep until concurrent transaction ends.  Note that we don't care
-		 * if the locker has an exclusive or shared lock, because we need
-		 * exclusive.
+		 * which lock mode the locker has, because we need the strongest one.
 		 */
 
 		if (infomask & HEAP_XMAX_IS_MULTI)
 		{
+			int		remain;
+
 			/* wait for multixact */
-			MultiXactIdWait((MultiXactId) xwait);
+			MultiXactIdWait((MultiXactId) xwait, MultiXactStatusKeyUpdate, &remain);
 			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 
 			/*
@@ -2234,8 +2240,8 @@ l1:
 		 * We may overwrite if previous xmax aborted, or if it committed but
 		 * only locked the tuple without updating it.
 		 */
-		if (tp.t_data->t_infomask & (HEAP_XMAX_INVALID |
-									 HEAP_IS_LOCKED))
+		if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+			HeapTupleHeaderIsLocked(tp.t_data))
 			result = HeapTupleMayBeUpdated;
 		else
 			result = HeapTupleUpdated;
@@ -2255,10 +2261,11 @@ l1:
 			   result == HeapTupleBeingUpdated);
 		Assert(!(tp.t_data->t_infomask & HEAP_XMAX_INVALID));
 		*ctid = tp.t_data->t_ctid;
-		*update_xmax = HeapTupleHeaderGetXmax(tp.t_data);
+		*update_xmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
 		UnlockReleaseBuffer(buffer);
 		if (have_tuple_lock)
-			UnlockTuple(relation, &(tp.t_self), ExclusiveLock);
+			UnlockTuple(relation, &(tp.t_self),
+						get_lockmode_for_tuplelock(LockTupleKeyUpdate));
 		if (vmbuffer != InvalidBuffer)
 			ReleaseBuffer(vmbuffer);
 		return result;
@@ -2296,7 +2303,7 @@ l1:
 	tp.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
 							   HEAP_XMAX_INVALID |
 							   HEAP_XMAX_IS_MULTI |
-							   HEAP_IS_LOCKED |
+							   HEAP_LOCK_BITS |
 							   HEAP_MOVED);
 	HeapTupleHeaderClearHotUpdated(tp.t_data);
 	HeapTupleHeaderSetXmax(tp.t_data, xid);
@@ -2368,7 +2375,8 @@ l1:
 	 * Release the lmgr tuple lock, if we had it.
 	 */
 	if (have_tuple_lock)
-		UnlockTuple(relation, &(tp.t_self), ExclusiveLock);
+		UnlockTuple(relation, &(tp.t_self),
+					get_lockmode_for_tuplelock(LockTupleKeyUpdate));
 
 	pgstat_count_heap_delete(relation);
 
@@ -2442,10 +2450,11 @@ simple_heap_delete(Relation relation, ItemPointer tid)
  * update was done.  However, any TOAST changes in the new tuple's
  * data are not reflected into *newtup.
  *
- * In the failure cases, the routine returns the tuple's t_ctid and t_xmax.
+ * In the failure cases, the routine returns the tuple's t_ctid and the
+ * updating Xid (resolving a possible MultiXact, if necessary).
  * If t_ctid is the same as otid, the tuple was deleted; if different, the
  * tuple was updated, and t_ctid is the location of the replacement tuple.
- * (t_xmax is needed to verify that the replacement tuple matches.)
+ * (xmax is needed to verify that the replacement tuple matches.)
  */
 HTSU_Result
 heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
@@ -2455,11 +2464,14 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
 	HTSU_Result result;
 	TransactionId xid = GetCurrentTransactionId();
 	Bitmapset  *hot_attrs;
+	Bitmapset  *key_attrs;
 	ItemId		lp;
 	HeapTupleData oldtup;
 	HeapTuple	heaptup;
 	Page		page;
 	BlockNumber	block;
+	LockTupleMode tuplock;
+	MultiXactStatus mxact_status;
 	Buffer		buffer,
 				newbuf,
 				vmbuffer = InvalidBuffer,
@@ -2471,8 +2483,14 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
 	bool		have_tuple_lock = false;
 	bool		iscombo;
 	bool		use_hot_update = false;
+	bool		key_intact;
 	bool		all_visible_cleared = false;
 	bool		all_visible_cleared_new = false;
+	bool			keep_xmax_multi = false;
+	TransactionId	keep_xmax = InvalidTransactionId;
+	TransactionId	keep_xmax_old = InvalidTransactionId;
+	uint16		keep_xmax_infomask = 0;
+	uint16		keep_xmax_old_infomask = 0;
 
 	Assert(ItemPointerIsValid(otid));
 
@@ -2488,7 +2506,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
 	 * Note that we get a copy here, so we need not worry about relcache flush
 	 * happening midway through.
 	 */
-	hot_attrs = RelationGetIndexAttrBitmap(relation);
+	hot_attrs = RelationGetIndexAttrBitmap(relation, false);
+	key_attrs = RelationGetIndexAttrBitmap(relation, true);
 
 	block = ItemPointerGetBlockNumber(otid);
 	buffer = ReadBuffer(relation, block);
@@ -2513,6 +2532,24 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
 	oldtup.t_self = *otid;
 
 	/*
+	 * If we're not updating any "key" column, we can grab a milder lock type.
+	 * This allows for more concurrency when we are running simultaneously with
+	 * foreign key checks.
+	 */
+	if (HeapSatisfiesHOTUpdate(relation, key_attrs, &oldtup, newtup))
+	{
+		tuplock = LockTupleUpdate;
+		mxact_status = MultiXactStatusUpdate;
+		key_intact = true;
+	}
+	else
+	{
+		tuplock = LockTupleKeyUpdate;
+		mxact_status = MultiXactStatusKeyUpdate;
+		key_intact = false;
+	}
+
+	/*
 	 * Note: beyond this point, use oldtup not otid to refer to old tuple.
 	 * otid may very well point at newtup->t_self, which we will overwrite
 	 * with the new tuple's location, so there's great risk of confusion if we
@@ -2522,6 +2559,9 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
 l2:
 	result = HeapTupleSatisfiesUpdate(oldtup.t_data, cid, buffer);
 
+	/* see below about the "no wait" case */
+	Assert(result != HeapTupleBeingUpdated || wait);
+
 	if (result == HeapTupleInvisible)
 	{
 		UnlockReleaseBuffer(buffer);
@@ -2529,8 +2569,21 @@ l2:
 	}
 	else if (result == HeapTupleBeingUpdated && wait)
 	{
-		TransactionId xwait;
+		TransactionId	xwait;
 		uint16		infomask;
+		bool		none_remain = false;
+
+		/*
+		 * XXX note that we don't consider the "no wait" case here.  This
+		 * isn't a problem currently because no caller uses that case, but it
+		 * should be fixed if such a caller is introduced.  It wasn't a problem
+		 * previously because this code would always wait, but now that some
+		 * tuple locks do not conflict with one of the lock modes we use, it is
+		 * possible that this case is interesting to handle specially.
+		 *
+		 * This may cause failures with third-party code that calls heap_update
+		 * directly.
+		 */
 
 		/* must copy state data before unlocking buffer */
 		xwait = HeapTupleHeaderGetXmax(oldtup.t_data);
@@ -2549,20 +2602,26 @@ l2:
 		 */
 		if (!have_tuple_lock)
 		{
-			LockTuple(relation, &(oldtup.t_self), ExclusiveLock);
+			LockTuple(relation, &(oldtup.t_self),
+					  get_lockmode_for_tuplelock(tuplock));
 			have_tuple_lock = true;
 		}
 
 		/*
-		 * Sleep until concurrent transaction ends.  Note that we don't care
-		 * if the locker has an exclusive or shared lock, because we need
-		 * exclusive.
+		 * Now sleep on the locker.  Note that if there are only key-share
+		 * lockers and we're not updating the key columns, we will be awaken
+		 * before it is gone, so we may need to mark the new tuple with a
+		 * new MultiXactId including the original xmax and ourselves.
+		 *
+		 * XXX this comment needs to be more comprehensive
 		 */
-
 		if (infomask & HEAP_XMAX_IS_MULTI)
 		{
+			TransactionId	update_xact;
+			int				remain;
+
 			/* wait for multixact */
-			MultiXactIdWait((MultiXactId) xwait);
+			MultiXactIdWait((MultiXactId) xwait, mxact_status, &remain);
 			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 
 			/*
@@ -2576,41 +2635,98 @@ l2:
 				goto l2;
 
 			/*
-			 * You might think the multixact is necessarily done here, but not
-			 * so: it could have surviving members, namely our own xact or
-			 * other subxacts of this backend.	It is legal for us to update
-			 * the tuple in either case, however (the latter case is
-			 * essentially a situation of upgrading our former shared lock to
-			 * exclusive).	We don't bother changing the on-disk hint bits
-			 * since we are about to overwrite the xmax altogether.
+			 * Note that the multixact may not be done by now.  It could have
+			 * surviving members; our own xact or other subxacts of this
+			 * backend, and also any other concurrent transaction that locked
+			 * the tuple with KeyShare if we only got TupleLockUpdate.  If this
+			 * is the case, we have to be careful to mark the updated tuple
+			 * with the surviving members in Xmax.
+			 *
+			 * Note that there could have been another update in the MultiXact.
+			 * In that case, we need to check whether it committed or aborted.
+			 * If it aborted we are safe to update it again; otherwise there is
+			 * an update conflict that must be handled below.
+			 *
+			 * In the LockTupleKeyUpdate case, we still need to preserve the
+			 * surviving members: those would include the tuple locks we had
+			 * before this one, which are important to keep in case this
+			 * subxact aborts.
 			 */
+			update_xact = InvalidTransactionId;
+			if (!(oldtup.t_data->t_infomask & HEAP_XMAX_IS_NOT_UPDATE))
+				update_xact = HeapTupleGetUpdateXid(oldtup.t_data);
+
+			/* there was no UPDATE in the MultiXact; or it aborted. */
+			if (update_xact == InvalidTransactionId ||
+				TransactionIdDidAbort(update_xact))
+			{
+				/*
+				 * if the multixact still has live members, we need to preserve
+				 * it by creating a new multixact.  If all members are gone, we
+				 * can simply update the tuple by setting ourselves in Xmax.
+				 */
+				if (remain > 0)
+				{
+					keep_xmax = HeapTupleHeaderGetXmax(oldtup.t_data);
+					keep_xmax_multi =
+						(oldtup.t_data->t_infomask & HEAP_XMAX_IS_MULTI) != 0;
+				}
+				else
+				{
+					/*
+					 * We could set the HEAP_XMAX_INVALID bit here instead of
+					 * using a separate boolean flag.  However, since we're going
+					 * to set up a new xmax below, this would waste time
+					 * setting up the buffer's dirty bit.
+					 */
+					none_remain = false;
+				}
+			}
 		}
 		else
 		{
-			/* wait for regular transaction to end */
-			XactLockTableWait(xwait);
-			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
-
 			/*
-			 * xwait is done, but if xwait had just locked the tuple then some
-			 * other xact could update this tuple before we get to this point.
-			 * Check for xmax change, and start over if so.
+			 * If it's just a key-share locker, and we're not changing the
+			 * key columns, we don't need to wait for it to wait; but we
+			 * need to preserve it as locker.
 			 */
-			if ((oldtup.t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
-				!TransactionIdEquals(HeapTupleHeaderGetXmax(oldtup.t_data),
-									 xwait))
-				goto l2;
+			if ((oldtup.t_data->t_infomask & HEAP_XMAX_KEYSHR_LOCK) &&
+				key_intact)
+			{
+				LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+				keep_xmax = xwait;
+				keep_xmax_multi = false;
+			}
+			else
+			{
+				/* wait for regular transaction to end */
+				XactLockTableWait(xwait);
+				LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 
-			/* Otherwise check if it committed or aborted */
-			UpdateXmaxHintBits(oldtup.t_data, buffer, xwait);
+				/*
+				 * xwait is done, but if xwait had just locked the tuple then some
+				 * other xact could update this tuple before we get to this point.
+				 * Check for xmax change, and start over if so.
+				 */
+				if ((oldtup.t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
+					!TransactionIdEquals(HeapTupleHeaderGetXmax(oldtup.t_data),
+										 xwait))
+					goto l2;
+
+				/* Otherwise check if it committed or aborted */
+				UpdateXmaxHintBits(oldtup.t_data, buffer, xwait);
+			}
 		}
 
 		/*
 		 * We may overwrite if previous xmax aborted, or if it committed but
-		 * only locked the tuple without updating it.
+		 * only locked the tuple without updating it, or if we are going to
+		 * keep it around in Xmax.
 		 */
-		if (oldtup.t_data->t_infomask & (HEAP_XMAX_INVALID |
-										 HEAP_IS_LOCKED))
+		if (TransactionIdIsValid(keep_xmax) ||
+			none_remain ||
+			(oldtup.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+			HeapTupleHeaderIsLocked(oldtup.t_data))
 			result = HeapTupleMayBeUpdated;
 		else
 			result = HeapTupleUpdated;
@@ -2630,13 +2746,15 @@ l2:
 			   result == HeapTupleBeingUpdated);
 		Assert(!(oldtup.t_data->t_infomask & HEAP_XMAX_INVALID));
 		*ctid = oldtup.t_data->t_ctid;
-		*update_xmax = HeapTupleHeaderGetXmax(oldtup.t_data);
+		*update_xmax = HeapTupleHeaderGetUpdateXid(oldtup.t_data);
 		UnlockReleaseBuffer(buffer);
 		if (have_tuple_lock)
-			UnlockTuple(relation, &(oldtup.t_self), ExclusiveLock);
+			UnlockTuple(relation, &(oldtup.t_self),
+						get_lockmode_for_tuplelock(tuplock));
 		if (vmbuffer != InvalidBuffer)
 			ReleaseBuffer(vmbuffer);
 		bms_free(hot_attrs);
+		bms_free(key_attrs);
 		return result;
 	}
 
@@ -2645,7 +2763,7 @@ l2:
 	 * visible while we were busy locking the buffer, or during some subsequent
 	 * window during which we had it unlocked, we'll have to unlock and
 	 * re-lock, to avoid holding the buffer lock across an I/O.  That's a bit
-	 * unfortunate, esepecially since we'll now have to recheck whether the
+	 * unfortunate, especially since we'll now have to recheck whether the
 	 * tuple has been locked or updated under us, but hopefully it won't
 	 * happen very often.
 	 */
@@ -2678,13 +2796,54 @@ l2:
 		Assert(!(newtup->t_data->t_infomask & HEAP_HASOID));
 	}
 
+	/*
+	 * If the tuple we're updating is locked, we need to preserve this in the
+	 * new tuple's Xmax as well as in the old tuple.  Prepare the new xmax
+	 * value for these uses.
+	 *
+	 * Note there cannot be an xmax to save if we're changing key columns; in
+	 * this case, the wait above should have only returned when the locking
+	 * transactions finished.
+	 */
+	if (TransactionIdIsValid(keep_xmax))
+	{
+		if (keep_xmax_multi)
+		{
+			keep_xmax_old = MultiXactIdExpand(keep_xmax,
+											  xid, MultiXactStatusUpdate);
+			keep_xmax_infomask = HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_IS_MULTI;
+		}
+		else
+		{
+			/* not a multi? must be a KEY SHARE locker */
+			keep_xmax_old = MultiXactIdCreate(keep_xmax, MultiXactStatusForKeyShare,
+											  xid, MultiXactStatusUpdate);
+			keep_xmax_infomask = HEAP_XMAX_KEYSHR_LOCK;
+		}
+		keep_xmax_old_infomask = HEAP_XMAX_IS_MULTI | HEAP_XMAX_KEYSHR_LOCK;
+		/* FIXME -- need more infomask bits? */
+	}
+
+	/*
+	 * Prepare the new tuple with the appropriate initial values of Xmin and
+	 * Xmax, as well as initial infomask bits.
+	 */
 	newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
 	newtup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);
-	newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED);
+	newtup->t_data->t_infomask |= HEAP_UPDATED;
 	HeapTupleHeaderSetXmin(newtup->t_data, xid);
 	HeapTupleHeaderSetCmin(newtup->t_data, cid);
-	HeapTupleHeaderSetXmax(newtup->t_data, 0);	/* for cleanliness */
 	newtup->t_tableOid = RelationGetRelid(relation);
+	if (TransactionIdIsValid(keep_xmax))
+	{
+		newtup->t_data->t_infomask |= keep_xmax_infomask;
+		HeapTupleHeaderSetXmax(newtup->t_data, keep_xmax);
+	}
+	else
+	{
+		newtup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+		HeapTupleHeaderSetXmax(newtup->t_data, 0);	/* for cleanliness */
+	}
 
 	/*
 	 * Replace cid with a combo cid if necessary.  Note that we already put
@@ -2725,11 +2884,20 @@ l2:
 		oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
 									   HEAP_XMAX_INVALID |
 									   HEAP_XMAX_IS_MULTI |
-									   HEAP_IS_LOCKED |
+									   HEAP_LOCK_BITS |
 									   HEAP_MOVED);
+		oldtup.t_data->t_infomask2 &= ~HEAP_UPDATE_KEY_INTACT;
 		HeapTupleClearHotUpdated(&oldtup);
 		/* ... and store info about transaction updating this tuple */
-		HeapTupleHeaderSetXmax(oldtup.t_data, xid);
+		if (TransactionIdIsValid(keep_xmax_old))
+		{
+			HeapTupleHeaderSetXmax(oldtup.t_data, keep_xmax_old);
+			oldtup.t_data->t_infomask |= keep_xmax_old_infomask;
+		}
+		else
+			HeapTupleHeaderSetXmax(oldtup.t_data, xid);
+		if (key_intact)
+			oldtup.t_data->t_infomask2 |= HEAP_UPDATE_KEY_INTACT;
 		HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
 		/* temporarily make it look not-updated */
 		oldtup.t_data->t_ctid = oldtup.t_self;
@@ -2883,10 +3051,19 @@ l2:
 		oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
 									   HEAP_XMAX_INVALID |
 									   HEAP_XMAX_IS_MULTI |
-									   HEAP_IS_LOCKED |
+									   HEAP_LOCK_BITS |
 									   HEAP_MOVED);
+		oldtup.t_data->t_infomask2 &= ~HEAP_UPDATE_KEY_INTACT;
 		/* ... and store info about transaction updating this tuple */
-		HeapTupleHeaderSetXmax(oldtup.t_data, xid);
+		if (TransactionIdIsValid(keep_xmax_old))
+		{
+			HeapTupleHeaderSetXmax(oldtup.t_data, keep_xmax_old);
+			oldtup.t_data->t_infomask |= keep_xmax_old_infomask;
+		}
+		else
+			HeapTupleHeaderSetXmax(oldtup.t_data, xid);
+		if (key_intact)
+			oldtup.t_data->t_infomask2 |= HEAP_UPDATE_KEY_INTACT;
 		HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
 	}
 
@@ -2959,7 +3136,8 @@ l2:
 	 * Release the lmgr tuple lock, if we had it.
 	 */
 	if (have_tuple_lock)
-		UnlockTuple(relation, &(oldtup.t_self), ExclusiveLock);
+		UnlockTuple(relation, &(oldtup.t_self),
+					get_lockmode_for_tuplelock(tuplock));
 
 	pgstat_count_heap_update(relation, use_hot_update);
 
@@ -2974,6 +3152,7 @@ l2:
 	}
 
 	bms_free(hot_attrs);
+	bms_free(key_attrs);
 
 	return HeapTupleMayBeUpdated;
 }
@@ -3129,6 +3308,54 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
 }
 
 /*
+ * Return the appropriate LOCKMODE to acquire by LockTuple corresponding to the
+ * given lock tuple mode.
+ *
+ * These heavyweight lock modes have been chosen because they exactly mimic
+ * the lock conflict behavior that our tuple lock modes need to have.
+ */
+static LOCKMODE
+get_lockmode_for_tuplelock(LockTupleMode mode)
+{
+	switch (mode)
+	{
+		case LockTupleKeyShare:
+			return AccessShareLock;
+		case LockTupleShare:
+			return RowShareLock;
+		case LockTupleUpdate:
+			return ExclusiveLock;
+		case LockTupleKeyUpdate:
+			return AccessExclusiveLock;
+		default:
+			elog(ERROR, "invalid lock tuple mode %d", mode);
+			return 0;	/* keep compiler quiet */
+	}
+}
+
+/*
+ * Return the MultiXactStatus corresponding to the given tuple lock mode.
+ */
+static MultiXactStatus
+get_mxact_status_for_tuplelock(LockTupleMode mode)
+{
+	switch (mode)
+	{
+		case LockTupleKeyShare:
+			return MultiXactStatusForKeyShare;
+		case LockTupleShare:
+			return MultiXactStatusForShare;
+		case LockTupleUpdate:
+			return MultiXactStatusForUpdate;
+		case LockTupleKeyUpdate:
+			return MultiXactStatusUpdate;
+		default:
+			elog(ERROR, "invalid lock tuple mode %d", mode);
+			return 0;	/* keep compiler quiet */
+	}
+}
+
+/*
  *	heap_lock_tuple - lock a tuple in shared or exclusive mode
  *
  * Note that this acquires a buffer pin, which the caller must release.
@@ -3152,10 +3379,11 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
  *	HeapTupleSelfUpdated: lock failed because tuple updated by self
  *	HeapTupleUpdated: lock failed because tuple updated by other xact
  *
- * In the failure cases, the routine returns the tuple's t_ctid and t_xmax.
+ * In the failure cases, the routine returns the tuple's t_ctid and the
+ * updating Xid (resolving a possible MultiXact, if necessary).
  * If t_ctid is the same as t_self, the tuple was deleted; if different, the
  * tuple was updated, and t_ctid is the location of the replacement tuple.
- * (t_xmax is needed to verify that the replacement tuple matches.)
+ * (xmax is needed to verify that the replacement tuple matches.)
  *
  *
  * NOTES: because the shared-memory lock table is of finite size, but users
@@ -3201,13 +3429,13 @@ heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer *buffer,
 	Page		page;
 	TransactionId xid;
 	TransactionId xmax;
+	TransactionId keep_xmax = InvalidTransactionId;
+	bool		keep_xmax_multi = false;
+	bool		none_remains = false;
 	uint16		old_infomask;
 	uint16		new_infomask;
-	LOCKMODE	tuple_lock_type;
 	bool		have_tuple_lock = false;
 
-	tuple_lock_type = (mode == LockTupleShared) ? ShareLock : ExclusiveLock;
-
 	*buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
 	LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
 
@@ -3220,6 +3448,9 @@ heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer *buffer,
 	tuple->t_tableOid = RelationGetRelid(relation);
 
 l3:
+	/* shouldn't get back here if we already set keep_xmax */
+	Assert(keep_xmax == InvalidTransactionId);
+
 	result = HeapTupleSatisfiesUpdate(tuple->t_data, cid, *buffer);
 
 	if (result == HeapTupleInvisible)
@@ -3231,30 +3462,70 @@ l3:
 	{
 		TransactionId xwait;
 		uint16		infomask;
+		uint16		infomask2;
+		bool		require_sleep;
 
 		/* must copy state data before unlocking buffer */
 		xwait = HeapTupleHeaderGetXmax(tuple->t_data);
 		infomask = tuple->t_data->t_infomask;
+		infomask2 = tuple->t_data->t_infomask2;
 
 		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
 
 		/*
-		 * If we wish to acquire share lock, and the tuple is already
-		 * share-locked by a multixact that includes any subtransaction of the
-		 * current top transaction, then we effectively hold the desired lock
-		 * already.  We *must* succeed without trying to take the tuple lock,
-		 * else we will deadlock against anyone waiting to acquire exclusive
-		 * lock.  We don't need to make any state changes in this case.
+		 * If we wish to acquire share or key lock, and the tuple is already
+		 * key or share locked by a multixact that includes any subtransaction
+		 * of the current top transaction, then we effectively hold the desired
+		 * lock already (except if we own key share lock and now desire share
+		 * lock).  We *must* succeed without trying to take the tuple lock,
+		 * else we will deadlock against anyone wanting to acquire a stronger
+		 * lock.
+		 *
+		 * FIXME -- we don't do the below currently, but I think we should:
+		 *
+		 * We update the Xmax with a new MultiXactId to include the new lock
+		 * mode in this case.
+		 *
+		 * Note that since we want to alter the Xmax, we need to re-acquire the
+		 * buffer lock.  The xmax could have changed in the meantime, so we
+		 * recheck it in that case, but we keep the buffer lock while doing it
+		 * to prevent starvation.  The second time around we know we must be
+		 * part of the MultiXactId in any case, which is why we don't need to
+		 * go back to recheck HeapTupleSatisfiesUpdate.  Also, after we
+		 * re-acquire lock, the MultiXact is likely to (but not necessarily) be
+		 * the same that we see here, so it should be in multixact's cache and
+		 * thus quick to obtain.
 		 */
-		if (mode == LockTupleShared &&
-			(infomask & HEAP_XMAX_IS_MULTI) &&
-			MultiXactIdIsCurrent((MultiXactId) xwait))
+		if ((infomask & HEAP_XMAX_IS_MULTI) &&
+			((mode == LockTupleShare) || (mode == LockTupleKeyShare)))
 		{
-			Assert(infomask & HEAP_XMAX_SHARED_LOCK);
-			/* Probably can't hold tuple lock here, but may as well check */
-			if (have_tuple_lock)
-				UnlockTuple(relation, tid, tuple_lock_type);
-			return HeapTupleMayBeUpdated;
+			int		i;
+			int		nmembers;
+			MultiXactMember *members;
+
+			nmembers = GetMultiXactIdMembers(xwait, &members);
+
+			for (i = 0; i < nmembers; i++)
+			{
+				if (TransactionIdIsCurrentTransactionId(members[i].xid))
+				{
+					if ((mode == LockTupleKeyShare) ||
+						((mode == LockTupleShare) &&
+						 (members[i].status >= MultiXactStatusForShare)))
+					{
+						if (have_tuple_lock)
+							UnlockTuple(relation, tid, get_lockmode_for_tuplelock(mode));
+						/*
+						 * FIXME -- here we should lock buffer, update xmax,
+						 * release buffer
+						 */
+						pfree(members);
+						return HeapTupleMayBeUpdated;
+					}
+				}
+			}
+
+			pfree(members);
 		}
 
 		/*
@@ -3270,105 +3541,240 @@ l3:
 		{
 			if (nowait)
 			{
-				if (!ConditionalLockTuple(relation, tid, tuple_lock_type))
+				if (!ConditionalLockTuple(relation, tid, get_lockmode_for_tuplelock(mode)))
 					ereport(ERROR,
 							(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
 					errmsg("could not obtain lock on row in relation \"%s\"",
 						   RelationGetRelationName(relation))));
 			}
 			else
-				LockTuple(relation, tid, tuple_lock_type);
+				LockTuple(relation, tid, get_lockmode_for_tuplelock(mode));
 			have_tuple_lock = true;
 		}
 
-		if (mode == LockTupleShared && (infomask & HEAP_XMAX_SHARED_LOCK))
+		/*
+		 * If we're requesting KeyShare, and there's no update present, we
+		 * don't need to wait for locking transaction(s) to finish.  Even if
+		 * there is an update, we can still continue if the key hasn't been
+		 * modified.
+		 */
+		require_sleep = true;
+		if ((mode == LockTupleKeyShare) &&
+			(HeapTupleHeaderInfomaskIsLocked(infomask) ||
+			 infomask2 & HEAP_UPDATE_KEY_INTACT))
 		{
-			/*
-			 * Acquiring sharelock when there's at least one sharelocker
-			 * already.  We need not wait for him/them to complete.
-			 */
 			LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
 
 			/*
-			 * Make sure it's still a shared lock, else start over.  (It's OK
-			 * if the ownership of the shared lock has changed, though.)
+			 * Make sure it's still an appropriate lock, else start over.
 			 */
-			if (!(tuple->t_data->t_infomask & HEAP_XMAX_SHARED_LOCK))
+			if (!(HeapTupleHeaderIsLocked(tuple->t_data) ||
+				  (tuple->t_data->t_infomask2 & HEAP_UPDATE_KEY_INTACT)))
 				goto l3;
+			require_sleep = false;
+			/* acquire fresh values -- XXX do we need to restart if xmax changed? */
+			keep_xmax = HeapTupleHeaderGetXmax(tuple->t_data);
+			keep_xmax_multi = (tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) != 0;
 		}
-		else if (infomask & HEAP_XMAX_IS_MULTI)
-		{
-			/* wait for multixact to end */
-			if (nowait)
-			{
-				if (!ConditionalMultiXactIdWait((MultiXactId) xwait))
-					ereport(ERROR,
-							(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
-					errmsg("could not obtain lock on row in relation \"%s\"",
-						   RelationGetRelationName(relation))));
-			}
-			else
-				MultiXactIdWait((MultiXactId) xwait);
 
+		/*
+		 * If we're requesting Share, we need to ensure there's no update
+		 * and no exclusive lock present.
+		 */
+		if (mode == LockTupleShare &&
+			(infomask & (HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_IS_NOT_UPDATE)) &&
+			!(infomask & HEAP_XMAX_EXCL_LOCK))
+		{
 			LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
 
 			/*
-			 * If xwait had just locked the tuple then some other xact could
-			 * update this tuple before we get to this point. Check for xmax
-			 * change, and start over if so.
+			 * make sure it's still an appropriate lock, else start over.
 			 */
-			if (!(tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
-				!TransactionIdEquals(HeapTupleHeaderGetXmax(tuple->t_data),
-									 xwait))
+			if (!(tuple->t_data->t_infomask &
+				  (HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_IS_NOT_UPDATE)) ||
+				(tuple->t_data->t_infomask & HEAP_XMAX_EXCL_LOCK))
 				goto l3;
+			require_sleep = false;
+			/* acquire fresh values */
+			keep_xmax = HeapTupleHeaderGetXmax(tuple->t_data);
+			keep_xmax_multi = (tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) != 0;
+		}
 
-			/*
-			 * You might think the multixact is necessarily done here, but not
-			 * so: it could have surviving members, namely our own xact or
-			 * other subxacts of this backend.	It is legal for us to lock the
-			 * tuple in either case, however.  We don't bother changing the
-			 * on-disk hint bits since we are about to overwrite the xmax
-			 * altogether.
-			 */
+
+		/*
+		 * If our lock is Update, we might also be able to skip the sleep; for
+		 * this to be true, we need to ensure that there's no other lock type
+		 * than KeyShare.
+		 */
+		if (mode == LockTupleUpdate)
+		{
+			if (infomask & HEAP_XMAX_IS_MULTI)
+			{
+				int		nmembers;
+				MultiXactMember *members;
+
+				/*
+				 * This needs to be done the slow way: there might be
+				 * MultiXactStatusForShare locks hiding in there, and there's
+				 * no way to tell from just the hint bits.
+				 */
+				nmembers = GetMultiXactIdMembers(xwait, &members);
+				if (nmembers == 0)
+				{
+					require_sleep = false;
+					/*
+					 * No need to keep the previous xmax here. Unlikely to
+					 * happen anyway.
+					 */
+				}
+				else
+				{
+					int		i;
+					bool	allowed = true;
+
+					for (i = 0; i < nmembers; i++)
+					{
+						if (members[i].status != MultiXactStatusForKeyShare)
+						{
+							allowed = false;
+							break;
+						}
+					}
+					if (allowed)
+					{
+						/*
+						 * if the xmax changed under us in the meantime, start
+						 * over.
+						 */
+						LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+						if (!(tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
+							!TransactionIdEquals(HeapTupleHeaderGetXmax(tuple->t_data),
+												 xwait))
+							goto l3;
+						/* otherwise, we're good */
+						require_sleep = false;
+						keep_xmax = xwait;
+						keep_xmax_multi = true;
+					}
+				}
+			}
+			else if (infomask & HEAP_XMAX_KEYSHR_LOCK)
+			{
+				LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+
+				/* if the xmax changed in the meantime, start over */
+				if ((tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
+					!TransactionIdEquals(HeapTupleHeaderGetXmax(tuple->t_data),
+										 xwait))
+					goto l3;
+				/* otherwise, we're good */
+				require_sleep = false;
+				keep_xmax = xwait;
+				keep_xmax_multi = false;
+			}
 		}
-		else
+
+		/*
+		 * By here, we either require to wait for the locking transaction or
+		 * multixact, or have already acquired the buffer exclusive lock.
+		 */
+
+		if (require_sleep)
 		{
-			/* wait for regular transaction to end */
-			if (nowait)
+			if (infomask & HEAP_XMAX_IS_MULTI)
 			{
-				if (!ConditionalXactLockTableWait(xwait))
-					ereport(ERROR,
-							(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
-					errmsg("could not obtain lock on row in relation \"%s\"",
-						   RelationGetRelationName(relation))));
+				MultiXactStatus status = get_mxact_status_for_tuplelock(mode);
+				int		remain;
+
+				/* We only ever lock tuples, never update them */
+				if (status >= MultiXactStatusUpdate)
+					elog(ERROR, "invalid lock mode in heap_tuple_lock");
+
+				/* wait for multixact to end */
+				if (nowait)
+				{
+					if (!ConditionalMultiXactIdWait((MultiXactId) xwait, status, &remain))
+						ereport(ERROR,
+								(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+								 errmsg("could not obtain lock on row in relation \"%s\"",
+										RelationGetRelationName(relation))));
+				}
+				else
+					MultiXactIdWait((MultiXactId) xwait, status, &remain);
+
+				LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+
+				/*
+				 * If xwait had just locked the tuple then some other xact could
+				 * update this tuple before we get to this point. Check for xmax
+				 * change, and start over if so.
+				 */
+				if (!(tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
+					!TransactionIdEquals(HeapTupleHeaderGetXmax(tuple->t_data),
+										 xwait))
+					goto l3;
+
+				/*
+				 * Of course, the multixact might not be done here: if we're requesting
+				 * a light lock mode, other transactions with light locks could still
+				 * be alive, as well as locks owned by our own xact or other
+				 * subxacts of this backend.  We need to preserve the surviving
+				 * MultiXact members.  Note that it isn't absolutely necessary
+				 * in the latter case, but doing so is simpler.
+				 */
+				if (remain > 0)
+				{
+					keep_xmax = xwait;
+					keep_xmax_multi = true;
+				}
+				else
+					none_remains = true;
 			}
 			else
-				XactLockTableWait(xwait);
+			{
+				/* wait for regular transaction to end */
+				if (nowait)
+				{
+					if (!ConditionalXactLockTableWait(xwait))
+						ereport(ERROR,
+								(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+								 errmsg("could not obtain lock on row in relation \"%s\"",
+										RelationGetRelationName(relation))));
+				}
+				else
+					XactLockTableWait(xwait);
 
-			LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+				LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
 
-			/*
-			 * xwait is done, but if xwait had just locked the tuple then some
-			 * other xact could update this tuple before we get to this point.
-			 * Check for xmax change, and start over if so.
-			 */
-			if ((tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
-				!TransactionIdEquals(HeapTupleHeaderGetXmax(tuple->t_data),
-									 xwait))
-				goto l3;
+				/*
+				 * xwait is done, but if xwait had just locked the tuple then
+				 * some other xact could update this tuple before we get to
+				 * this point.  Check for xmax change, and start over if so.
+				 */
+				if ((tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
+					!TransactionIdEquals(HeapTupleHeaderGetXmax(tuple->t_data),
+										 xwait))
+					goto l3;
 
-			/* Otherwise check if it committed or aborted */
-			UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
+				/*
+				 * Otherwise check if it committed or aborted.  Note we cannot
+				 * be here if the tuple was only locked by somebody who didn't
+				 * conflict with us; that should have been handled above.  So
+				 * that transaction must necessarily be gone by now.
+				 */
+				UpdateXmaxHintBits(tuple->t_data, *buffer, xwait);
+			}
 		}
 
 		/*
 		 * We may lock if previous xmax aborted, or if it committed but only
-		 * locked the tuple without updating it.  The case where we didn't
-		 * wait because we are joining an existing shared lock is correctly
-		 * handled, too.
+		 * locked the tuple without updating it; or if we didn't have to wait
+		 * at all for whatever reason.
 		 */
-		if (tuple->t_data->t_infomask & (HEAP_XMAX_INVALID |
-										 HEAP_IS_LOCKED))
+		if (!require_sleep ||
+			(tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
+			HeapTupleHeaderIsLocked(tuple->t_data) ||
+			none_remains)
 			result = HeapTupleMayBeUpdated;
 		else
 			result = HeapTupleUpdated;
@@ -3379,10 +3785,10 @@ l3:
 		Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated);
 		Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
 		*ctid = tuple->t_data->t_ctid;
-		*update_xmax = HeapTupleHeaderGetXmax(tuple->t_data);
+		*update_xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
 		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
 		if (have_tuple_lock)
-			UnlockTuple(relation, tid, tuple_lock_type);
+			UnlockTuple(relation, tid, get_lockmode_for_tuplelock(mode));
 		return result;
 	}
 
@@ -3394,8 +3800,10 @@ l3:
 	 * for cases where it is a plain TransactionId.
 	 *
 	 * Note in particular that this covers the case where we already hold
-	 * exclusive lock on the tuple and the caller only wants shared lock. It
-	 * would certainly not do to give up the exclusive lock.
+	 * exclusive lock on the tuple and the caller only wants key share or share
+	 * lock. It would certainly not do to give up the exclusive lock.  Note
+	 * there's no explicit test for a share lock only; this was already covered
+	 * above, because it's only representable by a MultiXactId.
 	 */
 	xmax = HeapTupleHeaderGetXmax(tuple->t_data);
 	old_infomask = tuple->t_data->t_infomask;
@@ -3403,15 +3811,15 @@ l3:
 	if (!(old_infomask & (HEAP_XMAX_INVALID |
 						  HEAP_XMAX_COMMITTED |
 						  HEAP_XMAX_IS_MULTI)) &&
-		(mode == LockTupleShared ?
-		 (old_infomask & HEAP_IS_LOCKED) :
-		 (old_infomask & HEAP_XMAX_EXCL_LOCK)) &&
-		TransactionIdIsCurrentTransactionId(xmax))
+		(mode == LockTupleKeyShare ?
+		 (old_infomask & (HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_EXCL_LOCK)) :
+		 (old_infomask & HEAP_XMAX_EXCL_LOCK) &&
+		TransactionIdIsCurrentTransactionId(xmax)))
 	{
 		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
 		/* Probably can't hold tuple lock here, but may as well check */
 		if (have_tuple_lock)
-			UnlockTuple(relation, tid, tuple_lock_type);
+			UnlockTuple(relation, tid, get_lockmode_for_tuplelock(mode));
 		return HeapTupleMayBeUpdated;
 	}
 
@@ -3425,22 +3833,69 @@ l3:
 	new_infomask = old_infomask & ~(HEAP_XMAX_COMMITTED |
 									HEAP_XMAX_INVALID |
 									HEAP_XMAX_IS_MULTI |
-									HEAP_IS_LOCKED |
+									HEAP_LOCK_BITS |
 									HEAP_MOVED);
 
-	if (mode == LockTupleShared)
+	/*
+	 * if we have keep_xmax, this is easy to compute -- just create a new mxact
+	 * including our new xid plus whatever there was on Xmax previously.
+	 */
+	if (TransactionIdIsValid(keep_xmax))
 	{
-		/*
-		 * If this is the first acquisition of a shared lock in the current
-		 * transaction, set my per-backend OldestMemberMXactId setting. We can
-		 * be certain that the transaction will never become a member of any
-		 * older MultiXactIds than that.  (We have to do this even if we end
-		 * up just using our own TransactionId below, since some other backend
-		 * could incorporate our XID into a MultiXact immediately afterwards.)
-		 */
-		MultiXactIdSetOldestMember();
+		if (keep_xmax_multi)
+		{
+			/*
+			 * MultiXactIdExpand takes care to remove members that are no
+			 * longer current.
+			 */
+			xid = MultiXactIdExpand((MultiXactId) keep_xmax, xid,
+									get_mxact_status_for_tuplelock(mode));
+			new_infomask |= GetMultiXactIdHintBits(xid);
+		}
+		else if (TransactionIdIsInProgress(keep_xmax))
+		{
+			MultiXactStatus		existing_lock_mode;
 
-		new_infomask |= HEAP_XMAX_SHARED_LOCK;
+			if (old_infomask & HEAP_XMAX_EXCL_LOCK)
+				existing_lock_mode = MultiXactStatusForUpdate;
+			else if (old_infomask & HEAP_XMAX_KEYSHR_LOCK)
+				existing_lock_mode = MultiXactStatusForKeyShare;
+			else
+				/* must be a shared lock */
+				existing_lock_mode = MultiXactStatusForShare;
+
+			xid = MultiXactIdCreate(keep_xmax, existing_lock_mode,
+									xid, get_mxact_status_for_tuplelock(mode));
+			new_infomask |= GetMultiXactIdHintBits(xid);
+		}
+		else
+		{
+			/*
+			 * Not multi, not in progress.  Use only our own Xid.
+			 */
+			switch (mode)
+			{
+				case LockTupleKeyShare:
+					new_infomask |= HEAP_XMAX_KEYSHR_LOCK;
+					break;
+				case LockTupleShare:
+					/* need a multixact here in any case */
+					xid = MultiXactIdCreateSingleton(xid, MultiXactStatusForShare);
+					new_infomask |= GetMultiXactIdHintBits(xid);
+					break;
+				case LockTupleUpdate:
+					new_infomask |= HEAP_XMAX_EXCL_LOCK;
+					break;
+				default:
+					elog(ERROR, "invalid lock mode");
+			}
+		}
+	}
+	else
+	{
+		MultiXactStatus		new_mxact_status;
+
+		new_mxact_status = get_mxact_status_for_tuplelock(mode);
 
 		/*
 		 * Check to see if we need a MultiXactId because there are multiple
@@ -3465,8 +3920,9 @@ l3:
 				 * If the XMAX is already a MultiXactId, then we need to
 				 * expand it to include our own TransactionId.
 				 */
-				xid = MultiXactIdExpand((MultiXactId) xmax, xid);
-				new_infomask |= HEAP_XMAX_IS_MULTI;
+				xid = MultiXactIdExpand((MultiXactId) xmax, xid, new_mxact_status);
+				new_infomask |= GetMultiXactIdHintBits(xid);
+				/* FIXME -- we need to add bits to the infomask here! */
 			}
 			else if (TransactionIdIsInProgress(xmax))
 			{
@@ -3475,8 +3931,30 @@ l3:
 				 * create a new MultiXactId that includes both the old locker
 				 * and our own TransactionId.
 				 */
-				xid = MultiXactIdCreate(xmax, xid);
-				new_infomask |= HEAP_XMAX_IS_MULTI;
+				MultiXactStatus status;
+
+				if (old_infomask & HEAP_XMAX_EXCL_LOCK)
+					status = MultiXactStatusForUpdate;
+				else if (old_infomask & HEAP_XMAX_KEYSHR_LOCK)
+					status = MultiXactStatusForKeyShare;
+				else
+				{
+					status = 0;		/* keep compiler quiet */
+					elog(ERROR, "no lock bit found on old infomask %u", old_infomask);
+				}
+
+				xid = MultiXactIdCreate(xmax, status, xid, new_mxact_status);
+				new_infomask |= GetMultiXactIdHintBits(xid);
+				/* FIXME -- we need to add bits to the infomask here! */
+			}
+			else if (mode == LockTupleShare)
+			{
+				/*
+				 * There's no hint bit for FOR SHARE, so we need a multixact
+				 * here no matter what.
+				 */
+				xid = MultiXactIdCreateSingleton(xid, new_mxact_status);
+				new_infomask |= GetMultiXactIdHintBits(xid);
 			}
 			else
 			{
@@ -3486,6 +3964,22 @@ l3:
 				 * TransactionIdIsInProgress() got to run.	Treat it like
 				 * there's no locker in the tuple.
 				 */
+				switch (mode)
+				{
+					case LockTupleKeyShare:
+						new_infomask |= HEAP_XMAX_KEYSHR_LOCK;
+						break;
+					case LockTupleShare:
+						/* need a multixact here in any case */
+						xid = MultiXactIdCreateSingleton(xid, MultiXactStatusForShare);
+						new_infomask |= GetMultiXactIdHintBits(xid);
+						break;
+					case LockTupleUpdate:
+						new_infomask |= HEAP_XMAX_EXCL_LOCK;
+						break;
+					default:
+						elog(ERROR, "invalid lock mode");
+				}
 			}
 		}
 		else
@@ -3494,13 +3988,24 @@ l3:
 			 * There was no previous locker, so just insert our own
 			 * TransactionId.
 			 */
+			switch (mode)
+			{
+				case LockTupleKeyShare:
+					new_infomask |= HEAP_XMAX_KEYSHR_LOCK;
+					break;
+				case LockTupleShare:
+					/* need a multixact here in any case */
+					xid = MultiXactIdCreateSingleton(xid, MultiXactStatusForShare);
+					new_infomask |= GetMultiXactIdHintBits(xid);
+					break;
+				case LockTupleUpdate:
+					new_infomask |= HEAP_XMAX_EXCL_LOCK;
+					break;
+				default:
+					elog(ERROR, "invalid lock mode");
+			}
 		}
 	}
-	else
-	{
-		/* We want an exclusive lock on the tuple */
-		new_infomask |= HEAP_XMAX_EXCL_LOCK;
-	}
 
 	START_CRIT_SECTION();
 
@@ -3508,12 +4013,14 @@ l3:
 	 * Store transaction information of xact locking the tuple.
 	 *
 	 * Note: Cmax is meaningless in this context, so don't set it; this avoids
-	 * possibly generating a useless combo CID.
+	 * possibly generating a useless combo CID.  FIXME -- it's not useless
+	 * if a multixact contains an update.
 	 */
 	tuple->t_data->t_infomask = new_infomask;
 	HeapTupleHeaderClearHotUpdated(tuple->t_data);
 	HeapTupleHeaderSetXmax(tuple->t_data, xid);
 	/* Make sure there is no forward chain link in t_ctid */
+	/* FIXME -- this needs some thought */
 	tuple->t_data->t_ctid = *tid;
 
 	MarkBufferDirty(*buffer);
@@ -3539,8 +4046,17 @@ l3:
 		xlrec.target.node = relation->rd_node;
 		xlrec.target.tid = tuple->t_self;
 		xlrec.locking_xid = xid;
-		xlrec.xid_is_mxact = ((new_infomask & HEAP_XMAX_IS_MULTI) != 0);
-		xlrec.shared_lock = (mode == LockTupleShared);
+		xlrec.infobits_set =
+			(((new_infomask & HEAP_XMAX_IS_MULTI) != 0) ?
+			 XLHL_XMAX_IS_MULTI : 0) |
+			(((new_infomask & HEAP_XMAX_IS_NOT_UPDATE) != 0) ?
+			 XLHL_XMAX_IS_NOT_UPDATE : 0) |
+			(((new_infomask & HEAP_XMAX_EXCL_LOCK) != 0) ?
+			 XLHL_XMAX_EXCL_LOCK : 0) |
+			(((new_infomask & HEAP_XMAX_KEYSHR_LOCK) != 0) ?
+			 XLHL_XMAX_KEYSHR_LOCK : 0) |
+			(((tuple->t_data->t_infomask2 & HEAP_UPDATE_KEY_INTACT) != 0) ?
+			 XLHL_XMAX_KEYSHR_LOCK : 0);
 		rdata[0].data = (char *) &xlrec;
 		rdata[0].len = SizeOfHeapLock;
 		rdata[0].buffer = InvalidBuffer;
@@ -3572,7 +4088,7 @@ l3:
 	 * release the lmgr tuple lock, if we had it.
 	 */
 	if (have_tuple_lock)
-		UnlockTuple(relation, tid, tuple_lock_type);
+		UnlockTuple(relation, tid, get_lockmode_for_tuplelock(mode));
 
 	return HeapTupleMayBeUpdated;
 }
@@ -3789,6 +4305,8 @@ recheck_xmax:
 		 * extremely low-probability scenario with minimal downside even if
 		 * it does happen, so for now we don't do the extra bookkeeping that
 		 * would be needed to clean out MultiXactIds.
+		 *
+		 * FIXME -- today is that day.  Figure this out.
 		 *----------
 		 */
 	}
@@ -3841,6 +4359,105 @@ recheck_xvac:
 	return changed;
 }
 
+/*
+ * For a given MultiXactId, return the hint bits that should be set in the
+ * tuple's infomask.
+ *
+ * Normally this should be called for a multixact that was just created, and
+ * so is on our local cache, so the GetMembers call is fast.
+ */
+static uint16
+GetMultiXactIdHintBits(MultiXactId multi)
+{
+	int		nmembers;
+	MultiXactMember	*members;
+	int		i;
+	uint16	bits = HEAP_XMAX_IS_MULTI;
+	bool	has_update = false;
+
+	nmembers = GetMultiXactIdMembers(multi, &members);
+
+	for (i = 0; i < nmembers; i++)
+	{
+		Assert(members[i].status != MultiXactStatusKeyUpdate);
+		switch (members[i].status)
+		{
+			case MultiXactStatusForKeyShare:
+				bits |= HEAP_XMAX_KEYSHR_LOCK;
+				break;
+			case MultiXactStatusForShare:
+				break;
+			case MultiXactStatusForUpdate:
+				Assert(!has_update);
+				bits |= HEAP_XMAX_EXCL_LOCK;
+				break;
+			case MultiXactStatusUpdate:
+				Assert(!(bits & HEAP_XMAX_EXCL_LOCK));
+				has_update = true;
+				break;
+			case MultiXactStatusKeyUpdate:
+				elog(ERROR, "invalid multixact value");
+				break;
+		}
+	}
+	if (!has_update)
+		bits |= HEAP_XMAX_IS_NOT_UPDATE;
+
+	return bits;
+}
+
+/*
+ * HeapTupleGetUpdateXid
+ *
+ * Given a tuple with a multixact Xmax, and which does not have the
+ * HEAP_XMAX_IS_NOT_UPDATE bit set, obtain and return the Xid of the updating
+ * transaction.
+ *
+ * See also HeapTupleHeaderGetUpdateXid, which can be used without previously
+ * checking the hint bits.
+ */
+TransactionId
+HeapTupleGetUpdateXid(HeapTupleHeader tuple)
+{
+	TransactionId	update_xact = InvalidTransactionId;
+	MultiXactMember	*members;
+	int				nmembers;
+
+	Assert(!(tuple->t_infomask & HEAP_XMAX_IS_NOT_UPDATE));
+	Assert(tuple->t_infomask & HEAP_XMAX_IS_MULTI);
+
+	nmembers = GetMultiXactIdMembers(HeapTupleHeaderGetXmax(tuple), &members);
+
+	if (nmembers > 0)
+	{
+		int		i;
+
+		for (i = 0; i < nmembers; i++)
+		{
+			/* KEY SHARE lockers are okay -- ignore it */
+			if (members[i].status == MultiXactStatusForKeyShare)
+				continue;
+			/*
+			 * SHARE lockers are okay, though since they normally conflict with
+			 * UPDATE, they are not expected unless they come from the same
+			 * xact as the update.
+			 */
+			if (members[i].status == MultiXactStatusForShare ||
+				members[i].status == MultiXactStatusForUpdate)
+				continue;
+			/* there should be at most one updater */
+			Assert(update_xact == InvalidTransactionId);
+			Assert(members[i].status == MultiXactStatusUpdate);
+			update_xact = members[i].xid;
+#ifndef USE_ASSERT_CHECKING
+			break;
+#endif
+		}
+	}
+
+	return update_xact;
+}
+
 
 /* ----------------
  *		heap_markpos	- mark scan position
@@ -3919,6 +4536,7 @@ HeapTupleHeaderAdvanceLatestRemovedXid(HeapTupleHeader tuple,
 									   TransactionId *latestRemovedXid)
 {
 	TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
+	/* FIXME -- change this? */
 	TransactionId xmax = HeapTupleHeaderGetXmax(tuple);
 	TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
 
@@ -4606,7 +5224,7 @@ heap_xlog_delete(XLogRecPtr lsn, XLogRecord *record)
 	htup->t_infomask &= ~(HEAP_XMAX_COMMITTED |
 						  HEAP_XMAX_INVALID |
 						  HEAP_XMAX_IS_MULTI |
-						  HEAP_IS_LOCKED |
+						  HEAP_LOCK_BITS |
 						  HEAP_MOVED);
 	HeapTupleHeaderClearHotUpdated(htup);
 	HeapTupleHeaderSetXmax(htup, record->xl_xid);
@@ -4813,7 +5431,7 @@ heap_xlog_update(XLogRecPtr lsn, XLogRecord *record, bool hot_update)
 	htup->t_infomask &= ~(HEAP_XMAX_COMMITTED |
 						  HEAP_XMAX_INVALID |
 						  HEAP_XMAX_IS_MULTI |
-						  HEAP_IS_LOCKED |
+						  HEAP_LOCK_BITS |
 						  HEAP_MOVED);
 	if (hot_update)
 		HeapTupleHeaderSetHotUpdated(htup);
@@ -4991,14 +5609,18 @@ heap_xlog_lock(XLogRecPtr lsn, XLogRecord *record)
 	htup->t_infomask &= ~(HEAP_XMAX_COMMITTED |
 						  HEAP_XMAX_INVALID |
 						  HEAP_XMAX_IS_MULTI |
-						  HEAP_IS_LOCKED |
+						  HEAP_LOCK_BITS |
 						  HEAP_MOVED);
-	if (xlrec->xid_is_mxact)
+	if (xlrec->infobits_set & XLHL_XMAX_IS_MULTI)
 		htup->t_infomask |= HEAP_XMAX_IS_MULTI;
-	if (xlrec->shared_lock)
-		htup->t_infomask |= HEAP_XMAX_SHARED_LOCK;
-	else
+	if (xlrec->infobits_set & XLHL_XMAX_IS_NOT_UPDATE)
+		htup->t_infomask |= HEAP_XMAX_IS_NOT_UPDATE;
+	if (xlrec->infobits_set & XLHL_XMAX_EXCL_LOCK)
 		htup->t_infomask |= HEAP_XMAX_EXCL_LOCK;
+	if (xlrec->infobits_set & XLHL_XMAX_KEYSHR_LOCK)
+		htup->t_infomask |= HEAP_XMAX_KEYSHR_LOCK;
+	if (xlrec->infobits_set & XLHL_UPDATE_KEY_INTACT)
+		htup->t_infomask2 |= HEAP_UPDATE_KEY_INTACT;
 	HeapTupleHeaderClearHotUpdated(htup);
 	HeapTupleHeaderSetXmax(htup, xlrec->locking_xid);
 	HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
@@ -5202,16 +5824,19 @@ heap_desc(StringInfo buf, uint8 xl_info, char *rec)
 	{
 		xl_heap_lock *xlrec = (xl_heap_lock *) rec;
 
-		if (xlrec->shared_lock)
-			appendStringInfo(buf, "shared_lock: ");
-		else
-			appendStringInfo(buf, "exclusive_lock: ");
-		if (xlrec->xid_is_mxact)
-			appendStringInfo(buf, "mxid ");
-		else
-			appendStringInfo(buf, "xid ");
-		appendStringInfo(buf, "%u ", xlrec->locking_xid);
+		appendStringInfo(buf, "lock %u: ", xlrec->locking_xid);
 		out_target(buf, &(xlrec->target));
+		appendStringInfoChar(buf, ' ');
+		if (xlrec->infobits_set & XLHL_XMAX_IS_MULTI)
+			appendStringInfo(buf, "XMAX_IS_MULTI ");
+		if (xlrec->infobits_set & XLHL_XMAX_IS_NOT_UPDATE)
+			appendStringInfo(buf, "XMAX_IS_NOT_UPDATE ");
+		if (xlrec->infobits_set & XLHL_XMAX_EXCL_LOCK)
+			appendStringInfo(buf, "XMAX_EXCL_LOCK ");
+		if (xlrec->infobits_set & XLHL_XMAX_KEYSHR_LOCK)
+			appendStringInfo(buf, "XMAX_KEYSHR_LOCK ");
+		if (xlrec->infobits_set & XLHL_UPDATE_KEY_INTACT)
+			appendStringInfo(buf, "UPDATE_KEY_INTACT ");
 	}
 	else if (info == XLOG_HEAP_INPLACE)
 	{
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index e561409..3469ebe 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -352,15 +352,15 @@ rewrite_heap_tuple(RewriteState state,
 	/*
 	 * If the tuple has been updated, check the old-to-new mapping hash table.
 	 */
-	if (!(old_tuple->t_data->t_infomask & (HEAP_XMAX_INVALID |
-										   HEAP_IS_LOCKED)) &&
+	if (!((old_tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
+		  HeapTupleHeaderIsLocked(old_tuple->t_data)) &&
 		!(ItemPointerEquals(&(old_tuple->t_self),
 							&(old_tuple->t_data->t_ctid))))
 	{
 		OldToNewMapping mapping;
 
 		memset(&hashkey, 0, sizeof(hashkey));
-		hashkey.xmin = HeapTupleHeaderGetXmax(old_tuple->t_data);
+		hashkey.xmin = HeapTupleHeaderGetUpdateXid(old_tuple->t_data);
 		hashkey.tid = old_tuple->t_data->t_ctid;
 
 		mapping = (OldToNewMapping)
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index c1c8ba5..a4dc146 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -4,7 +4,7 @@
  *		PostgreSQL multi-transaction-log manager
  *
  * The pg_multixact manager is a pg_clog-like manager that stores an array
- * of TransactionIds for each MultiXactId.	It is a fundamental part of the
+ * of MultiXactMember for each MultiXactId.	It is a fundamental part of the
  * shared-row-lock implementation.	A share-locked tuple stores a
  * MultiXactId in its Xmax, and a transaction that needs to wait for the
  * tuple to be unlocked can sleep on the potentially-several TransactionIds
@@ -48,6 +48,8 @@
  */
 #include "postgres.h"
 
+#include <unistd.h>
+
 #include "access/multixact.h"
 #include "access/slru.h"
 #include "access/transam.h"
@@ -60,6 +62,7 @@
 #include "storage/procarray.h"
 #include "utils/builtins.h"
 #include "utils/memutils.h"
+#include "utils/snapmgr.h"
 
 
 /*
@@ -75,19 +78,58 @@
  * (see MultiXact{Offset,Member}PagePrecedes).
  */
 
-/* We need four bytes per offset and also four bytes per member */
+/* We need four bytes per offset */
 #define MULTIXACT_OFFSETS_PER_PAGE (BLCKSZ / sizeof(MultiXactOffset))
-#define MULTIXACT_MEMBERS_PER_PAGE (BLCKSZ / sizeof(TransactionId))
 
 #define MultiXactIdToOffsetPage(xid) \
 	((xid) / (MultiXactOffset) MULTIXACT_OFFSETS_PER_PAGE)
 #define MultiXactIdToOffsetEntry(xid) \
 	((xid) % (MultiXactOffset) MULTIXACT_OFFSETS_PER_PAGE)
 
-#define MXOffsetToMemberPage(xid) \
-	((xid) / (TransactionId) MULTIXACT_MEMBERS_PER_PAGE)
-#define MXOffsetToMemberEntry(xid) \
-	((xid) % (TransactionId) MULTIXACT_MEMBERS_PER_PAGE)
+/*
+ * The situation for members is a bit more complex: we need to store two
+ * additional flag bits for each TransactionId.  To do this without getting
+ * into alignment issues, we store four bytes of flags (so 16 bit pairs), and
+ * then the corresponding 16 Xids.  Each such 17-word (68-byte) set we call a
+ * "group", and are stored as a whole in pages.  Thus, with 8kB BLCKSZ, we keep
+ * 120 groups per page.  This wastes 32 bytes per page, but that's OK --
+ * simplicity (and performance) trumps space efficiency here.
+ *
+ * Note that the "offset" macros work with byte offset, not array indexes, so
+ * arithmetic must be done using "char *" pointers.
+ */
+/* We need two bits per xact, so four xacts fit in a byte */
+#define MXACT_MEMBER_BITS_PER_XACT			2
+#define MXACT_MEMBER_FLAGS_PER_BYTE			4
+#define MXACT_MEMBER_XACT_BITMASK	((1 << MXACT_MEMBER_BITS_PER_XACT) - 1)
+
+/* how many full bytes of flags are there in a group? */
+#define MULTIXACT_FLAGBYTES_PER_GROUP		4
+#define MULTIXACT_MEMBERS_PER_MEMBERGROUP	\
+	(MULTIXACT_FLAGBYTES_PER_GROUP * MXACT_MEMBER_FLAGS_PER_BYTE)
+/* size in bytes of a complete group */
+#define MULTIXACT_MEMBERGROUP_SIZE \
+	(sizeof(TransactionId) * MULTIXACT_MEMBERS_PER_MEMBERGROUP + MULTIXACT_FLAGBYTES_PER_GROUP)
+#define MULTIXACT_MEMBERGROUPS_PER_PAGE (BLCKSZ / MULTIXACT_MEMBERGROUP_SIZE)
+#define MULTIXACT_MEMBERS_PER_PAGE	\
+	(MULTIXACT_MEMBERGROUPS_PER_PAGE * MULTIXACT_MEMBERS_PER_MEMBERGROUP)
+
+/* page in which a member is to be found */
+#define MXOffsetToMemberPage(xid) ((xid) / (TransactionId) MULTIXACT_MEMBERS_PER_PAGE)
+
+/* Location (byte offset within page) of flag word for a given member */
+#define MXOffsetToFlagsOffset(xid) \
+	((((xid) / (TransactionId) MULTIXACT_MEMBERS_PER_MEMBERGROUP) % \
+	  (TransactionId) MULTIXACT_MEMBERGROUPS_PER_PAGE) * \
+	 (TransactionId) MULTIXACT_MEMBERGROUP_SIZE)
+#define MXOffsetToFlagsBitShift(xid) \
+	(((xid) % (TransactionId) MULTIXACT_MEMBERS_PER_MEMBERGROUP) * \
+	 MXACT_MEMBER_BITS_PER_XACT)
+
+/* Location (byte offset within page) of TransactionId of given member */
+#define MXOffsetToMemberOffset(xid) \
+	(MXOffsetToFlagsOffset(xid) + MULTIXACT_FLAGBYTES_PER_GROUP + \
+	 ((xid) % MULTIXACT_MEMBERS_PER_MEMBERGROUP) * sizeof(TransactionId))
 
 
 /*
@@ -114,60 +156,51 @@ typedef struct MultiXactStateData
 	/* next-to-be-assigned offset */
 	MultiXactOffset nextOffset;
 
-	/* the Offset SLRU area was last truncated at this MultiXactId */
-	MultiXactId lastTruncationPoint;
+	/* truncation info for the oldest segment in the offset SLRU area */
+	TransactionId	truncateXid;
+	uint32			truncateXidEpoch;
 
 	/*
-	 * Per-backend data starts here.  We have two arrays stored in the area
-	 * immediately following the MultiXactStateData struct. Each is indexed by
-	 * BackendId.
-	 *
-	 * In both arrays, there's a slot for all normal backends (1..MaxBackends)
-	 * followed by a slot for max_prepared_xacts prepared transactions. Valid
-	 * BackendIds start from 1; element zero of each array is never used.
-	 *
-	 * OldestMemberMXactId[k] is the oldest MultiXactId each backend's current
-	 * transaction(s) could possibly be a member of, or InvalidMultiXactId
-	 * when the backend has no live transaction that could possibly be a
-	 * member of a MultiXact.  Each backend sets its entry to the current
-	 * nextMXact counter just before first acquiring a shared lock in a given
-	 * transaction, and clears it at transaction end. (This works because only
-	 * during or after acquiring a shared lock could an XID possibly become a
-	 * member of a MultiXact, and that MultiXact would have to be created
-	 * during or after the lock acquisition.)
-	 *
-	 * OldestVisibleMXactId[k] is the oldest MultiXactId each backend's
-	 * current transaction(s) think is potentially live, or InvalidMultiXactId
-	 * when not in a transaction or not in a transaction that's paid any
-	 * attention to MultiXacts yet.  This is computed when first needed in a
-	 * given transaction, and cleared at transaction end.  We can compute it
-	 * as the minimum of the valid OldestMemberMXactId[] entries at the time
-	 * we compute it (using nextMXact if none are valid).  Each backend is
-	 * required not to attempt to access any SLRU data for MultiXactIds older
-	 * than its own OldestVisibleMXactId[] setting; this is necessary because
-	 * the checkpointer could truncate away such data at any instant.
-	 *
-	 * The checkpointer can compute the safe truncation point as the oldest
-	 * valid value among all the OldestMemberMXactId[] and
-	 * OldestVisibleMXactId[] entries, or nextMXact if none are valid.
-	 * Clearly, it is not possible for any later-computed OldestVisibleMXactId
-	 * value to be older than this, and so there is no risk of truncating data
-	 * that is still needed.
+	 * oldest multixact that is still on disk.  Anything older than this should
+	 * not be consulted.
 	 */
-	MultiXactId perBackendXactIds[1];	/* VARIABLE LENGTH ARRAY */
+	MultiXactId		oldestMultiXactId;
 } MultiXactStateData;
 
+/* Pointer to the state data in shared memory */
+static MultiXactStateData *MultiXactState;
+
+#define firstPageOf(segment) ((segment) * SLRU_PAGES_PER_SEGMENT)
+
 /*
- * Last element of OldestMemberMXactID and OldestVisibleMXactId arrays.
- * Valid elements are (1..MaxOldestSlot); element 0 is never used.
+ * structs to pass data around in our private SlruScanDirectory callback for
+ * the offset truncation support code.
  */
-#define MaxOldestSlot	(MaxBackends + max_prepared_xacts)
+typedef struct SegmentInfo
+{
+	int				segno;			/* segment number */
+	TransactionId	truncateXid;	/* after this Xid is frozen, the previous
+									 * segment can be removed */
+	uint32			truncateXidEpoch;	/* epoch of above Xid */
+	MultiXactOffset	firstOffset;	/* first valid offset in segment */
+} SegmentInfo;
 
-/* Pointers to the state data in shared memory */
-static MultiXactStateData *MultiXactState;
-static MultiXactId *OldestMemberMXactId;
-static MultiXactId *OldestVisibleMXactId;
+typedef struct TruncateCbData
+{
+	int				remaining_alloc;
+	int				remaining_used;
+	SegmentInfo	   *remaining;
+} TruncateCbData;
 
+/*
+ * MultiXactZeroOffsetPage xlog record
+ */
+typedef struct MxactZeroOffPg
+{
+	int				pageno;
+	TransactionId	truncateXid;
+	TransactionId	truncateXidEpoch;
+} MxactZeroOffPg;
 
 /*
  * Definitions for the backend-local MultiXactId cache.
@@ -180,7 +213,8 @@ static MultiXactId *OldestVisibleMXactId;
  * so they will be uninteresting by the time our next transaction starts.
  * (XXX not clear that this is correct --- other members of the MultiXact
  * could hang around longer than we did.  However, it's not clear what a
- * better policy for flushing old cache entries would be.)
+ * better policy for flushing old cache entries would be.)  FIXME actually
+ * this is plain wrong now that multixact's may contain update Xids.
  *
  * We allocate the cache entries in a memory context that is deleted at
  * transaction end, so we don't need to do retail freeing of entries.
@@ -189,44 +223,72 @@ typedef struct mXactCacheEnt
 {
 	struct mXactCacheEnt *next;
 	MultiXactId multi;
-	int			nxids;
-	TransactionId xids[1];		/* VARIABLE LENGTH ARRAY */
+	int			nmembers;
+	MultiXactMember members[FLEXIBLE_ARRAY_MEMBER];
 } mXactCacheEnt;
 
 static mXactCacheEnt *MXactCache = NULL;
 static MemoryContext MXactContext = NULL;
 
+/* status conflict table */
+static const bool MultiXactConflicts[5][5] =
+{
+	{	/* ForKeyShare */
+		false, false, false, false, true
+	},
+	{	/* ForShare */
+		false, false, true, true, true
+	},
+	{	/* ForUpdate */
+		false, true, true, true, true
+	},
+	{	/* Update */
+		false, true, true, true, true
+	},
+	{	/* KeyUpdate */
+		true, true, true, true, true
+	}
+};
+
+#define MultiXactStatusConflict(status1, status2) \
+	MultiXactConflicts[status1][status2]
 
+
+#define MULTIXACT_DEBUG
 #ifdef MULTIXACT_DEBUG
 #define debug_elog2(a,b) elog(a,b)
 #define debug_elog3(a,b,c) elog(a,b,c)
 #define debug_elog4(a,b,c,d) elog(a,b,c,d)
 #define debug_elog5(a,b,c,d,e) elog(a,b,c,d,e)
+#define debug_elog7(a,b,c,d,e,f,g) elog(a,b,c,d,e,f,g)
 #else
 #define debug_elog2(a,b)
 #define debug_elog3(a,b,c)
 #define debug_elog4(a,b,c,d)
 #define debug_elog5(a,b,c,d,e)
+#define debug_elog7(a,b,c,d,e,f,g)
 #endif
 
 /* internal MultiXactId management */
-static void MultiXactIdSetOldestVisible(void);
-static MultiXactId CreateMultiXactId(int nxids, TransactionId *xids);
+static MultiXactId CreateMultiXactId(int nmembers, MultiXactMember *members);
 static void RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
-				   int nxids, TransactionId *xids);
-static MultiXactId GetNewMultiXactId(int nxids, MultiXactOffset *offset);
+				   int nmembers, MultiXactMember *members);
+static MultiXactId GetNewMultiXactId(int nmembers, MultiXactOffset *offset);
+static MultiXactId HandleMxactOffsetCornerCases(MultiXactId multi);
 
 /* MultiXact cache management */
-static MultiXactId mXactCacheGetBySet(int nxids, TransactionId *xids);
-static int	mXactCacheGetById(MultiXactId multi, TransactionId **xids);
-static void mXactCachePut(MultiXactId multi, int nxids, TransactionId *xids);
+static int mxactMemberComparator(const void *arg1, const void *arg2);
+static MultiXactId mXactCacheGetBySet(int nmembers, MultiXactMember *members);
+static int	mXactCacheGetById(MultiXactId multi, MultiXactMember **members);
+static void mXactCachePut(MultiXactId multi, int nmembers, MultiXactMember *members);
 
 #ifdef MULTIXACT_DEBUG
-static char *mxid_to_string(MultiXactId multi, int nxids, TransactionId *xids);
+static char *mxid_to_string(MultiXactId multi, int nmembers, MultiXactMember *members);
 #endif
 
 /* management of SLRU infrastructure */
-static int	ZeroMultiXactOffsetPage(int pageno, bool writeXlog);
+static int	ZeroMultiXactOffsetPage(int pageno, bool writeXlog,
+						TransactionId truncateXid, uint32 truncateXidEpoch);
 static int	ZeroMultiXactMemberPage(int pageno, bool writeXlog);
 static bool MultiXactOffsetPagePrecedes(int page1, int page2);
 static bool MultiXactMemberPagePrecedes(int page1, int page2);
@@ -235,29 +297,59 @@ static bool MultiXactOffsetPrecedes(MultiXactOffset offset1,
 						MultiXactOffset offset2);
 static void ExtendMultiXactOffset(MultiXactId multi);
 static void ExtendMultiXactMember(MultiXactOffset offset, int nmembers);
-static void TruncateMultiXact(void);
-static void WriteMZeroPageXlogRec(int pageno, uint8 info);
+static void fillSegmentInfoData(SlruCtl ctl, SegmentInfo *segment);
+static int	compareTruncateXidEpoch(const void *a, const void *b);
+static void WriteMZeroOffsetPageXlogRec(int pageno, TransactionId truncateXid,
+							uint32 truncateXidEpoch);
+static void WriteMZeroMemberPageXlogRec(int pageno);
 
 
 /*
+ * MultiXactIdCreateSingleton
+ * 		Construct a MultiXactId representing a single transaction.
+ *
+ * NB - we don't worry about our local MultiXactId cache here, because that
+ * is handled by the lower-level routines.
+ */
+MultiXactId
+MultiXactIdCreateSingleton(TransactionId xid, MultiXactStatus status)
+{
+	MultiXactId	newMulti;
+	MultiXactMember	member[1];
+
+	AssertArg(TransactionIdIsValid(xid));
+
+	member[0].xid = xid;
+	member[0].status = status;
+
+	newMulti = CreateMultiXactId(1, member);
+
+	debug_elog4(DEBUG2, "Create: returning %u for %u",
+			   newMulti, xid);
+
+	return newMulti;
+}
+
+/*
  * MultiXactIdCreate
  *		Construct a MultiXactId representing two TransactionIds.
  *
- * The two XIDs must be different.
+ * The two XIDs must be different, or be requesting different lock modes.
  *
  * NB - we don't worry about our local MultiXactId cache here, because that
  * is handled by the lower-level routines.
  */
 MultiXactId
-MultiXactIdCreate(TransactionId xid1, TransactionId xid2)
+MultiXactIdCreate(TransactionId xid1, MultiXactStatus status1,
+				  TransactionId xid2, MultiXactStatus status2)
 {
 	MultiXactId newMulti;
-	TransactionId xids[2];
+	MultiXactMember members[2];
 
 	AssertArg(TransactionIdIsValid(xid1));
 	AssertArg(TransactionIdIsValid(xid2));
 
-	Assert(!TransactionIdEquals(xid1, xid2));
+	Assert(!TransactionIdEquals(xid1, xid2) || (status1 != status2));
 
 	/*
 	 * Note: unlike MultiXactIdExpand, we don't bother to check that both XIDs
@@ -265,11 +357,14 @@ MultiXactIdCreate(TransactionId xid1, TransactionId xid2)
 	 * caller just did a check on xid1, so it'd be wasted effort.
 	 */
 
-	xids[0] = xid1;
-	xids[1] = xid2;
+	members[0].xid = xid1;
+	members[0].status = status1;
+	members[1].xid = xid2;
+	members[1].status = status2;
 
-	newMulti = CreateMultiXactId(2, xids);
+	newMulti = CreateMultiXactId(2, members);
 
+	/* XXX -- need better debug? */
 	debug_elog5(DEBUG2, "Create: returning %u for %u, %u",
 				newMulti, xid1, xid2);
 
@@ -280,8 +375,8 @@ MultiXactIdCreate(TransactionId xid1, TransactionId xid2)
  * MultiXactIdExpand
  *		Add a TransactionId to a pre-existing MultiXactId.
  *
- * If the TransactionId is already a member of the passed MultiXactId,
- * just return it as-is.
+ * If the TransactionId is already a member of the passed MultiXactId with the
+ * same status, just return it as-is.
  *
  * Note that we do NOT actually modify the membership of a pre-existing
  * MultiXactId; instead we create a new one.  This is necessary to avoid
@@ -291,11 +386,11 @@ MultiXactIdCreate(TransactionId xid1, TransactionId xid2)
  * is handled by the lower-level routines.
  */
 MultiXactId
-MultiXactIdExpand(MultiXactId multi, TransactionId xid)
+MultiXactIdExpand(MultiXactId multi, TransactionId xid, MultiXactStatus status)
 {
 	MultiXactId newMulti;
-	TransactionId *members;
-	TransactionId *newMembers;
+	MultiXactMember *members;
+	MultiXactMember *newMembers;
 	int			nmembers;
 	int			i;
 	int			j;
@@ -310,6 +405,8 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid)
 
 	if (nmembers < 0)
 	{
+		MultiXactMember		member;
+
 		/*
 		 * The MultiXactId is obsolete.  This can only happen if all the
 		 * MultiXactId members stop running between the caller checking and
@@ -317,7 +414,9 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid)
 		 * caller, but it would complicate the API and it's unlikely to happen
 		 * too often, so just deal with it by creating a singleton MultiXact.
 		 */
-		newMulti = CreateMultiXactId(1, &xid);
+		member.xid = xid;
+		member.status = status;
+		newMulti = CreateMultiXactId(1, &member);
 
 		debug_elog4(DEBUG2, "Expand: %u has no members, create singleton %u",
 					multi, newMulti);
@@ -325,12 +424,13 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid)
 	}
 
 	/*
-	 * If the TransactionId is already a member of the MultiXactId, just
-	 * return the existing MultiXactId.
+	 * If the TransactionId is already a member of the MultiXactId with the
+	 * same status, just return the existing MultiXactId.
 	 */
 	for (i = 0; i < nmembers; i++)
 	{
-		if (TransactionIdEquals(members[i], xid))
+		if (TransactionIdEquals(members[i].xid, xid) &&
+			(members[i].status == status))
 		{
 			debug_elog4(DEBUG2, "Expand: %u is already a member of %u",
 						xid, multi);
@@ -345,16 +445,20 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid)
 	 * optimization, but a useful one.	Note we have the same race condition
 	 * here as above: j could be 0 at the end of the loop.)
 	 */
-	newMembers = (TransactionId *)
-		palloc(sizeof(TransactionId) * (nmembers + 1));
+	newMembers = (MultiXactMember *)
+		palloc(sizeof(MultiXactMember) * (nmembers + 1));
 
 	for (i = 0, j = 0; i < nmembers; i++)
 	{
-		if (TransactionIdIsInProgress(members[i]))
-			newMembers[j++] = members[i];
+		if (TransactionIdIsInProgress(members[i].xid))
+		{
+			newMembers[j].xid = members[i].xid;
+			newMembers[j++].status = members[i].status;
+		}
 	}
 
-	newMembers[j++] = xid;
+	newMembers[j].xid = xid;
+	newMembers[j++].status = status;
 	newMulti = CreateMultiXactId(j, newMembers);
 
 	pfree(members);
@@ -376,7 +480,7 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid)
 bool
 MultiXactIdIsRunning(MultiXactId multi)
 {
-	TransactionId *members;
+	MultiXactMember *members;
 	int			nmembers;
 	int			i;
 
@@ -397,7 +501,7 @@ MultiXactIdIsRunning(MultiXactId multi)
 	 */
 	for (i = 0; i < nmembers; i++)
 	{
-		if (TransactionIdIsCurrentTransactionId(members[i]))
+		if (TransactionIdIsCurrentTransactionId(members[i].xid))
 		{
 			debug_elog3(DEBUG2, "IsRunning: I (%d) am running!", i);
 			pfree(members);
@@ -412,10 +516,10 @@ MultiXactIdIsRunning(MultiXactId multi)
 	 */
 	for (i = 0; i < nmembers; i++)
 	{
-		if (TransactionIdIsInProgress(members[i]))
+		if (TransactionIdIsInProgress(members[i].xid))
 		{
 			debug_elog4(DEBUG2, "IsRunning: member %d (%u) is running",
-						i, members[i]);
+						i, members[i].xid);
 			pfree(members);
 			return true;
 		}
@@ -429,145 +533,6 @@ MultiXactIdIsRunning(MultiXactId multi)
 }
 
 /*
- * MultiXactIdIsCurrent
- *		Returns true if the current transaction is a member of the MultiXactId.
- *
- * We return true if any live subtransaction of the current top-level
- * transaction is a member.  This is appropriate for the same reason that a
- * lock held by any such subtransaction is globally equivalent to a lock
- * held by the current subtransaction: no such lock could be released without
- * aborting this subtransaction, and hence releasing its locks.  So it's not
- * necessary to add the current subxact to the MultiXact separately.
- */
-bool
-MultiXactIdIsCurrent(MultiXactId multi)
-{
-	bool		result = false;
-	TransactionId *members;
-	int			nmembers;
-	int			i;
-
-	nmembers = GetMultiXactIdMembers(multi, &members);
-
-	if (nmembers < 0)
-		return false;
-
-	for (i = 0; i < nmembers; i++)
-	{
-		if (TransactionIdIsCurrentTransactionId(members[i]))
-		{
-			result = true;
-			break;
-		}
-	}
-
-	pfree(members);
-
-	return result;
-}
-
-/*
- * MultiXactIdSetOldestMember
- *		Save the oldest MultiXactId this transaction could be a member of.
- *
- * We set the OldestMemberMXactId for a given transaction the first time
- * it's going to acquire a shared lock.  We need to do this even if we end
- * up using a TransactionId instead of a MultiXactId, because there is a
- * chance that another transaction would add our XID to a MultiXactId.
- *
- * The value to set is the next-to-be-assigned MultiXactId, so this is meant
- * to be called just before acquiring a shared lock.
- */
-void
-MultiXactIdSetOldestMember(void)
-{
-	if (!MultiXactIdIsValid(OldestMemberMXactId[MyBackendId]))
-	{
-		MultiXactId nextMXact;
-
-		/*
-		 * You might think we don't need to acquire a lock here, since
-		 * fetching and storing of TransactionIds is probably atomic, but in
-		 * fact we do: suppose we pick up nextMXact and then lose the CPU for
-		 * a long time.  Someone else could advance nextMXact, and then
-		 * another someone else could compute an OldestVisibleMXactId that
-		 * would be after the value we are going to store when we get control
-		 * back.  Which would be wrong.
-		 */
-		LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
-
-		/*
-		 * We have to beware of the possibility that nextMXact is in the
-		 * wrapped-around state.  We don't fix the counter itself here, but we
-		 * must be sure to store a valid value in our array entry.
-		 */
-		nextMXact = MultiXactState->nextMXact;
-		if (nextMXact < FirstMultiXactId)
-			nextMXact = FirstMultiXactId;
-
-		OldestMemberMXactId[MyBackendId] = nextMXact;
-
-		LWLockRelease(MultiXactGenLock);
-
-		debug_elog4(DEBUG2, "MultiXact: setting OldestMember[%d] = %u",
-					MyBackendId, nextMXact);
-	}
-}
-
-/*
- * MultiXactIdSetOldestVisible
- *		Save the oldest MultiXactId this transaction considers possibly live.
- *
- * We set the OldestVisibleMXactId for a given transaction the first time
- * it's going to inspect any MultiXactId.  Once we have set this, we are
- * guaranteed that the checkpointer won't truncate off SLRU data for
- * MultiXactIds at or after our OldestVisibleMXactId.
- *
- * The value to set is the oldest of nextMXact and all the valid per-backend
- * OldestMemberMXactId[] entries.  Because of the locking we do, we can be
- * certain that no subsequent call to MultiXactIdSetOldestMember can set
- * an OldestMemberMXactId[] entry older than what we compute here.	Therefore
- * there is no live transaction, now or later, that can be a member of any
- * MultiXactId older than the OldestVisibleMXactId we compute here.
- */
-static void
-MultiXactIdSetOldestVisible(void)
-{
-	if (!MultiXactIdIsValid(OldestVisibleMXactId[MyBackendId]))
-	{
-		MultiXactId oldestMXact;
-		int			i;
-
-		LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
-
-		/*
-		 * We have to beware of the possibility that nextMXact is in the
-		 * wrapped-around state.  We don't fix the counter itself here, but we
-		 * must be sure to store a valid value in our array entry.
-		 */
-		oldestMXact = MultiXactState->nextMXact;
-		if (oldestMXact < FirstMultiXactId)
-			oldestMXact = FirstMultiXactId;
-
-		for (i = 1; i <= MaxOldestSlot; i++)
-		{
-			MultiXactId thisoldest = OldestMemberMXactId[i];
-
-			if (MultiXactIdIsValid(thisoldest) &&
-				MultiXactIdPrecedes(thisoldest, oldestMXact))
-				oldestMXact = thisoldest;
-		}
-
-		OldestVisibleMXactId[MyBackendId] = oldestMXact;
-
-		LWLockRelease(MultiXactGenLock);
-
-		debug_elog4(DEBUG2, "MultiXact: setting OldestVisible[%d] = %u",
-					MyBackendId, oldestMXact);
-	}
-}
-
-/*
  * MultiXactIdWait
  *		Sleep on a MultiXactId.
  *
@@ -576,17 +541,24 @@ MultiXactIdSetOldestVisible(void)
  * this would not merely be useless but would lead to Assert failure inside
  * XactLockTableWait.  By the time this returns, it is certain that all
  * transactions *of other backends* that were members of the MultiXactId
- * are dead (and no new ones can have been added, since it is not legal
- * to add members to an existing MultiXactId).
+ * that conflict with the requested status are dead (and no new ones can have
+ * been added, since it is not legal to add members to an existing
+ * MultiXactId).
+ *
+ * We return the number of members that we did not test for.  This is dubbed
+ * "remaining" as in "the number of members that remaing running", but this is
+ * slightly incorrect, because lockers whose status did not conflict with ours
+ * are not even considered and so might have gone away anyway.
  *
  * But by the time we finish sleeping, someone else may have changed the Xmax
  * of the containing tuple, so the caller needs to iterate on us somehow.
  */
 void
-MultiXactIdWait(MultiXactId multi)
+MultiXactIdWait(MultiXactId multi, MultiXactStatus status, int *remaining)
 {
-	TransactionId *members;
+	MultiXactMember *members;
 	int			nmembers;
+	int			remain = 0;
 
 	nmembers = GetMultiXactIdMembers(multi, &members);
 
@@ -596,28 +568,37 @@ MultiXactIdWait(MultiXactId multi)
 
 		for (i = 0; i < nmembers; i++)
 		{
-			TransactionId member = members[i];
-
 			debug_elog4(DEBUG2, "MultiXactIdWait: waiting for %d (%u)",
-						i, member);
-			if (!TransactionIdIsCurrentTransactionId(member))
-				XactLockTableWait(member);
-		}
+						i, members[i].xid);
+			if (TransactionIdIsCurrentTransactionId(members[i].xid) ||
+				!MultiXactStatusConflict(members[i].status, status))
+			{
+				remain++;
+				continue;
+			}
 
-		pfree(members);
+			XactLockTableWait(members[i].xid);
+		}
 	}
+
+	*remaining = remain;
 }
 
 /*
  * ConditionalMultiXactIdWait
  *		As above, but only lock if we can get the lock without blocking.
+ *
+ * Note that in case we return false, the number of remaining members is
+ * not to be trusted.
  */
 bool
-ConditionalMultiXactIdWait(MultiXactId multi)
+ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status,
+						   int *remaining)
 {
 	bool		result = true;
-	TransactionId *members;
+	MultiXactMember *members;
 	int			nmembers;
+	int			remain = 0;
 
 	nmembers = GetMultiXactIdMembers(multi, &members);
 
@@ -627,21 +608,26 @@ ConditionalMultiXactIdWait(MultiXactId multi)
 
 		for (i = 0; i < nmembers; i++)
 		{
-			TransactionId member = members[i];
+			TransactionId member = members[i].xid;
 
 			debug_elog4(DEBUG2, "ConditionalMultiXactIdWait: trying %d (%u)",
 						i, member);
-			if (!TransactionIdIsCurrentTransactionId(member))
+			if (TransactionIdIsCurrentTransactionId(member) ||
+				!MultiXactStatusConflict(members[i].status, status))
 			{
-				result = ConditionalXactLockTableWait(member);
-				if (!result)
-					break;
+				remain++;
+				continue;
 			}
+			result = ConditionalXactLockTableWait(member);
+			if (!result)
+				break;
 		}
 
 		pfree(members);
 	}
 
+	*remaining = remain;
+
 	return result;
 }
 
@@ -652,10 +638,10 @@ ConditionalMultiXactIdWait(MultiXactId multi)
  * Make XLOG, SLRU and cache entries for a new MultiXactId, recording the
  * given TransactionIds as members.  Returns the newly created MultiXactId.
  *
- * NB: the passed xids[] array will be sorted in-place.
+ * NB: the passed members[] array will be sorted in-place.
  */
 static MultiXactId
-CreateMultiXactId(int nxids, TransactionId *xids)
+CreateMultiXactId(int nmembers, MultiXactMember *members)
 {
 	MultiXactId multi;
 	MultiXactOffset offset;
@@ -663,7 +649,7 @@ CreateMultiXactId(int nxids, TransactionId *xids)
 	xl_multixact_create xlrec;
 
 	debug_elog3(DEBUG2, "Create: %s",
-				mxid_to_string(InvalidMultiXactId, nxids, xids));
+				mxid_to_string(InvalidMultiXactId, nmembers, members));
 
 	/*
 	 * See if the same set of XIDs already exists in our cache; if so, just
@@ -675,7 +661,7 @@ CreateMultiXactId(int nxids, TransactionId *xids)
 	 * corner cases where someone else added us to a MultiXact without our
 	 * knowledge, but it's not worth checking for.)
 	 */
-	multi = mXactCacheGetBySet(nxids, xids);
+	multi = mXactCacheGetBySet(nmembers, members);
 	if (MultiXactIdIsValid(multi))
 	{
 		debug_elog2(DEBUG2, "Create: in cache!");
@@ -687,7 +673,7 @@ CreateMultiXactId(int nxids, TransactionId *xids)
 	 * in the OFFSETs and MEMBERs files.  NB: this routine does
 	 * START_CRIT_SECTION().
 	 */
-	multi = GetNewMultiXactId(nxids, &offset);
+	multi = GetNewMultiXactId(nmembers, &offset);
 
 	/*
 	 * Make an XLOG entry describing the new MXID.
@@ -704,27 +690,32 @@ CreateMultiXactId(int nxids, TransactionId *xids)
 	 */
 	xlrec.mid = multi;
 	xlrec.moff = offset;
-	xlrec.nxids = nxids;
+	xlrec.nmembers = nmembers;
 
+	/*
+	 * XXX Note: there's a lot of padding space in MultiXactMember.  We could
+	 * find a more compact representation of this Xlog record -- perhaps all the
+	 * status flags in one XLogRecData, then all the xids in another one?
+	 */
 	rdata[0].data = (char *) (&xlrec);
 	rdata[0].len = MinSizeOfMultiXactCreate;
 	rdata[0].buffer = InvalidBuffer;
 	rdata[0].next = &(rdata[1]);
-	rdata[1].data = (char *) xids;
-	rdata[1].len = nxids * sizeof(TransactionId);
+	rdata[1].data = (char *) members;
+	rdata[1].len = nmembers * sizeof(MultiXactMember);
 	rdata[1].buffer = InvalidBuffer;
 	rdata[1].next = NULL;
 
 	(void) XLogInsert(RM_MULTIXACT_ID, XLOG_MULTIXACT_CREATE_ID, rdata);
 
 	/* Now enter the information into the OFFSETs and MEMBERs logs */
-	RecordNewMultiXact(multi, offset, nxids, xids);
+	RecordNewMultiXact(multi, offset, nmembers, members);
 
 	/* Done with critical section */
 	END_CRIT_SECTION();
 
 	/* Store the new MultiXactId in the local cache, too */
-	mXactCachePut(multi, nxids, xids);
+	mXactCachePut(multi, nmembers, members);
 
 	debug_elog2(DEBUG2, "Create: all done");
 
@@ -739,7 +730,7 @@ CreateMultiXactId(int nxids, TransactionId *xids)
  */
 static void
 RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
-				   int nxids, TransactionId *xids)
+				   int nmembers, MultiXactMember *members)
 {
 	int			pageno;
 	int			prev_pageno;
@@ -775,12 +766,22 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 
 	prev_pageno = -1;
 
-	for (i = 0; i < nxids; i++, offset++)
+	for (i = 0; i < nmembers; i++, offset++)
 	{
 		TransactionId *memberptr;
+		uint32	   *flagsptr;
+		uint32		flagsval;
+		int			bshift;
+		int			flagsoff;
+		int			memberoff;
+
+		/* this status value is not representable on disk */
+		Assert(members[i].status < MultiXactStatusKeyUpdate);
 
 		pageno = MXOffsetToMemberPage(offset);
-		entryno = MXOffsetToMemberEntry(offset);
+		memberoff = MXOffsetToMemberOffset(offset);
+		flagsoff = MXOffsetToFlagsOffset(offset);
+		bshift = MXOffsetToFlagsBitShift(offset);
 
 		if (pageno != prev_pageno)
 		{
@@ -789,10 +790,17 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 		}
 
 		memberptr = (TransactionId *)
-			MultiXactMemberCtl->shared->page_buffer[slotno];
-		memberptr += entryno;
+			(MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
 
-		*memberptr = xids[i];
+		*memberptr = members[i].xid;
+
+		flagsptr = (uint32 *)
+			(MultiXactMemberCtl->shared->page_buffer[slotno] + flagsoff);
+
+		flagsval = *flagsptr;
+		flagsval &= ~(((1 << MXACT_MEMBER_BITS_PER_XACT) - 1) << bshift);
+		flagsval |= (members[i].status << bshift);
+		*flagsptr = flagsval;
 
 		MultiXactMemberCtl->shared->page_dirty[slotno] = true;
 	}
@@ -816,21 +824,18 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
  * caller must end the critical section after writing SLRU data.
  */
 static MultiXactId
-GetNewMultiXactId(int nxids, MultiXactOffset *offset)
+GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
 {
 	MultiXactId result;
 	MultiXactOffset nextOffset;
 
-	debug_elog3(DEBUG2, "GetNew: for %d xids", nxids);
-
-	/* MultiXactIdSetOldestMember() must have been called already */
-	Assert(MultiXactIdIsValid(OldestMemberMXactId[MyBackendId]));
+	debug_elog3(DEBUG2, "GetNew: for %d xids", nmembers);
 
 	LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
 
-	/* Handle wraparound of the nextMXact counter */
-	if (MultiXactState->nextMXact < FirstMultiXactId)
-		MultiXactState->nextMXact = FirstMultiXactId;
+	/* Handle corner cases of the nextMXact counter */
+	MultiXactState->nextMXact =
+		HandleMxactOffsetCornerCases(MultiXactState->nextMXact);
 
 	/*
 	 * Assign the MXID, and make sure there is room for it in the file.
@@ -848,12 +853,12 @@ GetNewMultiXactId(int nxids, MultiXactOffset *offset)
 	if (nextOffset == 0)
 	{
 		*offset = 1;
-		nxids++;				/* allocate member slot 0 too */
+		nmembers++;				/* allocate member slot 0 too */
 	}
 	else
 		*offset = nextOffset;
 
-	ExtendMultiXactMember(nextOffset, nxids);
+	ExtendMultiXactMember(nextOffset, nmembers);
 
 	/*
 	 * Critical section from here until caller has written the data into the
@@ -870,13 +875,14 @@ GetNewMultiXactId(int nxids, MultiXactOffset *offset)
 	 *
 	 * We don't care about MultiXactId wraparound here; it will be handled by
 	 * the next iteration.	But note that nextMXact may be InvalidMultiXactId
-	 * after this routine exits, so anyone else looking at the variable must
-	 * be prepared to deal with that.  Similarly, nextOffset may be zero, but
-	 * we won't use that as the actual start offset of the next multixact.
+	 * or the first value on a segment-beggining page after this routine exits,
+	 * so anyone else looking at the variable must be prepared to deal with
+	 * either case.  Similarly, nextOffset may be zero, but we won't use that
+	 * as the actual start offset of the next multixact.
 	 */
 	(MultiXactState->nextMXact)++;
 
-	MultiXactState->nextOffset += nxids;
+	MultiXactState->nextOffset += nmembers;
 
 	LWLockRelease(MultiXactGenLock);
 
@@ -885,15 +891,37 @@ GetNewMultiXactId(int nxids, MultiXactOffset *offset)
 }
 
 /*
+ * HandleMxactOffsetCornerCases
+ * 		Properly handle corner cases of MultiXactId enumeration
+ *
+ * This function takes a MultiXactId and returns a value that's actually a
+ * valid multi, that is, it skips the first two values of any segment-
+ * beginning page, which are used to store the truncateXid and
+ * truncateXidEpoch.
+ */
+static MultiXactId
+HandleMxactOffsetCornerCases(MultiXactId multi)
+{
+	if (multi < FirstMultiXactId)
+		return FirstMultiXactId;
+
+	if (MultiXactIdToOffsetEntry(multi) == 0 &&
+		multi % SLRU_PAGES_PER_SEGMENT == 0)
+		return multi + 2;
+
+	return multi;
+}
+
+/*
  * GetMultiXactIdMembers
- *		Returns the set of TransactionIds that make up a MultiXactId
+ *		Returns the set of MultiXactMembers that make up a MultiXactId
  *
  * We return -1 if the MultiXactId is too old to possibly have any members
  * still running; in that case we have not actually looked them up, and
- * *xids is not set.
+ * *members is not set.
  */
 int
-GetMultiXactIdMembers(MultiXactId multi, TransactionId **xids)
+GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members)
 {
 	int			pageno;
 	int			prev_pageno;
@@ -904,64 +932,61 @@ GetMultiXactIdMembers(MultiXactId multi, TransactionId **xids)
 	int			length;
 	int			truelength;
 	int			i;
+	MultiXactId oldestMXact;
 	MultiXactId nextMXact;
 	MultiXactId tmpMXact;
 	MultiXactOffset nextOffset;
-	TransactionId *ptr;
+	MultiXactMember *ptr;
 
 	debug_elog3(DEBUG2, "GetMembers: asked for %u", multi);
 
 	Assert(MultiXactIdIsValid(multi));
 
 	/* See if the MultiXactId is in the local cache */
-	length = mXactCacheGetById(multi, xids);
+	length = mXactCacheGetById(multi, members);
 	if (length >= 0)
 	{
 		debug_elog3(DEBUG2, "GetMembers: found %s in the cache",
-					mxid_to_string(multi, length, *xids));
+					mxid_to_string(multi, length, *members));
 		return length;
 	}
 
-	/* Set our OldestVisibleMXactId[] entry if we didn't already */
-	MultiXactIdSetOldestVisible();
-
 	/*
 	 * We check known limits on MultiXact before resorting to the SLRU area.
 	 *
-	 * An ID older than our OldestVisibleMXactId[] entry can't possibly still
-	 * be running, and we'd run the risk of trying to read already-truncated
-	 * SLRU data if we did try to examine it.
+	 * An ID older than MultiXactState->oldestMultiXactId cannot possibly be
+	 * useful; it should have already been frozen by vacuum.  We've truncated
+	 * the on-disk structures anyway, so we return empty if such a value is
+	 * queried.
 	 *
 	 * Conversely, an ID >= nextMXact shouldn't ever be seen here; if it is
 	 * seen, it implies undetected ID wraparound has occurred.	We just
 	 * silently assume that such an ID is no longer running.
 	 *
 	 * Shared lock is enough here since we aren't modifying any global state.
-	 * Also, we can examine our own OldestVisibleMXactId without the lock,
-	 * since no one else is allowed to change it.
-	 */
-	if (MultiXactIdPrecedes(multi, OldestVisibleMXactId[MyBackendId]))
-	{
-		debug_elog2(DEBUG2, "GetMembers: it's too old");
-		*xids = NULL;
-		return -1;
-	}
-
-	/*
+	 *
 	 * Acquire the shared lock just long enough to grab the current counter
 	 * values.	We may need both nextMXact and nextOffset; see below.
 	 */
 	LWLockAcquire(MultiXactGenLock, LW_SHARED);
 
+	oldestMXact = MultiXactState->oldestMultiXactId;
 	nextMXact = MultiXactState->nextMXact;
 	nextOffset = MultiXactState->nextOffset;
 
 	LWLockRelease(MultiXactGenLock);
 
+	if (MultiXactIdPrecedes(multi, oldestMXact))
+	{
+		debug_elog2(DEBUG2, "GetMembers: it's too old");
+		*members = NULL;
+		return -1;
+	}
+
 	if (!MultiXactIdPrecedes(multi, nextMXact))
 	{
 		debug_elog2(DEBUG2, "GetMembers: it's too new!");
-		*xids = NULL;
+		*members = NULL;
 		return -1;
 	}
 
@@ -1026,9 +1051,8 @@ retry:
 	{
 		MultiXactOffset nextMXOffset;
 
-		/* handle wraparound if needed */
-		if (tmpMXact < FirstMultiXactId)
-			tmpMXact = FirstMultiXactId;
+		/* Handle corner cases if needed */
+		tmpMXact = HandleMxactOffsetCornerCases(tmpMXact);
 
 		prev_pageno = pageno;
 
@@ -1055,8 +1079,8 @@ retry:
 
 	LWLockRelease(MultiXactOffsetControlLock);
 
-	ptr = (TransactionId *) palloc(length * sizeof(TransactionId));
-	*xids = ptr;
+	ptr = (MultiXactMember *) palloc(length * sizeof(MultiXactMember));
+	*members = ptr;
 
 	/* Now get the members themselves. */
 	LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);
@@ -1066,9 +1090,13 @@ retry:
 	for (i = 0; i < length; i++, offset++)
 	{
 		TransactionId *xactptr;
+		uint32	   *flagsptr;
+		int			flagsoff;
+		int			bshift;
+		int			memberoff;
 
 		pageno = MXOffsetToMemberPage(offset);
-		entryno = MXOffsetToMemberEntry(offset);
+		memberoff = MXOffsetToMemberOffset(offset);
 
 		if (pageno != prev_pageno)
 		{
@@ -1077,8 +1105,7 @@ retry:
 		}
 
 		xactptr = (TransactionId *)
-			MultiXactMemberCtl->shared->page_buffer[slotno];
-		xactptr += entryno;
+			(MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
 
 		if (!TransactionIdIsValid(*xactptr))
 		{
@@ -1087,7 +1114,13 @@ retry:
 			continue;
 		}
 
-		ptr[truelength++] = *xactptr;
+		flagsoff = MXOffsetToFlagsOffset(offset);
+		bshift = MXOffsetToFlagsBitShift(offset);
+		flagsptr = (uint32 *) (MultiXactMemberCtl->shared->page_buffer[slotno] + flagsoff);
+
+		ptr[truelength].xid = *xactptr;
+		ptr[truelength].status = (*flagsptr >> bshift) & MXACT_MEMBER_XACT_BITMASK;
+		truelength++;
 	}
 
 	LWLockRelease(MultiXactMemberControlLock);
@@ -1103,6 +1136,30 @@ retry:
 }
 
 /*
+ * mxactMemberComparator
+ *		qsort comparison function for MultiXactMember
+ *
+ * We can't use wraparound comparison for XIDs because that does not respect
+ * the triangle inequality!  Any old sort order will do.
+ */
+static int
+mxactMemberComparator(const void *arg1, const void *arg2)
+{
+	MultiXactMember member1 = *(const MultiXactMember *) arg1;
+	MultiXactMember member2 = *(const MultiXactMember *) arg2;
+
+	if (member1.xid > member2.xid)
+		return 1;
+	if (member1.xid < member2.xid)
+		return -1;
+	if (member1.status > member2.status)
+		return 1;
+	if (member1.status < member2.status)
+		return -1;
+	return 0;
+}
+
+/*
  * mXactCacheGetBySet
  *		returns a MultiXactId from the cache based on the set of
  *		TransactionIds that compose it, or InvalidMultiXactId if
@@ -1113,26 +1170,27 @@ retry:
  * for the majority of tuples, thus keeping MultiXactId usage low (saving
  * both I/O and wraparound issues).
  *
- * NB: the passed xids[] array will be sorted in-place.
+ * NB: the passed members array will be sorted in-place.
  */
 static MultiXactId
-mXactCacheGetBySet(int nxids, TransactionId *xids)
+mXactCacheGetBySet(int nmembers, MultiXactMember *members)
 {
 	mXactCacheEnt *entry;
 
 	debug_elog3(DEBUG2, "CacheGet: looking for %s",
-				mxid_to_string(InvalidMultiXactId, nxids, xids));
+				mxid_to_string(InvalidMultiXactId, nmembers, members));
 
 	/* sort the array so comparison is easy */
-	qsort(xids, nxids, sizeof(TransactionId), xidComparator);
+	qsort(members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);
 
 	for (entry = MXactCache; entry != NULL; entry = entry->next)
 	{
-		if (entry->nxids != nxids)
+		if (entry->nmembers != nmembers)
 			continue;
 
 		/* We assume the cache entries are sorted */
-		if (memcmp(xids, entry->xids, nxids * sizeof(TransactionId)) == 0)
+		/* XXX we assume the unused bits in "status" are zeroed */
+		if (memcmp(members, entry->members, nmembers * sizeof(MultiXactMember)) == 0)
 		{
 			debug_elog3(DEBUG2, "CacheGet: found %u", entry->multi);
 			return entry->multi;
@@ -1145,14 +1203,14 @@ mXactCacheGetBySet(int nxids, TransactionId *xids)
 
 /*
  * mXactCacheGetById
- *		returns the composing TransactionId set from the cache for a
+ *		returns the composing MultiXactMember set from the cache for a
  *		given MultiXactId, if present.
  *
  * If successful, *xids is set to the address of a palloc'd copy of the
- * TransactionId set.  Return value is number of members, or -1 on failure.
+ * MultiXactMember set.  Return value is number of members, or -1 on failure.
  */
 static int
-mXactCacheGetById(MultiXactId multi, TransactionId **xids)
+mXactCacheGetById(MultiXactId multi, MultiXactMember **members)
 {
 	mXactCacheEnt *entry;
 
@@ -1162,18 +1220,18 @@ mXactCacheGetById(MultiXactId multi, TransactionId **xids)
 	{
 		if (entry->multi == multi)
 		{
-			TransactionId *ptr;
+			MultiXactMember *ptr;
 			Size		size;
 
-			size = sizeof(TransactionId) * entry->nxids;
-			ptr = (TransactionId *) palloc(size);
-			*xids = ptr;
+			size = sizeof(MultiXactMember) * entry->nmembers;
+			ptr = (MultiXactMember *) palloc(size);
+			*members = ptr;
 
-			memcpy(ptr, entry->xids, size);
+			memcpy(ptr, entry->members, size);
 
 			debug_elog3(DEBUG2, "CacheGet: found %s",
-						mxid_to_string(multi, entry->nxids, entry->xids));
-			return entry->nxids;
+						mxid_to_string(multi, entry->nmembers, entry->members));
+			return entry->nmembers;
 		}
 	}
 
@@ -1186,12 +1244,12 @@ mXactCacheGetById(MultiXactId multi, TransactionId **xids)
  *		Add a new MultiXactId and its composing set into the local cache.
  */
 static void
-mXactCachePut(MultiXactId multi, int nxids, TransactionId *xids)
+mXactCachePut(MultiXactId multi, int nmembers, MultiXactMember *members)
 {
 	mXactCacheEnt *entry;
 
 	debug_elog3(DEBUG2, "CachePut: storing %s",
-				mxid_to_string(multi, nxids, xids));
+				mxid_to_string(multi, nmembers, members));
 
 	if (MXactContext == NULL)
 	{
@@ -1206,15 +1264,15 @@ mXactCachePut(MultiXactId multi, int nxids, TransactionId *xids)
 
 	entry = (mXactCacheEnt *)
 		MemoryContextAlloc(MXactContext,
-						   offsetof(mXactCacheEnt, xids) +
-						   nxids * sizeof(TransactionId));
+						   offsetof(mXactCacheEnt, members) +
+						   nmembers * sizeof(MultiXactMember));
 
 	entry->multi = multi;
-	entry->nxids = nxids;
-	memcpy(entry->xids, xids, nxids * sizeof(TransactionId));
+	entry->nmembers = nmembers;
+	memcpy(entry->members, members, nmembers * sizeof(MultiXactMember));
 
 	/* mXactCacheGetBySet assumes the entries are sorted, so sort them */
-	qsort(entry->xids, nxids, sizeof(TransactionId), xidComparator);
+	qsort(entry->members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);
 
 	entry->next = MXactCache;
 	MXactCache = entry;
@@ -1222,15 +1280,38 @@ mXactCachePut(MultiXactId multi, int nxids, TransactionId *xids)
 
 #ifdef MULTIXACT_DEBUG
 static char *
-mxid_to_string(MultiXactId multi, int nxids, TransactionId *xids)
+mxstatus_to_string(MultiXactStatus status)
 {
-	char	   *str = palloc(15 * (nxids + 1) + 4);
+	switch (status)
+	{
+		case MultiXactStatusForKeyShare:
+			return "keysh";
+		case MultiXactStatusForShare:
+			return "sh";
+		case MultiXactStatusForUpdate:
+			return "forupd";
+		case MultiXactStatusUpdate:
+			return "upd";
+		case MultiXactStatusKeyUpdate:
+			return "keyup";
+		default:
+			elog(ERROR, "unrecognized multixact status %d", status);
+			return "";
+	}
+}
+
+static char *
+mxid_to_string(MultiXactId multi, int nmembers, MultiXactMember *members)
+{
+	char	   *str = palloc(15 * (nmembers + 1) + 4);
 	int			i;
 
-	snprintf(str, 47, "%u %d[%u", multi, nxids, xids[0]);
+	snprintf(str, 47, "%u %d[%u (%s)", multi, nmembers, members[0].xid,
+			 mxstatus_to_string(members[0].status));
 
-	for (i = 1; i < nxids; i++)
-		snprintf(str + strlen(str), 17, ", %u", xids[i]);
+	for (i = 1; i < nmembers; i++)
+		snprintf(str + strlen(str), 17, ", %u (%s)", members[i].xid,
+				 mxstatus_to_string(members[i].status));
 
 	strcat(str, "]");
 	return str;
@@ -1247,16 +1328,6 @@ void
 AtEOXact_MultiXact(void)
 {
 	/*
-	 * Reset our OldestMemberMXactId and OldestVisibleMXactId values, both of
-	 * which should only be valid while within a transaction.
-	 *
-	 * We assume that storing a MultiXactId is atomic and so we need not take
-	 * MultiXactGenLock to do this.
-	 */
-	OldestMemberMXactId[MyBackendId] = InvalidMultiXactId;
-	OldestVisibleMXactId[MyBackendId] = InvalidMultiXactId;
-
-	/*
 	 * Discard the local MultiXactId cache.  Since MXactContext was created as
 	 * a child of TopTransactionContext, we needn't delete it explicitly.
 	 */
@@ -1267,18 +1338,11 @@ AtEOXact_MultiXact(void)
 /*
  * AtPrepare_MultiXact
  *		Save multixact state at 2PC tranasction prepare
- *
- * In this phase, we only store our OldestMemberMXactId value in the two-phase
- * state file.
  */
 void
 AtPrepare_MultiXact(void)
 {
-	MultiXactId myOldestMember = OldestMemberMXactId[MyBackendId];
-
-	if (MultiXactIdIsValid(myOldestMember))
-		RegisterTwoPhaseRecord(TWOPHASE_RM_MULTIXACT_ID, 0,
-							   &myOldestMember, sizeof(MultiXactId));
+	/* nothing to do */
 }
 
 /*
@@ -1288,41 +1352,6 @@ AtPrepare_MultiXact(void)
 void
 PostPrepare_MultiXact(TransactionId xid)
 {
-	MultiXactId myOldestMember;
-
-	/*
-	 * Transfer our OldestMemberMXactId value to the slot reserved for the
-	 * prepared transaction.
-	 */
-	myOldestMember = OldestMemberMXactId[MyBackendId];
-	if (MultiXactIdIsValid(myOldestMember))
-	{
-		BackendId	dummyBackendId = TwoPhaseGetDummyBackendId(xid);
-
-		/*
-		 * Even though storing MultiXactId is atomic, acquire lock to make
-		 * sure others see both changes, not just the reset of the slot of the
-		 * current backend. Using a volatile pointer might suffice, but this
-		 * isn't a hot spot.
-		 */
-		LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
-
-		OldestMemberMXactId[dummyBackendId] = myOldestMember;
-		OldestMemberMXactId[MyBackendId] = InvalidMultiXactId;
-
-		LWLockRelease(MultiXactGenLock);
-	}
-
-	/*
-	 * We don't need to transfer OldestVisibleMXactId value, because the
-	 * transaction is not going to be looking at any more multixacts once it's
-	 * prepared.
-	 *
-	 * We assume that storing a MultiXactId is atomic and so we need not take
-	 * MultiXactGenLock to do this.
-	 */
-	OldestVisibleMXactId[MyBackendId] = InvalidMultiXactId;
-
 	/*
 	 * Discard the local MultiXactId cache like in AtEOX_MultiXact
 	 */
@@ -1338,17 +1367,7 @@ void
 multixact_twophase_recover(TransactionId xid, uint16 info,
 						   void *recdata, uint32 len)
 {
-	BackendId	dummyBackendId = TwoPhaseGetDummyBackendId(xid);
-	MultiXactId oldestMember;
-
-	/*
-	 * Get the oldest member XID from the state file record, and set it in the
-	 * OldestMemberMXactId slot reserved for this prepared transaction.
-	 */
-	Assert(len == sizeof(MultiXactId));
-	oldestMember = *((MultiXactId *) recdata);
-
-	OldestMemberMXactId[dummyBackendId] = oldestMember;
+	/* nothing to do */
 }
 
 /*
@@ -1359,11 +1378,7 @@ void
 multixact_twophase_postcommit(TransactionId xid, uint16 info,
 							  void *recdata, uint32 len)
 {
-	BackendId	dummyBackendId = TwoPhaseGetDummyBackendId(xid);
-
-	Assert(len == sizeof(MultiXactId));
-
-	OldestMemberMXactId[dummyBackendId] = InvalidMultiXactId;
+	/* nothing to do */
 }
 
 /*
@@ -1374,7 +1389,7 @@ void
 multixact_twophase_postabort(TransactionId xid, uint16 info,
 							 void *recdata, uint32 len)
 {
-	multixact_twophase_postcommit(xid, info, recdata, len);
+	/* nothing to do */
 }
 
 /*
@@ -1387,11 +1402,7 @@ MultiXactShmemSize(void)
 {
 	Size		size;
 
-#define SHARED_MULTIXACT_STATE_SIZE \
-	add_size(sizeof(MultiXactStateData), \
-			 mul_size(sizeof(MultiXactId) * 2, MaxOldestSlot))
-
-	size = SHARED_MULTIXACT_STATE_SIZE;
+	size = sizeof(MultiXactStateData);
 	size = add_size(size, SimpleLruShmemSize(NUM_MXACTOFFSET_BUFFERS, 0));
 	size = add_size(size, SimpleLruShmemSize(NUM_MXACTMEMBER_BUFFERS, 0));
 
@@ -1417,24 +1428,17 @@ MultiXactShmemInit(void)
 
 	/* Initialize our shared state struct */
 	MultiXactState = ShmemInitStruct("Shared MultiXact State",
-									 SHARED_MULTIXACT_STATE_SIZE,
+									 sizeof(MultiXactStateData),
 									 &found);
 	if (!IsUnderPostmaster)
 	{
 		Assert(!found);
 
 		/* Make sure we zero out the per-backend state */
-		MemSet(MultiXactState, 0, SHARED_MULTIXACT_STATE_SIZE);
+		MemSet(MultiXactState, 0, sizeof(MultiXactStateData));
 	}
 	else
 		Assert(found);
-
-	/*
-	 * Set up array pointers.  Note that perBackendXactIds[0] is wasted space
-	 * since we only use indexes 1..MaxOldestSlot in each array.
-	 */
-	OldestMemberMXactId = MultiXactState->perBackendXactIds;
-	OldestVisibleMXactId = OldestMemberMXactId + MaxOldestSlot;
 }
 
 /*
@@ -1450,7 +1454,7 @@ BootStrapMultiXact(void)
 	LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
 
 	/* Create and zero the first page of the offsets log */
-	slotno = ZeroMultiXactOffsetPage(0, false);
+	slotno = ZeroMultiXactOffsetPage(0, false, InvalidTransactionId, 0);
 
 	/* Make sure it's written out */
 	SimpleLruWritePage(MultiXactOffsetCtl, slotno);
@@ -1474,26 +1478,40 @@ BootStrapMultiXact(void)
  * Initialize (or reinitialize) a page of MultiXactOffset to zeroes.
  * If writeXlog is TRUE, also emit an XLOG record saying we did this.
  *
+ * If truncateXid is valid, store it in the first position of the page.
+ *
  * The page is not actually written, just set up in shared memory.
  * The slot number of the new page is returned.
  *
  * Control lock must be held at entry, and will be held at exit.
  */
 static int
-ZeroMultiXactOffsetPage(int pageno, bool writeXlog)
+ZeroMultiXactOffsetPage(int pageno, bool writeXlog, TransactionId truncateXid,
+						uint32 truncateXidEpoch)
 {
 	int			slotno;
 
 	slotno = SimpleLruZeroPage(MultiXactOffsetCtl, pageno);
 
 	if (writeXlog)
-		WriteMZeroPageXlogRec(pageno, XLOG_MULTIXACT_ZERO_OFF_PAGE);
+		WriteMZeroOffsetPageXlogRec(pageno, truncateXid, truncateXidEpoch);
+
+	if (TransactionIdIsValid(truncateXid))
+	{
+		MultiXactOffset *offptr;
+
+		offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
+		*(offptr++) = truncateXid;
+		*offptr = truncateXidEpoch;
+
+		MultiXactOffsetCtl->shared->page_dirty[slotno] = true;
+	}
 
 	return slotno;
 }
 
 /*
- * Ditto, for MultiXactMember
+ * Ditto for MultiXactMember, except these don't worry about truncation info.
  */
 static int
 ZeroMultiXactMemberPage(int pageno, bool writeXlog)
@@ -1503,7 +1521,7 @@ ZeroMultiXactMemberPage(int pageno, bool writeXlog)
 	slotno = SimpleLruZeroPage(MultiXactMemberCtl, pageno);
 
 	if (writeXlog)
-		WriteMZeroPageXlogRec(pageno, XLOG_MULTIXACT_ZERO_MEM_PAGE);
+		WriteMZeroMemberPageXlogRec(pageno);
 
 	return slotno;
 }
@@ -1525,6 +1543,7 @@ StartupMultiXact(void)
 	MultiXactOffset offset = MultiXactState->nextOffset;
 	int			pageno;
 	int			entryno;
+	int			flagsoff;
 
 	/* Clean up offsets state */
 	LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
@@ -1569,28 +1588,30 @@ StartupMultiXact(void)
 	 * Zero out the remainder of the current members page.	See notes in
 	 * TrimCLOG() for motivation.
 	 */
-	entryno = MXOffsetToMemberEntry(offset);
-	if (entryno != 0)
+	flagsoff = MXOffsetToFlagsOffset(offset);
+	if (flagsoff != 0)
 	{
 		int			slotno;
 		TransactionId *xidptr;
+		int			memberoff;
 
+		memberoff = MXOffsetToMemberOffset(offset);
 		slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, offset);
-		xidptr = (TransactionId *) MultiXactMemberCtl->shared->page_buffer[slotno];
-		xidptr += entryno;
+		xidptr = (TransactionId *)
+			(MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
 
-		MemSet(xidptr, 0, BLCKSZ - (entryno * sizeof(TransactionId)));
+		MemSet(xidptr, 0, BLCKSZ - memberoff);
+
+		/*
+		 * Note: we don't need to zero out the flag bits in the remaining
+		 * members of the current group, because they are always reset before
+		 * writing.
+		 */
 
 		MultiXactMemberCtl->shared->page_dirty[slotno] = true;
 	}
 
 	LWLockRelease(MultiXactMemberControlLock);
-
-	/*
-	 * Initialize lastTruncationPoint to invalid, ensuring that the first
-	 * checkpoint will try to do truncation.
-	 */
-	MultiXactState->lastTruncationPoint = InvalidMultiXactId;
 }
 
 /*
@@ -1607,22 +1628,31 @@ ShutdownMultiXact(void)
 }
 
 /*
- * Get the next MultiXactId and offset to save in a checkpoint record
+ * Get the next MultiXactId, offset and truncate info to save in a checkpoint
+ * record
  */
 void
 MultiXactGetCheckptMulti(bool is_shutdown,
 						 MultiXactId *nextMulti,
-						 MultiXactOffset *nextMultiOffset)
+						 MultiXactOffset *nextMultiOffset,
+						 TransactionId *oldestTruncateXid,
+						 uint32 *oldestTruncateXidEpoch,
+						 MultiXactId *oldestMulti)
 {
 	LWLockAcquire(MultiXactGenLock, LW_SHARED);
 
 	*nextMulti = MultiXactState->nextMXact;
 	*nextMultiOffset = MultiXactState->nextOffset;
+	*oldestTruncateXid = MultiXactState->truncateXid;
+	*oldestTruncateXidEpoch = MultiXactState->truncateXidEpoch;
+	*oldestMulti = MultiXactState->oldestMultiXactId;
 
 	LWLockRelease(MultiXactGenLock);
 
-	debug_elog4(DEBUG2, "MultiXact: checkpoint is nextMulti %u, nextOffset %u",
-				*nextMulti, *nextMultiOffset);
+	debug_elog7(DEBUG2,
+				"MultiXact: checkpoint is nextMulti %u, nextOffset %u; truncate xid %u, epoch %u; oldest multi %u",
+				*nextMulti, *nextMultiOffset, *oldestTruncateXid,
+				*oldestTruncateXidEpoch, *oldestMulti);
 }
 
 /*
@@ -1637,17 +1667,6 @@ CheckPointMultiXact(void)
 	SimpleLruFlush(MultiXactOffsetCtl, true);
 	SimpleLruFlush(MultiXactMemberCtl, true);
 
-	/*
-	 * Truncate the SLRU files.  This could be done at any time, but
-	 * checkpoint seems a reasonable place for it.	There is one exception: if
-	 * we are called during xlog recovery, then shared->latest_page_number
-	 * isn't valid (because StartupMultiXact hasn't been called yet) and so
-	 * SimpleLruTruncate would get confused.  It seems best not to risk
-	 * removing any data during recovery anyway, so don't truncate.
-	 */
-	if (!RecoveryInProgress())
-		TruncateMultiXact();
-
 	TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_DONE(true);
 }
 
@@ -1670,7 +1689,7 @@ MultiXactSetNextMXact(MultiXactId nextMulti,
 
 /*
  * Ensure the next-to-be-assigned MultiXactId is at least minMulti,
- * and similarly nextOffset is at least minMultiOffset
+ * and similarly nextOffset is at least minMultiOffset.
  *
  * This is used when we can determine minimum safe values from an XLog
  * record (either an on-line checkpoint or an mxact creation log entry).
@@ -1696,6 +1715,9 @@ MultiXactAdvanceNextMXact(MultiXactId minMulti,
 /*
  * Make sure that MultiXactOffset has room for a newly-allocated MultiXactId.
  *
+ * If the newly allocated page is the first page on the segment, store an
+ * appropriate truncate Xid value in the page first position.
+ *
  * NB: this is called while holding MultiXactGenLock.  We want it to be very
  * fast most of the time; even when it's not so fast, no actual I/O need
  * happen unless we're forced to write out a dirty log or xlog page to make
@@ -1705,6 +1727,8 @@ static void
 ExtendMultiXactOffset(MultiXactId multi)
 {
 	int			pageno;
+	TransactionId truncateXid;
+	uint32		truncateXidEpoch;
 
 	/*
 	 * No work except at first MultiXactId of a page.  But beware: just after
@@ -1716,12 +1740,49 @@ ExtendMultiXactOffset(MultiXactId multi)
 
 	pageno = MultiXactIdToOffsetPage(multi);
 
-	LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
+	/*
+	 * Determine the truncateXid and epoch that the new segment needs, if
+	 * this is the first page of the segment.
+	 */
+	if (pageno % SLRU_PAGES_PER_SEGMENT == 0)
+	{
+		TransactionId	nextXid;
+
+		Assert(TransactionIdIsValid(RecentGlobalXmin));
+		truncateXid = RecentGlobalXmin;
+
+		GetNextXidAndEpoch(&nextXid, &truncateXidEpoch);
+		/*
+		 * nextXid is certainly logically later than RecentGlobalXmin.  So if
+		 * it's numerically less, it must have wrapped into the next epoch.
+		 */
+		if (nextXid < truncateXid)
+			truncateXidEpoch--;
+	}
+	else
+	{
+		truncateXid = InvalidTransactionId;
+		truncateXidEpoch = 0;
+	}
 
-	/* Zero the page and make an XLOG entry about it */
-	ZeroMultiXactOffsetPage(pageno, true);
+	LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
 
+	/*
+	 * Zero the page, mark it with its truncate info, and make an XLOG entry
+	 * about it.
+	 */
+	ZeroMultiXactOffsetPage(pageno, true, truncateXid, truncateXidEpoch);
 	LWLockRelease(MultiXactOffsetControlLock);
+
+	/*
+	 * Finally, record the new truncation point in shared memory, if
+	 * there isn't one already.
+	 */
+	if (!TransactionIdIsValid(MultiXactState->truncateXid))
+	{
+		MultiXactState->truncateXid = truncateXid;
+		MultiXactState->truncateXidEpoch = truncateXidEpoch;
+	}
 }
 
 /*
@@ -1742,13 +1803,16 @@ ExtendMultiXactMember(MultiXactOffset offset, int nmembers)
 	 */
 	while (nmembers > 0)
 	{
-		int			entryno;
+		int			flagsoff;
+		int			flagsbit;
+		int			difference;
 
 		/*
 		 * Only zero when at first entry of a page.
 		 */
-		entryno = MXOffsetToMemberEntry(offset);
-		if (entryno == 0)
+		flagsoff = MXOffsetToFlagsOffset(offset);
+		flagsbit = MXOffsetToFlagsBitShift(offset);
+		if (flagsoff == 0 && flagsbit == 0)
 		{
 			int			pageno;
 
@@ -1763,122 +1827,241 @@ ExtendMultiXactMember(MultiXactOffset offset, int nmembers)
 		}
 
 		/* Advance to next page (OK if nmembers goes negative) */
-		offset += (MULTIXACT_MEMBERS_PER_PAGE - entryno);
-		nmembers -= (MULTIXACT_MEMBERS_PER_PAGE - entryno);
+		difference = MULTIXACT_MEMBERS_PER_PAGE - offset % MULTIXACT_MEMBERS_PER_PAGE;
+		offset += difference;
+		nmembers -= difference;
+	}
+}
+
+/*
+ * Complete a SegmentInfo with the truncate Xid and epoch, as read from its
+ * first page.
+ */
+static void
+fillSegmentInfoData(SlruCtl ctl, SegmentInfo *segment)
+{
+	int			slotno;
+	MultiXactId *offptr;
+
+	/* lock is acquired by SimpleLruReadPage_ReadOnly */
+	/* FIXME it'd be nice not to trash the entire SLRU cache while at this */
+	slotno = SimpleLruReadPage_ReadOnly(ctl, segment->segno, InvalidTransactionId);
+	offptr = (MultiXactId *) MultiXactOffsetCtl->shared->page_buffer[slotno];
+	segment->truncateXid = *offptr;
+	offptr++;
+	segment->truncateXidEpoch = *offptr;
+	offptr++;
+	segment->firstOffset = *offptr;
+	LWLockRelease(ctl->shared->ControlLock);
+}
+
+/* SegmentInfo comparator, for qsort and bsearch */
+static int
+compareTruncateXidEpoch(const void *a, const void *b)
+{
+	const SegmentInfo *sega = (const SegmentInfo *) a;
+	const SegmentInfo *segb = (const SegmentInfo *) b;
+	uint32	epocha = sega->truncateXidEpoch;
+	uint32	epochb = segb->truncateXidEpoch;
+	TransactionId	xida = sega->truncateXid;
+	TransactionId	xidb = segb->truncateXid;
+
+	if (epocha < epochb)
+		return -1;
+	if (epocha > epochb)
+		return 1;
+	if (xida < xidb)
+		return -1;
+	if (xida > xidb)
+		return 1;
+	return 0;
+}
+
+/*
+ * SlruScanDirectory callback
+ * 		This callback is in charge of scanning all existing segments,
+ * 		to determine their respective truncation points.
+ *
+ * This does not delete any segments.
+ */
+static bool
+mxactSlruGathererCb(SlruCtl ctl, char *segname, int segpage,
+					void *data)
+{
+	TruncateCbData *truncdata = (TruncateCbData *) data;
+	SegmentInfo		seg;
+
+	/*
+	 * Keep track of the truncate Xid and other data for the caller to sort out
+	 * the new truncation point.
+	 */
+	seg.segno = segpage % SLRU_PAGES_PER_SEGMENT;
+	fillSegmentInfoData(ctl, &seg);
+
+	if (truncdata->remaining == NULL)
+	{
+		truncdata->remaining_alloc = 8;
+		truncdata->remaining_used = 0;
+		truncdata->remaining = palloc(truncdata->remaining_alloc *
+									  sizeof(SegmentInfo));
 	}
+	else if (truncdata->remaining_used == truncdata->remaining_alloc - 1)
+	{
+		truncdata->remaining_alloc *= 2;
+		truncdata->remaining = repalloc(truncdata->remaining,
+										truncdata->remaining_alloc);
+	}
+	truncdata->remaining[truncdata->remaining_used++] = seg;
+
+	return false;	/* keep going */
 }
 
 /*
  * Remove all MultiXactOffset and MultiXactMember segments before the oldest
  * ones still of interest.
  *
+ * The truncation rules for the Offset SLRU area are:
+ *
+ * 1. the current segment is never to be deleted.
+ * 2. for all the remaining segments, keep track of their respective number
+ *    and truncate Xid info.  The caller is to determine the new truncation
+ *    point from this data.
+ *
  * This is called only during checkpoints.	We assume no more than one
  * backend does this at a time.
  *
  * XXX do we have any issues with needing to checkpoint here?
  */
-static void
-TruncateMultiXact(void)
+void
+TruncateMultiXact(TransactionId frozenXid)
 {
-	MultiXactId nextMXact;
-	MultiXactOffset nextOffset;
-	MultiXactId oldestMXact;
-	MultiXactOffset oldestOffset;
+	TransactionId	currentXid;
+	uint32		frozenXidEpoch;
+	TruncateCbData	truncdata;
+	SegmentInfo *truncateSegment;
+	SegmentInfo	frozenPosition;
 	int			cutoffPage;
 	int			i;
+	TransactionId	newTruncateXid;
+	int		newTruncateXidEpoch;
 
 	/*
-	 * First, compute where we can safely truncate.  Per notes above, this is
-	 * the oldest valid value among all the OldestMemberMXactId[] and
-	 * OldestVisibleMXactId[] entries, or nextMXact if none are valid.
+	 * Quick exit #1: if the truncateXid is not valid, bail out.  We do this
+	 * check without a lock so that it's fast in the common case when there's
+	 * only one segment (which cannot be removed).  If a concurrent backend is
+	 * creating a new segment, no problem: it just means we delay removing
+	 * files until we're next called.  This assumes that storing an aligned
+	 * 32-bit value is atomic.
 	 */
-	LWLockAcquire(MultiXactGenLock, LW_SHARED);
+	if (!TransactionIdIsValid(MultiXactState->truncateXid))
+		return;
 
 	/*
-	 * We have to beware of the possibility that nextMXact is in the
-	 * wrapped-around state.  We don't fix the counter itself here, but we
-	 * must be sure to use a valid value in our calculation.
+	 * Compute the epoch corresponding to the frozenXid value we were given.
+	 *
+	 * The current Xid value must be logically newer than frozenXid, so if it's
+	 * numerically lower, it must belong to the next epoch.
 	 */
-	nextMXact = MultiXactState->nextMXact;
-	if (nextMXact < FirstMultiXactId)
-		nextMXact = FirstMultiXactId;
+	GetNextXidAndEpoch(&currentXid, &frozenXidEpoch);
+	if (currentXid < frozenXid)
+		frozenXidEpoch--;
 
-	oldestMXact = nextMXact;
-	for (i = 1; i <= MaxOldestSlot; i++)
+	/*
+	 * Quick exit #2: the oldest segment is not yet old enough to be removed.
+	 * In that case we don't need to scan the whole directory.
+	 */
+	LWLockAcquire(MultiXactGenLock, LW_SHARED);
+	Assert(frozenXidEpoch >= MultiXactState->truncateXidEpoch);
+	if ((frozenXidEpoch == MultiXactState->truncateXidEpoch) &&
+		(frozenXid < MultiXactState->truncateXid))
 	{
-		MultiXactId thisoldest;
-
-		thisoldest = OldestMemberMXactId[i];
-		if (MultiXactIdIsValid(thisoldest) &&
-			MultiXactIdPrecedes(thisoldest, oldestMXact))
-			oldestMXact = thisoldest;
-		thisoldest = OldestVisibleMXactId[i];
-		if (MultiXactIdIsValid(thisoldest) &&
-			MultiXactIdPrecedes(thisoldest, oldestMXact))
-			oldestMXact = thisoldest;
+		LWLockRelease(MultiXactGenLock);
+		return;
 	}
-
-	/* Save the current nextOffset too */
-	nextOffset = MultiXactState->nextOffset;
-
 	LWLockRelease(MultiXactGenLock);
 
-	debug_elog3(DEBUG2, "MultiXact: truncation point = %u", oldestMXact);
-
 	/*
-	 * If we already truncated at this point, do nothing.  This saves time
-	 * when no MultiXacts are getting used, which is probably not uncommon.
+	 * Have our callback scan the SLRU directory to let us determine the
+	 * truncation point.
 	 */
-	if (MultiXactState->lastTruncationPoint == oldestMXact)
-		return;
+	truncdata.remaining_used = 0;
+	truncdata.remaining_alloc = 0;
+	truncdata.remaining = NULL;
+	SlruScanDirectory(MultiXactOffsetCtl, mxactSlruGathererCb, &truncdata);
 
 	/*
-	 * We need to determine where to truncate MultiXactMember.	If we found a
-	 * valid oldest MultiXactId, read its starting offset; otherwise we use
-	 * the nextOffset value we saved above.
+	 * Determine the maximum segment whose truncateXid is less than the
+	 * truncate point.
 	 */
-	if (oldestMXact == nextMXact)
-		oldestOffset = nextOffset;
-	else
+	frozenPosition.truncateXid = frozenXid;
+	frozenPosition.truncateXidEpoch = frozenXidEpoch;
+	truncateSegment = NULL;
+	for (i = 0; i < truncdata.remaining_used; i++)
 	{
-		int			pageno;
-		int			slotno;
-		int			entryno;
-		MultiXactOffset *offptr;
+		if ((compareTruncateXidEpoch(&frozenPosition,
+									 &(truncdata.remaining[i])) > 0) &&
+			(truncateSegment->segno < truncdata.remaining[i].segno))
+		{
+			truncateSegment = &(truncdata.remaining[i]);
+		}
+	}
 
-		/* lock is acquired by SimpleLruReadPage_ReadOnly */
+	/*
+	 * Nothing to delete? This shouldn't happen, due to quick exit #2 above,
+	 * but we'd better cope.
+	 */
+	if (truncateSegment == NULL)
+		return;
 
-		pageno = MultiXactIdToOffsetPage(oldestMXact);
-		entryno = MultiXactIdToOffsetEntry(oldestMXact);
+	/* truncate MultiXactOffset */
+	SimpleLruTruncate(MultiXactOffsetCtl, firstPageOf(truncateSegment->segno));
 
-		slotno = SimpleLruReadPage_ReadOnly(MultiXactOffsetCtl, pageno, oldestMXact);
-		offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
-		offptr += entryno;
-		oldestOffset = *offptr;
+	/*
+	 * And truncate MultiXactMember at the first offset used by the oldest
+	 * remaining segment.
+	 */
+	cutoffPage = MXOffsetToMemberPage(truncateSegment->firstOffset);
 
-		LWLockRelease(MultiXactOffsetControlLock);
-	}
+	SimpleLruTruncate(MultiXactMemberCtl, cutoffPage);
 
 	/*
-	 * The cutoff point is the start of the segment containing oldestMXact. We
-	 * pass the *page* containing oldestMXact to SimpleLruTruncate.
+	 * Finally, update shared memory to keep track of the next usable
+	 * truncation point, if any.  If the truncation point for offsets was the
+	 * last remaining segment, then there's no next truncation point: it will
+	 * be set when the next segment is created.  Otherwise, the second
+	 * remaining segment determines the next truncation point.
 	 */
-	cutoffPage = MultiXactIdToOffsetPage(oldestMXact);
+	newTruncateXid = InvalidTransactionId;
+	newTruncateXidEpoch = 0;
+	for (i = 0; i < truncdata.remaining_used; i++)
+	{
+		if (truncdata.remaining[i].segno == truncateSegment->segno + 1)
+		{
+			newTruncateXid = truncdata.remaining[i].truncateXid;
+			newTruncateXidEpoch = truncdata.remaining[i].truncateXidEpoch;
+			break;
+		}
+	}
 
-	SimpleLruTruncate(MultiXactOffsetCtl, cutoffPage);
+	LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
 
 	/*
-	 * Also truncate MultiXactMember at the previously determined offset.
+	 * FIXME there's a race condition here: somebody might have created a new
+	 * segment after we finished scanning the dir.  That scenario would leave
+	 * us with an invalid truncateXid in shared memory, which is not an easy
+	 * situation to get out of.  Needs more thought.
 	 */
-	cutoffPage = MXOffsetToMemberPage(oldestOffset);
 
-	SimpleLruTruncate(MultiXactMemberCtl, cutoffPage);
+	MultiXactState->truncateXid = newTruncateXid;
+	MultiXactState->truncateXidEpoch = newTruncateXidEpoch;
 
 	/*
-	 * Set the last known truncation point.  We don't need a lock for this
-	 * since only one backend does checkpoints at a time.
+	 * we also set the oldest visible MultiXactId to the frozenXid value we
+	 * were given; although the segments we kept may have values earlier than
+	 * that, they are not supposed to remain on disk anyway.
 	 */
-	MultiXactState->lastTruncationPoint = oldestMXact;
+	MultiXactState->oldestMultiXactId = frozenXid;
+	LWLockRelease(MultiXactGenLock);
 }
 
 /*
@@ -1947,13 +2130,29 @@ MultiXactOffsetPrecedes(MultiXactOffset offset1, MultiXactOffset offset2)
 	return (diff < 0);
 }
 
+static void
+WriteMZeroOffsetPageXlogRec(int pageno, TransactionId truncateXid,
+							uint32 truncateXidEpoch)
+{
+	XLogRecData	rdata;
+	MxactZeroOffPg zerooff;
+
+	zerooff.pageno = pageno;
+	zerooff.truncateXid = truncateXid;
+	zerooff.truncateXidEpoch = truncateXidEpoch;
+
+	rdata.data = (char *) (&zerooff);
+	rdata.len = sizeof(MxactZeroOffPg);
+	rdata.buffer = InvalidBuffer;
+	rdata.next = NULL;
+	(void) XLogInsert(RM_MULTIXACT_ID, XLOG_MULTIXACT_ZERO_OFF_PAGE, &rdata);
+}
 
 /*
- * Write an xlog record reflecting the zeroing of either a MEMBERs or
- * OFFSETs page (info shows which)
+ * Write an xlog record reflecting the zeroing of either a MEMBERs page.
  */
 static void
-WriteMZeroPageXlogRec(int pageno, uint8 info)
+WriteMZeroMemberPageXlogRec(int pageno)
 {
 	XLogRecData rdata;
 
@@ -1961,7 +2160,7 @@ WriteMZeroPageXlogRec(int pageno, uint8 info)
 	rdata.len = sizeof(int);
 	rdata.buffer = InvalidBuffer;
 	rdata.next = NULL;
-	(void) XLogInsert(RM_MULTIXACT_ID, info, &rdata);
+	(void) XLogInsert(RM_MULTIXACT_ID, XLOG_MULTIXACT_ZERO_MEM_PAGE, &rdata);
 }
 
 /*
@@ -1977,18 +2176,25 @@ multixact_redo(XLogRecPtr lsn, XLogRecord *record)
 
 	if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE)
 	{
-		int			pageno;
+		MxactZeroOffPg zerooff;
 		int			slotno;
 
-		memcpy(&pageno, XLogRecGetData(record), sizeof(int));
+		memcpy(&zerooff, XLogRecGetData(record), sizeof(MxactZeroOffPg));
 
 		LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
 
-		slotno = ZeroMultiXactOffsetPage(pageno, false);
+		slotno = ZeroMultiXactOffsetPage(zerooff.pageno, false,
+										 zerooff.truncateXid,
+										 zerooff.truncateXidEpoch);
 		SimpleLruWritePage(MultiXactOffsetCtl, slotno);
 		Assert(!MultiXactOffsetCtl->shared->page_dirty[slotno]);
 
 		LWLockRelease(MultiXactOffsetControlLock);
+
+		LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
+		if (!TransactionIdIsValid(MultiXactState->truncateXid))
+			MultiXactState->truncateXid = zerooff.truncateXid;
+		LWLockRelease(MultiXactGenLock);
 	}
 	else if (info == XLOG_MULTIXACT_ZERO_MEM_PAGE)
 	{
@@ -2008,15 +2214,18 @@ multixact_redo(XLogRecPtr lsn, XLogRecord *record)
 	else if (info == XLOG_MULTIXACT_CREATE_ID)
 	{
 		xl_multixact_create *xlrec = (xl_multixact_create *) XLogRecGetData(record);
-		TransactionId *xids = xlrec->xids;
+		MultiXactMember *members = xlrec->members;
 		TransactionId max_xid;
 		int			i;
 
 		/* Store the data back into the SLRU files */
-		RecordNewMultiXact(xlrec->mid, xlrec->moff, xlrec->nxids, xids);
+		RecordNewMultiXact(xlrec->mid, xlrec->moff, xlrec->nmembers, members);
 
-		/* Make sure nextMXact/nextOffset are beyond what this record has */
-		MultiXactAdvanceNextMXact(xlrec->mid + 1, xlrec->moff + xlrec->nxids);
+		/*
+		 * Make sure nextMXact/nextOffset are beyond what this record has.
+		 * We cannot compute a truncateXid from this.
+		 */
+		MultiXactAdvanceNextMXact(xlrec->mid + 1, xlrec->moff + xlrec->nmembers);
 
 		/*
 		 * Make sure nextXid is beyond any XID mentioned in the record. This
@@ -2024,10 +2233,10 @@ multixact_redo(XLogRecPtr lsn, XLogRecord *record)
 		 * evidence in the XLOG, but let's be safe.
 		 */
 		max_xid = record->xl_xid;
-		for (i = 0; i < xlrec->nxids; i++)
+		for (i = 0; i < xlrec->nmembers; i++)
 		{
-			if (TransactionIdPrecedes(max_xid, xids[i]))
-				max_xid = xids[i];
+			if (TransactionIdPrecedes(max_xid, members[i].xid))
+				max_xid = members[i].xid;
 		}
 
 		/*
@@ -2055,10 +2264,13 @@ multixact_desc(StringInfo buf, uint8 xl_info, char *rec)
 
 	if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE)
 	{
-		int			pageno;
+		MxactZeroOffPg zerooff;
 
-		memcpy(&pageno, rec, sizeof(int));
-		appendStringInfo(buf, "zero offsets page: %d", pageno);
+		memcpy(&zerooff, XLogRecGetData(rec), sizeof(MxactZeroOffPg));
+		appendStringInfo(buf, "zero offsets page: %d truncate: %u/%u",
+						 zerooff.pageno,
+						 zerooff.truncateXidEpoch,
+						 zerooff.truncateXid);
 	}
 	else if (info == XLOG_MULTIXACT_ZERO_MEM_PAGE)
 	{
@@ -2072,10 +2284,11 @@ multixact_desc(StringInfo buf, uint8 xl_info, char *rec)
 		xl_multixact_create *xlrec = (xl_multixact_create *) rec;
 		int			i;
 
+		/* XXX describe status too? */
 		appendStringInfo(buf, "create multixact %u offset %u:",
 						 xlrec->mid, xlrec->moff);
-		for (i = 0; i < xlrec->nxids; i++)
-			appendStringInfo(buf, " %u", xlrec->xids[i]);
+		for (i = 0; i < xlrec->nmembers; i++)
+			appendStringInfo(buf, " %u", xlrec->members[i].xid);
 	}
 	else
 		appendStringInfo(buf, "UNKNOWN");
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 85f79b9..facf6f0 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -7792,7 +7792,10 @@ CreateCheckPoint(int flags)
 
 	MultiXactGetCheckptMulti(shutdown,
 							 &checkPoint.nextMulti,
-							 &checkPoint.nextMultiOffset);
+							 &checkPoint.nextMultiOffset,
+							 &checkPoint.oldestSegTruncateXid,
+							 &checkPoint.oldestSegTruncateXidEpoch,
+							 &checkPoint.oldestMultiXactId);
 
 	/*
 	 * Having constructed the checkpoint record, ensure all shmem disk buffers
@@ -7930,6 +7933,15 @@ CreateCheckPoint(int flags)
 	if (!RecoveryInProgress())
 		TruncateSUBTRANS(GetOldestXmin(true, false));
 
+	/*
+	 * Also truncate pg_multixact if possible.  We can throw away all data
+	 * before the oldestXid value used by the most recent vacuum.  As with
+	 * subtrans, skip doing this during recovery, because StartupMultiXact
+	 * hasn't been called yet.
+	 */
+	if (!RecoveryInProgress())
+		TruncateMultiXact(checkPoint.oldestXid);
+
 	/* All real work is done, but log before releasing lock. */
 	if (log_checkpoints)
 		LogCheckpointEnd(false);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 99e130c..078073a 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -3001,7 +3001,7 @@ reindex_relation(Oid relid, int flags)
 
 	/* Ensure rd_indexattr is valid; see comments for RelationSetIndexList */
 	if (is_pg_class)
-		(void) RelationGetIndexAttrBitmap(rel);
+		(void) RelationGetIndexAttrBitmap(rel, false);
 
 	PG_TRY();
 	{
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 32985a4..82f1aa7 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1150,6 +1150,7 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
 					 * right.  (Note: this works out properly when the row was
 					 * both inserted and deleted in our xact.)
 					 */
+					Assert(!(targtuple.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
 					if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(targtuple.t_data)))
 						deadrows += 1;
 					else
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 54660f4..5d0cd9e 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1090,6 +1090,7 @@ read_info(SeqTable elm, Relation rel, Buffer *buf)
 	 * bit update, ie, don't bother to WAL-log it, since we can certainly do
 	 * this again if the update gets lost.
 	 */
+	Assert(!(tuple.t_data->t_infomask & HEAP_XMAX_IS_MULTI));
 	if (HeapTupleHeaderGetXmax(tuple.t_data) != InvalidTransactionId)
 	{
 		HeapTupleHeaderSetXmax(tuple.t_data, InvalidTransactionId);
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index a6e7268..7c1586f 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2578,7 +2578,7 @@ ltrmark:;
 		test = heap_lock_tuple(relation, &tuple, &buffer,
 							   &update_ctid, &update_xmax,
 							   estate->es_output_cid,
-							   LockTupleExclusive, false);
+							   LockTupleUpdate, false);
 		switch (test)
 		{
 			case HeapTupleSelfUpdated:
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index f42504c..37a1ca8 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -680,7 +680,7 @@ vac_update_datfrozenxid(void)
 	 * Initialize the "min" calculation with GetOldestXmin, which is a
 	 * reasonable approximation to the minimum relfrozenxid for not-yet-
 	 * committed pg_class entries for new tables; see AddNewRelationTuple().
-	 * Se we cannot produce a wrong minimum by starting with this.
+	 * So we cannot produce a wrong minimum by starting with this.
 	 */
 	newFrozenXid = GetOldestXmin(true, true);
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index fd7a9ed..d018a95 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -800,7 +800,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 	}
 
 	/*
-	 * Similarly, we have to lock relations selected FOR UPDATE/FOR SHARE
+	 * Similarly, we have to lock relations selected FOR UPDATE/SHARE/KEY SHARE
 	 * before we initialize the plan tree, else we'd be risking lock upgrades.
 	 * While we are at it, build the ExecRowMark list.
 	 */
@@ -820,6 +820,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 		{
 			case ROW_MARK_EXCLUSIVE:
 			case ROW_MARK_SHARE:
+			case ROW_MARK_KEYSHARE:
 				relid = getrelid(rc->rti, rangeTable);
 				relation = heap_open(relid, RowShareLock);
 				break;
@@ -1691,7 +1692,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate,
 	/*
 	 * Get and lock the updated version of the row; if fail, return NULL.
 	 */
-	copyTuple = EvalPlanQualFetch(estate, relation, LockTupleExclusive,
+	copyTuple = EvalPlanQualFetch(estate, relation, LockTupleUpdate,
 								  tid, priorXmax);
 
 	if (copyTuple == NULL)
@@ -1929,7 +1930,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
 		/* updated, so look at the updated row */
 		tuple.t_self = tuple.t_data->t_ctid;
 		/* updated row should have xmin matching this xmax */
-		priorXmax = HeapTupleHeaderGetXmax(tuple.t_data);
+		priorXmax = HeapTupleHeaderGetUpdateXid(tuple.t_data);
 		ReleaseBuffer(buffer);
 		/* loop back to fetch next in chain */
 	}
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 0c48b6b..892fee5 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -111,10 +111,22 @@ lnext:
 		tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
 
 		/* okay, try to lock the tuple */
-		if (erm->markType == ROW_MARK_EXCLUSIVE)
-			lockmode = LockTupleExclusive;
-		else
-			lockmode = LockTupleShared;
+		switch (erm->markType)
+		{
+			case ROW_MARK_EXCLUSIVE:
+				lockmode = LockTupleUpdate;
+				break;
+			case ROW_MARK_SHARE:
+				lockmode = LockTupleShare;
+				break;
+			case ROW_MARK_KEYSHARE:
+				lockmode = LockTupleKeyShare;
+				break;
+			default:
+				elog(ERROR, "unsupported rowmark type");
+				lockmode = LockTupleUpdate;	/* keep compiler quiet */
+				break;
+		}
 
 		test = heap_lock_tuple(erm->relation, &tuple, &buffer,
 							   &update_ctid, &update_xmax,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 63958c3..4345e84 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2028,7 +2028,7 @@ _copyRowMarkClause(RowMarkClause *from)
 	RowMarkClause *newnode = makeNode(RowMarkClause);
 
 	COPY_SCALAR_FIELD(rti);
-	COPY_SCALAR_FIELD(forUpdate);
+	COPY_SCALAR_FIELD(strength);
 	COPY_SCALAR_FIELD(noWait);
 	COPY_SCALAR_FIELD(pushedDown);
 
@@ -2387,7 +2387,7 @@ _copyLockingClause(LockingClause *from)
 	LockingClause *newnode = makeNode(LockingClause);
 
 	COPY_NODE_FIELD(lockedRels);
-	COPY_SCALAR_FIELD(forUpdate);
+	COPY_SCALAR_FIELD(strength);
 	COPY_SCALAR_FIELD(noWait);
 
 	return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index f3a34a1..0f3f914 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2300,7 +2300,7 @@ static bool
 _equalLockingClause(LockingClause *a, LockingClause *b)
 {
 	COMPARE_NODE_FIELD(lockedRels);
-	COMPARE_SCALAR_FIELD(forUpdate);
+	COMPARE_SCALAR_FIELD(strength);
 	COMPARE_SCALAR_FIELD(noWait);
 
 	return true;
@@ -2371,7 +2371,7 @@ static bool
 _equalRowMarkClause(RowMarkClause *a, RowMarkClause *b)
 {
 	COMPARE_SCALAR_FIELD(rti);
-	COMPARE_SCALAR_FIELD(forUpdate);
+	COMPARE_SCALAR_FIELD(strength);
 	COMPARE_SCALAR_FIELD(noWait);
 	COMPARE_SCALAR_FIELD(pushedDown);
 
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index f7d39ed..5340c07 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2077,7 +2077,7 @@ _outLockingClause(StringInfo str, LockingClause *node)
 	WRITE_NODE_TYPE("LOCKINGCLAUSE");
 
 	WRITE_NODE_FIELD(lockedRels);
-	WRITE_BOOL_FIELD(forUpdate);
+	WRITE_ENUM_FIELD(strength, LockClauseStrength);
 	WRITE_BOOL_FIELD(noWait);
 }
 
@@ -2255,7 +2255,7 @@ _outRowMarkClause(StringInfo str, RowMarkClause *node)
 	WRITE_NODE_TYPE("ROWMARKCLAUSE");
 
 	WRITE_UINT_FIELD(rti);
-	WRITE_BOOL_FIELD(forUpdate);
+	WRITE_ENUM_FIELD(strength, LockClauseStrength);
 	WRITE_BOOL_FIELD(noWait);
 	WRITE_BOOL_FIELD(pushedDown);
 }
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 29a0e8f..7c08964 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -301,7 +301,7 @@ _readRowMarkClause(void)
 	READ_LOCALS(RowMarkClause);
 
 	READ_UINT_FIELD(rti);
-	READ_BOOL_FIELD(forUpdate);
+	READ_ENUM_FIELD(strength, LockClauseStrength);
 	READ_BOOL_FIELD(noWait);
 	READ_BOOL_FIELD(pushedDown);
 
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 5b170b3..81b0be1 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -564,11 +564,11 @@ make_outerjoininfo(PlannerInfo *root,
 	Assert(jointype != JOIN_RIGHT);
 
 	/*
-	 * Presently the executor cannot support FOR UPDATE/SHARE marking of rels
+	 * Presently the executor cannot support FOR UPDATE/SHARE/KEY SHARE marking of rels
 	 * appearing on the nullable side of an outer join. (It's somewhat unclear
 	 * what that would mean, anyway: what should we mark when a result row is
 	 * generated from no element of the nullable relation?)  So, complain if
-	 * any nullable rel is FOR UPDATE/SHARE.
+	 * any nullable rel is FOR UPDATE/SHARE/KEY SHARE.
 	 *
 	 * You might be wondering why this test isn't made far upstream in the
 	 * parser.	It's because the parser hasn't got enough info --- consider
@@ -586,7 +586,7 @@ make_outerjoininfo(PlannerInfo *root,
 			(jointype == JOIN_FULL && bms_is_member(rc->rti, left_rels)))
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("SELECT FOR UPDATE/SHARE cannot be applied to the nullable side of an outer join")));
+					 errmsg("SELECT FOR UPDATE/SHARE/KEY SHARE cannot be applied to the nullable side of an outer join")));
 	}
 
 	sjinfo->syn_lefthand = left_rels;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 5c18b72..5c83d10 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1927,7 +1927,7 @@ preprocess_rowmarks(PlannerInfo *root)
 	if (parse->rowMarks)
 	{
 		/*
-		 * We've got trouble if FOR UPDATE/SHARE appears inside grouping,
+		 * We've got trouble if FOR UPDATE/SHARE/KEY SHARE appears inside grouping,
 		 * since grouping renders a reference to individual tuple CTIDs
 		 * invalid.  This is also checked at parse time, but that's
 		 * insufficient because of rule substitution, query pullup, etc.
@@ -1937,7 +1937,7 @@ preprocess_rowmarks(PlannerInfo *root)
 	else
 	{
 		/*
-		 * We only need rowmarks for UPDATE, DELETE, or FOR UPDATE/SHARE.
+		 * We only need rowmarks for UPDATE, DELETE, or FOR UPDATE/SHARE/KEY SHARE.
 		 */
 		if (parse->commandType != CMD_UPDATE &&
 			parse->commandType != CMD_DELETE)
@@ -1947,7 +1947,7 @@ preprocess_rowmarks(PlannerInfo *root)
 	/*
 	 * We need to have rowmarks for all base relations except the target. We
 	 * make a bitmapset of all base rels and then remove the items we don't
-	 * need or have FOR UPDATE/SHARE marks for.
+	 * need or have FOR UPDATE/SHARE/KEY SHARE marks for.
 	 */
 	rels = get_base_rel_indexes((Node *) parse->jointree);
 	if (parse->resultRelation)
@@ -1984,10 +1984,20 @@ preprocess_rowmarks(PlannerInfo *root)
 		newrc = makeNode(PlanRowMark);
 		newrc->rti = newrc->prti = rc->rti;
 		newrc->rowmarkId = ++(root->glob->lastRowMarkId);
-		if (rc->forUpdate)
-			newrc->markType = ROW_MARK_EXCLUSIVE;
-		else
-			newrc->markType = ROW_MARK_SHARE;
+		switch (rc->strength)
+		{
+			case LCS_FORUPDATE:
+				newrc->markType = ROW_MARK_EXCLUSIVE;
+				break;
+			case LCS_FORSHARE:
+				newrc->markType = ROW_MARK_SHARE;
+				break;
+			case LCS_FORKEYSHARE:
+				newrc->markType = ROW_MARK_KEYSHARE;
+				break;
+			default:
+				elog(ERROR, "unsupported rowmark type %d", rc->strength);
+		}
 		newrc->noWait = rc->noWait;
 		newrc->isParent = false;
 
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index e4a4e3a..e2ff39f 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -2310,7 +2310,7 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
 	/* make a clause we can pass down to subqueries to select all rels */
 	allrels = makeNode(LockingClause);
 	allrels->lockedRels = NIL;	/* indicates all rels */
-	allrels->forUpdate = lc->forUpdate;
+	allrels->strength = lc->strength;
 	allrels->noWait = lc->noWait;
 
 	if (lockedRels == NIL)
@@ -2329,12 +2329,12 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
 					if (rte->relkind == RELKIND_FOREIGN_TABLE)
 						break;
 					applyLockingClause(qry, i,
-									   lc->forUpdate, lc->noWait, pushedDown);
+									   lc->strength, lc->noWait, pushedDown);
 					rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
 					break;
 				case RTE_SUBQUERY:
 					applyLockingClause(qry, i,
-									   lc->forUpdate, lc->noWait, pushedDown);
+									   lc->strength, lc->noWait, pushedDown);
 
 					/*
 					 * FOR UPDATE/SHARE of subquery is propagated to all of
@@ -2384,13 +2384,13 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
 											 rte->eref->aliasname),
 									  parser_errposition(pstate, thisrel->location)));
 							applyLockingClause(qry, i,
-											   lc->forUpdate, lc->noWait,
+											   lc->strength, lc->noWait,
 											   pushedDown);
 							rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
 							break;
 						case RTE_SUBQUERY:
 							applyLockingClause(qry, i,
-											   lc->forUpdate, lc->noWait,
+											   lc->strength, lc->noWait,
 											   pushedDown);
 							/* see comment above */
 							transformLockingClause(pstate, rte->subquery,
@@ -2443,7 +2443,7 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
  */
 void
 applyLockingClause(Query *qry, Index rtindex,
-				   bool forUpdate, bool noWait, bool pushedDown)
+				   LockClauseStrength strength, bool noWait, bool pushedDown)
 {
 	RowMarkClause *rc;
 
@@ -2455,10 +2455,10 @@ applyLockingClause(Query *qry, Index rtindex,
 	if ((rc = get_parse_rowmark(qry, rtindex)) != NULL)
 	{
 		/*
-		 * If the same RTE is specified both FOR UPDATE and FOR SHARE, treat
-		 * it as FOR UPDATE.  (Reasonable, since you can't take both a shared
-		 * and exclusive lock at the same time; it'll end up being exclusive
-		 * anyway.)
+		 * If the same RTE is specified for more than one locking strength,
+		 * treat is as the strongest.  (Reasonable, since you can't take both a
+		 * shared and exclusive lock at the same time; it'll end up being
+		 * exclusive anyway.)
 		 *
 		 * We also consider that NOWAIT wins if it's specified both ways. This
 		 * is a bit more debatable but raising an error doesn't seem helpful.
@@ -2467,7 +2467,7 @@ applyLockingClause(Query *qry, Index rtindex,
 		 *
 		 * And of course pushedDown becomes false if any clause is explicit.
 		 */
-		rc->forUpdate |= forUpdate;
+		rc->strength = Max(rc->strength, strength);
 		rc->noWait |= noWait;
 		rc->pushedDown &= pushedDown;
 		return;
@@ -2476,7 +2476,7 @@ applyLockingClause(Query *qry, Index rtindex,
 	/* Make a new RowMarkClause */
 	rc = makeNode(RowMarkClause);
 	rc->rti = rtindex;
-	rc->forUpdate = forUpdate;
+	rc->strength = strength;
 	rc->noWait = noWait;
 	rc->pushedDown = pushedDown;
 	qry->rowMarks = lappend(qry->rowMarks, rc);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c135465..1eb9962 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -8786,7 +8786,7 @@ for_locking_item:
 				{
 					LockingClause *n = makeNode(LockingClause);
 					n->lockedRels = $3;
-					n->forUpdate = TRUE;
+					n->strength = LCS_FORUPDATE;
 					n->noWait = $4;
 					$$ = (Node *) n;
 				}
@@ -8794,10 +8794,18 @@ for_locking_item:
 				{
 					LockingClause *n = makeNode(LockingClause);
 					n->lockedRels = $3;
-					n->forUpdate = FALSE;
+					n->strength = LCS_FORSHARE;
 					n->noWait = $4;
 					$$ = (Node *) n;
 				}
+			| FOR KEY SHARE locked_rels_list opt_nowait
+				{
+					LockingClause *n = makeNode(LockingClause);
+					n->lockedRels = $4;
+					n->strength = LCS_FORKEYSHARE;
+					n->noWait = $5;
+					$$ = (Node *) n;
+				}
 		;
 
 locked_rels_list:
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 3b31108..dc14a0d 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -55,7 +55,7 @@ static void rewriteValuesRTE(RangeTblEntry *rte, Relation target_relation,
 static void rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
 					Relation target_relation);
 static void markQueryForLocking(Query *qry, Node *jtnode,
-					bool forUpdate, bool noWait, bool pushedDown);
+					LockClauseStrength strength, bool noWait, bool pushedDown);
 static List *matchLocks(CmdType event, RuleLock *rulelocks,
 		   int varno, Query *parsetree);
 static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
@@ -1401,8 +1401,8 @@ ApplyRetrieveRule(Query *parsetree,
 	rte->modifiedCols = NULL;
 
 	/*
-	 * If FOR UPDATE/SHARE of view, mark all the contained tables as implicit
-	 * FOR UPDATE/SHARE, the same as the parser would have done if the view's
+	 * If FOR UPDATE/SHARE/KEY SHARE of view, mark all the contained tables as implicit
+	 * FOR UPDATE/SHARE/KEY SHARE, the same as the parser would have done if the view's
 	 * subquery had been written out explicitly.
 	 *
 	 * Note: we don't consider forUpdatePushedDown here; such marks will be
@@ -1410,13 +1410,13 @@ ApplyRetrieveRule(Query *parsetree,
 	 */
 	if (rc != NULL)
 		markQueryForLocking(rule_action, (Node *) rule_action->jointree,
-							rc->forUpdate, rc->noWait, true);
+							rc->strength, rc->noWait, true);
 
 	return parsetree;
 }
 
 /*
- * Recursively mark all relations used by a view as FOR UPDATE/SHARE.
+ * Recursively mark all relations used by a view as FOR UPDATE/SHARE/KEY SHARE.
  *
  * This may generate an invalid query, eg if some sub-query uses an
  * aggregate.  We leave it to the planner to detect that.
@@ -1428,7 +1428,7 @@ ApplyRetrieveRule(Query *parsetree,
  */
 static void
 markQueryForLocking(Query *qry, Node *jtnode,
-					bool forUpdate, bool noWait, bool pushedDown)
+					LockClauseStrength strength, bool noWait, bool pushedDown)
 {
 	if (jtnode == NULL)
 		return;
@@ -1442,16 +1442,16 @@ markQueryForLocking(Query *qry, Node *jtnode,
 			/* ignore foreign tables */
 			if (rte->relkind != RELKIND_FOREIGN_TABLE)
 			{
-				applyLockingClause(qry, rti, forUpdate, noWait, pushedDown);
+				applyLockingClause(qry, rti, strength, noWait, pushedDown);
 				rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
 			}
 		}
 		else if (rte->rtekind == RTE_SUBQUERY)
 		{
-			applyLockingClause(qry, rti, forUpdate, noWait, pushedDown);
-			/* FOR UPDATE/SHARE of subquery is propagated to subquery's rels */
+			applyLockingClause(qry, rti, strength, noWait, pushedDown);
+			/* FOR UPDATE/SHARE/KEY SHARE of subquery is propagated to subquery's rels */
 			markQueryForLocking(rte->subquery, (Node *) rte->subquery->jointree,
-								forUpdate, noWait, true);
+								strength, noWait, true);
 		}
 		/* other RTE types are unaffected by FOR UPDATE */
 	}
@@ -1461,14 +1461,14 @@ markQueryForLocking(Query *qry, Node *jtnode,
 		ListCell   *l;
 
 		foreach(l, f->fromlist)
-			markQueryForLocking(qry, lfirst(l), forUpdate, noWait, pushedDown);
+			markQueryForLocking(qry, lfirst(l), strength, noWait, pushedDown);
 	}
 	else if (IsA(jtnode, JoinExpr))
 	{
 		JoinExpr   *j = (JoinExpr *) jtnode;
 
-		markQueryForLocking(qry, j->larg, forUpdate, noWait, pushedDown);
-		markQueryForLocking(qry, j->rarg, forUpdate, noWait, pushedDown);
+		markQueryForLocking(qry, j->larg, strength, noWait, pushedDown);
+		markQueryForLocking(qry, j->rarg, strength, noWait, pushedDown);
 	}
 	else
 		elog(ERROR, "unrecognized node type: %d",
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 345f6f5..45b7c7b 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -3869,9 +3869,10 @@ CheckForSerializableConflictOut(bool visible, Relation relation,
 		case HEAPTUPLE_RECENTLY_DEAD:
 			if (!visible)
 				return;
-			xid = HeapTupleHeaderGetXmax(tuple->t_data);
+			xid = HeapTupleHeaderGetUpdateXid(tuple->t_data);
 			break;
 		case HEAPTUPLE_DELETE_IN_PROGRESS:
+			Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
 			xid = HeapTupleHeaderGetXmax(tuple->t_data);
 			break;
 		case HEAPTUPLE_INSERT_IN_PROGRESS:
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 5b06333..65f629b 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -130,7 +130,7 @@ CommandIsReadOnly(Node *parsetree)
 				if (stmt->intoClause != NULL)
 					return false;		/* SELECT INTO */
 				else if (stmt->rowMarks != NIL)
-					return false;		/* SELECT FOR UPDATE/SHARE */
+					return false;		/* SELECT FOR UPDATE/SHARE/KEY SHARE */
 				else if (stmt->hasModifyingCTE)
 					return false;		/* data-modifying CTE */
 				else
@@ -2147,10 +2147,21 @@ CreateCommandTag(Node *parsetree)
 						else if (stmt->rowMarks != NIL)
 						{
 							/* not 100% but probably close enough */
-							if (((PlanRowMark *) linitial(stmt->rowMarks))->markType == ROW_MARK_EXCLUSIVE)
-								tag = "SELECT FOR UPDATE";
-							else
-								tag = "SELECT FOR SHARE";
+							switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
+							{
+								case LCS_FORUPDATE:
+									tag = "SELECT FOR UPDATE";
+									break;
+								case LCS_FORSHARE:
+									tag = "SELECT FOR SHARE";
+									break;
+								case LCS_FORKEYSHARE:
+									tag = "SELECT FOR KEY SHARE";
+									break;
+								default:
+									tag =  "???";
+									break;
+							}
 						}
 						else
 							tag = "SELECT";
@@ -2197,10 +2208,21 @@ CreateCommandTag(Node *parsetree)
 						else if (stmt->rowMarks != NIL)
 						{
 							/* not 100% but probably close enough */
-							if (((RowMarkClause *) linitial(stmt->rowMarks))->forUpdate)
-								tag = "SELECT FOR UPDATE";
-							else
-								tag = "SELECT FOR SHARE";
+							switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
+							{
+								case LCS_FORUPDATE:
+									tag = "SELECT FOR UPDATE";
+									break;
+								case LCS_FORSHARE:
+									tag = "SELECT FOR SHARE";
+									break;
+								case LCS_FORKEYSHARE:
+									tag = "SELECT FOR KEY SHARE";
+									break;
+								default:
+									tag =  "???";
+									break;
+							}
 						}
 						else
 							tag = "SELECT";
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 522a540..f4a4456 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -308,7 +308,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 	 * Get the relation descriptors of the FK and PK tables.
 	 *
 	 * pk_rel is opened in RowShareLock mode since that's what our eventual
-	 * SELECT FOR SHARE will get on it.
+	 * SELECT FOR KEY SHARE will get on it.
 	 */
 	fk_rel = trigdata->tg_relation;
 	pk_rel = heap_open(riinfo.pk_relid, RowShareLock);
@@ -338,12 +338,12 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 
 			/* ---------
 			 * The query string built is
-			 *	SELECT 1 FROM ONLY <pktable>
+			 *	SELECT 1 FROM ONLY <pktable> x FOR KEY SHARE OF x
 			 * ----------
 			 */
 			quoteRelationName(pkrelname, pk_rel);
 			snprintf(querystr, sizeof(querystr),
-					 "SELECT 1 FROM ONLY %s x FOR SHARE OF x",
+					 "SELECT 1 FROM ONLY %s x FOR KEY SHARE OF x",
 					 pkrelname);
 
 			/* Prepare and save the plan */
@@ -463,7 +463,8 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 
 		/* ----------
 		 * The query string built is
-		 *	SELECT 1 FROM ONLY <pktable> WHERE pkatt1 = $1 [AND ...] FOR SHARE
+		 *	SELECT 1 FROM ONLY <pktable> x WHERE pkatt1 = $1 [AND ...]
+		 *	       FOR KEY SHARE OF x
 		 * The type id's for the $ parameters are those of the
 		 * corresponding FK attributes.
 		 * ----------
@@ -487,7 +488,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
 			querysep = "AND";
 			queryoids[i] = fk_type;
 		}
-		appendStringInfo(&querybuf, " FOR SHARE OF x");
+		appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
 
 		/* Prepare and save the plan */
 		qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
@@ -625,7 +626,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 
 		/* ----------
 		 * The query string built is
-		 *	SELECT 1 FROM ONLY <pktable> WHERE pkatt1 = $1 [AND ...] FOR SHARE
+		 *	SELECT 1 FROM ONLY <pktable> x WHERE pkatt1 = $1 [AND ...]
+		 *	       FOR KEY SHARE OF x
 		 * The type id's for the $ parameters are those of the
 		 * PK attributes themselves.
 		 * ----------
@@ -648,7 +650,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
 			querysep = "AND";
 			queryoids[i] = pk_type;
 		}
-		appendStringInfo(&querybuf, " FOR SHARE OF x");
+		appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
 
 		/* Prepare and save the plan */
 		qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
@@ -712,7 +714,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 	 * Get the relation descriptors of the FK and PK tables and the old tuple.
 	 *
 	 * fk_rel is opened in RowShareLock mode since that's what our eventual
-	 * SELECT FOR SHARE will get on it.
+	 * SELECT FOR KEY SHARE will get on it.
 	 */
 	fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
 	pk_rel = trigdata->tg_relation;
@@ -780,7 +782,8 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 
 				/* ----------
 				 * The query string built is
-				 *	SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
+				 *	SELECT 1 FROM ONLY <fktable> x WHERE $1 = fkatt1 [AND ...]
+				 *	       FOR KEY SHARE OF x
 				 * The type id's for the $ parameters are those of the
 				 * corresponding PK attributes.
 				 * ----------
@@ -805,7 +808,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
 					querysep = "AND";
 					queryoids[i] = pk_type;
 				}
-				appendStringInfo(&querybuf, " FOR SHARE OF x");
+				appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
 
 				/* Prepare and save the plan */
 				qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
@@ -890,7 +893,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 	 * old tuple.
 	 *
 	 * fk_rel is opened in RowShareLock mode since that's what our eventual
-	 * SELECT FOR SHARE will get on it.
+	 * SELECT FOR KEY SHARE will get on it.
 	 */
 	fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
 	pk_rel = trigdata->tg_relation;
@@ -993,7 +996,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
 					querysep = "AND";
 					queryoids[i] = pk_type;
 				}
-				appendStringInfo(&querybuf, " FOR SHARE OF x");
+				appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
 
 				/* Prepare and save the plan */
 				qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
@@ -1431,7 +1434,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 	 * Get the relation descriptors of the FK and PK tables and the old tuple.
 	 *
 	 * fk_rel is opened in RowShareLock mode since that's what our eventual
-	 * SELECT FOR SHARE will get on it.
+	 * SELECT FOR KEY SHARE will get on it.
 	 */
 	fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
 	pk_rel = trigdata->tg_relation;
@@ -1489,7 +1492,8 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 
 				/* ----------
 				 * The query string built is
-				 *	SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
+				 *	SELECT 1 FROM ONLY <fktable> x WHERE $1 = fkatt1 [AND ...]
+				 *	       FOR KEY SHARE OF x
 				 * The type id's for the $ parameters are those of the
 				 * corresponding PK attributes.
 				 * ----------
@@ -1514,7 +1518,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
 					querysep = "AND";
 					queryoids[i] = pk_type;
 				}
-				appendStringInfo(&querybuf, " FOR SHARE OF x");
+				appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
 
 				/* Prepare and save the plan */
 				qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
@@ -1604,7 +1608,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 	 * old tuple.
 	 *
 	 * fk_rel is opened in RowShareLock mode since that's what our eventual
-	 * SELECT FOR SHARE will get on it.
+	 * SELECT FOR KEY SHARE will get on it.
 	 */
 	fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
 	pk_rel = trigdata->tg_relation;
@@ -1672,7 +1676,8 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 
 				/* ----------
 				 * The query string built is
-				 *	SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
+				 *	SELECT 1 FROM ONLY <fktable> x WHERE $1 = fkatt1 [AND ...]
+				 *	       FOR KEY SHARE OF x
 				 * The type id's for the $ parameters are those of the
 				 * corresponding PK attributes.
 				 * ----------
@@ -1697,7 +1702,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
 					querysep = "AND";
 					queryoids[i] = pk_type;
 				}
-				appendStringInfo(&querybuf, " FOR SHARE OF x");
+				appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
 
 				/* Prepare and save the plan */
 				qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 75923a6..fa1e863 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -2863,7 +2863,7 @@ get_select_query_def(Query *query, deparse_context *context,
 			get_rule_expr(query->limitCount, context, false);
 	}
 
-	/* Add FOR UPDATE/SHARE clauses if present */
+	/* Add FOR UPDATE/SHARE/KEY SHARE clauses if present */
 	if (query->hasForUpdate)
 	{
 		foreach(l, query->rowMarks)
@@ -2875,12 +2875,24 @@ get_select_query_def(Query *query, deparse_context *context,
 			if (rc->pushedDown)
 				continue;
 
-			if (rc->forUpdate)
-				appendContextKeyword(context, " FOR UPDATE",
-									 -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
-			else
-				appendContextKeyword(context, " FOR SHARE",
-									 -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
+			switch (rc->strength)
+			{
+				case LCS_FORKEYSHARE:
+					appendContextKeyword(context, " FOR KEY SHARE",
+										 -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
+					break;
+				case LCS_FORSHARE:
+					appendContextKeyword(context, " FOR SHARE",
+										 -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
+					break;
+				case LCS_FORUPDATE:
+					appendContextKeyword(context, " FOR UPDATE",
+										 -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
+					break;
+				default:
+					elog(ERROR, "unrecognized row locking clause %d", rc->strength);
+			}
+
 			appendStringInfo(buf, " OF %s",
 							 quote_identifier(rte->eref->aliasname));
 			if (rc->noWait)
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 603e4c1..0e8ef6f 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -3624,6 +3624,9 @@ RelationGetIndexPredicate(Relation relation)
  * simple index keys, but attributes used in expressions and partial-index
  * predicates.)
  *
+ * If "keyAttrs" is true, only attributes that can be referenced by foreign
+ * keys are considered.
+ *
  * Attribute numbers are offset by FirstLowInvalidHeapAttributeNumber so that
  * we can include system attributes (e.g., OID) in the bitmap representation.
  *
@@ -3635,16 +3638,17 @@ RelationGetIndexPredicate(Relation relation)
  * be bms_free'd when not needed anymore.
  */
 Bitmapset *
-RelationGetIndexAttrBitmap(Relation relation)
+RelationGetIndexAttrBitmap(Relation relation, bool keyAttrs)
 {
 	Bitmapset  *indexattrs;
+	Bitmapset  *uindexattrs;
 	List	   *indexoidlist;
 	ListCell   *l;
 	MemoryContext oldcxt;
 
 	/* Quick exit if we already computed the result. */
 	if (relation->rd_indexattr != NULL)
-		return bms_copy(relation->rd_indexattr);
+		return bms_copy(keyAttrs ? relation->rd_keyattr : relation->rd_indexattr);
 
 	/* Fast path if definitely no indexes */
 	if (!RelationGetForm(relation)->relhasindex)
@@ -3663,26 +3667,38 @@ RelationGetIndexAttrBitmap(Relation relation)
 	 * For each index, add referenced attributes to indexattrs.
 	 */
 	indexattrs = NULL;
+	uindexattrs = NULL;
 	foreach(l, indexoidlist)
 	{
 		Oid			indexOid = lfirst_oid(l);
 		Relation	indexDesc;
 		IndexInfo  *indexInfo;
 		int			i;
+		bool		isKey;
 
 		indexDesc = index_open(indexOid, AccessShareLock);
 
 		/* Extract index key information from the index's pg_index row */
 		indexInfo = BuildIndexInfo(indexDesc);
 
+		/* Can this index be referenced by a foreign key? */
+		isKey = indexInfo->ii_Unique &&
+				indexInfo->ii_Expressions == NIL &&
+				indexInfo->ii_Predicate == NIL;
+
 		/* Collect simple attribute references */
 		for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
 		{
 			int			attrnum = indexInfo->ii_KeyAttrNumbers[i];
 
 			if (attrnum != 0)
+			{
 				indexattrs = bms_add_member(indexattrs,
 							   attrnum - FirstLowInvalidHeapAttributeNumber);
+				if (isKey)
+					uindexattrs = bms_add_member(uindexattrs,
+												 attrnum - FirstLowInvalidHeapAttributeNumber);
+			}
 		}
 
 		/* Collect all attributes used in expressions, too */
@@ -3699,10 +3715,11 @@ RelationGetIndexAttrBitmap(Relation relation)
 	/* Now save a copy of the bitmap in the relcache entry. */
 	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
 	relation->rd_indexattr = bms_copy(indexattrs);
+	relation->rd_keyattr = bms_copy(uindexattrs);
 	MemoryContextSwitchTo(oldcxt);
 
 	/* We return our original working copy for caller to play with */
-	return indexattrs;
+	return keyAttrs ? uindexattrs : indexattrs;
 }
 
 /*
diff --git a/src/backend/utils/time/combocid.c b/src/backend/utils/time/combocid.c
index d9b37b2..560a53d 100644
--- a/src/backend/utils/time/combocid.c
+++ b/src/backend/utils/time/combocid.c
@@ -118,9 +118,11 @@ HeapTupleHeaderGetCmax(HeapTupleHeader tup)
 {
 	CommandId	cid = HeapTupleHeaderGetRawCommandId(tup);
 
+	Assert(!(tup->t_infomask & HEAP_MOVED));
 	/* We do not store cmax when locking a tuple */
-	Assert(!(tup->t_infomask & (HEAP_MOVED | HEAP_IS_LOCKED)));
-	Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tup)));
+	Assert(!HeapTupleHeaderIsLocked(tup));
+	Assert((tup->t_infomask & HEAP_XMAX_IS_MULTI) ||
+		   TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tup)));
 
 	if (tup->t_infomask & HEAP_COMBOCID)
 		return GetRealCmax(cid);
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index 1c4b74d..1e2d3fa 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -213,10 +213,23 @@ HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
 			if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
 				return true;
 
-			if (tuple->t_infomask & HEAP_IS_LOCKED)		/* not deleter */
+			if (HeapTupleHeaderIsLocked(tuple))		/* not deleter */
 				return true;
 
-			Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
+			if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+			{
+				TransactionId	xmax;
+
+				xmax = HeapTupleGetUpdateXid(tuple);
+				if (!TransactionIdIsValid(xmax))
+					return true;
+
+				/* updating subtransaction must have aborted */
+				if (!TransactionIdIsCurrentTransactionId(xmax))
+					return true;
+				else
+					return false;
+			}
 
 			if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
 			{
@@ -249,21 +262,34 @@ HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
 
 	if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
 	{
-		if (tuple->t_infomask & HEAP_IS_LOCKED)
+		if (HeapTupleHeaderIsLocked(tuple))
 			return true;
 		return false;			/* updated by other */
 	}
 
 	if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
 	{
-		/* MultiXacts are currently only allowed to lock tuples */
-		Assert(tuple->t_infomask & HEAP_IS_LOCKED);
+		TransactionId	xmax;
+
+		if (HeapTupleHeaderIsLocked(tuple))
+			return true;
+
+		xmax = HeapTupleGetUpdateXid(tuple);
+		if (TransactionIdIsCurrentTransactionId(xmax))
+			return false;
+		if (TransactionIdIsInProgress(xmax))
+			return true;
+		if (TransactionIdDidCommit(xmax))
+		{
+			SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, xmax);
+			return false;
+		}
 		return true;
 	}
 
 	if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
 	{
-		if (tuple->t_infomask & HEAP_IS_LOCKED)
+		if (HeapTupleHeaderIsLocked(tuple))
 			return true;
 		return false;
 	}
@@ -281,7 +307,7 @@ HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
 
 	/* xmax transaction committed */
 
-	if (tuple->t_infomask & HEAP_IS_LOCKED)
+	if (HeapTupleHeaderIsLocked(tuple))
 	{
 		SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
 					InvalidTransactionId);
@@ -389,10 +415,23 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
 			if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
 				return true;
 
-			if (tuple->t_infomask & HEAP_IS_LOCKED)		/* not deleter */
+			if (HeapTupleHeaderIsLocked(tuple))		/* not deleter */
 				return true;
 
-			Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
+			if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+			{
+				TransactionId	xmax;
+
+				xmax = HeapTupleGetUpdateXid(tuple);
+				if (!TransactionIdIsValid(xmax))
+					return true;
+
+				/* updating subtransaction must have aborted */
+				if (!TransactionIdIsCurrentTransactionId(xmax))
+					return true;
+				else
+					return false;
+			}
 
 			if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
 			{
@@ -428,21 +467,39 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
 
 	if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
 	{
-		if (tuple->t_infomask & HEAP_IS_LOCKED)
+		if (HeapTupleHeaderIsLocked(tuple))
 			return true;
 		return false;
 	}
 
 	if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
 	{
-		/* MultiXacts are currently only allowed to lock tuples */
-		Assert(tuple->t_infomask & HEAP_IS_LOCKED);
+		TransactionId	xmax;
+
+		if (HeapTupleHeaderIsLocked(tuple))
+			return true;
+
+		xmax = HeapTupleGetUpdateXid(tuple);
+		if (TransactionIdIsCurrentTransactionId(xmax))
+		{
+			if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId(false))
+				return true;	/* deleted after scan started */
+			else
+				return false;	/* deleted before scan started */
+		}
+		if (TransactionIdIsInProgress(xmax))
+			return true;
+		if (TransactionIdDidCommit(xmax))
+		{
+			SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, xmax);
+			return false;
+		}
 		return true;
 	}
 
 	if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
 	{
-		if (tuple->t_infomask & HEAP_IS_LOCKED)
+		if (HeapTupleHeaderIsLocked(tuple))
 			return true;
 		if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId(false))
 			return true;		/* deleted after scan started */
@@ -463,7 +520,7 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
 
 	/* xmax transaction committed */
 
-	if (tuple->t_infomask & HEAP_IS_LOCKED)
+	if (HeapTupleHeaderIsLocked(tuple))
 	{
 		SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
 					InvalidTransactionId);
@@ -636,10 +693,24 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
 			if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
 				return HeapTupleMayBeUpdated;
 
-			if (tuple->t_infomask & HEAP_IS_LOCKED)		/* not deleter */
+			if (HeapTupleHeaderIsLocked(tuple))		 /* not deleter */
 				return HeapTupleMayBeUpdated;
 
-			Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
+			if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+			{
+				TransactionId	xmax;
+
+				xmax = HeapTupleGetUpdateXid(tuple);
+				if (!TransactionIdIsValid(xmax))
+					return HeapTupleMayBeUpdated;
+
+				/* updating subtransaction must have aborted */
+				if (!TransactionIdIsCurrentTransactionId(xmax))
+					return HeapTupleMayBeUpdated;
+				else
+					return HeapTupleSelfUpdated;
+				/* FIXME -- what do we need to do with the Cmax here? */
+			}
 
 			if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
 			{
@@ -675,27 +746,49 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
 
 	if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
 	{
-		if (tuple->t_infomask & HEAP_IS_LOCKED)
+		if (HeapTupleHeaderIsLocked(tuple))
 			return HeapTupleMayBeUpdated;
+		/* XXX might have XMAX_IS_MULTI ... */
 		return HeapTupleUpdated;	/* updated by other */
 	}
 
 	if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
 	{
-		/* MultiXacts are currently only allowed to lock tuples */
-		Assert(tuple->t_infomask & HEAP_IS_LOCKED);
+		TransactionId	xmax;
+
+		if (HeapTupleHeaderIsLocked(tuple))
+		{
+			if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(tuple)))
+				return HeapTupleBeingUpdated;
+			return HeapTupleMayBeUpdated;
+		}
+
+		xmax = HeapTupleGetUpdateXid(tuple);
+
+		if (TransactionIdIsCurrentTransactionId(xmax))
+		{
+			if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+				return HeapTupleSelfUpdated;		/* updated after scan started */
+			else
+				return HeapTupleInvisible;	/* updated before scan started */
+		}
 
 		if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(tuple)))
 			return HeapTupleBeingUpdated;
-		SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
-					InvalidTransactionId);
+
+		if (TransactionIdDidCommit(xmax))
+		{
+			SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, xmax);
+			return HeapTupleUpdated;
+		}
+		/* it must have aborted or crashed */
 		return HeapTupleMayBeUpdated;
 	}
 
 	if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
 	{
-		if (tuple->t_infomask & HEAP_IS_LOCKED)
-			return HeapTupleMayBeUpdated;
+		if (HeapTupleHeaderIsLocked(tuple))
+			return HeapTupleMayBeUpdated;	/* FIXME might need rethinking */
 		if (HeapTupleHeaderGetCmax(tuple) >= curcid)
 			return HeapTupleSelfUpdated;		/* updated after scan started */
 		else
@@ -715,7 +808,7 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
 
 	/* xmax transaction committed */
 
-	if (tuple->t_infomask & HEAP_IS_LOCKED)
+	if (HeapTupleHeaderIsLocked(tuple))
 	{
 		SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
 					InvalidTransactionId);
@@ -802,10 +895,23 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot,
 			if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
 				return true;
 
-			if (tuple->t_infomask & HEAP_IS_LOCKED)		/* not deleter */
+			if (HeapTupleHeaderIsLocked(tuple))		 /* not deleter */
 				return true;
 
-			Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
+			if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+			{
+				TransactionId	xmax;
+
+				xmax = HeapTupleGetUpdateXid(tuple);
+				if (!TransactionIdIsValid(xmax))
+					return true;
+
+				/* updating subtransaction must have aborted */
+				if (!TransactionIdIsCurrentTransactionId(xmax))
+					return true;
+				else
+					return false;
+			}
 
 			if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
 			{
@@ -842,21 +948,37 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot,
 
 	if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
 	{
-		if (tuple->t_infomask & HEAP_IS_LOCKED)
+		if (HeapTupleHeaderIsLocked(tuple))
 			return true;
 		return false;			/* updated by other */
 	}
 
 	if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
 	{
-		/* MultiXacts are currently only allowed to lock tuples */
-		Assert(tuple->t_infomask & HEAP_IS_LOCKED);
+		TransactionId	xmax;
+
+		if (HeapTupleHeaderIsLocked(tuple))
+			return true;
+
+		xmax = HeapTupleGetUpdateXid(tuple);
+		if (TransactionIdIsCurrentTransactionId(xmax))
+			return false;
+		if (TransactionIdIsInProgress(xmax))
+		{
+			snapshot->xmax = xmax;
+			return true;
+		}
+		if (TransactionIdDidCommit(xmax))
+		{
+			SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, xmax);
+			return false;
+		}
 		return true;
 	}
 
 	if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
 	{
-		if (tuple->t_infomask & HEAP_IS_LOCKED)
+		if (HeapTupleHeaderIsLocked(tuple))
 			return true;
 		return false;
 	}
@@ -877,7 +999,7 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot,
 
 	/* xmax transaction committed */
 
-	if (tuple->t_infomask & HEAP_IS_LOCKED)
+	if (HeapTupleHeaderIsLocked(tuple))
 	{
 		SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
 					InvalidTransactionId);
@@ -966,10 +1088,25 @@ HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot,
 			if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
 				return true;
 
-			if (tuple->t_infomask & HEAP_IS_LOCKED)		/* not deleter */
+			if (HeapTupleHeaderIsLocked(tuple))		 /* not deleter */
 				return true;
 
-			Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
+			if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+			{
+				TransactionId	xmax;
+
+				xmax = HeapTupleGetUpdateXid(tuple);
+				if (!TransactionIdIsValid(xmax))
+					return true;
+
+				/* updating subtransaction must have aborted */
+				if (!TransactionIdIsCurrentTransactionId(xmax))
+					return true;
+				else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+					return true;	/* updated after scan started */
+				else
+					return false;	/* updated before scan started */
+			}
 
 			if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
 			{
@@ -1008,13 +1145,34 @@ HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot,
 	if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid or aborted */
 		return true;
 
-	if (tuple->t_infomask & HEAP_IS_LOCKED)
+	if (HeapTupleHeaderIsLocked(tuple))
 		return true;
 
 	if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
 	{
-		/* MultiXacts are currently only allowed to lock tuples */
-		Assert(tuple->t_infomask & HEAP_IS_LOCKED);
+		TransactionId	xmax;
+
+		if (HeapTupleHeaderIsLocked(tuple))
+			return true;
+
+		xmax = HeapTupleGetUpdateXid(tuple);
+		if (TransactionIdIsCurrentTransactionId(xmax))
+		{
+			if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+				return true;	/* deleted after scan started */
+			else
+				return false;	/* deleted before scan started */
+		}
+		if (TransactionIdIsInProgress(xmax))
+			return true;
+		if (TransactionIdDidCommit(xmax))
+		{
+			SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, xmax);
+			/* updating transaction committed, but when? */
+			if (XidInMVCCSnapshot(xmax, snapshot))
+				return true;	/* treat as still in progress */
+			return false;
+		}
 		return true;
 	}
 
@@ -1121,8 +1279,9 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin,
 		{
 			if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
 				return HEAPTUPLE_INSERT_IN_PROGRESS;
-			if (tuple->t_infomask & HEAP_IS_LOCKED)
+			if (HeapTupleHeaderIsLocked(tuple))
 				return HEAPTUPLE_INSERT_IN_PROGRESS;
+			/* FIXME -- probably need something here */
 			/* inserted and then deleted by same xact */
 			return HEAPTUPLE_DELETE_IN_PROGRESS;
 		}
@@ -1153,7 +1312,7 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin,
 	if (tuple->t_infomask & HEAP_XMAX_INVALID)
 		return HEAPTUPLE_LIVE;
 
-	if (tuple->t_infomask & HEAP_IS_LOCKED)
+	if (HeapTupleHeaderIsLocked(tuple))
 	{
 		/*
 		 * "Deleting" xact really only locked it, so the tuple is live in any
@@ -1177,6 +1336,10 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin,
 			}
 
 			/*
+			 * FIXME -- if the multixact is gone, we should replace it with
+			 * the plain updating Xid and remove the IS_MULTI bit.
+			 */
+			/*
 			 * We don't really care whether xmax did commit, abort or crash.
 			 * We know that xmax did lock the tuple, but it did not and will
 			 * never actually update it.
@@ -1184,14 +1347,44 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin,
 			SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
 						InvalidTransactionId);
 		}
+
 		return HEAPTUPLE_LIVE;
 	}
 
 	if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
 	{
-		/* MultiXacts are currently only allowed to lock tuples */
-		Assert(tuple->t_infomask & HEAP_IS_LOCKED);
-		return HEAPTUPLE_LIVE;
+		TransactionId xmax;
+
+		if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(tuple)))
+			return HEAPTUPLE_LIVE;
+
+		xmax = HeapTupleGetUpdateXid(tuple);
+		if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+		{
+			Assert(!TransactionIdIsInProgress(xmax));
+			Assert(!TransactionIdIsCurrentTransactionId(xmax));
+			if (TransactionIdDidCommit(xmax))
+				SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, xmax);
+			else
+			{
+				/*
+				 * Not in Progress, Not Committed, so either Aborted or crashed
+				 */
+				SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+							InvalidTransactionId);
+				return HEAPTUPLE_LIVE;
+			}
+		}
+
+		/*
+		 * Deleter committed, but perhaps it was recent enough that some open
+		 * transactions could still see the tuple.
+		 */
+		if (!TransactionIdPrecedes(xmax, OldestXmin))
+			return HEAPTUPLE_RECENTLY_DEAD;
+
+		/* Otherwise, it's dead and removable */
+		return HEAPTUPLE_DEAD;
 	}
 
 	if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
diff --git a/src/bin/pg_resetxlog/pg_resetxlog.c b/src/bin/pg_resetxlog/pg_resetxlog.c
index 5b8ae88..4fdd6de 100644
--- a/src/bin/pg_resetxlog/pg_resetxlog.c
+++ b/src/bin/pg_resetxlog/pg_resetxlog.c
@@ -87,6 +87,7 @@ main(int argc, char *argv[])
 	Oid			set_oid = 0;
 	MultiXactId set_mxid = 0;
 	MultiXactOffset set_mxoff = (MultiXactOffset) -1;
+	TransactionId set_mxfreeze = FrozenTransactionId;
 	uint32		minXlogTli = 0,
 				minXlogId = 0,
 				minXlogSeg = 0;
@@ -116,7 +117,7 @@ main(int argc, char *argv[])
 	}
 
 
-	while ((c = getopt(argc, argv, "fl:m:no:O:x:e:")) != -1)
+	while ((c = getopt(argc, argv, "fl:m:no:O:x:e:z:")) != -1)
 	{
 		switch (c)
 		{
@@ -203,6 +204,23 @@ main(int argc, char *argv[])
 				}
 				break;
 
+			case 'z':
+				set_mxfreeze = strtoul(optarg, &endptr, 0);
+				if (endptr == optarg || *endptr != '\0')
+				{
+					fprintf(stderr, _("%s: invalid argument for option -z\n"), progname);
+					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+					exit(1);
+				}
+				/* InvalidTransactionId is allowed here */
+				if (set_mxfreeze == FrozenTransactionId ||
+					set_mxfreeze == BootstrapTransactionId)
+				{
+					fprintf(stderr, _("%s: multitransaction freezeXid (-z) must not be 1 or 2\n"), progname);
+					exit(1);
+				}
+				break;
+
 			case 'l':
 				minXlogTli = strtoul(optarg, &endptr, 0);
 				if (endptr == optarg || *endptr != ',')
@@ -332,6 +350,11 @@ main(int argc, char *argv[])
 	if (set_mxoff != -1)
 		ControlFile.checkPointCopy.nextMultiOffset = set_mxoff;
 
+	/*
+	if (set_mxfreeze != -1)
+		ControlFile.checkPointCopy.mxactFreezeXid = set_mxfreeze;
+		*/
+
 	if (minXlogTli > ControlFile.checkPointCopy.ThisTimeLineID)
 		ControlFile.checkPointCopy.ThisTimeLineID = minXlogTli;
 
@@ -578,6 +601,10 @@ PrintControlValues(bool guessed)
 		   ControlFile.checkPointCopy.nextMulti);
 	printf(_("Latest checkpoint's NextMultiOffset:  %u\n"),
 		   ControlFile.checkPointCopy.nextMultiOffset);
+	/*
+	printf(_("Latest checkpoint's MultiXact freezeXid: %u\n"),
+		   ControlFile.checkPointCopy.mxactFreezeXid);
+		   */
 	printf(_("Latest checkpoint's oldestXID:        %u\n"),
 		   ControlFile.checkPointCopy.oldestXid);
 	printf(_("Latest checkpoint's oldestXID's DB:   %u\n"),
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 776ea5c..363c3bd 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -29,10 +29,22 @@
 
 typedef struct BulkInsertStateData *BulkInsertState;
 
+/*
+ * Possible lock modes for a tuple.
+ */
 typedef enum
 {
-	LockTupleShared,
-	LockTupleExclusive
+	/* SELECT FOR KEY SHARE */
+	LockTupleKeyShare,
+	/* SELECT FOR SHARE */
+	LockTupleShare,
+	/*
+	 * SELECT FOR UPDATE, and also plain UPDATE when the "key" columns are
+	 * not modified
+	 */
+	LockTupleUpdate,
+	/* other UPDATEs, and DELETE */
+	LockTupleKeyUpdate
 } LockTupleMode;
 
 
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 966e2d0..c84cd7f 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -164,12 +164,15 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
 #define HEAP_HASVARWIDTH		0x0002	/* has variable-width attribute(s) */
 #define HEAP_HASEXTERNAL		0x0004	/* has external stored attribute(s) */
 #define HEAP_HASOID				0x0008	/* has an object-id field */
-/* bit 0x0010 is available */
+#define HEAP_XMAX_KEYSHR_LOCK	0x0010	/* xmax is a key-shared locker */
 #define HEAP_COMBOCID			0x0020	/* t_cid is a combo cid */
 #define HEAP_XMAX_EXCL_LOCK		0x0040	/* xmax is exclusive locker */
-#define HEAP_XMAX_SHARED_LOCK	0x0080	/* xmax is shared locker */
-/* if either LOCK bit is set, xmax hasn't deleted the tuple, only locked it */
-#define HEAP_IS_LOCKED	(HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_SHARED_LOCK)
+#define HEAP_XMAX_IS_NOT_UPDATE	0x0080	/* xmax, if valid, is only a locker.
+										 * Note this is not set unless
+										 * XMAX_IS_MULTI is also set. */
+
+#define HEAP_LOCK_BITS	(HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_IS_NOT_UPDATE | \
+						 HEAP_XMAX_KEYSHR_LOCK)
 #define HEAP_XMIN_COMMITTED		0x0100	/* t_xmin committed */
 #define HEAP_XMIN_INVALID		0x0200	/* t_xmin invalid/aborted */
 #define HEAP_XMAX_COMMITTED		0x0400	/* t_xmax committed */
@@ -187,14 +190,30 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
 #define HEAP_XACT_MASK			0xFFE0	/* visibility-related bits */
 
 /*
+ * A tuple is only locked (i.e. not updated by its Xmax) if it the Xmax is not
+ * a multixact and it has either the EXCL_LOCK or KEYSHR_LOCK bits set, or if
+ * the xmax is a multi that doesn't contain an update.
+ *
+ * Beware of multiple evaluation of arguments.
+ */
+#define HeapTupleHeaderInfomaskIsLocked(infomask) \
+	((!((infomask) & HEAP_XMAX_IS_MULTI) && \
+	  (infomask) & (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)) || \
+	 (((infomask) & HEAP_XMAX_IS_MULTI) && ((infomask) & HEAP_XMAX_IS_NOT_UPDATE)))
+
+#define HeapTupleHeaderIsLocked(tup) \
+	HeapTupleHeaderInfomaskIsLocked((tup)->t_infomask)
+
+/*
  * information stored in t_infomask2:
  */
 #define HEAP_NATTS_MASK			0x07FF	/* 11 bits for number of attributes */
-/* bits 0x3800 are available */
+/* bits 0x1800 are available */
+#define HEAP_UPDATE_KEY_INTACT	0x2000	/* tuple updated, key cols untouched */
 #define HEAP_HOT_UPDATED		0x4000	/* tuple was HOT-updated */
 #define HEAP_ONLY_TUPLE			0x8000	/* this is heap-only tuple */
 
-#define HEAP2_XACT_MASK			0xC000	/* visibility-related bits */
+#define HEAP2_XACT_MASK			0xE000	/* visibility-related bits */
 
 /*
  * HEAP_TUPLE_HAS_MATCH is a temporary flag used during hash joins.  It is
@@ -221,6 +240,23 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
 	(tup)->t_choice.t_heap.t_xmin = (xid) \
 )
 
+/*
+ * HeapTupleHeaderGetXmax gets you the raw Xmax field.  To find out the Xid
+ * that updated a tuple, you might need to resolve the MultiXactId if certain
+ * bits are set.  HeapTupleHeaderGetUpdateXid checks those bits and takes care
+ * to resolve the MultiXactId if necessary.  This might involve multixact I/O,
+ * so it should only be used if absolutely necessary.
+ */
+#define HeapTupleHeaderGetUpdateXid(tup) \
+( \
+	(!((tup)->t_infomask & HEAP_XMAX_INVALID) && \
+	 ((tup)->t_infomask & HEAP_XMAX_IS_MULTI) && \
+	 !((tup)->t_infomask & HEAP_XMAX_IS_NOT_UPDATE)) ? \
+		HeapTupleGetUpdateXid(tup) \
+	: \
+		HeapTupleHeaderGetXmax(tup) \
+)
+
 #define HeapTupleHeaderGetXmax(tup) \
 ( \
 	(tup)->t_choice.t_heap.t_xmax \
@@ -721,16 +757,22 @@ typedef struct xl_heap_newpage
 
 #define SizeOfHeapNewpage	(offsetof(xl_heap_newpage, blkno) + sizeof(BlockNumber))
 
+/* flags for xl_heap_lock.infobits_set */
+#define XLHL_XMAX_IS_MULTI		0x01
+#define XLHL_XMAX_IS_NOT_UPDATE	0x02
+#define XLHL_XMAX_EXCL_LOCK		0x04
+#define XLHL_XMAX_KEYSHR_LOCK	0x08
+#define XLHL_UPDATE_KEY_INTACT	0x10
+
 /* This is what we need to know about lock */
 typedef struct xl_heap_lock
 {
 	xl_heaptid	target;			/* locked tuple id */
 	TransactionId locking_xid;	/* might be a MultiXactId not xid */
-	bool		xid_is_mxact;	/* is it? */
-	bool		shared_lock;	/* shared or exclusive row lock? */
+	int8		infobits_set;	/* infomask and infomask2 bits to set */
 } xl_heap_lock;
 
-#define SizeOfHeapLock	(offsetof(xl_heap_lock, shared_lock) + sizeof(bool))
+#define SizeOfHeapLock	(offsetof(xl_heap_lock, infobits_set) + sizeof(int8))
 
 /* This is what we need to know about in-place update */
 typedef struct xl_heap_inplace
@@ -768,8 +810,7 @@ extern void HeapTupleHeaderAdvanceLatestRemovedXid(HeapTupleHeader tuple,
 extern CommandId HeapTupleHeaderGetCmin(HeapTupleHeader tup);
 extern CommandId HeapTupleHeaderGetCmax(HeapTupleHeader tup);
 extern void HeapTupleHeaderAdjustCmax(HeapTupleHeader tup,
-						  CommandId *cmax,
-						  bool *iscombo);
+						  CommandId *cmax, bool *iscombo);
 
 /* ----------------
  *		fastgetattr
@@ -854,6 +895,9 @@ extern Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
 			heap_getsysattr((tup), (attnum), (tupleDesc), (isnull)) \
 	)
 
+/* Prototype for HeapTupleHeader accessor in heapam.c */
+extern TransactionId HeapTupleGetUpdateXid(HeapTupleHeader tuple);
+
 /* prototypes for functions in common/heaptuple.c */
 extern Size heap_compute_data_size(TupleDesc tupleDesc,
 					   Datum *values, bool *isnull);
diff --git a/src/include/access/multixact.h b/src/include/access/multixact.h
index c3ec763..ff255d7 100644
--- a/src/include/access/multixact.h
+++ b/src/include/access/multixact.h
@@ -13,8 +13,14 @@
 
 #include "access/xlog.h"
 
+
+/*
+ * The first two MultiXactId values are reserved to store the truncation Xid
+ * and epoch of the first segment, so we start assigning multixact values from
+ * 2.
+ */
 #define InvalidMultiXactId	((MultiXactId) 0)
-#define FirstMultiXactId	((MultiXactId) 1)
+#define FirstMultiXactId	((MultiXactId) 2)
 
 #define MultiXactIdIsValid(multi) ((multi) != InvalidMultiXactId)
 
@@ -22,6 +28,31 @@
 #define NUM_MXACTOFFSET_BUFFERS		8
 #define NUM_MXACTMEMBER_BUFFERS		16
 
+/*
+ * Possible multixact lock modes ("status").  The first three modes are for
+ * tuple locks (FOR KEY SHARE, FOR SHARE and FOR UPDATE, respectively); the
+ * fourth is used for an update that doesn't modify key columns.  The fifth one
+ * is used for other updates and deletes.  Note that we only use two bits to
+ * represent them on disk, which means we don't have space to represent the
+ * last one.  This is okay, because a multixact can never contain such an
+ * operation; this mode is only used to wait for other modes.
+ */
+typedef enum
+{
+	MultiXactStatusForKeyShare = 0x00,
+	MultiXactStatusForShare = 0x01,
+	MultiXactStatusForUpdate = 0x02,
+	MultiXactStatusUpdate = 0x03,
+	MultiXactStatusKeyUpdate = 0x04,
+} MultiXactStatus;
+
+typedef struct MultiXactMember
+{
+	TransactionId	xid;
+	MultiXactStatus	status;
+} MultiXactMember;
+
+
 /* ----------------
  *		multixact-related XLOG entries
  * ----------------
@@ -35,21 +66,27 @@ typedef struct xl_multixact_create
 {
 	MultiXactId mid;			/* new MultiXact's ID */
 	MultiXactOffset moff;		/* its starting offset in members file */
-	int32		nxids;			/* number of member XIDs */
-	TransactionId xids[1];		/* VARIABLE LENGTH ARRAY */
+	int32		nmembers;		/* number of member XIDs */
+	MultiXactMember members[FLEXIBLE_ARRAY_MEMBER];
 } xl_multixact_create;
 
-#define MinSizeOfMultiXactCreate offsetof(xl_multixact_create, xids)
+#define MinSizeOfMultiXactCreate offsetof(xl_multixact_create, members)
 
 
-extern MultiXactId MultiXactIdCreate(TransactionId xid1, TransactionId xid2);
-extern MultiXactId MultiXactIdExpand(MultiXactId multi, TransactionId xid);
+extern MultiXactId MultiXactIdCreateSingleton(TransactionId xid,
+						   MultiXactStatus status);
+extern MultiXactId MultiXactIdCreate(TransactionId xid1,
+				  MultiXactStatus status1, TransactionId xid2,
+				  MultiXactStatus status2);
+extern MultiXactId MultiXactIdExpand(MultiXactId multi, TransactionId xid,
+				  MultiXactStatus status);
 extern bool MultiXactIdIsRunning(MultiXactId multi);
-extern bool MultiXactIdIsCurrent(MultiXactId multi);
-extern void MultiXactIdWait(MultiXactId multi);
-extern bool ConditionalMultiXactIdWait(MultiXactId multi);
+extern void MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
+							int *remaining);
+extern bool ConditionalMultiXactIdWait(MultiXactId multi,
+						   MultiXactStatus status, int *remaining);
 extern void MultiXactIdSetOldestMember(void);
-extern int	GetMultiXactIdMembers(MultiXactId multi, TransactionId **xids);
+extern int	GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **xids);
 
 extern void AtEOXact_MultiXact(void);
 extern void AtPrepare_MultiXact(void);
@@ -62,8 +99,12 @@ extern void StartupMultiXact(void);
 extern void ShutdownMultiXact(void);
 extern void MultiXactGetCheckptMulti(bool is_shutdown,
 						 MultiXactId *nextMulti,
-						 MultiXactOffset *nextMultiOffset);
+						 MultiXactOffset *nextMultiOffset,
+						 TransactionId *oldestTruncateXid,
+						 uint32 *oldestTruncateXidEpoch,
+						 MultiXactId *oldestMulti);
 extern void CheckPointMultiXact(void);
+extern void TruncateMultiXact(TransactionId oldestXid);
 extern void MultiXactSetNextMXact(MultiXactId nextMulti,
 					  MultiXactOffset nextMultiOffset);
 extern void MultiXactAdvanceNextMXact(MultiXactId minMulti,
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index cb43879..2e73233 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -71,7 +71,7 @@ typedef struct XLogContRecord
 /*
  * Each page of XLOG file has a header like this:
  */
-#define XLOG_PAGE_MAGIC 0xD068	/* can be used as WAL version indicator */
+#define XLOG_PAGE_MAGIC 0xD069	/* can be used as WAL version indicator */
 
 typedef struct XLogPageHeaderData
 {
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index 6688c19..f82295a 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -38,6 +38,10 @@ typedef struct CheckPoint
 	Oid			nextOid;		/* next free OID */
 	MultiXactId nextMulti;		/* next free MultiXactId */
 	MultiXactOffset nextMultiOffset;	/* next free MultiXact offset */
+	TransactionId oldestSegTruncateXid;	/* truncate xid of oldest multixact
+										 * offset segment */
+	uint32		oldestSegTruncateXidEpoch;	/* epoch of above xid */
+	MultiXactId oldestMultiXactId;	/* oldest MultiXactId still on disk */
 	TransactionId oldestXid;	/* cluster-wide minimum datfrozenxid */
 	Oid			oldestXidDB;	/* database with minimum datfrozenxid */
 	pg_time_t	time;			/* time stamp of checkpoint */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 0a89f18..5167f09 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -404,9 +404,9 @@ typedef struct EState
 
 /*
  * ExecRowMark -
- *	   runtime representation of FOR UPDATE/SHARE clauses
+ *	   runtime representation of FOR UPDATE/SHARE/KEY SHARE clauses
  *
- * When doing UPDATE, DELETE, or SELECT FOR UPDATE/SHARE, we should have an
+ * When doing UPDATE, DELETE, or SELECT FOR UPDATE/SHARE/KEY SHARE, we should have an
  * ExecRowMark for each non-target relation in the query (except inheritance
  * parent RTEs, which can be ignored at runtime).  See PlanRowMark for details
  * about most of the fields.  In addition to fields directly derived from
@@ -427,7 +427,7 @@ typedef struct ExecRowMark
 
 /*
  * ExecAuxRowMark -
- *	   additional runtime representation of FOR UPDATE/SHARE clauses
+ *	   additional runtime representation of FOR UPDATE/SHARE/KEY SHARE clauses
  *
  * Each LockRows and ModifyTable node keeps a list of the rowmarks it needs to
  * deal with.  In addition to a pointer to the related entry in es_rowMarks,
@@ -1815,7 +1815,7 @@ typedef struct SetOpState
 /* ----------------
  *	 LockRowsState information
  *
- *		LockRows nodes are used to enforce FOR UPDATE/FOR SHARE locking.
+ *		LockRows nodes are used to enforce FOR UPDATE/SHARE/KEY SHARE locking.
  * ----------------
  */
 typedef struct LockRowsState
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index af6565e..1dc6202 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -74,7 +74,7 @@ typedef uint32 AclMode;			/* a bitmask of privilege bits */
 #define ACL_CONNECT		(1<<11) /* for databases */
 #define N_ACL_RIGHTS	12		/* 1 plus the last 1<<x */
 #define ACL_NO_RIGHTS	0
-/* Currently, SELECT ... FOR UPDATE/FOR SHARE requires UPDATE privileges */
+/* Currently, SELECT ... FOR UPDATE/SHARE/KEY SHARE requires UPDATE privileges */
 #define ACL_SELECT_FOR_UPDATE	ACL_UPDATE
 
 
@@ -119,7 +119,7 @@ typedef struct Query
 	bool		hasDistinctOn;	/* distinctClause is from DISTINCT ON */
 	bool		hasRecursive;	/* WITH RECURSIVE was specified */
 	bool		hasModifyingCTE;	/* has INSERT/UPDATE/DELETE in WITH */
-	bool		hasForUpdate;	/* FOR UPDATE or FOR SHARE was specified */
+	bool		hasForUpdate;	/* FOR UPDATE/SHARE/KEY SHARE was specified */
 
 	List	   *cteList;		/* WITH list (of CommonTableExpr's) */
 
@@ -570,18 +570,26 @@ typedef struct DefElem
 } DefElem;
 
 /*
- * LockingClause - raw representation of FOR UPDATE/SHARE options
+ * LockingClause - raw representation of FOR UPDATE/SHARE/KEY SHARE options
  *
  * Note: lockedRels == NIL means "all relations in query".	Otherwise it
  * is a list of RangeVar nodes.  (We use RangeVar mainly because it carries
  * a location field --- currently, parse analysis insists on unqualified
  * names in LockingClause.)
  */
+typedef enum LockClauseStrength
+{
+	/* order is important -- see applyLockingClause */
+	LCS_FORKEYSHARE,
+	LCS_FORSHARE,
+	LCS_FORUPDATE
+} LockClauseStrength;
+
 typedef struct LockingClause
 {
 	NodeTag		type;
-	List	   *lockedRels;		/* FOR UPDATE or FOR SHARE relations */
-	bool		forUpdate;		/* true = FOR UPDATE, false = FOR SHARE */
+	List	   *lockedRels;		/* FOR UPDATE, SHARE, KEY SHARE relations */
+	LockClauseStrength strength;
 	bool		noWait;			/* NOWAIT option */
 } LockingClause;
 
@@ -861,21 +869,21 @@ typedef struct WindowClause
 
 /*
  * RowMarkClause -
- *	   parser output representation of FOR UPDATE/SHARE clauses
+ *	   parser output representation of FOR UPDATE/SHARE/KEY SHARE clauses
  *
  * Query.rowMarks contains a separate RowMarkClause node for each relation
- * identified as a FOR UPDATE/SHARE target.  If FOR UPDATE/SHARE is applied
- * to a subquery, we generate RowMarkClauses for all normal and subquery rels
- * in the subquery, but they are marked pushedDown = true to distinguish them
- * from clauses that were explicitly written at this query level.  Also,
- * Query.hasForUpdate tells whether there were explicit FOR UPDATE/SHARE
- * clauses in the current query level.
+ * identified as a FOR UPDATE/SHARE/KEY SHARE target.  If one of these clauses
+ * is applied to a subquery, we generate RowMarkClauses for all normal and
+ * subquery rels in the subquery, but they are marked pushedDown = true to
+ * distinguish them from clauses that were explicitly written at this query
+ * level.  Also, Query.hasForUpdate tells whether there were explicit FOR
+ * UPDATE/SHARE/KEY SHARE clauses in the current query level.
  */
 typedef struct RowMarkClause
 {
 	NodeTag		type;
 	Index		rti;			/* range table index of target relation */
-	bool		forUpdate;		/* true = FOR UPDATE, false = FOR SHARE */
+	LockClauseStrength strength;
 	bool		noWait;			/* NOWAIT option */
 	bool		pushedDown;		/* pushed down from higher query level? */
 } RowMarkClause;
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 6685864..27acfd9 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -744,7 +744,7 @@ typedef struct Limit
  * RowMarkType -
  *	  enums for types of row-marking operations
  *
- * When doing UPDATE, DELETE, or SELECT FOR UPDATE/SHARE, we have to uniquely
+ * When doing UPDATE, DELETE, or SELECT FOR UPDATE/SHARE/KEY SHARE, we have to uniquely
  * identify all the source rows, not only those from the target relations, so
  * that we can perform EvalPlanQual rechecking at need.  For plain tables we
  * can just fetch the TID, the same as for a target relation.  Otherwise (for
@@ -756,19 +756,20 @@ typedef enum RowMarkType
 {
 	ROW_MARK_EXCLUSIVE,			/* obtain exclusive tuple lock */
 	ROW_MARK_SHARE,				/* obtain shared tuple lock */
+	ROW_MARK_KEYSHARE,			/* obtain keyshare tuple lock */
 	ROW_MARK_REFERENCE,			/* just fetch the TID */
 	ROW_MARK_COPY				/* physically copy the row value */
 } RowMarkType;
 
-#define RowMarkRequiresRowShareLock(marktype)  ((marktype) <= ROW_MARK_SHARE)
+#define RowMarkRequiresRowShareLock(marktype)  ((marktype) <= ROW_MARK_KEYSHARE)
 
 /*
  * PlanRowMark -
  *	   plan-time representation of FOR UPDATE/SHARE clauses
  *
- * When doing UPDATE, DELETE, or SELECT FOR UPDATE/SHARE, we create a separate
+ * When doing UPDATE, DELETE, or SELECT FOR UPDATE/SHARE/KEY LOCK, we create a separate
  * PlanRowMark node for each non-target relation in the query.	Relations that
- * are not specified as FOR UPDATE/SHARE are marked ROW_MARK_REFERENCE (if
+ * are not specified as FOR UPDATE/SHARE/KEY LOCK are marked ROW_MARK_REFERENCE (if
  * real tables) or ROW_MARK_COPY (if not).
  *
  * Initially all PlanRowMarks have rti == prti and isParent == false.
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index 88fc78b..dea10a0 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -31,6 +31,6 @@ extern bool analyze_requires_snapshot(Node *parseTree);
 
 extern void CheckSelectLocking(Query *qry);
 extern void applyLockingClause(Query *qry, Index rtindex,
-				   bool forUpdate, bool noWait, bool pushedDown);
+				   LockClauseStrength strength, bool noWait, bool pushedDown);
 
 #endif   /* ANALYZE_H */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 173dc16..e3aca6c 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -103,6 +103,7 @@ typedef struct RelationData
 	Oid			rd_id;			/* relation's object id */
 	List	   *rd_indexlist;	/* list of OIDs of indexes on relation */
 	Bitmapset  *rd_indexattr;	/* identifies columns used in indexes */
+	Bitmapset  *rd_keyattr;		/* cols that can be ref'd by foreign keys */
 	Oid			rd_oidindex;	/* OID of unique index on OID, if any */
 	LockInfoData rd_lockInfo;	/* lock mgr's info for locking relation */
 	RuleLock   *rd_rules;		/* rewrite rules */
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 9aaf969..5c6b27a 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -41,7 +41,7 @@ extern List *RelationGetIndexList(Relation relation);
 extern Oid	RelationGetOidIndex(Relation relation);
 extern List *RelationGetIndexExpressions(Relation relation);
 extern List *RelationGetIndexPredicate(Relation relation);
-extern Bitmapset *RelationGetIndexAttrBitmap(Relation relation);
+extern Bitmapset *RelationGetIndexAttrBitmap(Relation relation, bool keyAttrs);
 extern void RelationGetExclusionInfo(Relation indexRelation,
 						 Oid **operators,
 						 Oid **procs,
diff --git a/src/test/isolation/expected/fk-contention.out b/src/test/isolation/expected/fk-contention.out
index 24ed72d..0916f7f 100644
--- a/src/test/isolation/expected/fk-contention.out
+++ b/src/test/isolation/expected/fk-contention.out
@@ -7,9 +7,8 @@ step upd: UPDATE foo SET b = 'Hello World';
 
 starting permutation: ins upd com
 step ins: INSERT INTO bar VALUES (42);
-step upd: UPDATE foo SET b = 'Hello World'; <waiting ...>
+step upd: UPDATE foo SET b = 'Hello World';
 step com: COMMIT;
-step upd: <... completed>
 
 starting permutation: upd ins com
 step upd: UPDATE foo SET b = 'Hello World';
diff --git a/src/test/isolation/expected/fk-deadlock.out b/src/test/isolation/expected/fk-deadlock.out
index 36813f1..69a294a 100644
--- a/src/test/isolation/expected/fk-deadlock.out
+++ b/src/test/isolation/expected/fk-deadlock.out
@@ -11,57 +11,51 @@ step s2c: COMMIT;
 starting permutation: s1i s1u s2i s1c s2u s2c
 step s1i: INSERT INTO child VALUES (1, 1);
 step s1u: UPDATE parent SET aux = 'bar';
-step s2i: INSERT INTO child VALUES (2, 1); <waiting ...>
+step s2i: INSERT INTO child VALUES (2, 1);
 step s1c: COMMIT;
-step s2i: <... completed>
 step s2u: UPDATE parent SET aux = 'baz';
 step s2c: COMMIT;
 
 starting permutation: s1i s2i s1u s2u s1c s2c
 step s1i: INSERT INTO child VALUES (1, 1);
 step s2i: INSERT INTO child VALUES (2, 1);
-step s1u: UPDATE parent SET aux = 'bar'; <waiting ...>
-step s2u: UPDATE parent SET aux = 'baz';
-step s1u: <... completed>
-error in steps s2u s1u: ERROR:  deadlock detected
+step s1u: UPDATE parent SET aux = 'bar';
+step s2u: UPDATE parent SET aux = 'baz'; <waiting ...>
 step s1c: COMMIT;
+step s2u: <... completed>
 step s2c: COMMIT;
 
 starting permutation: s1i s2i s2u s1u s2c s1c
 step s1i: INSERT INTO child VALUES (1, 1);
 step s2i: INSERT INTO child VALUES (2, 1);
-step s2u: UPDATE parent SET aux = 'baz'; <waiting ...>
-step s1u: UPDATE parent SET aux = 'bar';
-step s2u: <... completed>
-error in steps s1u s2u: ERROR:  deadlock detected
+step s2u: UPDATE parent SET aux = 'baz';
+step s1u: UPDATE parent SET aux = 'bar'; <waiting ...>
 step s2c: COMMIT;
+step s1u: <... completed>
 step s1c: COMMIT;
 
 starting permutation: s2i s1i s1u s2u s1c s2c
 step s2i: INSERT INTO child VALUES (2, 1);
 step s1i: INSERT INTO child VALUES (1, 1);
-step s1u: UPDATE parent SET aux = 'bar'; <waiting ...>
-step s2u: UPDATE parent SET aux = 'baz';
-step s1u: <... completed>
-error in steps s2u s1u: ERROR:  deadlock detected
+step s1u: UPDATE parent SET aux = 'bar';
+step s2u: UPDATE parent SET aux = 'baz'; <waiting ...>
 step s1c: COMMIT;
+step s2u: <... completed>
 step s2c: COMMIT;
 
 starting permutation: s2i s1i s2u s1u s2c s1c
 step s2i: INSERT INTO child VALUES (2, 1);
 step s1i: INSERT INTO child VALUES (1, 1);
-step s2u: UPDATE parent SET aux = 'baz'; <waiting ...>
-step s1u: UPDATE parent SET aux = 'bar';
-step s2u: <... completed>
-error in steps s1u s2u: ERROR:  deadlock detected
+step s2u: UPDATE parent SET aux = 'baz';
+step s1u: UPDATE parent SET aux = 'bar'; <waiting ...>
 step s2c: COMMIT;
+step s1u: <... completed>
 step s1c: COMMIT;
 
 starting permutation: s2i s2u s1i s2c s1u s1c
 step s2i: INSERT INTO child VALUES (2, 1);
 step s2u: UPDATE parent SET aux = 'baz';
-step s1i: INSERT INTO child VALUES (1, 1); <waiting ...>
+step s1i: INSERT INTO child VALUES (1, 1);
 step s2c: COMMIT;
-step s1i: <... completed>
 step s1u: UPDATE parent SET aux = 'bar';
 step s1c: COMMIT;
diff --git a/src/test/isolation/expected/fk-deadlock2.out b/src/test/isolation/expected/fk-deadlock2.out
index 2d8e5e5..41a818d 100644
--- a/src/test/isolation/expected/fk-deadlock2.out
+++ b/src/test/isolation/expected/fk-deadlock2.out
@@ -17,91 +17,79 @@ step s2u1: <... completed>
 step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s2c: COMMIT;
 
-starting permutation: s1u1 s2u1 s1u2 s2u2 s1c s2c
+starting permutation: s1u1 s2u1 s1u2 s2u2 s2c s1c
 step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2c: COMMIT;
 step s1u2: <... completed>
-error in steps s2u2 s1u2: ERROR:  deadlock detected
 step s1c: COMMIT;
-step s2c: COMMIT;
 
-starting permutation: s1u1 s2u1 s1u2 s2u2 s2c s1c
+starting permutation: s1u1 s2u1 s2u2 s1u2 s2c s1c
 step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s1u2: <... completed>
-error in steps s2u2 s1u2: ERROR:  deadlock detected
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s2c: COMMIT;
+step s1u2: <... completed>
 step s1c: COMMIT;
 
-starting permutation: s1u1 s2u1 s2u2 s1u2 s1c s2c
+starting permutation: s1u1 s2u1 s2u2 s2c s1u2 s1c
 step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
-step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s2u2: <... completed>
-error in steps s1u2 s2u2: ERROR:  deadlock detected
-step s1c: COMMIT;
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s2c: COMMIT;
-
-starting permutation: s1u1 s2u1 s2u2 s1u2 s2c s1c
-step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
-step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s2u2: <... completed>
-error in steps s1u2 s2u2: ERROR:  deadlock detected
-step s2c: COMMIT;
 step s1c: COMMIT;
 
-starting permutation: s2u1 s1u1 s1u2 s2u2 s1c s2c
+starting permutation: s2u1 s1u1 s1u2 s2u2 s2c s1c
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
 step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2c: COMMIT;
 step s1u2: <... completed>
-error in steps s2u2 s1u2: ERROR:  deadlock detected
 step s1c: COMMIT;
-step s2c: COMMIT;
 
-starting permutation: s2u1 s1u1 s1u2 s2u2 s2c s1c
+starting permutation: s2u1 s1u1 s2u2 s1u2 s2c s1c
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
-step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s1u2: <... completed>
-error in steps s2u2 s1u2: ERROR:  deadlock detected
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s2c: COMMIT;
+step s1u2: <... completed>
 step s1c: COMMIT;
 
-starting permutation: s2u1 s1u1 s2u2 s1u2 s1c s2c
+starting permutation: s2u1 s1u1 s2u2 s2c s1u2 s1c
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
-step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2c: COMMIT;
 step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s2u2: <... completed>
-error in steps s1u2 s2u2: ERROR:  deadlock detected
 step s1c: COMMIT;
-step s2c: COMMIT;
 
-starting permutation: s2u1 s1u1 s2u2 s1u2 s2c s1c
+starting permutation: s2u1 s2u2 s1u1 s1u2 s2c s1c
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
-step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
-step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s2u2: <... completed>
-error in steps s1u2 s2u2: ERROR:  deadlock detected
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s2c: COMMIT;
+step s1u2: <... completed>
 step s1c: COMMIT;
 
 starting permutation: s2u1 s2u2 s1u1 s2c s1u2 s1c
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1; <waiting ...>
+step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+step s2c: COMMIT;
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s1c: COMMIT;
+
+starting permutation: s2u1 s2u2 s2c s1u1 s1u2 s1c
+step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s2c: COMMIT;
-step s1u1: <... completed>
+step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
 step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s1c: COMMIT;
diff --git a/src/test/isolation/expected/fk-deadlock2_1.out b/src/test/isolation/expected/fk-deadlock2_1.out
index 30c4c99..3827348 100644
--- a/src/test/isolation/expected/fk-deadlock2_1.out
+++ b/src/test/isolation/expected/fk-deadlock2_1.out
@@ -19,92 +19,87 @@ step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
 ERROR:  current transaction is aborted, commands ignored until end of transaction block
 step s2c: COMMIT;
 
-starting permutation: s1u1 s2u1 s1u2 s2u2 s1c s2c
+starting permutation: s1u1 s2u1 s1u2 s2u2 s2c s1c
 step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2c: COMMIT;
 step s1u2: <... completed>
-error in steps s2u2 s1u2: ERROR:  deadlock detected
+error in steps s2c s1u2: ERROR:  could not serialize access due to concurrent update
 step s1c: COMMIT;
-step s2c: COMMIT;
 
-starting permutation: s1u1 s2u1 s1u2 s2u2 s2c s1c
+starting permutation: s1u1 s2u1 s2u2 s1u2 s2c s1c
 step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s1u2: <... completed>
-error in steps s2u2 s1u2: ERROR:  deadlock detected
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s2c: COMMIT;
+step s1u2: <... completed>
+error in steps s2c s1u2: ERROR:  could not serialize access due to concurrent update
 step s1c: COMMIT;
 
-starting permutation: s1u1 s2u1 s2u2 s1u2 s1c s2c
+starting permutation: s1u1 s2u1 s2u2 s2c s1u2 s1c
 step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
-step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s2u2: <... completed>
-error in steps s1u2 s2u2: ERROR:  deadlock detected
-step s1c: COMMIT;
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s2c: COMMIT;
-
-starting permutation: s1u1 s2u1 s2u2 s1u2 s2c s1c
-step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
-step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s2u2: <... completed>
-error in steps s1u2 s2u2: ERROR:  deadlock detected
-step s2c: COMMIT;
+ERROR:  could not serialize access due to read/write dependencies among transactions
 step s1c: COMMIT;
 
-starting permutation: s2u1 s1u1 s1u2 s2u2 s1c s2c
+starting permutation: s2u1 s1u1 s1u2 s2u2 s2c s1c
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
 step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2c: COMMIT;
 step s1u2: <... completed>
-error in steps s2u2 s1u2: ERROR:  deadlock detected
+error in steps s2c s1u2: ERROR:  could not serialize access due to concurrent update
 step s1c: COMMIT;
-step s2c: COMMIT;
 
-starting permutation: s2u1 s1u1 s1u2 s2u2 s2c s1c
+starting permutation: s2u1 s1u1 s2u2 s1u2 s2c s1c
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
-step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s1u2: <... completed>
-error in steps s2u2 s1u2: ERROR:  deadlock detected
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s2c: COMMIT;
+step s1u2: <... completed>
+error in steps s2c s1u2: ERROR:  could not serialize access due to concurrent update
 step s1c: COMMIT;
 
-starting permutation: s2u1 s1u1 s2u2 s1u2 s1c s2c
+starting permutation: s2u1 s1u1 s2u2 s2c s1u2 s1c
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
-step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2c: COMMIT;
 step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s2u2: <... completed>
-error in steps s1u2 s2u2: ERROR:  deadlock detected
+ERROR:  could not serialize access due to read/write dependencies among transactions
 step s1c: COMMIT;
-step s2c: COMMIT;
 
-starting permutation: s2u1 s1u1 s2u2 s1u2 s2c s1c
+starting permutation: s2u1 s2u2 s1u1 s1u2 s2c s1c
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
-step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
-step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s2u2: <... completed>
-error in steps s1u2 s2u2: ERROR:  deadlock detected
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
 step s2c: COMMIT;
+step s1u2: <... completed>
+error in steps s2c s1u2: ERROR:  could not serialize access due to concurrent update
 step s1c: COMMIT;
 
 starting permutation: s2u1 s2u2 s1u1 s2c s1u2 s1c
 step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
 step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
-step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1; <waiting ...>
+step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
 step s2c: COMMIT;
-step s1u1: <... completed>
 step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
 ERROR:  could not serialize access due to read/write dependencies among transactions
 step s1c: COMMIT;
+
+starting permutation: s2u1 s2u2 s2c s1u1 s1u2 s1c
+step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2c: COMMIT;
+step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s1c: COMMIT;
diff --git a/src/test/isolation/expected/fk-deadlock2_2.out b/src/test/isolation/expected/fk-deadlock2_2.out
new file mode 100644
index 0000000..b6be4b9
--- /dev/null
+++ b/src/test/isolation/expected/fk-deadlock2_2.out
@@ -0,0 +1,105 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1u1 s1u2 s1c s2u1 s2u2 s2c
+step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s1c: COMMIT;
+step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2c: COMMIT;
+
+starting permutation: s1u1 s1u2 s2u1 s1c s2u2 s2c
+step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+step s1c: COMMIT;
+step s2u1: <... completed>
+error in steps s1c s2u1: ERROR:  could not serialize access due to concurrent update
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ERROR:  current transaction is aborted, commands ignored until end of transaction block
+step s2c: COMMIT;
+
+starting permutation: s1u1 s2u1 s1u2 s2u2 s2c s1c
+step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2c: COMMIT;
+step s1u2: <... completed>
+error in steps s2c s1u2: ERROR:  could not serialize access due to concurrent update
+step s1c: COMMIT;
+
+starting permutation: s1u1 s2u1 s2u2 s1u2 s2c s1c
+step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+step s2c: COMMIT;
+step s1u2: <... completed>
+error in steps s2c s1u2: ERROR:  could not serialize access due to concurrent update
+step s1c: COMMIT;
+
+starting permutation: s1u1 s2u1 s2u2 s2c s1u2 s1c
+step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2c: COMMIT;
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ERROR:  could not serialize access due to concurrent update
+step s1c: COMMIT;
+
+starting permutation: s2u1 s1u1 s1u2 s2u2 s2c s1c
+step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2c: COMMIT;
+step s1u2: <... completed>
+error in steps s2c s1u2: ERROR:  could not serialize access due to concurrent update
+step s1c: COMMIT;
+
+starting permutation: s2u1 s1u1 s2u2 s1u2 s2c s1c
+step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+step s2c: COMMIT;
+step s1u2: <... completed>
+error in steps s2c s1u2: ERROR:  could not serialize access due to concurrent update
+step s1c: COMMIT;
+
+starting permutation: s2u1 s1u1 s2u2 s2c s1u2 s1c
+step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2c: COMMIT;
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ERROR:  could not serialize access due to concurrent update
+step s1c: COMMIT;
+
+starting permutation: s2u1 s2u2 s1u1 s1u2 s2c s1c
+step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2; <waiting ...>
+step s2c: COMMIT;
+step s1u2: <... completed>
+error in steps s2c s1u2: ERROR:  could not serialize access due to concurrent update
+step s1c: COMMIT;
+
+starting permutation: s2u1 s2u2 s1u1 s2c s1u2 s1c
+step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+step s2c: COMMIT;
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+ERROR:  could not serialize access due to concurrent update
+step s1c: COMMIT;
+
+starting permutation: s2u1 s2u2 s2c s1u1 s1u2 s1c
+step s2u1: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s2c: COMMIT;
+step s1u1: UPDATE A SET Col1 = 1 WHERE AID = 1;
+step s1u2: UPDATE B SET Col2 = 1 WHERE BID = 2;
+step s1c: COMMIT;
diff --git a/src/test/isolation/expected/fk-deadlock3.out b/src/test/isolation/expected/fk-deadlock3.out
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/isolation/expected/fk-deadlock_1.out b/src/test/isolation/expected/fk-deadlock_1.out
index ca75322..d648e48 100644
--- a/src/test/isolation/expected/fk-deadlock_1.out
+++ b/src/test/isolation/expected/fk-deadlock_1.out
@@ -11,61 +11,57 @@ step s2c: COMMIT;
 starting permutation: s1i s1u s2i s1c s2u s2c
 step s1i: INSERT INTO child VALUES (1, 1);
 step s1u: UPDATE parent SET aux = 'bar';
-step s2i: INSERT INTO child VALUES (2, 1); <waiting ...>
+step s2i: INSERT INTO child VALUES (2, 1);
 step s1c: COMMIT;
-step s2i: <... completed>
-error in steps s1c s2i: ERROR:  could not serialize access due to concurrent update
 step s2u: UPDATE parent SET aux = 'baz';
-ERROR:  current transaction is aborted, commands ignored until end of transaction block
+ERROR:  could not serialize access due to read/write dependencies among transactions
 step s2c: COMMIT;
 
 starting permutation: s1i s2i s1u s2u s1c s2c
 step s1i: INSERT INTO child VALUES (1, 1);
 step s2i: INSERT INTO child VALUES (2, 1);
-step s1u: UPDATE parent SET aux = 'bar'; <waiting ...>
-step s2u: UPDATE parent SET aux = 'baz';
-step s1u: <... completed>
-error in steps s2u s1u: ERROR:  deadlock detected
+step s1u: UPDATE parent SET aux = 'bar';
+step s2u: UPDATE parent SET aux = 'baz'; <waiting ...>
 step s1c: COMMIT;
+step s2u: <... completed>
+error in steps s1c s2u: ERROR:  could not serialize access due to concurrent update
 step s2c: COMMIT;
 
 starting permutation: s1i s2i s2u s1u s2c s1c
 step s1i: INSERT INTO child VALUES (1, 1);
 step s2i: INSERT INTO child VALUES (2, 1);
-step s2u: UPDATE parent SET aux = 'baz'; <waiting ...>
-step s1u: UPDATE parent SET aux = 'bar';
-step s2u: <... completed>
-error in steps s1u s2u: ERROR:  deadlock detected
+step s2u: UPDATE parent SET aux = 'baz';
+step s1u: UPDATE parent SET aux = 'bar'; <waiting ...>
 step s2c: COMMIT;
+step s1u: <... completed>
+error in steps s2c s1u: ERROR:  could not serialize access due to concurrent update
 step s1c: COMMIT;
 
 starting permutation: s2i s1i s1u s2u s1c s2c
 step s2i: INSERT INTO child VALUES (2, 1);
 step s1i: INSERT INTO child VALUES (1, 1);
-step s1u: UPDATE parent SET aux = 'bar'; <waiting ...>
-step s2u: UPDATE parent SET aux = 'baz';
-step s1u: <... completed>
-error in steps s2u s1u: ERROR:  deadlock detected
+step s1u: UPDATE parent SET aux = 'bar';
+step s2u: UPDATE parent SET aux = 'baz'; <waiting ...>
 step s1c: COMMIT;
+step s2u: <... completed>
+error in steps s1c s2u: ERROR:  could not serialize access due to concurrent update
 step s2c: COMMIT;
 
 starting permutation: s2i s1i s2u s1u s2c s1c
 step s2i: INSERT INTO child VALUES (2, 1);
 step s1i: INSERT INTO child VALUES (1, 1);
-step s2u: UPDATE parent SET aux = 'baz'; <waiting ...>
-step s1u: UPDATE parent SET aux = 'bar';
-step s2u: <... completed>
-error in steps s1u s2u: ERROR:  deadlock detected
+step s2u: UPDATE parent SET aux = 'baz';
+step s1u: UPDATE parent SET aux = 'bar'; <waiting ...>
 step s2c: COMMIT;
+step s1u: <... completed>
+error in steps s2c s1u: ERROR:  could not serialize access due to concurrent update
 step s1c: COMMIT;
 
 starting permutation: s2i s2u s1i s2c s1u s1c
 step s2i: INSERT INTO child VALUES (2, 1);
 step s2u: UPDATE parent SET aux = 'baz';
-step s1i: INSERT INTO child VALUES (1, 1); <waiting ...>
+step s1i: INSERT INTO child VALUES (1, 1);
 step s2c: COMMIT;
-step s1i: <... completed>
-error in steps s2c s1i: ERROR:  could not serialize access due to concurrent update
 step s1u: UPDATE parent SET aux = 'bar';
-ERROR:  current transaction is aborted, commands ignored until end of transaction block
+ERROR:  could not serialize access due to read/write dependencies among transactions
 step s1c: COMMIT;
diff --git a/src/test/isolation/expected/fk-deadlock_2.out b/src/test/isolation/expected/fk-deadlock_2.out
new file mode 100644
index 0000000..2d3294e
--- /dev/null
+++ b/src/test/isolation/expected/fk-deadlock_2.out
@@ -0,0 +1,65 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1i s1u s1c s2i s2u s2c
+step s1i: INSERT INTO child VALUES (1, 1);
+step s1u: UPDATE parent SET aux = 'bar';
+step s1c: COMMIT;
+step s2i: INSERT INTO child VALUES (2, 1);
+step s2u: UPDATE parent SET aux = 'baz';
+step s2c: COMMIT;
+
+starting permutation: s1i s1u s2i s1c s2u s2c
+step s1i: INSERT INTO child VALUES (1, 1);
+step s1u: UPDATE parent SET aux = 'bar';
+step s2i: INSERT INTO child VALUES (2, 1);
+step s1c: COMMIT;
+step s2u: UPDATE parent SET aux = 'baz';
+step s2c: COMMIT;
+
+starting permutation: s1i s2i s1u s2u s1c s2c
+step s1i: INSERT INTO child VALUES (1, 1);
+step s2i: INSERT INTO child VALUES (2, 1);
+step s1u: UPDATE parent SET aux = 'bar';
+step s2u: UPDATE parent SET aux = 'baz'; <waiting ...>
+step s1c: COMMIT;
+step s2u: <... completed>
+error in steps s1c s2u: ERROR:  could not serialize access due to concurrent update
+step s2c: COMMIT;
+
+starting permutation: s1i s2i s2u s1u s2c s1c
+step s1i: INSERT INTO child VALUES (1, 1);
+step s2i: INSERT INTO child VALUES (2, 1);
+step s2u: UPDATE parent SET aux = 'baz';
+step s1u: UPDATE parent SET aux = 'bar'; <waiting ...>
+step s2c: COMMIT;
+step s1u: <... completed>
+error in steps s2c s1u: ERROR:  could not serialize access due to concurrent update
+step s1c: COMMIT;
+
+starting permutation: s2i s1i s1u s2u s1c s2c
+step s2i: INSERT INTO child VALUES (2, 1);
+step s1i: INSERT INTO child VALUES (1, 1);
+step s1u: UPDATE parent SET aux = 'bar';
+step s2u: UPDATE parent SET aux = 'baz'; <waiting ...>
+step s1c: COMMIT;
+step s2u: <... completed>
+error in steps s1c s2u: ERROR:  could not serialize access due to concurrent update
+step s2c: COMMIT;
+
+starting permutation: s2i s1i s2u s1u s2c s1c
+step s2i: INSERT INTO child VALUES (2, 1);
+step s1i: INSERT INTO child VALUES (1, 1);
+step s2u: UPDATE parent SET aux = 'baz';
+step s1u: UPDATE parent SET aux = 'bar'; <waiting ...>
+step s2c: COMMIT;
+step s1u: <... completed>
+error in steps s2c s1u: ERROR:  could not serialize access due to concurrent update
+step s1c: COMMIT;
+
+starting permutation: s2i s2u s1i s2c s1u s1c
+step s2i: INSERT INTO child VALUES (2, 1);
+step s2u: UPDATE parent SET aux = 'baz';
+step s1i: INSERT INTO child VALUES (1, 1);
+step s2c: COMMIT;
+step s1u: UPDATE parent SET aux = 'bar';
+step s1c: COMMIT;
diff --git a/src/test/isolation/specs/fk-deadlock2.spec b/src/test/isolation/specs/fk-deadlock2.spec
index a8f1516..eefe187 100644
--- a/src/test/isolation/specs/fk-deadlock2.spec
+++ b/src/test/isolation/specs/fk-deadlock2.spec
@@ -42,18 +42,18 @@ permutation "s1u1" "s1u2" "s2u1" "s1c" "s2u2" "s2c"
 #permutation "s1u1" "s1u2" "s2u1" "s2u2" "s1c" "s2c"
 #permutation "s1u1" "s1u2" "s2u1" "s2u2" "s2c" "s1c"
 #permutation "s1u1" "s2u1" "s1u2" "s1c" "s2u2" "s2c"
-permutation "s1u1" "s2u1" "s1u2" "s2u2" "s1c" "s2c"
+#permutation "s1u1" "s2u1" "s1u2" "s2u2" "s1c" "s2c"
 permutation "s1u1" "s2u1" "s1u2" "s2u2" "s2c" "s1c"
-permutation "s1u1" "s2u1" "s2u2" "s1u2" "s1c" "s2c"
+#permutation "s1u1" "s2u1" "s2u2" "s1u2" "s1c" "s2c"
 permutation "s1u1" "s2u1" "s2u2" "s1u2" "s2c" "s1c"
-#permutation "s1u1" "s2u1" "s2u2" "s2c" "s1u2" "s1c"
+permutation "s1u1" "s2u1" "s2u2" "s2c" "s1u2" "s1c"
 #permutation "s2u1" "s1u1" "s1u2" "s1c" "s2u2" "s2c"
-permutation "s2u1" "s1u1" "s1u2" "s2u2" "s1c" "s2c"
+#permutation "s2u1" "s1u1" "s1u2" "s2u2" "s1c" "s2c"
 permutation "s2u1" "s1u1" "s1u2" "s2u2" "s2c" "s1c"
-permutation "s2u1" "s1u1" "s2u2" "s1u2" "s1c" "s2c"
+#permutation "s2u1" "s1u1" "s2u2" "s1u2" "s1c" "s2c"
 permutation "s2u1" "s1u1" "s2u2" "s1u2" "s2c" "s1c"
-#permutation "s2u1" "s1u1" "s2u2" "s2c" "s1u2" "s1c"
+permutation "s2u1" "s1u1" "s2u2" "s2c" "s1u2" "s1c"
 #permutation "s2u1" "s2u2" "s1u1" "s1u2" "s1c" "s2c"
-#permutation "s2u1" "s2u2" "s1u1" "s1u2" "s2c" "s1c"
+permutation "s2u1" "s2u2" "s1u1" "s1u2" "s2c" "s1c"
 permutation "s2u1" "s2u2" "s1u1" "s2c" "s1u2" "s1c"
-#permutation "s2u1" "s2u2" "s2c" "s1u1" "s1u2" "s1c"
+permutation "s2u1" "s2u2" "s2c" "s1u1" "s1u2" "s1c"
#2Jeroen Vermeulen
jtv@xs4all.nl
In reply to: Alvaro Herrera (#1)
Re: foreign key locks, 2nd attempt

On 2011-11-04 01:12, Alvaro Herrera wrote:

I would like some opinions on the ideas on this patch, and on the patch
itself. If someone wants more discussion on implementation details of
each part of the patch, I'm happy to provide a textual description --
please just ask.

Jumping in a bit late here, but thanks for working on this: it looks
like it could solve some annoying problems for us.

I do find myself idly wondering if those problems couldn't be made to go
away more simply given some kind of “I will never ever update this key”
constraint. I'm having trouble picturing the possible lock interactions
as it is. :-)

Jeroen

#3Bruce Momjian
bruce@momjian.us
In reply to: Alvaro Herrera (#1)
Re: foreign key locks, 2nd attempt

Alvaro Herrera wrote:

Hello,

After some rather extensive rewriting, I submit the patch to improve
foreign key locks.

To recap, the point of this patch is to introduce a new lock tuple mode,
that lets the RI code obtain a lighter lock on tuples, which doesn't
conflict with updates that do not modify the key columns.

What kind of operations benefit from a non-key lock like this?

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ It's impossible for everything to be true. +

#4Christopher Browne
cbbrowne@gmail.com
In reply to: Jeroen Vermeulen (#2)
Re: foreign key locks, 2nd attempt

On Sun, Nov 6, 2011 at 2:28 AM, Jeroen Vermeulen <jtv@xs4all.nl> wrote:

On 2011-11-04 01:12, Alvaro Herrera wrote:

I would like some opinions on the ideas on this patch, and on the patch
itself.  If someone wants more discussion on implementation details of
each part of the patch, I'm happy to provide a textual description --
please just ask.

Jumping in a bit late here, but thanks for working on this: it looks like it
could solve some annoying problems for us.

I do find myself idly wondering if those problems couldn't be made to go
away more simply given some kind of “I will never ever update this key”
constraint.  I'm having trouble picturing the possible lock interactions as
it is.  :-)

+1 on that, though I'd make it more general than that. There's value
in having an "immutability" constraint on a column, where, in effect,
you're not allowed to modify the value of the column, once assigned.
That certainly doesn't prevent issuing DELETE + INSERT to get whatever
value you want into place, but that's a big enough hoop to need to
jump through to get rid of some nonsensical updates.

And if the target of a foreign key constraint consists of immutable
columns, then, yes, indeed, UPDATE on that table no longer conflicts
with references.

In nearly all cases, I'd expect that SERIAL would be reasonably
followed by IMMUTABLE.

create table something_assigned (
something_id serial immutable primary key,
something_identifier text not null unique
);
--
When confronted by a difficult problem, solve it by reducing it to the
question, "How would the Lone Ranger handle this?"

#5Pavel Stehule
pavel.stehule@gmail.com
In reply to: Christopher Browne (#4)
Re: foreign key locks, 2nd attempt

2011/11/10 Christopher Browne <cbbrowne@gmail.com>:

On Sun, Nov 6, 2011 at 2:28 AM, Jeroen Vermeulen <jtv@xs4all.nl> wrote:

On 2011-11-04 01:12, Alvaro Herrera wrote:

I would like some opinions on the ideas on this patch, and on the patch
itself.  If someone wants more discussion on implementation details of
each part of the patch, I'm happy to provide a textual description --
please just ask.

Jumping in a bit late here, but thanks for working on this: it looks like it
could solve some annoying problems for us.

I do find myself idly wondering if those problems couldn't be made to go
away more simply given some kind of “I will never ever update this key”
constraint.  I'm having trouble picturing the possible lock interactions as
it is.  :-)

+1 on that, though I'd make it more general than that.  There's value
in having an "immutability" constraint on a column, where, in effect,
you're not allowed to modify the value of the column, once assigned.
That certainly doesn't prevent issuing DELETE + INSERT to get whatever
value you want into place, but that's a big enough hoop to need to
jump through to get rid of some nonsensical updates.

And if the target of a foreign key constraint consists of immutable
columns, then, yes, indeed, UPDATE on that table no longer conflicts
with references.

In nearly all cases, I'd expect that SERIAL would be reasonably
followed by IMMUTABLE.

create table something_assigned (
  something_id serial immutable primary key,
  something_identifier text not null unique
);

I like this idea - it can solve two problem

Regards

Pavel Stehule

Show quoted text

--
When confronted by a difficult problem, solve it by reducing it to the
question, "How would the Lone Ranger handle this?"

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Kevin Grittner
Kevin.Grittner@wicourts.gov
In reply to: Christopher Browne (#4)
Re: foreign key locks, 2nd attempt

Christopher Browne <cbbrowne@gmail.com> wrote:

There's value in having an "immutability" constraint on a column,
where, in effect, you're not allowed to modify the value of the
column, once assigned.

+1 We would definitely use such a feature, should it become
available.

-Kevin

#7Christopher Browne
cbbrowne@gmail.com
In reply to: Kevin Grittner (#6)
Re: foreign key locks, 2nd attempt

On Thu, Nov 10, 2011 at 3:29 PM, Kevin Grittner
<Kevin.Grittner@wicourts.gov> wrote:

Christopher Browne <cbbrowne@gmail.com> wrote:

There's value in having an "immutability" constraint on a column,
where, in effect, you're not allowed to modify the value of the
column, once assigned.

+1  We would definitely use such a feature, should it become
available.

Added to TODO list.
--
When confronted by a difficult problem, solve it by reducing it to the
question, "How would the Lone Ranger handle this?"

#8Alvaro Herrera
alvherre@commandprompt.com
In reply to: Bruce Momjian (#3)
Re: foreign key locks, 2nd attempt

Excerpts from Bruce Momjian's message of jue nov 10 16:59:20 -0300 2011:

Alvaro Herrera wrote:

Hello,

After some rather extensive rewriting, I submit the patch to improve
foreign key locks.

To recap, the point of this patch is to introduce a new lock tuple mode,
that lets the RI code obtain a lighter lock on tuples, which doesn't
conflict with updates that do not modify the key columns.

What kind of operations benefit from a non-key lock like this?

I'm not sure I understand the question.

With this patch, a RI check does "SELECT FOR KEY SHARE". This means the
tuple is locked with that mode until the transaction finishes. An
UPDATE that modifies the referenced row will not conflict with that lock.

An UPDATE that modifies the key columns will be blocked, just as now.
Same with a DELETE.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#9Bruce Momjian
bruce@momjian.us
In reply to: Alvaro Herrera (#8)
Re: foreign key locks, 2nd attempt

Alvaro Herrera wrote:

Excerpts from Bruce Momjian's message of jue nov 10 16:59:20 -0300 2011:

Alvaro Herrera wrote:

Hello,

After some rather extensive rewriting, I submit the patch to improve
foreign key locks.

To recap, the point of this patch is to introduce a new lock tuple mode,
that lets the RI code obtain a lighter lock on tuples, which doesn't
conflict with updates that do not modify the key columns.

What kind of operations benefit from a non-key lock like this?

I'm not sure I understand the question.

With this patch, a RI check does "SELECT FOR KEY SHARE". This means the
tuple is locked with that mode until the transaction finishes. An
UPDATE that modifies the referenced row will not conflict with that lock.

An UPDATE that modifies the key columns will be blocked, just as now.
Same with a DELETE.

OK, so it prevents non-key data modifications from spilling to the
referred rows --- nice.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ It's impossible for everything to be true. +

#10David Kerr
dmk@mr-paradox.net
In reply to: Christopher Browne (#4)
Re: foreign key locks, 2nd attempt

On Thu, Nov 10, 2011 at 03:17:59PM -0500, Christopher Browne wrote:
- On Sun, Nov 6, 2011 at 2:28 AM, Jeroen Vermeulen <jtv@xs4all.nl> wrote:
- > On 2011-11-04 01:12, Alvaro Herrera wrote:
- >
- >> I would like some opinions on the ideas on this patch, and on the patch
- >> itself. �If someone wants more discussion on implementation details of
- >> each part of the patch, I'm happy to provide a textual description --
- >> please just ask.
- >
- > Jumping in a bit late here, but thanks for working on this: it looks like it
- > could solve some annoying problems for us.
- >
- > I do find myself idly wondering if those problems couldn't be made to go
- > away more simply given some kind of �I will never ever update this key�
- > constraint. �I'm having trouble picturing the possible lock interactions as
- > it is. �:-)
-
- +1 on that, though I'd make it more general than that. There's value
- in having an "immutability" constraint on a column, where, in effect,
- you're not allowed to modify the value of the column, once assigned.
- That certainly doesn't prevent issuing DELETE + INSERT to get whatever
- value you want into place, but that's a big enough hoop to need to
- jump through to get rid of some nonsensical updates.
-
- And if the target of a foreign key constraint consists of immutable
- columns, then, yes, indeed, UPDATE on that table no longer conflicts
- with references.
-
- In nearly all cases, I'd expect that SERIAL would be reasonably
- followed by IMMUTABLE.
-
- create table something_assigned (
- something_id serial immutable primary key,
- something_identifier text not null unique
- );

Is this being suggested in lieu of Alvaro's patch? because it seems to be adding
complexity to the system (multiple types of primary key definitions) instead of
just fixing an obvious problem (over-aggressive locking done on FK checks).

If it's going to be in addition to, then it sounds like it'd be really nice.

Dave

#11Josh Berkus
josh@agliodbs.com
In reply to: Bruce Momjian (#9)
Re: foreign key locks, 2nd attempt

An UPDATE that modifies the key columns will be blocked, just as now.
Same with a DELETE.

OK, so it prevents non-key data modifications from spilling to the
referred rows --- nice.

Yes. Eliminates the leading cause of deadlocks in Postgres applications.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

#12Jeroen Vermeulen
jtv@xs4all.nl
In reply to: David Kerr (#10)
Re: foreign key locks, 2nd attempt

On 2011-11-12 00:30, David Kerr wrote:

Is this being suggested in lieu of Alvaro's patch? because it seems to be adding
complexity to the system (multiple types of primary key definitions) instead of
just fixing an obvious problem (over-aggressive locking done on FK checks).

It wouldn't be a new type of primary key definition, just a new type of
column constraint similar to "not null." Particularly useful with keys,
but entirely orthogonal to them.

Parser and reserved words aside, it seems a relatively simple change.
Of course that's not necessarily the same as "small."

If it's going to be in addition to, then it sounds like it'd be really nice.

I wasn't thinking that far ahead, myself. But if some existing lock
type covers the situation well enough, then that could be a big argument
for doing it in-lieu-of.

I haven't looked at lock types much so I could be wrong, but my
impression is that there are dangerously many lock types already. One
would expect the risk of subtle locking bugs to grow as the square of
the number of interacting lock types.

Jeroen

#13Simon Riggs
simon@2ndQuadrant.com
In reply to: Christopher Browne (#4)
Re: foreign key locks, 2nd attempt

On Thu, Nov 10, 2011 at 8:17 PM, Christopher Browne <cbbrowne@gmail.com> wrote:

On Sun, Nov 6, 2011 at 2:28 AM, Jeroen Vermeulen <jtv@xs4all.nl> wrote:

On 2011-11-04 01:12, Alvaro Herrera wrote:

I would like some opinions on the ideas on this patch, and on the patch
itself.  If someone wants more discussion on implementation details of
each part of the patch, I'm happy to provide a textual description --
please just ask.

Jumping in a bit late here, but thanks for working on this: it looks like it
could solve some annoying problems for us.

I do find myself idly wondering if those problems couldn't be made to go
away more simply given some kind of “I will never ever update this key”
constraint.  I'm having trouble picturing the possible lock interactions as
it is.  :-)

+1 on that, though I'd make it more general than that.  There's value
in having an "immutability" constraint on a column, where, in effect,
you're not allowed to modify the value of the column, once assigned.
That certainly doesn't prevent issuing DELETE + INSERT to get whatever
value you want into place, but that's a big enough hoop to need to
jump through to get rid of some nonsensical updates.

And if the target of a foreign key constraint consists of immutable
columns, then, yes, indeed, UPDATE on that table no longer conflicts
with references.

In nearly all cases, I'd expect that SERIAL would be reasonably
followed by IMMUTABLE.

create table something_assigned (
  something_id serial immutable primary key,
  something_identifier text not null unique
);

This is a good idea but doesn't do what KEY LOCKS are designed to do so.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#14Simon Riggs
simon@2ndQuadrant.com
In reply to: Alvaro Herrera (#1)
Re: foreign key locks, 2nd attempt

On Thu, Nov 3, 2011 at 6:12 PM, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

So Noah Misch proposed using the FOR KEY SHARE syntax, and that's what I
have implemented here.  (There was some discussion that instead of
inventing new SQL syntax we could pass the necessary lock mode
internally in the ri_triggers code.  That can still be done of course,
though I haven't done so in the current version of the patch.)

FKs are a good short hand, but they aren't the only constraint people
implement. It can often be necessary to write triggers to enforce
complex constraints. So user triggers need access to the same
facilities that ri triggers uses. Please keep the syntax.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#15Tom Lane
tgl@sss.pgh.pa.us
In reply to: Simon Riggs (#14)
Re: foreign key locks, 2nd attempt

Simon Riggs <simon@2ndQuadrant.com> writes:

On Thu, Nov 3, 2011 at 6:12 PM, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

So Noah Misch proposed using the FOR KEY SHARE syntax, and that's what I
have implemented here. �(There was some discussion that instead of
inventing new SQL syntax we could pass the necessary lock mode
internally in the ri_triggers code. �That can still be done of course,
though I haven't done so in the current version of the patch.)

FKs are a good short hand, but they aren't the only constraint people
implement. It can often be necessary to write triggers to enforce
complex constraints. So user triggers need access to the same
facilities that ri triggers uses. Please keep the syntax.

It's already the case that RI triggers require access to special
executor features that are not accessible at the SQL level. I don't
think the above argument is a compelling reason for exposing more
such features at the SQL level. All we need is that C-coded functions
can get at them somehow.

regards, tom lane

#16Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#15)
Re: foreign key locks, 2nd attempt

On Sat, Nov 19, 2011 at 10:36 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Simon Riggs <simon@2ndQuadrant.com> writes:

On Thu, Nov 3, 2011 at 6:12 PM, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:

So Noah Misch proposed using the FOR KEY SHARE syntax, and that's what I
have implemented here.  (There was some discussion that instead of
inventing new SQL syntax we could pass the necessary lock mode
internally in the ri_triggers code.  That can still be done of course,
though I haven't done so in the current version of the patch.)

FKs are a good short hand, but they aren't the only constraint people
implement. It can often be necessary to write triggers to enforce
complex constraints. So user triggers need access to the same
facilities that ri triggers uses. Please keep the syntax.

It's already the case that RI triggers require access to special
executor features that are not accessible at the SQL level.  I don't
think the above argument is a compelling reason for exposing more
such features at the SQL level.  All we need is that C-coded functions
can get at them somehow.

I kinda agree with Simon. In general, if we don't need to expose
something at the SQL level, then sure, let's not. But it seems weird
to me to say, well, we have four lock modes internally, and you can
get to three of them via SQL. To me, that sort of inconsistency feels
like a wart.

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

#17Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Robert Haas (#16)
Re: foreign key locks, 2nd attempt

Robert Haas <robertmhaas@gmail.com> writes:

On Sat, Nov 19, 2011 at 10:36 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

It's already the case that RI triggers require access to special
executor features that are not accessible at the SQL level.  I don't
think the above argument is a compelling reason for exposing more
such features at the SQL level.  All we need is that C-coded functions
can get at them somehow.

I kinda agree with Simon. In general, if we don't need to expose
something at the SQL level, then sure, let's not. But it seems weird
to me to say, well, we have four lock modes internally, and you can
get to three of them via SQL. To me, that sort of inconsistency feels
like a wart.

+1

I know I've already rolled constraint triggers into production, being
able to use FOR KEY SHARE locks would be good.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

#18Noah Misch
noah@leadboat.com
In reply to: Alvaro Herrera (#1)
3 attachment(s)
Re: foreign key locks, 2nd attempt

Hi Alvaro,

On Thu, Nov 03, 2011 at 03:12:49PM -0300, Alvaro Herrera wrote:

After some rather extensive rewriting, I submit the patch to improve
foreign key locks.

I've reviewed this patch. The basic design and behaviors are sound. All the
bugs noted in my previous review are gone.

Making pg_multixact persistent across clean shutdowns is no bridge to cross
lightly, since it means committing to an on-disk format for an indefinite
period. We should do it; the benefits of this patch justify it, and I haven't
identified a way to avoid it without incurring worse problems.

FWIW, I pondered a dead-end alternate idea of having every MultiXactId also be
a SubTransactionId. That way, you could still truncate pg_multixact early,
with the subtransaction commit status being adequate going forward. However,
for the case when the locker arrives after the updater, this would require the
ability to create a new subtransaction on behalf of a different backend. It
would also burn the xid space more quickly, slow commits affected by the added
subtransaction load, and aggravate "suboverflowed" incidence.

I did some halfhearted benchmarking to at least ensure the absence of any
gross performance loss on at-risk operations. Benchmarks done with a vanilla
build, -O2, no --enable-cassert. First, to exercise the cost of comparing
large column values an extra time, I created a table with a 2000-byte key
column and another int4 column. I then did a HOT update of every tuple. The
patch did not significantly change runtime. See attached fklock-wide.sql for
the commands run and timings collected.

Second, I tried a SELECT FOR SHARE on a table of 1M tuples; this might incur
some cost due to the now-guaranteed use of pg_multixact for FOR SHARE. See
attached fklock-test-forshare.sql. The median run slowed by 7% under the
patch, albeit with a rather brief benchmark run. Both master and patched
PostgreSQL seemed to exhibit a statement-scope memory leak in this test case:
to lock 1M rows, backend-private memory grew by about 500M. When trying 10M
rows, I cancelled the query after 1.2 GiB of consumption. This limited the
duration of a convenient test run.

I planned to benchmark the overhead of the HeapTupleSatisfiesMVCC() changes
when no foreign keys are in use, but I did not get around to that.

For anyone else following along, here are some important past threads:
http://archives.postgresql.org/message-id/1294953201-sup-2099@alvh.no-ip.org
http://archives.postgresql.org/message-id/20110211071322.GB26971@tornado.leadboat.com
http://archives.postgresql.org/message-id/1312907125-sup-9346@alvh.no-ip.org
http://archives.postgresql.org/message-id/cmdap.323308e530.1315601945-sup-7377@alvh.no-ip.org
http://archives.postgresql.org/message-id/1317053656-sup-7193@alvh.no-ip.org
http://archives.postgresql.org/message-id/1317840445-sup-7142@alvh.no-ip.org

So Noah Misch proposed using the FOR KEY SHARE syntax, and that's what I
have implemented here. (There was some discussion that instead of
inventing new SQL syntax we could pass the necessary lock mode
internally in the ri_triggers code. That can still be done of course,
though I haven't done so in the current version of the patch.)

From a UI perspective, I'd somewhat rather we exposed not only FOR KEY SHARE,
but also FOR KEY UPDATE, in the grammar. That lets us document the tuple lock
conflict table entirely in terms of other documented tuple locks.

The other user-visible pending item is that it was said that instead of
simply using "columns used by unique indexes" as the key columns
considered by this patch, we should do some ALTER TABLE command. This
would be a comparatively trivial undertaking, I think, but I would like
there to be consensus on that this is really the way to go before I
implement it.

As I mentioned in my last review, the base heuristic should be to select
attributes actually referenced by some foreign key constraint. We don't gain
enough by automatically involving columns of indexes that could, but do not,
support an actual FK constraint.

I see value in that ALTER TABLE proposal, as a follow-on patch, for the
benefit of user-defined referential integrity constraints. Users ought to get
the benefit of the core patch automatically, not just when they know to mark
every key column. For that reason, the key columns defined through ALTER
TABLE should add to, not replace, the automatically-identified set.

The new code mostly works fine but I'm pretty sure there must be serious
bugs somewhere. Also, in places, heap_update and heap_lock_tuple have
become spaguettish, though I'm not sure I see better ways to write them.

Agreed, but I'm also short on ideas for rapidly and significantly improving
the situation. If I were going to try something, I'd try splitting out the
lock-acquisition loop into a separate function, preferably shared between
heap_update() and heap_lock_tuple().

Starting with source clusters of the catversion introducing this feature,
pg_upgrade must copy pg_multixact to the new cluster. The patch does not add
code for this.

This patch adds no user documentation. We need to document FOR KEY SHARE. I
don't see any current documentation of the tuple lock consequences of foreign
key constraints, so we don't need any changes there.

Should we add a few (2-6) unused flag bits to each multixact member to provide
growing room for future needs?

Does this patch have any special implications for REPEATABLE READ?

--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c

I used pgrowlocks to play with this patch, and the output clarity seems to
have fallen somewhat:

**** w/ patch
-- SELECT * FROM test_rowlock FOR KEY SHARE;
locked_row | lock_type | locker | multi | xids | modes | pids
------------+-----------+--------+-------+---------+-------+---------
(0,1) | KeyShare | 15371 | f | {15371} | | {27276}
-- SELECT * FROM test_rowlock FOR SHARE;
locked_row | lock_type | locker | multi | xids | modes | pids
------------+--------------+--------+-------+---------+-------+---------
(0,1) | IsNotUpdate | 70 | t | {15372} | {shr} | {27276}
-- SELECT * FROM test_rowlock FOR UPDATE;
locked_row | lock_type | locker | multi | xids | modes | pids
------------+------------------------+--------+-------+---------+----------+---------
(0,1) | Exclusive IsNotUpdate | 71 | t | {15373} | {forupd} | {27276}
-- UPDATE test_rowlock SET non_key_col = 11;
locked_row | lock_type | locker | multi | xids | modes | pids
------------+-----------+--------+-------+---------+-------+---------
(0,1) | | 15374 | f | {15374} | | {27276}
-- UPDATE test_rowlock SET key_col = 2;
locked_row | lock_type | locker | multi | xids | modes | pids
------------+-----------+--------+-------+---------+-------+---------
(0,1) | | 15375 | f | {15375} | | {27276}

**** 9.1.1
-- SELECT * FROM test_rowlock FOR SHARE;
locked_row | lock_type | locker | multi | xids | pids
------------+-----------+--------+-------+-------+---------
(0,1) | Shared | 757 | f | {757} | {27349}
-- SELECT * FROM test_rowlock FOR UPDATE;
locked_row | lock_type | locker | multi | xids | pids
------------+-----------+--------+-------+-------+---------
(0,1) | Exclusive | 758 | f | {758} | {27349}
-- UPDATE test_rowlock SET non_key_col = 11;
locked_row | lock_type | locker | multi | xids | pids
------------+-----------+--------+-------+-------+---------
(0,1) | Exclusive | 759 | f | {759} | {27349}
-- UPDATE test_rowlock SET key_col = 2;
locked_row | lock_type | locker | multi | xids | pids
------------+-----------+--------+-------+-------+---------
(0,1) | Exclusive | 760 | f | {760} | {27349}

I've attached fklock-pgrowlocks.sql, used to produce the above results. In
particular, the absence of any distinction between the key_col and non_key_col
update scenarios is suboptimal. Also, the "SELECT * FROM test_rowlock FOR
UPDATE" ought not to use a multi, right? Choices such as letting lock_type be
blank or contain IsNotUpdate tend to make the output reflect implementation
details more than user-relevant lock semantics. One could go either way on
those. I tend to think pageinspect is for raw implementation exposure and
pgrowlocks for something more cooked.

I have not reviewed your actual pgrowlocks code changes.

--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c

heap_xlog_update() still sets xmax of the old tuple and xmin of the new tuple
based on the XLogRecord, so it will always be the plain xid of the updater. I
suppose that's still fine, because locks don't matter after a crash. I
suggest adding a comment, though.

@@ -1620,7 +1622,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
ItemPointerGetBlockNumber(tid));
offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
at_chain_start = false;
-			prev_xmax = HeapTupleHeaderGetXmax(heapTuple->t_data);
+			prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
}
else
break;				/* end of chain */

The HOT search logic in pruneheap.c needs the same change.

@@ -1743,7 +1745,7 @@ heap_get_latest_tid(Relation relation,
* tuple.  Check for XMIN match.
*/
if (TransactionIdIsValid(priorXmax) &&
-		  !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
+			!TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))

pgindent will undo this change.

@@ -2174,20 +2178,22 @@ l1:
*/
if (!have_tuple_lock)
{
-			LockTuple(relation, &(tp.t_self), ExclusiveLock);
+			LockTuple(relation, &(tp.t_self),
+					  get_lockmode_for_tuplelock(LockTupleKeyUpdate));

I suggest hiding calls to get_lockmode_for_tuplelock() behind a local macro
wrapper around LockTuple(), itself taking a LockTupleMode directly.

@@ -2471,8 +2483,14 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
bool		have_tuple_lock = false;
bool		iscombo;
bool		use_hot_update = false;
+	bool		key_intact;
bool		all_visible_cleared = false;
bool		all_visible_cleared_new = false;
+	bool			keep_xmax_multi = false;
+	TransactionId	keep_xmax = InvalidTransactionId;

Those two new variables should be initialized after `l2'. If the final goto
fires (we lack a needed pin on the visibility map page), their existing values
become invalid. (Not positive there's a live bug here, but it's fragile.)

+	TransactionId	keep_xmax_old = InvalidTransactionId;
+	uint16		keep_xmax_infomask = 0;
+	uint16		keep_xmax_old_infomask = 0;

Assert(ItemPointerIsValid(otid));

@@ -2488,7 +2506,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
* Note that we get a copy here, so we need not worry about relcache flush
* happening midway through.
*/
-	hot_attrs = RelationGetIndexAttrBitmap(relation);
+	hot_attrs = RelationGetIndexAttrBitmap(relation, false);
+	key_attrs = RelationGetIndexAttrBitmap(relation, true);

block = ItemPointerGetBlockNumber(otid);
buffer = ReadBuffer(relation, block);
@@ -2513,6 +2532,24 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
oldtup.t_self = *otid;

/*
+	 * If we're not updating any "key" column, we can grab a milder lock type.
+	 * This allows for more concurrency when we are running simultaneously with
+	 * foreign key checks.
+	 */
+	if (HeapSatisfiesHOTUpdate(relation, key_attrs, &oldtup, newtup))

This will only pass when the toastedness matches, too. That is, something
like "UPDATE t SET keycol = keycol || ''" will take the stronger lock if
keycol is toasted. That's probably for the best -- the important case to
optimize is updates that never manipulate key columns at all, not those that
serendipitously arrive at the same key values. I wouldn't expect a net win
from the toaster effort needed to recognize the latter case.

Nonetheless, a comment here should note the decision.

+	{
+		tuplock = LockTupleUpdate;
+		mxact_status = MultiXactStatusUpdate;
+		key_intact = true;
+	}
+	else
+	{
+		tuplock = LockTupleKeyUpdate;
+		mxact_status = MultiXactStatusKeyUpdate;
+		key_intact = false;
+	}
+
+	/*
* Note: beyond this point, use oldtup not otid to refer to old tuple.
* otid may very well point at newtup->t_self, which we will overwrite
* with the new tuple's location, so there's great risk of confusion if we
@@ -2522,6 +2559,9 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
l2:
result = HeapTupleSatisfiesUpdate(oldtup.t_data, cid, buffer);
+	/* see below about the "no wait" case */
+	Assert(result != HeapTupleBeingUpdated || wait);

Maybe just Assert(wait)? Given that we're breaking that usage, no point in
giving the user any illusions. However ...

+
if (result == HeapTupleInvisible)
{
UnlockReleaseBuffer(buffer);
@@ -2529,8 +2569,21 @@ l2:
}
else if (result == HeapTupleBeingUpdated && wait)
{
- TransactionId xwait;
+ TransactionId xwait;

pgindent will undo this change.

uint16		infomask;
+		bool		none_remain = false;
+
+		/*
+		 * XXX note that we don't consider the "no wait" case here.  This
+		 * isn't a problem currently because no caller uses that case, but it
+		 * should be fixed if such a caller is introduced.  It wasn't a problem
+		 * previously because this code would always wait, but now that some
+		 * tuple locks do not conflict with one of the lock modes we use, it is
+		 * possible that this case is interesting to handle specially.
+		 *
+		 * This may cause failures with third-party code that calls heap_update
+		 * directly.
+		 */

... consider that this introduces code drift between heap_update() and the
presently-similar logic in heap_lock_tuple().

/* must copy state data before unlocking buffer */
xwait = HeapTupleHeaderGetXmax(oldtup.t_data);
@@ -2549,20 +2602,26 @@ l2:
*/
if (!have_tuple_lock)
{
-			LockTuple(relation, &(oldtup.t_self), ExclusiveLock);
+			LockTuple(relation, &(oldtup.t_self),
+					  get_lockmode_for_tuplelock(tuplock));
have_tuple_lock = true;
}
/*
-		 * Sleep until concurrent transaction ends.  Note that we don't care
-		 * if the locker has an exclusive or shared lock, because we need
-		 * exclusive.
+		 * Now sleep on the locker.  Note that if there are only key-share
+		 * lockers and we're not updating the key columns, we will be awaken
+		 * before it is gone, so we may need to mark the new tuple with a
+		 * new MultiXactId including the original xmax and ourselves.

Well, we'll never actually sleep at all.

+		 *
+		 * XXX this comment needs to be more comprehensive
*/
-
if (infomask & HEAP_XMAX_IS_MULTI)
{
+			TransactionId	update_xact;
+			int				remain;
+
/* wait for multixact */
-			MultiXactIdWait((MultiXactId) xwait);
+			MultiXactIdWait((MultiXactId) xwait, mxact_status, &remain);
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);

/*
@@ -2576,41 +2635,98 @@ l2:
goto l2;

/*
-			 * You might think the multixact is necessarily done here, but not
-			 * so: it could have surviving members, namely our own xact or
-			 * other subxacts of this backend.	It is legal for us to update
-			 * the tuple in either case, however (the latter case is
-			 * essentially a situation of upgrading our former shared lock to
-			 * exclusive).	We don't bother changing the on-disk hint bits
-			 * since we are about to overwrite the xmax altogether.
+			 * Note that the multixact may not be done by now.  It could have
+			 * surviving members; our own xact or other subxacts of this
+			 * backend, and also any other concurrent transaction that locked
+			 * the tuple with KeyShare if we only got TupleLockUpdate.  If this
+			 * is the case, we have to be careful to mark the updated tuple
+			 * with the surviving members in Xmax.
+			 *
+			 * Note that there could have been another update in the MultiXact.
+			 * In that case, we need to check whether it committed or aborted.
+			 * If it aborted we are safe to update it again; otherwise there is
+			 * an update conflict that must be handled below.

It's handled below in the sense that we bail, returning HeapTupleUpdated?

+			 *
+			 * In the LockTupleKeyUpdate case, we still need to preserve the
+			 * surviving members: those would include the tuple locks we had
+			 * before this one, which are important to keep in case this
+			 * subxact aborts.
*/
+			update_xact = InvalidTransactionId;
+			if (!(oldtup.t_data->t_infomask & HEAP_XMAX_IS_NOT_UPDATE))
+				update_xact = HeapTupleGetUpdateXid(oldtup.t_data);
+
+			/* there was no UPDATE in the MultiXact; or it aborted. */
+			if (update_xact == InvalidTransactionId ||
+				TransactionIdDidAbort(update_xact))
+			{
+				/*
+				 * if the multixact still has live members, we need to preserve
+				 * it by creating a new multixact.  If all members are gone, we
+				 * can simply update the tuple by setting ourselves in Xmax.
+				 */
+				if (remain > 0)
+				{
+					keep_xmax = HeapTupleHeaderGetXmax(oldtup.t_data);
+					keep_xmax_multi =
+						(oldtup.t_data->t_infomask & HEAP_XMAX_IS_MULTI) != 0;

Will keep_xmax_multi ever be false? Would we not have exited at the above
"goto l2;" in those cases?

+				}
+				else
+				{
+					/*
+					 * We could set the HEAP_XMAX_INVALID bit here instead of
+					 * using a separate boolean flag.  However, since we're going
+					 * to set up a new xmax below, this would waste time
+					 * setting up the buffer's dirty bit.
+					 */
+					none_remain = false;
+				}
+			}
}
else

This would require less reindentation as an "else if", rather than "else { if".

{
-			/* wait for regular transaction to end */
-			XactLockTableWait(xwait);
-			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
-
/*
-			 * xwait is done, but if xwait had just locked the tuple then some
-			 * other xact could update this tuple before we get to this point.
-			 * Check for xmax change, and start over if so.
+			 * If it's just a key-share locker, and we're not changing the
+			 * key columns, we don't need to wait for it to wait; but we
+			 * need to preserve it as locker.
*/
-			if ((oldtup.t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
-				!TransactionIdEquals(HeapTupleHeaderGetXmax(oldtup.t_data),
-									 xwait))
-				goto l2;
+			if ((oldtup.t_data->t_infomask & HEAP_XMAX_KEYSHR_LOCK) &&
+				key_intact)

You don't have a content lock on the buffer at this point, so the test should
be against "infomask", not "oldtup.t_data->t_infomask".

+			{
+				LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+				keep_xmax = xwait;
+				keep_xmax_multi = false;
+			}

Like the other branches, this one needs to recheck the t_infomask after
reacquiring the content lock.

It would be nice to completely avoid releasing the content lock in cases that
don't involve any waiting. However, since that (have_tuple_lock = true) is
already something of a slow path, I doubt it's worth the complexity.

+			else
+			{
+				/* wait for regular transaction to end */
+				XactLockTableWait(xwait);
+				LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
-			/* Otherwise check if it committed or aborted */
-			UpdateXmaxHintBits(oldtup.t_data, buffer, xwait);
+				/*
+				 * xwait is done, but if xwait had just locked the tuple then some
+				 * other xact could update this tuple before we get to this point.
+				 * Check for xmax change, and start over if so.
+				 */
+				if ((oldtup.t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||
+					!TransactionIdEquals(HeapTupleHeaderGetXmax(oldtup.t_data),
+										 xwait))
+					goto l2;
+
+				/* Otherwise check if it committed or aborted */
+				UpdateXmaxHintBits(oldtup.t_data, buffer, xwait);
+			}
}
/*
* We may overwrite if previous xmax aborted, or if it committed but
-		 * only locked the tuple without updating it.
+		 * only locked the tuple without updating it, or if we are going to
+		 * keep it around in Xmax.
*/
-		if (oldtup.t_data->t_infomask & (HEAP_XMAX_INVALID |
-										 HEAP_IS_LOCKED))
+		if (TransactionIdIsValid(keep_xmax) ||
+			none_remain ||
+			(oldtup.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+			HeapTupleHeaderIsLocked(oldtup.t_data))

When is the HeapTupleHeaderIsLocked(oldtup.t_data) condition needed? Offhand,
I'd think none_remain = true and HEAP_XMAX_INVALID cover its cases.

result = HeapTupleMayBeUpdated;
else
result = HeapTupleUpdated;
@@ -2630,13 +2746,15 @@ l2:
result == HeapTupleBeingUpdated);
Assert(!(oldtup.t_data->t_infomask & HEAP_XMAX_INVALID));
*ctid = oldtup.t_data->t_ctid;
-		*update_xmax = HeapTupleHeaderGetXmax(oldtup.t_data);
+		*update_xmax = HeapTupleHeaderGetUpdateXid(oldtup.t_data);
UnlockReleaseBuffer(buffer);
if (have_tuple_lock)
-			UnlockTuple(relation, &(oldtup.t_self), ExclusiveLock);
+			UnlockTuple(relation, &(oldtup.t_self),
+						get_lockmode_for_tuplelock(tuplock));
if (vmbuffer != InvalidBuffer)
ReleaseBuffer(vmbuffer);
bms_free(hot_attrs);
+		bms_free(key_attrs);
return result;
}
@@ -2645,7 +2763,7 @@ l2:
* visible while we were busy locking the buffer, or during some subsequent
* window during which we had it unlocked, we'll have to unlock and
* re-lock, to avoid holding the buffer lock across an I/O.  That's a bit
-	 * unfortunate, esepecially since we'll now have to recheck whether the
+	 * unfortunate, especially since we'll now have to recheck whether the
* tuple has been locked or updated under us, but hopefully it won't
* happen very often.
*/
@@ -2678,13 +2796,54 @@ l2:
Assert(!(newtup->t_data->t_infomask & HEAP_HASOID));
}
+	/*
+	 * If the tuple we're updating is locked, we need to preserve this in the
+	 * new tuple's Xmax as well as in the old tuple.  Prepare the new xmax
+	 * value for these uses.
+	 *
+	 * Note there cannot be an xmax to save if we're changing key columns; in
+	 * this case, the wait above should have only returned when the locking
+	 * transactions finished.
+	 */
+	if (TransactionIdIsValid(keep_xmax))
+	{
+		if (keep_xmax_multi)
+		{
+			keep_xmax_old = MultiXactIdExpand(keep_xmax,
+											  xid, MultiXactStatusUpdate);
+			keep_xmax_infomask = HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_IS_MULTI;

Not directly related to this line, but is the HEAP_IS_NOT_UPDATE bit getting
cleared where needed?

+		}
+		else
+		{
+			/* not a multi? must be a KEY SHARE locker */
+			keep_xmax_old = MultiXactIdCreate(keep_xmax, MultiXactStatusForKeyShare,
+											  xid, MultiXactStatusUpdate);
+			keep_xmax_infomask = HEAP_XMAX_KEYSHR_LOCK;
+		}
+		keep_xmax_old_infomask = HEAP_XMAX_IS_MULTI | HEAP_XMAX_KEYSHR_LOCK;
+		/* FIXME -- need more infomask bits? */

Maybe ... I haven't thought it all through.

+	}
+
+	/*
+	 * Prepare the new tuple with the appropriate initial values of Xmin and
+	 * Xmax, as well as initial infomask bits.
+	 */
newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
newtup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);
-	newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED);
+	newtup->t_data->t_infomask |= HEAP_UPDATED;
HeapTupleHeaderSetXmin(newtup->t_data, xid);
HeapTupleHeaderSetCmin(newtup->t_data, cid);
-	HeapTupleHeaderSetXmax(newtup->t_data, 0);	/* for cleanliness */
newtup->t_tableOid = RelationGetRelid(relation);
+	if (TransactionIdIsValid(keep_xmax))
+	{
+		newtup->t_data->t_infomask |= keep_xmax_infomask;
+		HeapTupleHeaderSetXmax(newtup->t_data, keep_xmax);
+	}
+	else
+	{
+		newtup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+		HeapTupleHeaderSetXmax(newtup->t_data, 0);	/* for cleanliness */
+	}
/*
* Replace cid with a combo cid if necessary.  Note that we already put
@@ -2725,11 +2884,20 @@ l2:
oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
HEAP_XMAX_IS_MULTI |
-									   HEAP_IS_LOCKED |
+									   HEAP_LOCK_BITS |
HEAP_MOVED);
+		oldtup.t_data->t_infomask2 &= ~HEAP_UPDATE_KEY_INTACT;
HeapTupleClearHotUpdated(&oldtup);
/* ... and store info about transaction updating this tuple */
-		HeapTupleHeaderSetXmax(oldtup.t_data, xid);
+		if (TransactionIdIsValid(keep_xmax_old))
+		{
+			HeapTupleHeaderSetXmax(oldtup.t_data, keep_xmax_old);
+			oldtup.t_data->t_infomask |= keep_xmax_old_infomask;
+		}
+		else
+			HeapTupleHeaderSetXmax(oldtup.t_data, xid);
+		if (key_intact)
+			oldtup.t_data->t_infomask2 |= HEAP_UPDATE_KEY_INTACT;
HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
/* temporarily make it look not-updated */
oldtup.t_data->t_ctid = oldtup.t_self;

Shortly after this, we release the content lock and go off toasting the tuple
and finding free space. When we come back, could the old tuple have
accumulated additional KEY SHARE locks that we need to re-copy?

@@ -2883,10 +3051,19 @@ l2:
oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
HEAP_XMAX_IS_MULTI |
-									   HEAP_IS_LOCKED |
+									   HEAP_LOCK_BITS |
HEAP_MOVED);
+		oldtup.t_data->t_infomask2 &= ~HEAP_UPDATE_KEY_INTACT;
/* ... and store info about transaction updating this tuple */
-		HeapTupleHeaderSetXmax(oldtup.t_data, xid);
+		if (TransactionIdIsValid(keep_xmax_old))
+		{
+			HeapTupleHeaderSetXmax(oldtup.t_data, keep_xmax_old);
+			oldtup.t_data->t_infomask |= keep_xmax_old_infomask;
+		}
+		else
+			HeapTupleHeaderSetXmax(oldtup.t_data, xid);
+		if (key_intact)
+			oldtup.t_data->t_infomask2 |= HEAP_UPDATE_KEY_INTACT;
HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
}
@@ -3201,13 +3429,13 @@ heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer *buffer,
Page		page;
TransactionId xid;
TransactionId xmax;
+	TransactionId keep_xmax = InvalidTransactionId;
+	bool		keep_xmax_multi = false;
+	bool		none_remains = false;
uint16		old_infomask;
uint16		new_infomask;
-	LOCKMODE	tuple_lock_type;
bool		have_tuple_lock = false;

- tuple_lock_type = (mode == LockTupleShared) ? ShareLock : ExclusiveLock;
-
*buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);

@@ -3220,6 +3448,9 @@ heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer *buffer,
tuple->t_tableOid = RelationGetRelid(relation);

l3:
+	/* shouldn't get back here if we already set keep_xmax */
+	Assert(keep_xmax == InvalidTransactionId);
+
result = HeapTupleSatisfiesUpdate(tuple->t_data, cid, *buffer);

if (result == HeapTupleInvisible)
@@ -3231,30 +3462,70 @@ l3:
{
TransactionId xwait;
uint16 infomask;
+ uint16 infomask2;
+ bool require_sleep;

/* must copy state data before unlocking buffer */
xwait = HeapTupleHeaderGetXmax(tuple->t_data);
infomask = tuple->t_data->t_infomask;
+ infomask2 = tuple->t_data->t_infomask2;

LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);

/*
-		 * If we wish to acquire share lock, and the tuple is already
-		 * share-locked by a multixact that includes any subtransaction of the
-		 * current top transaction, then we effectively hold the desired lock
-		 * already.  We *must* succeed without trying to take the tuple lock,
-		 * else we will deadlock against anyone waiting to acquire exclusive
-		 * lock.  We don't need to make any state changes in this case.
+		 * If we wish to acquire share or key lock, and the tuple is already
+		 * key or share locked by a multixact that includes any subtransaction
+		 * of the current top transaction, then we effectively hold the desired
+		 * lock already (except if we own key share lock and now desire share
+		 * lock).  We *must* succeed without trying to take the tuple lock,

This can now apply to FOR UPDATE as well.

For the first sentence, I suggest the wording "If any subtransaction of the
current top transaction already holds a stronger lock, we effectively hold the
desired lock already."

+		 * else we will deadlock against anyone wanting to acquire a stronger
+		 * lock.
+		 *
+		 * FIXME -- we don't do the below currently, but I think we should:
+		 *
+		 * We update the Xmax with a new MultiXactId to include the new lock
+		 * mode in this case.
+		 *
+		 * Note that since we want to alter the Xmax, we need to re-acquire the
+		 * buffer lock.  The xmax could have changed in the meantime, so we
+		 * recheck it in that case, but we keep the buffer lock while doing it
+		 * to prevent starvation.  The second time around we know we must be
+		 * part of the MultiXactId in any case, which is why we don't need to
+		 * go back to recheck HeapTupleSatisfiesUpdate.  Also, after we
+		 * re-acquire lock, the MultiXact is likely to (but not necessarily) be
+		 * the same that we see here, so it should be in multixact's cache and
+		 * thus quick to obtain.

What is the benefit of doing so?

*/
-		if (mode == LockTupleShared &&
-			(infomask & HEAP_XMAX_IS_MULTI) &&
-			MultiXactIdIsCurrent((MultiXactId) xwait))
+		if ((infomask & HEAP_XMAX_IS_MULTI) &&
+			((mode == LockTupleShare) || (mode == LockTupleKeyShare)))
{
-			Assert(infomask & HEAP_XMAX_SHARED_LOCK);
-			/* Probably can't hold tuple lock here, but may as well check */
-			if (have_tuple_lock)
-				UnlockTuple(relation, tid, tuple_lock_type);
-			return HeapTupleMayBeUpdated;
+			int		i;
+			int		nmembers;
+			MultiXactMember *members;
+
+			nmembers = GetMultiXactIdMembers(xwait, &members);
+
+			for (i = 0; i < nmembers; i++)
+			{
+				if (TransactionIdIsCurrentTransactionId(members[i].xid))

This does not handle subtransactions like the previous code.

I have not yet reviewed the rest of the heap_lock_tuple() changes.

@@ -3789,6 +4305,8 @@ recheck_xmax:
* extremely low-probability scenario with minimal downside even if
* it does happen, so for now we don't do the extra bookkeeping that
* would be needed to clean out MultiXactIds.
+		 *
+		 * FIXME -- today is that day.  Figure this out.

Yep. I think you can just use HeapTupleHeaderGetUpdateXid() and remove the
explicit conditional on HEAP_XMAX_IS_MULTI.

@@ -3919,6 +4536,7 @@ HeapTupleHeaderAdvanceLatestRemovedXid(HeapTupleHeader tuple,
TransactionId *latestRemovedXid)
{
TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
+ /* FIXME -- change this? */
TransactionId xmax = HeapTupleHeaderGetXmax(tuple);

Yes. Since this function is only passed dead tuples, it could previously
expect to never see a multixact xmax. No longer.

@@ -4991,14 +5609,18 @@ heap_xlog_lock(XLogRecPtr lsn, XLogRecord *record)
htup->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
HEAP_XMAX_IS_MULTI |
-						  HEAP_IS_LOCKED |
+						  HEAP_LOCK_BITS |
HEAP_MOVED);
-	if (xlrec->xid_is_mxact)
+	if (xlrec->infobits_set & XLHL_XMAX_IS_MULTI)
htup->t_infomask |= HEAP_XMAX_IS_MULTI;
-	if (xlrec->shared_lock)
-		htup->t_infomask |= HEAP_XMAX_SHARED_LOCK;
-	else
+	if (xlrec->infobits_set & XLHL_XMAX_IS_NOT_UPDATE)
+		htup->t_infomask |= HEAP_XMAX_IS_NOT_UPDATE;
+	if (xlrec->infobits_set & XLHL_XMAX_EXCL_LOCK)
htup->t_infomask |= HEAP_XMAX_EXCL_LOCK;
+	if (xlrec->infobits_set & XLHL_XMAX_KEYSHR_LOCK)
+		htup->t_infomask |= HEAP_XMAX_KEYSHR_LOCK;
+	if (xlrec->infobits_set & XLHL_UPDATE_KEY_INTACT)
+		htup->t_infomask2 |= HEAP_UPDATE_KEY_INTACT;
HeapTupleHeaderClearHotUpdated(htup);
HeapTupleHeaderSetXmax(htup, xlrec->locking_xid);
HeapTupleHeaderSetCmax(htup, FirstCommandId, false);

Just after here is this code:

/* Make sure there is no forward chain link in t_ctid */
htup->t_ctid = xlrec->target.tid;

Now that a KEY SHARE locker could apply over an UPDATE, that's no longer
always valid.

Incidentally, why is this level of xlog detail needed for tuple locks? We
need an FPI of the page before the lock-related changes start scribbling on
it, and we need to log any xid, even that of a locker, that could land in the
heap on disk. But, why do we actually need to replay each lock?

--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -4,7 +4,7 @@
*		PostgreSQL multi-transaction-log manager
*
* The pg_multixact manager is a pg_clog-like manager that stores an array
- * of TransactionIds for each MultiXactId.	It is a fundamental part of the
+ * of MultiXactMember for each MultiXactId.	It is a fundamental part of the
* shared-row-lock implementation.	A share-locked tuple stores a
* MultiXactId in its Xmax, and a transaction that needs to wait for the
* tuple to be unlocked can sleep on the potentially-several TransactionIds

This header comment (including more than the portion quoted here) needs
further updates. In particular, there's no direct reference to the flag bits
now stored with each member xid. Also, the comment only mentions merely
preserving state across crashes, but this data now has the pg_clog life cycle.
Consider mentioning that the name is a bit historical: a singleton multixact
now has value for storing flags having no other expression.

@@ -48,6 +48,8 @@
*/
#include "postgres.h"

+#include <unistd.h>
+
#include "access/multixact.h"
#include "access/slru.h"
#include "access/transam.h"
@@ -60,6 +62,7 @@
#include "storage/procarray.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
+#include "utils/snapmgr.h"

/*
@@ -75,19 +78,58 @@
* (see MultiXact{Offset,Member}PagePrecedes).
*/

The comment just ending here mentions "MULTIXACT_*_PER_PAGE", but it's now
only correct for MULTIXACT_OFFSETS_PER_PAGE.

@@ -180,7 +213,8 @@ static MultiXactId *OldestVisibleMXactId;
* so they will be uninteresting by the time our next transaction starts.
* (XXX not clear that this is correct --- other members of the MultiXact
* could hang around longer than we did.  However, it's not clear what a
- * better policy for flushing old cache entries would be.)
+ * better policy for flushing old cache entries would be.)  FIXME actually
+ * this is plain wrong now that multixact's may contain update Xids.

A key role of the cache is to avoid creating vast numbers of multixacts each
having the same membership. In that role, the existing policy seems no less
suitable than before. I agree that this patch makes the policy less suitable
for readers, though. Not sure what should be done about that, if anything.

@@ -235,29 +297,59 @@ static bool MultiXactOffsetPrecedes(MultiXactOffset offset1,
MultiXactOffset offset2);
static void ExtendMultiXactOffset(MultiXactId multi);
static void ExtendMultiXactMember(MultiXactOffset offset, int nmembers);
-static void TruncateMultiXact(void);
-static void WriteMZeroPageXlogRec(int pageno, uint8 info);
+static void fillSegmentInfoData(SlruCtl ctl, SegmentInfo *segment);
+static int	compareTruncateXidEpoch(const void *a, const void *b);
+static void WriteMZeroOffsetPageXlogRec(int pageno, TransactionId truncateXid,
+							uint32 truncateXidEpoch);
+static void WriteMZeroMemberPageXlogRec(int pageno);
/*
+ * MultiXactIdCreateSingleton
+ * 		Construct a MultiXactId representing a single transaction.

I suggest mentioning that this is useful for marking a tuple in a manner that
can only be achieved through multixact flags.

+ *
+ * NB - we don't worry about our local MultiXactId cache here, because that
+ * is handled by the lower-level routines.
+ */
+MultiXactId
+MultiXactIdCreateSingleton(TransactionId xid, MultiXactStatus status)
+{
+	MultiXactId	newMulti;
+	MultiXactMember	member[1];
+
+	AssertArg(TransactionIdIsValid(xid));
+
+	member[0].xid = xid;
+	member[0].status = status;
+
+	newMulti = CreateMultiXactId(1, member);
+
+	debug_elog4(DEBUG2, "Create: returning %u for %u",
+			   newMulti, xid);
+
+	return newMulti;
+}
+
+/*
* MultiXactIdCreate
*		Construct a MultiXactId representing two TransactionIds.
*
- * The two XIDs must be different.
+ * The two XIDs must be different, or be requesting different lock modes.

Why is it not sufficient to store the strongest type for a particular xid?

@@ -376,7 +480,7 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid)
bool
MultiXactIdIsRunning(MultiXactId multi)
{
- TransactionId *members;
+ MultiXactMember *members;
int nmembers;
int i;

@@ -397,7 +501,7 @@ MultiXactIdIsRunning(MultiXactId multi)
*/
for (i = 0; i < nmembers; i++)
{
-		if (TransactionIdIsCurrentTransactionId(members[i]))
+		if (TransactionIdIsCurrentTransactionId(members[i].xid))
{
debug_elog3(DEBUG2, "IsRunning: I (%d) am running!", i);
pfree(members);
@@ -412,10 +516,10 @@ MultiXactIdIsRunning(MultiXactId multi)
*/

Just before here, there's a comment referring to the now-nonexistent
MultiXactIdIsCurrent().

@@ -576,17 +541,24 @@ MultiXactIdSetOldestVisible(void)
* this would not merely be useless but would lead to Assert failure inside
* XactLockTableWait.  By the time this returns, it is certain that all
* transactions *of other backends* that were members of the MultiXactId
- * are dead (and no new ones can have been added, since it is not legal
- * to add members to an existing MultiXactId).
+ * that conflict with the requested status are dead (and no new ones can have
+ * been added, since it is not legal to add members to an existing
+ * MultiXactId).
+ *
+ * We return the number of members that we did not test for.  This is dubbed
+ * "remaining" as in "the number of members that remaing running", but this is

Typo: "remaing".

+ * slightly incorrect, because lockers whose status did not conflict with ours
+ * are not even considered and so might have gone away anyway.
*
* But by the time we finish sleeping, someone else may have changed the Xmax
* of the containing tuple, so the caller needs to iterate on us somehow.
*/
void
-MultiXactIdWait(MultiXactId multi)
+MultiXactIdWait(MultiXactId multi, MultiXactStatus status, int *remaining)

This function should probably move (with a new name) to heapam.c (or maybe
lmgr.c, in part). It's an abstraction violation to have multixact.c knowing
about lock conflict tables. multixact.c should be marshalling those two bits
alongside each xid without any deep knowledge of their meaning.

@@ -663,7 +649,7 @@ CreateMultiXactId(int nxids, TransactionId *xids)
xl_multixact_create xlrec;

debug_elog3(DEBUG2, "Create: %s",
-				mxid_to_string(InvalidMultiXactId, nxids, xids));
+				mxid_to_string(InvalidMultiXactId, nmembers, members));

/*
* See if the same set of XIDs already exists in our cache; if so, just

XIDs -> members

@@ -870,13 +875,14 @@ GetNewMultiXactId(int nxids, MultiXactOffset *offset)
*
* We don't care about MultiXactId wraparound here; it will be handled by
* the next iteration.	But note that nextMXact may be InvalidMultiXactId
-	 * after this routine exits, so anyone else looking at the variable must
-	 * be prepared to deal with that.  Similarly, nextOffset may be zero, but
-	 * we won't use that as the actual start offset of the next multixact.
+	 * or the first value on a segment-beggining page after this routine exits,

Typo: "beggining".

@@ -904,64 +932,61 @@ GetMultiXactIdMembers(MultiXactId multi, TransactionId **xids)
int length;
int truelength;
int i;
+ MultiXactId oldestMXact;
MultiXactId nextMXact;
MultiXactId tmpMXact;
MultiXactOffset nextOffset;
- TransactionId *ptr;
+ MultiXactMember *ptr;

debug_elog3(DEBUG2, "GetMembers: asked for %u", multi);

Assert(MultiXactIdIsValid(multi));

/* See if the MultiXactId is in the local cache */
-	length = mXactCacheGetById(multi, xids);
+	length = mXactCacheGetById(multi, members);
if (length >= 0)
{
debug_elog3(DEBUG2, "GetMembers: found %s in the cache",
-					mxid_to_string(multi, length, *xids));
+					mxid_to_string(multi, length, *members));
return length;
}
-	/* Set our OldestVisibleMXactId[] entry if we didn't already */
-	MultiXactIdSetOldestVisible();
-
/*
* We check known limits on MultiXact before resorting to the SLRU area.
*
-	 * An ID older than our OldestVisibleMXactId[] entry can't possibly still
-	 * be running, and we'd run the risk of trying to read already-truncated
-	 * SLRU data if we did try to examine it.
+	 * An ID older than MultiXactState->oldestMultiXactId cannot possibly be
+	 * useful; it should have already been frozen by vacuum.  We've truncated
+	 * the on-disk structures anyway, so we return empty if such a value is
+	 * queried.

Per the "XXX perhaps someday" comment in heap_freeze_tuple(), the implication
of probing for an old multixact record has been heretofore minor. From now,
it can mean making the wrong visibility decision. Enter data loss. Hence, an
elog(ERROR) is normally in order. For the benefit of binary upgrades, we
could be permissive in the face of HEAP_XMAX_IS_NOT_UPDATE (formerly known as
HEAP_XMAX_SHARED_LOCK).

*
* Conversely, an ID >= nextMXact shouldn't ever be seen here; if it is
* seen, it implies undetected ID wraparound has occurred. We just
* silently assume that such an ID is no longer running.

Likewise, this is now fatal.

This raises a notable formal hazard: it's possible to burn through the
MultiXactId space faster than the regular TransactionId space. We could get
into a situation where pg_clog is covering 2B xids, and yet we need >4B
MultiXactId to cover that period. We had better at least notice this and
halt, if not have autovacuum actively prevent it.

@@ -1026,9 +1051,8 @@ retry:
{
MultiXactOffset nextMXOffset;

-		/* handle wraparound if needed */
-		if (tmpMXact < FirstMultiXactId)
-			tmpMXact = FirstMultiXactId;
+		/* Handle corner cases if needed */
+		tmpMXact = HandleMxactOffsetCornerCases(tmpMXact);

Is there a reason apart from cycle shaving to increment a MultiXactId in one
place and elsewhere fix up the incremented value to skip the special values?
Compare to just having a MultiXactIdIncrement() function. This isn't new with
your patch, but it certainly looks odd.

@@ -1113,26 +1170,27 @@ retry:
* for the majority of tuples, thus keeping MultiXactId usage low (saving
* both I/O and wraparound issues).
*
- * NB: the passed xids[] array will be sorted in-place.
+ * NB: the passed members array will be sorted in-place.
*/
static MultiXactId
-mXactCacheGetBySet(int nxids, TransactionId *xids)
+mXactCacheGetBySet(int nmembers, MultiXactMember *members)
{
mXactCacheEnt *entry;
debug_elog3(DEBUG2, "CacheGet: looking for %s",
-				mxid_to_string(InvalidMultiXactId, nxids, xids));
+				mxid_to_string(InvalidMultiXactId, nmembers, members));
/* sort the array so comparison is easy */
-	qsort(xids, nxids, sizeof(TransactionId), xidComparator);
+	qsort(members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);

for (entry = MXactCache; entry != NULL; entry = entry->next)
{
- if (entry->nxids != nxids)
+ if (entry->nmembers != nmembers)
continue;

/* We assume the cache entries are sorted */
-		if (memcmp(xids, entry->xids, nxids * sizeof(TransactionId)) == 0)
+		/* XXX we assume the unused bits in "status" are zeroed */

That's a fair assumption if the public entry points assert it. However, ...

+ if (memcmp(members, entry->members, nmembers * sizeof(MultiXactMember)) == 0)

... this also assumes the structure has no padding. To make that safe,
MultiXactStatus should be an int32, not an enum.

@@ -1338,17 +1367,7 @@ void
multixact_twophase_recover(TransactionId xid, uint16 info,
void *recdata, uint32 len)
{
-	BackendId	dummyBackendId = TwoPhaseGetDummyBackendId(xid);
-	MultiXactId oldestMember;
-
-	/*
-	 * Get the oldest member XID from the state file record, and set it in the
-	 * OldestMemberMXactId slot reserved for this prepared transaction.
-	 */
-	Assert(len == sizeof(MultiXactId));
-	oldestMember = *((MultiXactId *) recdata);
-
-	OldestMemberMXactId[dummyBackendId] = oldestMember;
+	/* nothing to do */
}
/*
@@ -1359,11 +1378,7 @@ void
multixact_twophase_postcommit(TransactionId xid, uint16 info,
void *recdata, uint32 len)
{
-	BackendId	dummyBackendId = TwoPhaseGetDummyBackendId(xid);
-
-	Assert(len == sizeof(MultiXactId));
-
-	OldestMemberMXactId[dummyBackendId] = InvalidMultiXactId;
+	/* nothing to do */
}
/*
@@ -1374,7 +1389,7 @@ void
multixact_twophase_postabort(TransactionId xid, uint16 info,
void *recdata, uint32 len)
{
-	multixact_twophase_postcommit(xid, info, recdata, len);
+	/* nothing to do */
}

Looks like you can completely remove TWOPHASE_RM_MULTIXACT_ID.

/*
-	 * Also truncate MultiXactMember at the previously determined offset.
+	 * FIXME there's a race condition here: somebody might have created a new
+	 * segment after we finished scanning the dir.  That scenario would leave
+	 * us with an invalid truncateXid in shared memory, which is not an easy
+	 * situation to get out of.  Needs more thought.

Agreed. Not sure.

Broadly, this feels like a lot of code to handle truncating the segments, but
I don't know how to simplify it.

@@ -1947,13 +2130,29 @@ MultiXactOffsetPrecedes(MultiXactOffset offset1, MultiXactOffset offset2)
return (diff < 0);
}

+static void
+WriteMZeroOffsetPageXlogRec(int pageno, TransactionId truncateXid,
+							uint32 truncateXidEpoch)
+{
+	XLogRecData	rdata;
+	MxactZeroOffPg zerooff;
+
+	zerooff.pageno = pageno;
+	zerooff.truncateXid = truncateXid;
+	zerooff.truncateXidEpoch = truncateXidEpoch;
+
+	rdata.data = (char *) (&zerooff);
+	rdata.len = sizeof(MxactZeroOffPg);

A MinSizeOf* macro is more conventional.

+	rdata.buffer = InvalidBuffer;
+	rdata.next = NULL;
+	(void) XLogInsert(RM_MULTIXACT_ID, XLOG_MULTIXACT_ZERO_OFF_PAGE, &rdata);
+}
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -966,10 +1088,25 @@ HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot,
if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
return true;
-			if (tuple->t_infomask & HEAP_IS_LOCKED)		/* not deleter */
+			if (HeapTupleHeaderIsLocked(tuple))		 /* not deleter */
return true;
-			Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
+			if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+			{
+				TransactionId	xmax;
+
+				xmax = HeapTupleGetUpdateXid(tuple);
+				if (!TransactionIdIsValid(xmax))
+					return true;

When does this happen? Offhand, I'd expect the HeapTupleHeaderIsLocked() test
to keep us from reaching this scenario. Anyway, the next test would catch it.

+
+				/* updating subtransaction must have aborted */
+				if (!TransactionIdIsCurrentTransactionId(xmax))
+					return true;
+				else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+					return true;	/* updated after scan started */
+				else
+					return false;	/* updated before scan started */
+			}

if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
{
@@ -1008,13 +1145,34 @@ HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot,
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
return true;

-	if (tuple->t_infomask & HEAP_IS_LOCKED)
+	if (HeapTupleHeaderIsLocked(tuple))
return true;
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
{
-		/* MultiXacts are currently only allowed to lock tuples */
-		Assert(tuple->t_infomask & HEAP_IS_LOCKED);
+		TransactionId	xmax;
+
+		if (HeapTupleHeaderIsLocked(tuple))
+			return true;

This test is redundant with the one just prior.

+
+		xmax = HeapTupleGetUpdateXid(tuple);
+		if (TransactionIdIsCurrentTransactionId(xmax))
+		{
+			if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+				return true;	/* deleted after scan started */
+			else
+				return false;	/* deleted before scan started */
+		}
+		if (TransactionIdIsInProgress(xmax))
+			return true;
+		if (TransactionIdDidCommit(xmax))
+		{
+			SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, xmax);
+			/* updating transaction committed, but when? */
+			if (XidInMVCCSnapshot(xmax, snapshot))
+				return true;	/* treat as still in progress */
+			return false;
+		}

In both HEAP_XMAX_MULTI conditional blocks, you do not set HEAP_XMAX_INVALID
for an aborted updater. What is the new meaning of HEAP_XMAX_INVALID for
multixacts? What implications would arise if we instead had it mean that the
updating xid is aborted? That would allow us to get the mid-term performance
benefit of the hint bit when the updating xid spills into a multixact, and it
would reduce code duplication in this function.

I did not review the other tqual.c changes. Could you summarize how the
changes to the other functions must differ from the changes to
HeapTupleSatisfiesMVCC()?

--- a/src/bin/pg_resetxlog/pg_resetxlog.c
+++ b/src/bin/pg_resetxlog/pg_resetxlog.c
@@ -332,6 +350,11 @@ main(int argc, char *argv[])
if (set_mxoff != -1)
ControlFile.checkPointCopy.nextMultiOffset = set_mxoff;
+	/*
+	if (set_mxfreeze != -1)
+		ControlFile.checkPointCopy.mxactFreezeXid = set_mxfreeze;
+		*/
+
if (minXlogTli > ControlFile.checkPointCopy.ThisTimeLineID)
ControlFile.checkPointCopy.ThisTimeLineID = minXlogTli;
@@ -578,6 +601,10 @@ PrintControlValues(bool guessed)
ControlFile.checkPointCopy.nextMulti);
printf(_("Latest checkpoint's NextMultiOffset:  %u\n"),
ControlFile.checkPointCopy.nextMultiOffset);
+	/*
+	printf(_("Latest checkpoint's MultiXact freezeXid: %u\n"),
+		   ControlFile.checkPointCopy.mxactFreezeXid);
+		   */

Should these changes be live? They look reasonable at first glance.

--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -164,12 +164,15 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
#define HEAP_HASVARWIDTH		0x0002	/* has variable-width attribute(s) */
#define HEAP_HASEXTERNAL		0x0004	/* has external stored attribute(s) */
#define HEAP_HASOID				0x0008	/* has an object-id field */
-/* bit 0x0010 is available */
+#define HEAP_XMAX_KEYSHR_LOCK	0x0010	/* xmax is a key-shared locker */
#define HEAP_COMBOCID			0x0020	/* t_cid is a combo cid */
#define HEAP_XMAX_EXCL_LOCK		0x0040	/* xmax is exclusive locker */
-#define HEAP_XMAX_SHARED_LOCK	0x0080	/* xmax is shared locker */
-/* if either LOCK bit is set, xmax hasn't deleted the tuple, only locked it */
-#define HEAP_IS_LOCKED	(HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_SHARED_LOCK)
+#define HEAP_XMAX_IS_NOT_UPDATE	0x0080	/* xmax, if valid, is only a locker.
+										 * Note this is not set unless
+										 * XMAX_IS_MULTI is also set. */
+
+#define HEAP_LOCK_BITS	(HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_IS_NOT_UPDATE | \
+						 HEAP_XMAX_KEYSHR_LOCK)
#define HEAP_XMIN_COMMITTED		0x0100	/* t_xmin committed */
#define HEAP_XMIN_INVALID		0x0200	/* t_xmin invalid/aborted */
#define HEAP_XMAX_COMMITTED		0x0400	/* t_xmax committed */
@@ -187,14 +190,30 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
#define HEAP_XACT_MASK			0xFFE0	/* visibility-related bits */

HEAP_XACT_MASK should gain HEAP_XMAX_KEYSHR_LOCK, becoming 0xFFF0.

/*
+ * A tuple is only locked (i.e. not updated by its Xmax) if it the Xmax is not
+ * a multixact and it has either the EXCL_LOCK or KEYSHR_LOCK bits set, or if
+ * the xmax is a multi that doesn't contain an update.
+ *
+ * Beware of multiple evaluation of arguments.
+ */
+#define HeapTupleHeaderInfomaskIsLocked(infomask) \
+	((!((infomask) & HEAP_XMAX_IS_MULTI) && \
+	  (infomask) & (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)) || \
+	 (((infomask) & HEAP_XMAX_IS_MULTI) && ((infomask) & HEAP_XMAX_IS_NOT_UPDATE)))
+
+#define HeapTupleHeaderIsLocked(tup) \
+	HeapTupleHeaderInfomaskIsLocked((tup)->t_infomask)

I'm uneasy having a HeapTupleHeaderIsLocked() that returns false when a tuple
is both updated and KEY SHARE-locked. Perhaps HeapTupleHeaderIsUpdated() with
the opposite meaning, or HeapTupleHeaderIsOnlyLocked()?

+
+/*
* information stored in t_infomask2:
*/
#define HEAP_NATTS_MASK			0x07FF	/* 11 bits for number of attributes */
-/* bits 0x3800 are available */
+/* bits 0x1800 are available */
+#define HEAP_UPDATE_KEY_INTACT	0x2000	/* tuple updated, key cols untouched */
#define HEAP_HOT_UPDATED		0x4000	/* tuple was HOT-updated */
#define HEAP_ONLY_TUPLE			0x8000	/* this is heap-only tuple */
-#define HEAP2_XACT_MASK			0xC000	/* visibility-related bits */
+#define HEAP2_XACT_MASK			0xE000	/* visibility-related bits */

/*
* HEAP_TUPLE_HAS_MATCH is a temporary flag used during hash joins. It is
@@ -221,6 +240,23 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
(tup)->t_choice.t_heap.t_xmin = (xid) \
)

+/*
+ * HeapTupleHeaderGetXmax gets you the raw Xmax field.  To find out the Xid
+ * that updated a tuple, you might need to resolve the MultiXactId if certain
+ * bits are set.  HeapTupleHeaderGetUpdateXid checks those bits and takes care
+ * to resolve the MultiXactId if necessary.  This might involve multixact I/O,
+ * so it should only be used if absolutely necessary.
+ */
+#define HeapTupleHeaderGetUpdateXid(tup) \
+( \
+	(!((tup)->t_infomask & HEAP_XMAX_INVALID) && \
+	 ((tup)->t_infomask & HEAP_XMAX_IS_MULTI) && \
+	 !((tup)->t_infomask & HEAP_XMAX_IS_NOT_UPDATE)) ? \
+		HeapTupleGetUpdateXid(tup) \
+	: \
+		HeapTupleHeaderGetXmax(tup) \
+)
+
#define HeapTupleHeaderGetXmax(tup) \

How about having making HeapTupleHeaderGetXmax() do an AssertMacro() against
HEAP_XMAX_IS_MULTI and adding HeapTupleHeaderGetRawXmax() for places that
truly do not care?

( \
(tup)->t_choice.t_heap.t_xmax \
@@ -721,16 +757,22 @@ typedef struct xl_heap_newpage

#define SizeOfHeapNewpage (offsetof(xl_heap_newpage, blkno) + sizeof(BlockNumber))

+/* flags for xl_heap_lock.infobits_set */
+#define XLHL_XMAX_IS_MULTI		0x01
+#define XLHL_XMAX_IS_NOT_UPDATE	0x02
+#define XLHL_XMAX_EXCL_LOCK		0x04
+#define XLHL_XMAX_KEYSHR_LOCK	0x08
+#define XLHL_UPDATE_KEY_INTACT	0x10
+
/* This is what we need to know about lock */
typedef struct xl_heap_lock
{
xl_heaptid	target;			/* locked tuple id */
TransactionId locking_xid;	/* might be a MultiXactId not xid */
-	bool		xid_is_mxact;	/* is it? */
-	bool		shared_lock;	/* shared or exclusive row lock? */
+	int8		infobits_set;	/* infomask and infomask2 bits to set */
} xl_heap_lock;
-#define SizeOfHeapLock	(offsetof(xl_heap_lock, shared_lock) + sizeof(bool))
+#define SizeOfHeapLock	(offsetof(xl_heap_lock, infobits_set) + sizeof(int8))
/* This is what we need to know about in-place update */
typedef struct xl_heap_inplace
@@ -768,8 +810,7 @@ extern void HeapTupleHeaderAdvanceLatestRemovedXid(HeapTupleHeader tuple,
extern CommandId HeapTupleHeaderGetCmin(HeapTupleHeader tup);
extern CommandId HeapTupleHeaderGetCmax(HeapTupleHeader tup);
extern void HeapTupleHeaderAdjustCmax(HeapTupleHeader tup,
-						  CommandId *cmax,
-						  bool *iscombo);
+						  CommandId *cmax, bool *iscombo);

Spurious change?

/* ----------------
* fastgetattr
@@ -854,6 +895,9 @@ extern Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
heap_getsysattr((tup), (attnum), (tupleDesc), (isnull)) \
)

+/* Prototype for HeapTupleHeader accessor in heapam.c */
+extern TransactionId HeapTupleGetUpdateXid(HeapTupleHeader tuple);
+
/* prototypes for functions in common/heaptuple.c */
extern Size heap_compute_data_size(TupleDesc tupleDesc,
Datum *values, bool *isnull);
diff --git a/src/include/access/multixact.h b/src/include/access/multixact.h
index c3ec763..ff255d7 100644
--- a/src/include/access/multixact.h
+++ b/src/include/access/multixact.h
@@ -13,8 +13,14 @@

#include "access/xlog.h"

+
+/*
+ * The first two MultiXactId values are reserved to store the truncation Xid
+ * and epoch of the first segment, so we start assigning multixact values from
+ * 2.
+ */
#define InvalidMultiXactId	((MultiXactId) 0)
-#define FirstMultiXactId	((MultiXactId) 1)
+#define FirstMultiXactId	((MultiXactId) 2)

#define MultiXactIdIsValid(multi) ((multi) != InvalidMultiXactId)

Seems like this should reject 1, as well.

--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -71,7 +71,7 @@ typedef struct XLogContRecord
/*
* Each page of XLOG file has a header like this:
*/
-#define XLOG_PAGE_MAGIC 0xD068	/* can be used as WAL version indicator */
+#define XLOG_PAGE_MAGIC 0xD069	/* can be used as WAL version indicator */

Need to bump pg_control_version, too.

--- a/src/test/isolation/expected/fk-contention.out
+++ b/src/test/isolation/expected/fk-contention.out
@@ -7,9 +7,8 @@ step upd: UPDATE foo SET b = 'Hello World';
starting permutation: ins upd com
step ins: INSERT INTO bar VALUES (42);
-step upd: UPDATE foo SET b = 'Hello World'; <waiting ...>
+step upd: UPDATE foo SET b = 'Hello World';
step com: COMMIT;
-step upd: <... completed>

Excellent!

Thanks,
nm

Attachments:

fklock-wide.sqltext/plain; charset=us-asciiDownload
fklock-test-forshare.sqltext/plain; charset=us-asciiDownload
fklock-pgrowlocks.sqltext/plain; charset=us-asciiDownload
#19Alvaro Herrera
alvherre@commandprompt.com
In reply to: Noah Misch (#18)
Re: foreign key locks, 2nd attempt

Noah,

Many thanks for this review. I'm going through items on it; definitely
there are serious issues here, as well as minor things that also need
fixing. Thanks for all the detail.

I'll post an updated patch shortly (probably not today though); in the
meantime, this bit:

Excerpts from Noah Misch's message of dom dic 04 09:20:27 -0300 2011:

Second, I tried a SELECT FOR SHARE on a table of 1M tuples; this might incur
some cost due to the now-guaranteed use of pg_multixact for FOR SHARE. See
attached fklock-test-forshare.sql. The median run slowed by 7% under the
patch, albeit with a rather brief benchmark run. Both master and patched
PostgreSQL seemed to exhibit a statement-scope memory leak in this test case:
to lock 1M rows, backend-private memory grew by about 500M. When trying 10M
rows, I cancelled the query after 1.2 GiB of consumption. This limited the
duration of a convenient test run.

I found that this is caused by mxid_to_string being leaked all over the
place :-( I "fixed" it by making the returned string be a static that's
malloced and then freed on the next call. There's still virtsize growth
(not sure it's a legitimate leak) with that, but it's much smaller.
This being a debugging aid, I don't think there's any need to backpatch
this.

diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index ddf76b3..c45bd36 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -1305,9 +1305,14 @@ mxstatus_to_string(MultiXactStatus status)
 static char *
 mxid_to_string(MultiXactId multi, int nmembers, MultiXactMember *members)
 {
-	char	   *str = palloc(15 * (nmembers + 1) + 4);
+	static char	   *str = NULL;
 	int			i;
+	if (str != NULL)
+		free(str);
+
+	str = malloc(15 * (nmembers + 1) + 4);
+
 	snprintf(str, 47, "%u %d[%u (%s)", multi, nmembers, members[0].xid,
 			 mxstatus_to_string(members[0].status));

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#20Alvaro Herrera
alvherre@commandprompt.com
In reply to: Alvaro Herrera (#19)
Re: foreign key locks, 2nd attempt

Excerpts from Alvaro Herrera's message of lun dic 12 17:20:39 -0300 2011:

I found that this is caused by mxid_to_string being leaked all over the
place :-( I "fixed" it by making the returned string be a static that's
malloced and then freed on the next call. There's still virtsize growth
(not sure it's a legitimate leak) with that, but it's much smaller.

this fixes the remaining leaks. AFAICS it now grows to a certain point
and it's fixed size after that. I was able to share-lock a 10M rows
table with a 30MB RSS process.

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 49d3369..7069950 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -3936,6 +3936,8 @@ l3:
 						keep_xmax = xwait;
 						keep_xmax_multi = true;
 					}
+
+					pfree(members);
 				}
 			}
 			else if (infomask & HEAP_XMAX_KEYSHR_LOCK)
@@ -4693,6 +4695,9 @@ GetMultiXactIdHintBits(MultiXactId multi)
 	if (!has_update)
 		bits |= HEAP_XMAX_IS_NOT_UPDATE;
+	if (nmembers > 0)
+		pfree(members);
+
 	return bits;
 }

@@ -4743,6 +4748,8 @@ HeapTupleGetUpdateXid(HeapTupleHeader tuple)
break;
#endif
}
+
+ pfree(members);
}

return update_xact;

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#21Noah Misch
noah@leadboat.com
In reply to: Alvaro Herrera (#19)
Re: foreign key locks, 2nd attempt

On Mon, Dec 12, 2011 at 05:20:39PM -0300, Alvaro Herrera wrote:

Excerpts from Noah Misch's message of dom dic 04 09:20:27 -0300 2011:

Second, I tried a SELECT FOR SHARE on a table of 1M tuples; this might incur
some cost due to the now-guaranteed use of pg_multixact for FOR SHARE. See
attached fklock-test-forshare.sql. The median run slowed by 7% under the
patch, albeit with a rather brief benchmark run. Both master and patched
PostgreSQL seemed to exhibit a statement-scope memory leak in this test case:
to lock 1M rows, backend-private memory grew by about 500M. When trying 10M
rows, I cancelled the query after 1.2 GiB of consumption. This limited the
duration of a convenient test run.

I found that this is caused by mxid_to_string being leaked all over the
place :-( I "fixed" it by making the returned string be a static that's
malloced and then freed on the next call. There's still virtsize growth
(not sure it's a legitimate leak) with that, but it's much smaller.

Great. I'll retry that benchmark with the next patch version. I no longer
see a leak on master, so I probably messed up that part of the test somehow.

By the way, do you have a rapid procedure for finding the call site behind a
leak like this?

This being a debugging aid, I don't think there's any need to backpatch
this.

Agreed.

diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index ddf76b3..c45bd36 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -1305,9 +1305,14 @@ mxstatus_to_string(MultiXactStatus status)
static char *
mxid_to_string(MultiXactId multi, int nmembers, MultiXactMember *members)
{
-	char	   *str = palloc(15 * (nmembers + 1) + 4);
+	static char	   *str = NULL;
int			i;
+	if (str != NULL)
+		free(str);
+
+	str = malloc(15 * (nmembers + 1) + 4);

Need a check for NULL return.

+
snprintf(str, 47, "%u %d[%u (%s)", multi, nmembers, members[0].xid,
mxstatus_to_string(members[0].status));

Thanks,
nm

#22Alvaro Herrera
alvherre@commandprompt.com
In reply to: Noah Misch (#21)
Re: foreign key locks, 2nd attempt

Excerpts from Noah Misch's message of mar dic 13 11:44:49 -0300 2011:

On Mon, Dec 12, 2011 at 05:20:39PM -0300, Alvaro Herrera wrote:

Excerpts from Noah Misch's message of dom dic 04 09:20:27 -0300 2011:

Second, I tried a SELECT FOR SHARE on a table of 1M tuples; this might incur
some cost due to the now-guaranteed use of pg_multixact for FOR SHARE. See
attached fklock-test-forshare.sql. The median run slowed by 7% under the
patch, albeit with a rather brief benchmark run. Both master and patched
PostgreSQL seemed to exhibit a statement-scope memory leak in this test case:
to lock 1M rows, backend-private memory grew by about 500M. When trying 10M
rows, I cancelled the query after 1.2 GiB of consumption. This limited the
duration of a convenient test run.

I found that this is caused by mxid_to_string being leaked all over the
place :-( I "fixed" it by making the returned string be a static that's
malloced and then freed on the next call. There's still virtsize growth
(not sure it's a legitimate leak) with that, but it's much smaller.

Great. I'll retry that benchmark with the next patch version. I no longer
see a leak on master, so I probably messed up that part of the test somehow.

Maybe you recompiled without the MULTIXACT_DEBUG symbol defined?

By the way, do you have a rapid procedure for finding the call site behind a
leak like this?

Not really ... I tried some games with GDB (which yielded the first
report: I did some "call MemoryContextStats(TopMemoryContext)" to see
where the bloat was, and then stepped with breaks on MemoryContextAlloc,
also with a watch on CurrentMemoryContext and noting when it was
pointing to the bloated context. But since I'm a rookie with GDB I
didn't find a way to only break when MemoryContextAlloc was pointing at
that context. I know there must be a way.) and then went to do some
code inspection instead. I gather some people use valgrind
successfully.

+    if (str != NULL)
+        free(str);
+
+    str = malloc(15 * (nmembers + 1) + 4);

Need a check for NULL return.

Yeah, thanks ... I changed it to MemoryContextAlloc(TopMemoryContext),
because I'm not sure that a combination of malloc plus palloc would end
up in extra memory fragmentation.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#23Noah Misch
noah@leadboat.com
In reply to: Alvaro Herrera (#22)
Re: foreign key locks, 2nd attempt

On Tue, Dec 13, 2011 at 01:09:46PM -0300, Alvaro Herrera wrote:

Excerpts from Noah Misch's message of mar dic 13 11:44:49 -0300 2011:

On Mon, Dec 12, 2011 at 05:20:39PM -0300, Alvaro Herrera wrote:

Excerpts from Noah Misch's message of dom dic 04 09:20:27 -0300 2011:

Second, I tried a SELECT FOR SHARE on a table of 1M tuples; this might incur
some cost due to the now-guaranteed use of pg_multixact for FOR SHARE. See
attached fklock-test-forshare.sql. The median run slowed by 7% under the
patch, albeit with a rather brief benchmark run. Both master and patched
PostgreSQL seemed to exhibit a statement-scope memory leak in this test case:
to lock 1M rows, backend-private memory grew by about 500M. When trying 10M
rows, I cancelled the query after 1.2 GiB of consumption. This limited the
duration of a convenient test run.

I found that this is caused by mxid_to_string being leaked all over the
place :-( I "fixed" it by making the returned string be a static that's
malloced and then freed on the next call. There's still virtsize growth
(not sure it's a legitimate leak) with that, but it's much smaller.

Great. I'll retry that benchmark with the next patch version. I no longer
see a leak on master, so I probably messed up that part of the test somehow.

Maybe you recompiled without the MULTIXACT_DEBUG symbol defined?

Neither my brain nor my shell history recall that, but it remains possible.

By the way, do you have a rapid procedure for finding the call site behind a
leak like this?

Not really ... I tried some games with GDB (which yielded the first
report: I did some "call MemoryContextStats(TopMemoryContext)" to see
where the bloat was, and then stepped with breaks on MemoryContextAlloc,
also with a watch on CurrentMemoryContext and noting when it was
pointing to the bloated context. But since I'm a rookie with GDB I
didn't find a way to only break when MemoryContextAlloc was pointing at
that context. I know there must be a way.) and then went to do some
code inspection instead. I gather some people use valgrind
successfully.

Understood. Incidentally, the GDB command in question is "condition".

+    if (str != NULL)
+        free(str);
+
+    str = malloc(15 * (nmembers + 1) + 4);

Need a check for NULL return.

Yeah, thanks ... I changed it to MemoryContextAlloc(TopMemoryContext),
because I'm not sure that a combination of malloc plus palloc would end
up in extra memory fragmentation.

Sounds good.

#24Alvaro Herrera
alvherre@commandprompt.com
In reply to: Noah Misch (#18)
Re: foreign key locks, 2nd attempt

Excerpts from Noah Misch's message of dom dic 04 09:20:27 -0300 2011:

+    /*
+     * If the tuple we're updating is locked, we need to preserve this in the
+     * new tuple's Xmax as well as in the old tuple.  Prepare the new xmax
+     * value for these uses.
+     *
+     * Note there cannot be an xmax to save if we're changing key columns; in
+     * this case, the wait above should have only returned when the locking
+     * transactions finished.
+     */
+    if (TransactionIdIsValid(keep_xmax))
+    {
+        if (keep_xmax_multi)
+        {
+            keep_xmax_old = MultiXactIdExpand(keep_xmax,
+                                              xid, MultiXactStatusUpdate);
+            keep_xmax_infomask = HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_IS_MULTI;

Not directly related to this line, but is the HEAP_IS_NOT_UPDATE bit getting
cleared where needed?

AFAICS it's reset along the rest of the HEAP_LOCK_BITS when the tuple is
modified.

@@ -2725,11 +2884,20 @@ l2:
oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
HEAP_XMAX_IS_MULTI |
-                                       HEAP_IS_LOCKED |
+                                       HEAP_LOCK_BITS |
HEAP_MOVED);
+        oldtup.t_data->t_infomask2 &= ~HEAP_UPDATE_KEY_INTACT;
HeapTupleClearHotUpdated(&oldtup);
/* ... and store info about transaction updating this tuple */
-        HeapTupleHeaderSetXmax(oldtup.t_data, xid);
+        if (TransactionIdIsValid(keep_xmax_old))
+        {
+            HeapTupleHeaderSetXmax(oldtup.t_data, keep_xmax_old);
+            oldtup.t_data->t_infomask |= keep_xmax_old_infomask;
+        }
+        else
+            HeapTupleHeaderSetXmax(oldtup.t_data, xid);
+        if (key_intact)
+            oldtup.t_data->t_infomask2 |= HEAP_UPDATE_KEY_INTACT;
HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
/* temporarily make it look not-updated */
oldtup.t_data->t_ctid = oldtup.t_self;

Shortly after this, we release the content lock and go off toasting the tuple
and finding free space. When we come back, could the old tuple have
accumulated additional KEY SHARE locks that we need to re-copy?

Yeah, I've been wondering about this: do we have a problem already with
exclusion constraints? I mean, if a concurrent inserter doesn't see the
tuple that we've marked here as deleted while we toast it, it could
result in a violated constraint, right? I haven't built a test case to
prove it.

@@ -3231,30 +3462,70 @@ l3:
{
TransactionId xwait;
uint16        infomask;
+        uint16        infomask2;
+        bool        require_sleep;

/* must copy state data before unlocking buffer */
xwait = HeapTupleHeaderGetXmax(tuple->t_data);
infomask = tuple->t_data->t_infomask;
+ infomask2 = tuple->t_data->t_infomask2;

LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);

/*
-         * If we wish to acquire share lock, and the tuple is already
-         * share-locked by a multixact that includes any subtransaction of the
-         * current top transaction, then we effectively hold the desired lock
-         * already.  We *must* succeed without trying to take the tuple lock,
-         * else we will deadlock against anyone waiting to acquire exclusive
-         * lock.  We don't need to make any state changes in this case.
+         * If we wish to acquire share or key lock, and the tuple is already
+         * key or share locked by a multixact that includes any subtransaction
+         * of the current top transaction, then we effectively hold the desired
+         * lock already (except if we own key share lock and now desire share
+         * lock).  We *must* succeed without trying to take the tuple lock,

This can now apply to FOR UPDATE as well.

For the first sentence, I suggest the wording "If any subtransaction of the
current top transaction already holds a stronger lock, we effectively hold the
desired lock already."

I settled on this:

/*
* If any subtransaction of the current top transaction already holds a
* lock as strong or stronger than what we're requesting, we
* effectively hold the desired lock already. We *must* succeed
* without trying to take the tuple lock, else we will deadlock against
* anyone wanting to acquire a stronger lock.
*/
if (infomask & HEAP_XMAX_IS_MULTI)
{
int i;
int nmembers;
MultiXactMember *members;
MultiXactStatus cutoff = get_mxact_status_for_tuplelock(mode);

nmembers = GetMultiXactIdMembers(xwait, &members);

for (i = 0; i < nmembers; i++)
{
if (TransactionIdIsCurrentTransactionId(members[i].xid))
{
if (members[i].status >= cutoff)
{
if (have_tuple_lock)
UnlockTupleTuplock(relation, tid, mode);

pfree(members);
return HeapTupleMayBeUpdated;
}
}
}

pfree(members);
}

Now, I can't see the reason that we didn't previously consider locks "as
strong as what we're requesting" ... but surely it's the same case?

+         * else we will deadlock against anyone wanting to acquire a stronger
+         * lock.
+         *
+         * FIXME -- we don't do the below currently, but I think we should:
+         *
+         * We update the Xmax with a new MultiXactId to include the new lock
+         * mode in this case.
+         *
+         * Note that since we want to alter the Xmax, we need to re-acquire the
+         * buffer lock.  The xmax could have changed in the meantime, so we
+         * recheck it in that case, but we keep the buffer lock while doing it
+         * to prevent starvation.  The second time around we know we must be
+         * part of the MultiXactId in any case, which is why we don't need to
+         * go back to recheck HeapTupleSatisfiesUpdate.  Also, after we
+         * re-acquire lock, the MultiXact is likely to (but not necessarily) be
+         * the same that we see here, so it should be in multixact's cache and
+         * thus quick to obtain.

What is the benefit of doing so?

After thinking more about it, I think it's bogus. I've removed it.

Incidentally, why is this level of xlog detail needed for tuple locks? We
need an FPI of the page before the lock-related changes start scribbling on
it, and we need to log any xid, even that of a locker, that could land in the
heap on disk. But, why do we actually need to replay each lock?

Uhm. I remember thinking that a hot standby replica needed it ...

+ * slightly incorrect, because lockers whose status did not conflict with ours
+ * are not even considered and so might have gone away anyway.
*
* But by the time we finish sleeping, someone else may have changed the Xmax
* of the containing tuple, so the caller needs to iterate on us somehow.
*/
void
-MultiXactIdWait(MultiXactId multi)
+MultiXactIdWait(MultiXactId multi, MultiXactStatus status, int *remaining)

This function should probably move (with a new name) to heapam.c (or maybe
lmgr.c, in part). It's an abstraction violation to have multixact.c knowing
about lock conflict tables. multixact.c should be marshalling those two bits
alongside each xid without any deep knowledge of their meaning.

Interesting thought.

/*
-     * Also truncate MultiXactMember at the previously determined offset.
+     * FIXME there's a race condition here: somebody might have created a new
+     * segment after we finished scanning the dir.  That scenario would leave
+     * us with an invalid truncateXid in shared memory, which is not an easy
+     * situation to get out of.  Needs more thought.

Agreed. Not sure.

Broadly, this feels like a lot of code to handle truncating the segments, but
I don't know how to simplify it.

It is a lot of code. And it took me quite a while to even figure out
how to do it. I don't see any other way to go about it.

+        xmax = HeapTupleGetUpdateXid(tuple);
+        if (TransactionIdIsCurrentTransactionId(xmax))
+        {
+            if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+                return true;    /* deleted after scan started */
+            else
+                return false;    /* deleted before scan started */
+        }
+        if (TransactionIdIsInProgress(xmax))
+            return true;
+        if (TransactionIdDidCommit(xmax))
+        {
+            SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, xmax);
+            /* updating transaction committed, but when? */
+            if (XidInMVCCSnapshot(xmax, snapshot))
+                return true;    /* treat as still in progress */
+            return false;
+        }

In both HEAP_XMAX_MULTI conditional blocks, you do not set HEAP_XMAX_INVALID
for an aborted updater. What is the new meaning of HEAP_XMAX_INVALID for
multixacts? What implications would arise if we instead had it mean that the
updating xid is aborted? That would allow us to get the mid-term performance
benefit of the hint bit when the updating xid spills into a multixact, and it
would reduce code duplication in this function.

Well, HEAP_XMAX_INVALID means the Xmax is not valid, period. If there's
a multi whose updater is aborted, there's still a multi that needs to be
checked in various places, so we cannot set that bit.

I did not review the other tqual.c changes. Could you summarize how the
changes to the other functions must differ from the changes to
HeapTupleSatisfiesMVCC()?

I don't think they should differ in any significant way ... if they do,
it's probably bogus. Only HeapTupleSatisfiesVacuum should differ
significantly, because it's a world on its own.

--- a/src/bin/pg_resetxlog/pg_resetxlog.c
+++ b/src/bin/pg_resetxlog/pg_resetxlog.c
@@ -332,6 +350,11 @@ main(int argc, char *argv[])
if (set_mxoff != -1)
ControlFile.checkPointCopy.nextMultiOffset = set_mxoff;
+    /*
+    if (set_mxfreeze != -1)
+        ControlFile.checkPointCopy.mxactFreezeXid = set_mxfreeze;
+        */
+
if (minXlogTli > ControlFile.checkPointCopy.ThisTimeLineID)
ControlFile.checkPointCopy.ThisTimeLineID = minXlogTli;
@@ -578,6 +601,10 @@ PrintControlValues(bool guessed)
ControlFile.checkPointCopy.nextMulti);
printf(_("Latest checkpoint's NextMultiOffset:  %u\n"),
ControlFile.checkPointCopy.nextMultiOffset);
+    /*
+    printf(_("Latest checkpoint's MultiXact freezeXid: %u\n"),
+           ControlFile.checkPointCopy.mxactFreezeXid);
+           */

Should these changes be live? They look reasonable at first glance.

Oh, I forgot about these. Yeah, these need to be live, but not in the
exact for they have here; there were some tweaks I needed to do IIRC.

/*
+ * A tuple is only locked (i.e. not updated by its Xmax) if it the Xmax is not
+ * a multixact and it has either the EXCL_LOCK or KEYSHR_LOCK bits set, or if
+ * the xmax is a multi that doesn't contain an update.
+ *
+ * Beware of multiple evaluation of arguments.
+ */
+#define HeapTupleHeaderInfomaskIsLocked(infomask) \
+    ((!((infomask) & HEAP_XMAX_IS_MULTI) && \
+      (infomask) & (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)) || \
+     (((infomask) & HEAP_XMAX_IS_MULTI) && ((infomask) & HEAP_XMAX_IS_NOT_UPDATE)))
+
+#define HeapTupleHeaderIsLocked(tup) \
+    HeapTupleHeaderInfomaskIsLocked((tup)->t_infomask)

I'm uneasy having a HeapTupleHeaderIsLocked() that returns false when a tuple
is both updated and KEY SHARE-locked. Perhaps HeapTupleHeaderIsUpdated() with
the opposite meaning, or HeapTupleHeaderIsOnlyLocked()?

I had the IsOnlyLocked thought too. I will go that route.

(I changed HeapTupleHeaderGetXmax to GetRawXmax, thanks for that
suggestion)

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#25Noah Misch
noah@leadboat.com
In reply to: Alvaro Herrera (#24)
Re: foreign key locks, 2nd attempt

On Tue, Dec 13, 2011 at 06:36:21PM -0300, Alvaro Herrera wrote:

Excerpts from Noah Misch's message of dom dic 04 09:20:27 -0300 2011:

@@ -2725,11 +2884,20 @@ l2:
oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
HEAP_XMAX_IS_MULTI |
-                                       HEAP_IS_LOCKED |
+                                       HEAP_LOCK_BITS |
HEAP_MOVED);
+        oldtup.t_data->t_infomask2 &= ~HEAP_UPDATE_KEY_INTACT;
HeapTupleClearHotUpdated(&oldtup);
/* ... and store info about transaction updating this tuple */
-        HeapTupleHeaderSetXmax(oldtup.t_data, xid);
+        if (TransactionIdIsValid(keep_xmax_old))
+        {
+            HeapTupleHeaderSetXmax(oldtup.t_data, keep_xmax_old);
+            oldtup.t_data->t_infomask |= keep_xmax_old_infomask;
+        }
+        else
+            HeapTupleHeaderSetXmax(oldtup.t_data, xid);
+        if (key_intact)
+            oldtup.t_data->t_infomask2 |= HEAP_UPDATE_KEY_INTACT;
HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
/* temporarily make it look not-updated */
oldtup.t_data->t_ctid = oldtup.t_self;

Shortly after this, we release the content lock and go off toasting the tuple
and finding free space. When we come back, could the old tuple have
accumulated additional KEY SHARE locks that we need to re-copy?

Yeah, I've been wondering about this: do we have a problem already with
exclusion constraints? I mean, if a concurrent inserter doesn't see the
tuple that we've marked here as deleted while we toast it, it could
result in a violated constraint, right? I haven't built a test case to
prove it.

Does the enforcement code for exclusion constraints differ significantly from
the ordinary unique constraint code? If not, I'd expect the concurrent inserter
to treat the tuple precisely like an uncommitted delete, in which case it will
wait for the deleter.

I settled on this:

/*
* If any subtransaction of the current top transaction already holds a
* lock as strong or stronger than what we're requesting, we
* effectively hold the desired lock already. We *must* succeed
* without trying to take the tuple lock, else we will deadlock against
* anyone wanting to acquire a stronger lock.
*/

Now, I can't see the reason that we didn't previously consider locks "as
strong as what we're requesting" ... but surely it's the same case?

I think it does degenerate to the same case. When we hold an exclusive lock
in master, HeapTupleSatisfiesUpdate() will return HeapTupleMayBeUpdated. So,
we can only get here while holding a mere share lock.

In both HEAP_XMAX_MULTI conditional blocks, you do not set HEAP_XMAX_INVALID
for an aborted updater. What is the new meaning of HEAP_XMAX_INVALID for
multixacts? What implications would arise if we instead had it mean that the
updating xid is aborted? That would allow us to get the mid-term performance
benefit of the hint bit when the updating xid spills into a multixact, and it
would reduce code duplication in this function.

Well, HEAP_XMAX_INVALID means the Xmax is not valid, period. If there's
a multi whose updater is aborted, there's still a multi that needs to be
checked in various places, so we cannot set that bit.

Ah, yes. Perhaps a better question: would changing HEAP_XMAX_INVALID to
HEAP_UPDATER_INVALID pay off? That would help HeapTupleSatisfiesMVCC() at the
expense of HeapTupleSatisfiesUpdate(), probably along with other consequences I
haven't contemplated adequately.

#26Tom Lane
tgl@sss.pgh.pa.us
In reply to: Noah Misch (#25)
Re: foreign key locks, 2nd attempt

Noah Misch <noah@leadboat.com> writes:

On Tue, Dec 13, 2011 at 06:36:21PM -0300, Alvaro Herrera wrote:

Yeah, I've been wondering about this: do we have a problem already with
exclusion constraints? I mean, if a concurrent inserter doesn't see the
tuple that we've marked here as deleted while we toast it, it could
result in a violated constraint, right? I haven't built a test case to
prove it.

Does the enforcement code for exclusion constraints differ significantly from
the ordinary unique constraint code?

It's an entirely separate code path (involving an AFTER trigger). I
don't know if there's a problem, but Alvaro's right to worry that it
might behave differently.

regards, tom lane

#27Greg Smith
greg@2ndQuadrant.com
In reply to: Alvaro Herrera (#24)
Re: foreign key locks, 2nd attempt

Sounds like there's still a few things left to research out on Alvaro's
side, and I'm thinking there's a performance/reliability under load
testing side of this that will take some work to validate too. Since I
can't see all that happening fast enough to commit for a bit, I'm going
to mark it returned with feedback for now. I'm trying to remove
everything that isn't likely to end up in the next alpha from the open list.

--
Greg Smith 2ndQuadrant US greg@2ndQuadrant.com Baltimore, MD
PostgreSQL Training, Services, and 24x7 Support www.2ndQuadrant.us

#28Alvaro Herrera
alvherre@commandprompt.com
In reply to: Alvaro Herrera (#1)
1 attachment(s)
Re: foreign key locks, 2nd attempt

Here's an updated version of this patch. It fixes many of Noah's review
points from the previous version, and it also contains some fixes for
other problems. The most glaring one was the fact that when we locked
an old version of a tuple, the locking code did not walk the update
chain to make the newer versions locked too. This is necessary for
correctness; moreover, locking a tuple whose future version is being
deleted by a concurrent transaction needs to cause the locking
transaction to block until the deleting transaction finishes. The
current code correctly sleeps on the deleter (or, if the deleter has
already committed, causes the tuple lock acquisition to fail.)

One other interesting change is that I flipped the
HEAP_UPDATE_KEY_INTACT bit meaning, so that it's now
HEAP_UPDATE_KEY_REVOKED; it's now set not only when an UPDATE changes a
key column, but also when a tuple is deleted. Only now that I write
this message I realize that I should have changed the name too, because
it's no longer just about UPDATE.

There are a number of smaller items still remaining, and I will be
working on those in the next few days. Most notably,

- I have not updated the docs yet.

- I haven't done anything about exposing FOR KEY UPDATE to the SQL
level. There clearly isn't consensus about exposing this; in fact there
isn't consensus on exposing FOR KEY SHARE, but I haven't changed that
from the previous patch, either.

- pg_rowlocks hasn't been updated; in this patch it's in the same shape
as it was previously. I agree with the idea that this module should
display user-level lock information instead of just decoding infomask
bits.

- I'm not sure that the multixact truncation code is sane on
checkpoints. It might be that I need to tweak more the pg_control info
we keep about truncation. The whole truncation thing needs more
testing, too.

- pg_upgrade bits are missing.

- Go over Noah's two reviews again and see if I missed anything; also
make sure I haven't missed anything from other reviewers.

At the core, ISTM this patch is in much closer form to final. The
number of things that are still open is much shorter. I'm pretty sure
this can be taken to committable state during the 2012-01 commitfest.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

Attachments:

fklocks-5.patch.gzapplication/x-gzip; name=fklocks-5.patch.gzDownload
��VOfklocks-5.patch�]{W������):����16�l�N�;�6s����R�� K=0�L��oUu���e0f��rbK����U��zH�����M�������!w�(�V�9�f0H<+��~�V����Vk���j��Qk�2���q{�������������^������[���R�'���[V�zI�r�`������w�{	��+o���yc�	�>5>C���o����'�YRp�r�xe1Vh���,I����3h.E��������;#�6�X42Cn������i�����?�/&Bb���D���o��/Y���)eo������p��}����kW���[7f-�������um��|��|VQZ)i��m	
=>=�8j���a�-���|O���[�]����/{��.�0�uy��{��k�jmck#���]K����Allu_v6��n�����^����| �}X����iRV�j�������o|����6����]����R��bG�25���(��������X�8pM��E��
� fN�"?	-��@��f<�!�G��n����{���@�n�|vq�2{Wc�
�}���
�` ��
��o��)�����=����K��F9��1(��-����tNX�]�s��
h�?�=�
������94J%`��2��O�z0�
�1�6K-d�)%��xC���'n��+�wE+���Skt�����y�Q�S�m�����(�q�y�4;'�������5E�F,}�G�����8?��n�w��g�!^���Uir��{O����[�X�
���O>\�h����wzw�[C�)W!H1j;R[���T5�~�P5���4���nu)��.)���d����V�������Z,��e��Z$�(_��y�_��7d�_�����]��y�pA�"���3�
8qG"Eq�X1�����r<IT�,�M�^���+;�J6��Xg����K���K�}����V�u�T����g�T���p"���vA��R�1�v�T���P�&
?c��_M��|�/��U���8���PJ�Ir/�<���F�b��E��U��u�*��[���w��/���-����z[���I85`���<g�D';@�
L�_O��Y�1�i���;���p��?����o�����V��Z�O<p�|����A4�$6
�3���qw�h�����_�����c{��y���~�?o��N~���33@��$(�t��\I}[[pw����&�s�Wf�a��	����y�)�9���<(k#Lt1�����-od�W���|�t��G���/r����"���c�5�'9�T��%�]�5���~6]��tl��R	�E��������0��������O���`�2��m��1��K�n�>��+FwD�4v�5Q$�m��[V����%U@���r����v�p\��q�v�0�C�,�Y��������M'm��Y����%+�*Z����.�L[��
�����b��������V)V�=)�	�$��Y#C|�IP'�xg�?�D,#�O�~�(��
����ME]�Z�B�8�L#[�|�t��F_�'�z��Y�K'���������3x�.��3�������{:F�&]�j����[������UZPAI^'s�3�����9Y^�$�g����� ��������&V��+>Tx�<,�Nz�����9�`��:�d(x�n��--,~�R���E��
2>v��S����Q�����
W�Z}�P�S����s $���0 [?�)���h����GK��cFqr��_�����h�����L��
X�����I�p?�����D���P�AS�j!�B�v���?T�|��*EXrVY���%I����/��7���U�	[�fg��E>�����;�34�M�.�����vA��"��
��@^V��Z�
H�CcE
�2�m�T��.@&�l���~i������h�����5:"HTl-3�L	r���yV�x-��5�&����W!7���9���A��.KD�q��w?�h>�xz�.��C(���XpR9��~�T�!�1��,V�t��i��@;��>W!��}�e��]��[��X|Zi��CL�R?���]���xj�|9�8:Ztt-:M0��x�K�*���JQ����e�����������#���`�>3��p_U{�<�a�'��n�\;�����M�%v�ktL�(&s���u]�*���� �<s��]�/k�6���}����/{e,���[��)K%����]��yv��|�/"�y�����������3�Ata��j�|F�F~W]�l<J�p�a�����L'����`,�����R�<��8�,S���M/U�up/?nX�F�7���~������U�)t
���~�*����L�$�cmckH�H\��xD�4���se;����a�iC��W.��[�wR�x����u�b�����g��05����=*Q$�T9���f�uc���3yM#3�������9���4]>��%�
?�RdkN�����b���K���y��������Q����$x�K������|�SQ����Y.nE�A�����X��~���/�1�wiF�Y�R�v�]�T��3E���g�B�p���ox�GR�\tn��Y~|z�f�Y}�{�3}�#}R�RF\@L=�{�V'J
��.A�FJz������)z��n���d�����,f���Z�"�!��:�!b7�(��7��-�Mc�.,�����?<��Y���ij	�9��xo�0�������V�1���?VM=>!`�!��vHv&cm�����}@I�mi��q����VjG�������������7�;'�x����}�����8�i�U����z�j�pI�H6w[F�V�C����*���A<����������V
�����1�������{K�u�&���	���]L���~_��W��������L/���+���^���i&=�� �l��$9�L;v6��U�����c���C��"�^T��������_�p�>` @�7�s�g���"��#Z U��Y��z#G�"����	���`
 ;�3&���D1�W���y�P.����bl��q��6�;����\g8��)0&��\�F�&�������?8e��Qb)��@�*���As����S��6���.5���F;�=�Aq1���X����?��,t`��m��i�mP6�����>�E���	�*z�����t�.N���)���=���`e������y����L������JxH�:�7djt7y�PN%���M���k
�
5���az���lm������qT]:��p�P�Z��Qk���^W�Pc�v�CsE�c#n]3��\wN����hC6A���
j����C��
{��x���\��o	0,kX\7�^G���]z�[�X8/^B=f��V���H>�BK�:�����uo=X6�R.ux
�|5��O$5�*0���
����8���&|�0��x��W���*g��e-k{���Q���!2��.E,���
���bMs%_a�'0��� ���sTH�.rmO����������kF|:���N���[��?�U������>X�T�x	�����mU������ �>
�*SH�mu}?��U
��6
�v�<�t���i�\�3����Z����W�]Af?"�9p��	�����?cs�<?fS�7UI��@yf#)&���c03
z�I�N7�7C$��K����6��Z�A��k�����Et����5l�r+f6D��X���2��vw?�x����]�f*c��Z�WYb��*�wz����9� ������]��[W�����u�E��s�4���0�1����&�E�D�`g	n9h��C����TQ���2i��KCAa�s�P���o������4��3��D0�^�r�����W�~�k]8d��R]��-,xT�(���B~�D&&H�[	�+B���Hn��&!�d�#�.�~���0�L���l7=<�Q�I���#:D��1#��e��	\����L���������L����t���A�B�B���)�$��XfYL���s[�ox��������FE���o��b!E{=	�R�tg����Y��ZF\P�m����N9���R���f�BVN���0�i����K�B������4AnM$sD������>����d�9&�M8�bK�4�*�{
�o��,�-���A�s���2��da�x�Qo�R��3���-SN����M����[�M���W#���PE�R�UZ�"B��t�.+��ZUW�MSz�d� �k���������q1�l���"P���E�l��
XKDD�����@�M��*���w�`��2r)I;S*���P�Z�2Z:{ J$��R�7Z������+��)e�=�u����m�gsMJ~N�Fs[��<t���q����������!12=#s/�P����
;O��T����xo���O!���Z�1��IL�il�5�yB%P#��-��(��>�����F�JVkI<}x&e������=N���uJ���>��*�Z�l��h��[��9����5]���f�[�.�����=�;{��M~��}�-o!�������B��I���1���I$�b^�a�F�M'�BWI�"�A�C��N<� C�N<�}H ;�R�A�T���w�;$�d��o����l5��N���v��=_����X,�{{��YS�)�E�i������l�=��3�	����C���es��U�x8�F�7c���z>9�P��r�J[K�E��N�������-���������G�����BU�A����;z��-���(fv�`W��]p_�p�eN(�\����g0����������3�s[:��;��"����LSCDN���O#;�vZ�
�Vt��u�b������oEK�3>��?o�|
[��6�P��5@o�Z����S*�p��C�i�>�/9�|vzxJ��P�g��d0�`Q���}�����L%C��m8��
�t�����f�k*��]P�f��(=��t�R�8���[t�A��	>n�c����nO��_"���$
8"�����B>p��;�3����� ������gLnT���`�|q3)J��m�[u=��}J������e��JR�yB�����>��b���P��.�%�)|��syo��	�&����8D��{���N{��5��w�N�c�Jn�?��Q���Q����U|�z���R��?KW����n����;3�J6�hg_�<@$�K�^�Jp����B�3�y���j;h.�O��
8�Z;Y��x�h>�-+VU�h�+I��^Y1�e=m�SVi�
�(��>>Q8�S��;y�T-�^�+��e9��]� a��j�XC�����%��*6^���qk{��v]�\�a�e�� �*,�U�����S��$���eZx^LIj#3���~��m"���Bs���^���S����g�[|�Nh��������\�W��J���Ko3�QJ]�VMd�����C�)P�@��e�bv��Z,�;xf4��h��)��g������D�|����nbu�,����
� ��w��0�n��2=6�+�	71����7���zXgFf��za]%��)���G��3:�G��X%��T�FaPI������q|�]���3��c����"�}3B�79�+~������$�I4,�Cl$��k
(���M��ik\����JO���6$�4��B"|9IK&���d>����o�6�I~��	d"����;���H����JE�slzN��Q���v1�j>l��Q������C��K�Fx���:H�K���\��`&�J���LL��YAZ"S*��B��x��M��D���TIf�L�����]#*�w�Ag�������4"�-e� DS��o#P�
��&�-�J�vpJ�	��^��%j����d�H��	>�oL�KERe*$%����G
��5O��!��]��2�7H��	�X`��
<=jOk�v���v��$S�n�����y ��~'5&�����J7�����G"�|h����O{����5���O�0wa�Ebf����_��
d��grq5R=���n�@f�|��l�uWka3N�k��T]]u����h����R�cz.���3��Z�P�H�
,���0Z�1�#-m;�Xs��,rq�i�+D�����.5����7\������2Q3������`���5�K)�a�)�E�=y�l�G!��	�Y�A�/���^	�k��#�&���b`l����(�����z������@;\A��o����av�������n�\k�-�4��{��H0"Qf5����S����`2�7����HxK�Di/O(p���H���Y���#�a7�����5�)8h)�0o��)u���$���yr�6�� ���(8`������0I
8���\�m-�X%`_�{���y��E���^Z\PMdl�hu#F�L����E��<���r��w�yT��%V!\B��#A�\oPz��d���������K���51����%Y{�����+��u����To��c����
�x�+��v[@����C����w�
������
]�\cC��z��Zi�wlh
D��%��D�����A��S-fP5`�����9r��Dfk	�Pxl��],��6�-���9|��jq��xwn�<H�jpn�G�^���%S��!���%��61�)�c�{>����W")�f:�Zz�y��<��T��.�3�M-�FkC��T��\6���"y�w
��g�F=����F���k�&"�@��SL�����d�K�!��FD6����FA�=_��<�T�VM�������C�6����XqI��W+��}�m�o|(c���*���t(wi��PsR�~b�,�F�'Y��y��9G��D�P�cB�8�GX�I���^�zE��9RjT�����% X�T/[���u���C5��9"y�I�:�A���,^�gSeg
���,��@��
H>���	�e���/������c��ar\Og��k�K3�(�y��#tR�����F;���5+��Lk�P��
���9,�3H/b�����2���2�����a'��/��m��0o#�'T���*~�8�\,#�u	��Et?�O1<�_)�s�fN��]�!tb�VEzH�����e5Z���.zyV]�Os�g=�~�t�U��C�UX�L�H�����[��#��mQ���r�_�iw�uD���':z�^WW��fO<42�5���!�n�H%��q�����bE�0C5�Lg��m*���A�
Cv��!SH�_c�e3:z��u�1�I	�\E�(�����H��0Kr?�I;v��n�}��K1r�I�m����)�J��O�<M����@��Eh�i�P���m�)��W������5E��3��yl����m>1"t�|�T���+�;T�&]�.^Q�%h�-�LB����y��:���/?��@0��vEo6%qL��D�x��������P��`	h���c��zrG��EeH����Q������h^������D�
@����3A����O]B�mb9���6q����{E���u>~��wpF{
�-Q����6��������nDi�lf �l������%e���A�Z����ktIS��������y<�4�����EJ�B�4����`E���������W|X�{��r�t�P^K���"�0����fY�5LJt)Mk�	�y��2���I�y'���*�`�-�&AkA����h����\��=$��zV�<��g���m��\������o��w�.Q����B"{�n�E���=�	�
��� =��l�VG��Zk�*��� ����P"��M�+���������Z����0�����bY�<��R�Y���(����%?G����m������*TCKD\��d�wTU�*:����Q�A�I�8�
_�������t������/��V�F�Y��|o*��:���Z�S�o�
��d��+�j"����Z��� ��,����5io�Q}-���,F}6)����{��������ln
�S�jN�77]����Y�>G�d�N���s��Y7?��H:l���)1d?Y]Y]WW��"��V+4�w��{�$�/t���#�_��\]�'WcN9�IL$'��ot?##*Yl3r�����<��0�
t�_��^�1:�	%v�4�����)�?G*�d�";��
�g����a�� ����!��D��EiO���4�(��^�a��6���e�5�p��	KAU��;�J�L���P�
�)�i�O����A����C����|�t:��u�o�o�����z����d[�F��Z]s����&X��d��s|�����F=f��wvON�����V?�05��fG���/FT�f��?��X���s�����o����D�����m�_QJ���^'�a�;�`
�?$�d%l���^Jy/:����
qQl8����YJ�I��(����An�>�|��k�j}�j4'tYpS+,!��
F����5�����?�����j�Q����Nz5��A�.CO�����;YT��b�1:��VA��'�
 $����G�����l��t ���q�*���.������I3`~J)qO��<���<8OzI~��w7]a��o�"�����v?1��������>�`�t��U��q�o��2z��������<����?��l�	}���x����s������m�^w�F[{�x�A�`o�2��WXc�t���������kH)KzB�w�^�d�x0>/��L���wei?K��D�	�b�=$���y.�l�\j~�Y�F�'��l���6�cj�0z�Y��2F�����	�mKS���-/��~��_T�\���-WT��NPwg|��	j��)d�-ed�_,�#	q2���$�JEgT��e��b�e�V�1�{�nU������z<��A�NX����	����,*I^":����J;����7 s��a���{�.y�.U������K��3��rQ1D�b2<s';r�a:���	0(����L��j��%G��3S ��]!]]��A�)�uH�|�xx"j�����!P��'	&�+����i���yX{��3k@N�)���f���&{X��(�z���N�ifLX�f���Zw{�}�p��F@��f����M���z����~F��xV�V�������
~�o��ts�6VV��l��S�q���-9��KVM!����z���Z�d�G9���9�a���6�y���5�$�R�<h4|��Z���;������t���6�8�i�Q4��8o!���;Lc��A�v�������
���W����r�de
6�i���c`��$��iz>���-����6��PR�M)�=9b1��Z_A`�t��0)����vf�`�C(%�)}hC�X� �)�����&���MOC���M!|�#��
W7�us������AP4Q����Jl�{�dC��A4��8�cV)g��wd����SE�vl7!<!Ol���UL�|V������U� �	bC*�&-�M���g16�'�F4�t
C��-���o����K�\�>����}H�����W��u���b���u;�������-���B}q�j)�<u�^�|������}�\�
=O���h��h�Em����(��("E�u{N��o;�X!�$vat��/��e|����'}�*XZ@�*���S|+�},"Hy���}g^~�.�5��(����=�;��,J��I���YL���"���<���Q]7��P�Q���Z8;����/.c�q��1lm�|��)�}�@7��L����y��B������LE����$�M`��z���R�s��m������s�U
�.���f7r"��IGt����o�
�e9K� �o4W��G���h>�0�i����������nwT7tylGd���3��t�5LU�b-b�������
g��~��N��#jB���k�0��������@t������	���:[]�VT��A�M�D�a���Q���Z����[}n���R��B9�=-Y�������N��*U4�"��U�_���I>}RH��j�(q�g7v�v���q��G#���S&j*���m��3`�1l
>��uP.)�S'����I��`��3<2L�e!n���Av�Dpt��	������T��6L��Ic�^�S��xe.B-���:\m��bfy�4�NN��O+�l*�j��7�"2&kH�����s)�����T��8D�;(������*��.K��cy<���d"m��`6�
5�^;���_at�.�SKg�s���� C��2���i�����]%2�	wN�8a�Wp�%��}j�~�6���K�������6wa�oJ��8���??���*U�#A�Xl�tc��K}m*��*m��H����%C����fuE��$�Q��dMSQE�zE�����D���^��4������#��>�}��V�� =���c�#����F�R�~bQ@~k^��j��8�Q4��*b$�_�� YX(�<{��N+2�?�_����(�����#��{!������Q�sf�o��^^�_�<'6<����UFI�������8k�{��u��l��;B�J����R]��=��]U�0�L�ETM��[XDLD�]��+��� �3��;��?�yy�g����n~Q���c��~6��O���d�W(�E���:����y�Cn�)��4?l:��$����zK�V�Oztl���rJ����LX�v�Q�F+Pq�T"
�wO7cbS+����G��m�L��0W���H�K]�F�DM���=�A�,�&���PI��}%m�"��>>�IO#O��\����X�]Ys�$D%ijj"�W��J.=��T�����E	&&`������D�5������y�{�')\+S�.��ty��yq����yQ_d�a�b�����S�Z(�X��LWj�PaA�2m��I�����	�_n�t�J��/3��8�Z��g����j����YK����u?-N`�:���r��TP&�<�*�h	�@�f��O�w�O����
A��"�B�)iR��|�EQgKd��P��**uOEQ�5��j>aUTB�ik�Pj����-������A�����1+\���hV�~�C����e�4�-�%��$j�7�[����QX����ZawV������p%��]l�r�a#���J$^%�
j�j2>�����+����**�VJ�/��m�*�j�ek>T��)��d��8�q���4de�M����p���2�o���2n�a�����5z�n�)�X�=��]�]y�X5A��d'������y�i�,�1'�6iH���.���G��y�I���g������L2����liS�%��.=��`d`����T=���[.�{��/��3���2���%��+L5�
+\rk%�G��K�U��4���2��"U�!v�^E��1�&IOCU�"�h��s*m���V���@�e[`�|�v�Lu�)�8�5��
��Y:�����f�q�\k-�W���������J����N�n�a�%��-��$u"qy�����ppn������P>6���{^=iac�Hh��P�����Mq��aV�n�O��=�Y#�~�K��d���b�@�k0�����~4W�.d���)_��V$���D�����I�+�1��vB��p�����		�t�;�`2���Z�����$��7UZu]0B;� .!Y�d�T���S������!��0m3�z0[����2c����K~�b����v�S-����:��$hy0���.��uI��J��Uv�/��������f�w�eWY:}�L�dS��D=���~�	�v*V]����T:�<B����lO����{Nd���������W���a�Z���E�����<�����Q��W*�����T:��E��\�,_f���=����T�(�B
���B�z��tW��7��j��J���e����u-b=M���T��F�S7�t�`1�^��vIMb�|����	>"�(�$�&�(Cy�J���Y����,���8eZ�HL���*x��!6��k~wD5"���f������\F��dW�l�Q\�������k��6r�
nLme�P���;���+t����J�������
o��\t�
�K�1S	6]c�$Eq(�/��QCT�}"�U���i�#�
x�{��=�5�5(�FPzL��
�~��.p��VJGq����j�����T���������|Y�)]E)�E�cl�k��|��v����!�
�Pr�~���Wt-qf����pw���k�D`�|P�g���e!���ZH��q�����	��d$T��@�.��3���B��$��^�V��	1�X+r6�����[m���K��=4�d9�-c�����
vcX��R��~������md��:g���>�P?S���T��h�J��HS[���V�NU�%����{E��b��������v����o��x���������T��K���7�5�8kv�������h�c��!Ff�3:6;�c; �
��"�,��[=���B�b-f���7�p�XE���hC���G��������H�Q��i��+O�NB]qM�����B��|Fb�pQ����\�z���������l��0(v�5���)>�Wo��=���d��J�?YK�r3�2���"t{��9�F�;X��0�B�,a�k��m�V���:����G*��^"��)�k|�����g���#�2Uv���I����o�s]��La)�Q�q�V�rPNIj2�@3	yT�6�L������j�<#L9v��)�w��a&�Vuz�	��V0��{*,�qV$�&r�Egy�bsz���>�W5��S�}`O"��N�/�W�������c���p�o|X�����m�mx��;��#�^�7�33�|���}��n��@E��Ts3�Y,�C����i��u���ejG���
�M2������(�Bb���nx�<#7V��\;������^��s�E����k����S��8#��4e���*�`���OO�mz��Y9�Cj��|tm0���,���W��uM�c�tq������D� ����;��4v{����s��p5��b.n�)�
�Y��c6����puc��dhJT��qD5��(�w�D9LK�����*`Hg�b�[;�H���4n��
&<ut���1�YDK9��Dq���v�R����qH�Pb�F��o�T�Mu�v�h���/�,N�8�:"�4�
7Q���;��6�� �������R�M��Vt-�wN�_-}U4����������vW�]�{�����M���
b�$W�A����t�R��:����H��KE��*�C���K'�����Hi�(*D�I\�+P��[,��io��W��	J�[;����?�+>��1�z����Z����R��x(����i��������K�����@���P������V�2�Z��r����k�+-J}�k�q�+�Z
X�����qMJ
������U�����/�0!�������'J������-|�y��XX��{��lQ���a@	�y�T����D��?+����u��`$ �D"���b{Z�����,��s����~������c�� G�.b����2F�K*�[j����d�Z����M60(���� ���V����d6H����1y��^��(kc��YK��vwl(vq�i�fO�d����X����M�3�+�1�����|gy)9D+��b:S+k1	��|�0��$v��u����6�/��d��d?�`DPq
������)�*M�������ZU�M5�Z��.����(�IIrm�A{�Ix)u�,�N�/'��m*t���1��L�[�
V�!�����{��.2��#����.�J/�2�s�f�u��S�0�I��r`MIM�c��[���n������Sc����&>�d~�_X)T�|�nU-tq�T,y�$��rYT	C?%*����������W��PE6LoU��[U�2J��J��8��!�l2j_R��#q�7���C��|[�J��e5�v�~�e_l����4����om3W�P]PM��`�V��|p�J�\K ��
0�6Rk����d��J`eZ��.��(�^j���#��"%=��5{V-��L���+�	i"�z�:_�#�D���U�m~��������������������%��������Bn�
T�A|��sz�
��D2���H��7��l:��6^=cs��n���c����V����.Mn5;�OP�hfx�J�h����������A|= ,���c����1�?\�>/�^�	��J�4#����e���f��[�y��3��B�c��h�U�
i��l���|b��X�X����JA .�������.�Y�D�D�����lp��e�f�V�fwI��O:���S�6�&�Fj2!xh4�	m dI+�������nT�.u�Id�LM�!"j��Y�2v�=i:��N��2|��Y%�R�������H��B��pH���|d��_7���r�%-X�@
q��l�
���*���eLe)�P+s1L<��+F%+"]��2��Z��R�b�"�L%��T��1X��i�����++_3lU�.@�����������t�����X���~�\�&�ZDHtK=2��������)~�]Z�br�[3J�)������_�������������YLY�'a����Qiu�!B�+�.Q)h(������d�A@��q�[|x�G�s����6�G�}����p��0��?	���Ow����;��a-��������s���MieJ����xr�^5�2��v
N����0��r�}/*�W=2:{	I���6E�6{����~?�k|�J�U�:Lq�b��.+�7�����
��SG��#�5����t�A�_[_Y�e��8���0-�~��k& %I/���F���u*�+rK���O�����s6F��%����zr�
������C�!utv�������?�x��U��3|������4��oR�L&kn����v��Ci!��g[�BDxb���2�7�Yy{��y�Qy:k2O�&e�-��7yG;�2 �|��"{��j�����%��� ��
U�=�]����j���-@�����>�����t����q����N��iU�E�����Rm@Fy;Uq�!|r��i�h�}�����l�2��K�����z2S��gW�(5��e�n1����Ni���o��5X��=/u���L��L&����GeS��Jf�c2V�^�A��v��U�r�� a�����sZ^Nf-����&�?�����y��"p9�3�S&����J��=�����{����[��G&�#�{R8.^�������8�T}��*yx7�}=�x<�O��tq���[a�{Rc=Yz��aa����bzcZ��Y���f���z$�e�&��;�_1���K�}7��[�D3�z�^��j������h��d�Z'Ya�<�TB���@�?Rej?��1;�Bz_H
������#_��RH�1�Vd�\�c�V��(n���Q@�0d�7A.��	���)�pb�G�������?������Y{�4��cYkj�&_�cT_�<K�F�dY����Ix�a��QL����BM�uC���8�@8�t�EN����������2>����k}���z��[Q�+���1�
[�����ke��U�v�a��:��
�0����Z�l�jZl+	�BS�������7�
}��Q<��y�
�_^Td�0a�kC��8j��i�Jt����K��u�������~��F�^b���zgg�^G1ntX�1���q/����`�w����q�	�
��R����@jK:wa�S
i:�\����8����L������>4WT&���I�J�Dn/cl���_,��q&��y2]:���fW���������&E	��q�cRiQ&��i}U���������{s���CSIS��[i�2���n��7�\�/��cbI�]��f����6<���*�,�%=���>gt!���)���}@y���*�`�+7U��*�I��%�dpZ��������b�h���=sJ�0g�����\�*.��[�3�����T2u��]��8�O�7x�C=��R�5�U�ad�`-M�I��Q'H4-�`���FUUR�2��?b�D�����J�5:�4�����Sz�8�}�"^%���0C�:���������+��������?%LPw�<M���4����s���Lvzm��.	L*�`lDW��� e��C(�Z�����@P��@V�\���9�)��:N�U,��S��b���h������h0���}�������P��~'�R%�q0��.��{���,F�H����<���|�~��r��F�|�FGIG��@.�����>��&�6(�0F1��=RA8��T��p�9��X�Da�)K��J��r���y�~V]���VWDI����//�$x���}U�!
%�������
�j6����&�D���o��.�B)e���]�)���'vi��[!2���2��r��J2I�S��o;�?��F�AA5�
p�z�Z�����
��eN�|2���u�h�[��E�x���(	Xd�[���_j��z�"��j�M�VG������<��[F��0�J~u��C���5���`t�
�{����66����?����S�3 ����<�qQ�@
pr�d��J!�v�U;���t3$���B���)�{�<I�Y2��62�6�&tw�D>v�Q5�X���V�R���Y�a�$�f>b�����V��|��n�����6��Q(<�,��������%$����X�m,�KQ����7�U��*��L�p�=��*X���.���
<��1���H���x7�GYjh�$Q����*��<[��]��L/?[��������2������*Vu�~�����kLtd��~/3�	�������u�H��4�MO�/��JM"��'���`����7�N���f�Z��a���7j�m~�Z�a�Ju
�w�*7>��V@O��>P�����T1��r�MM�@}I�_�xC�)���'QA,X��c�:J���U���TH�ZhL�g25e���'�+5P�VE��J��ZdQ3h��Xx���9F�{\Z`��O��jGBx�b�\M.�-��Xc���gyw���Y��`L����du�V��Nzl�F��E�y���1N��Ka�*��C�'���Pl�-������������H�C&�f�\�]���(��e���6�;nb\Z������.�Y�����-�����~�>8��
a���pmsE-?8�
/�Qh.�
�*)���Oi
�
8��&�*�~��d����h����K
����c�bB��-�=�����/x'n�E�A5�a<��E���[EQ����zs��57V^���!
�{M�k.(��������6�i����D����'�����B��T�t�:��?2�.9�*k�LK�b'(�Rx�C�a�6O$
�1��8�<��C�\��4'.�n�B���
lf��_��-�[B �g#� j�r�JK-t9�	��t������
������O���������g�����,$(M�W�?}>�x����1.��K.�q����F3W�7SS;�h�w�C��57���9�#Y�S�f@D_���$������a�&|��U��
q��-O��i�����E�$��*E�����2)������5��Ul�<�9�K�l��"���Ni�B<���b�����#�P�.�����
�&U���
��n������^`��Y����'g�U:`�O�����Z��a1
����&��c
}�{���F�A�'���.?� 6��R��89`@.H.z)W�SQ*��_/F��bZ��So���O�^�0���!�V���D>p
3����BI�`�B��v�396AMy�5}3	���X�0�W���>��E=���e#EXb������v����)�]���kuz�[�_s��_��^;>~>�;�9>�Co��{�?����'���/q�-}Bu,f�����rZ�w	�1�s�������K������IaCc��b,v���vn����*e@�����+��Z��j�������x��<<��:6�GHin���l6l�������G��#+�u5V6#
+�i���`�������L5��9Y�.� u
��
��e1/*�U`C����-?6�1���*�����1������6WB���*j@��2^�k6�p���,rt
��)[#k������n�s�M�N3Ji�������	�>�4OF������((�{.?��m����:����D.)(�����u��+� ��o�o����>(l3�����Z������(���	P+Dq'�LZG|��FI���=�,��icee+�X��&�t��z�e����V�El�����I�����cUua���^]z���*���FOS�!k��~�2Q�	�
XUY��5�������hw&#��<��f��XY�.�{�N��o<Ed��"�&�`O�7�k�d��0ma��K)��_P4�y�'L����~K���	^/�Xm�8��iQa�mZ���,��\;�W�	�!�����YU��K��<��c����@j��*�U�m���m�����>��Ch��V<����+i�����cN�j{p�����\�i����)�����%���>��~9M��r��������;����#���n�5�0{����
�z����[��b��6�������.�85���M7�Mg'y��!��M���Qo1���yC9{�;��_����V���0w�Gj-�YkY�,G-Q_F_�g�^��-����M4��oc������
�d�I�*�ZT����s���!�P���M�aW~T�$
��.�\�������������`��K�i�];c���k��>k���;Uv�l*^NYvU���4N~~w�w�j�`s����������%.A�fs��F�-������&���e�@<����
^a0������7����h>Z���J���DV���Q0�[e�0�@^S.W��7����^Q�,-��$���43A�
�"C��K�������C����ps��6a�<ov;��wx3�zp�
J��|�+��~A�� oE=U8zQw��q��zp[(Lw���V3X�U/�h~�������Q6���c���Qz�c���;���������������R�&
���^�^*<���1u����?��G�}��Mp�CH����t�� X#������cw�0y��������,2D�=�v��zS�7r����}�_�d!Z��N�$}_��=�^5^u�.�`
��C|+���8=��G��D���/ �xiY�N�i�P]�x�.Y/���-�{S���0�c1��vIr
��!�GCC_;*�v��~1���7����uVK������wi>�����=��E+Lq���Q�S�����D����	��!�G����-Rm(�
��c�Lb	�,��� �)p<�9	�q��t����K���a�u�uj�:���Z����3t��AL�)�rf'������e�v88�lr+^�-M�� �����W��s��'�D����H��~:�&��c�E��V����x<�s+t~9���y/`3�^����us�(��EU����!�K�s_Q=s��_A�����8�<#���z��}���_	)�o}�X	7��6��V��\x8�B�X������ ���g������M�_�����y/�w/2��z�J������zA
�5��������w�U����>@�eS4��R�n�}���30�u(���Z�����cL,�(�~g�����������w{G��v~�j/v:��`Y10�T^)��E�<o����;5�3*����\���_���0�"����AiA#v3?�{�AvS~��S��G��=?r����������c(�G���`����[��UX_����a�=������p�<#���B���a6� �:C����]�5�6iW)O�U���]p�������'�o�������<w4�$=d�����2\	c�X-�04J��-�%Y>�w���A��*`8H�X�lWI�&���W�S�h.��)S�������7w�����0�L������e��b�h��)���R�N������v���<c������������r��j�Bq6��n����Op�x���[a�t
���]��d�.f��1�;�BF���D��$v��J|��@xjei�9��!\�\����!����5Y��#����U��6�Pc�9v����A�U�KYv�9 �B���t�/�7�N���qC�q�`��!(j����v~��/�~�#��F��0������n����u�;����>�=�`��)���$�
x��p�5B�����K�W�pt����������L�GN�"i5��ySHLq�z;��8�D���������/�����Q�0@��V����5���g�'���#v*�$�N��Or���W�^T��%x%)��j�Pmep��
����V*��(}�|����p�)�k��(M�}�p:�~��D������dp|���5���lK-n������`�c�*��3sc�{���D��)T+X��ZX���,�\��hk���Q�a��!@��Y��9�1X��Kg
B=����%N��Ip|p�3���
t"�V�
{-*��*[�v�����C'�l�,���������E�����������H�V����s%�H�\�L�t�q;�VI\�Pe���&�\b9����@�{x���(i���|����/&���`�N���8A3����X_Zz]��������j��XY���}
�cIC�i�^@��f/W�39����l��zJ�,!���"`�!.3e�������������\���+�>n�Vviq}W��Zv-�7e����n�K��SO%�����I�^%Z+�����T��d���H������(�\���B2�=�LLB��5�I |�������O��wb���J,a!M+���},��Q�P�t��`yK@�I`�8w���l�!���V��R���NX�<`�Ud���/��Kr��$�X��M��I�[�� ����vl�i�����<��e�!	�&�DTA�����O��Ewu��N	+^�����!n��R��� ��m�k��>�G�b���"!�EB"�Y�q1Q�-��T�
k��s�K]����tK�v�hqXQ�*��j�������zD�������3�b�a�]����W��R�2�/b(HP��q�,��n�������~E���m����+�'���(J�\+�6�����O���=�\s�~����T�q��y�f���X�"���L��EI�����E��{�l���t���n�8A�P@������J�����.���-j����f��
$!WOFL��F��y���YT�
���7�O��N�|�l��MP�q&V��_�L��v��w^�{o8�1�9:��;K]�{D�m-����%+��a��CyE���D��]�,��b5^�~���y#
��j�A�����pYD1����u%��`�J%|�	�V�7Ju)�����l�3EO&���q���D�
A�p�vqg)J������XLk��������b�&��hn�:�r�U	����\J��������Q�������������^?m]��vyi���H��6�;nx����xW�h���R��a`*�Xy�7
���h{�%[��qB\\�����-����qx^��@����i��+���f���<�9`���N�E8EFv������#n���
��/�n���b�X�����
,���~�
�0���U��W���%����B��^����
�zC���@3|D	}�T�8�����3�Lv�T`�#U��h�R:���o$��a�c|g����������$c1� "��Jh��m0m3,�Q4�x��3���(���S,/���~�������!e���mZ��&}�r�
�h���`������VE���y���S�ofM5�
�JVu�����������&{
,�����0�ix�R��I���BT�q�rg7Z����������$~H,����s�`8I��j�#�������4x���\(�1�����H.��MP��1�B�r+#�n��|Mg1�����uC�>�sE��Z�W�j�h�
��%�j�-��}��"!�X��`$���n��"�#�����%������.��:����+7��a�a��|9��s�
,i���b$��~���2�#zTa>��XK7�S�aRj�\�O=�b+l�6��.��.�x�����J�w�3��mG�SeQ-:����
�$�a�U1��
��VX��g����m��o��p)�����#@�	�������	�����Kd6jQx6P�L��;h�
[fX��\�/��=4l������R���H�A��G�t������O����q��^��q���6��>��S
�88���+ n�i}RC�aa���3�p��z1���[5G�����St���T�w�[���!�ovW_|�3~���#,%��
;��w��n���'�+^���Wa��Dy3�����d����������9e��m$��
$��S�e���'�f�O���`�X�a��f�Sj$Qb�{�f6��f�\���@�x����i���Q=�y��	Hji��b����it=��RY]������}�4��b������3�����.r�]wl5M��wex���_������z�����6�������Tb3�g�8>`�[~�3"
O�>|�'u�j���)�Y��
&�B�S�S]�mo����QlDF�*�4~����Q*>ic����������O.�L����2�&B��"
T��Q&y�4�Jy������G�F~`7�zq�������2.�J���v�n?��A����`�E����'��5W�NS�tb�4�o�)���������	+S����,K����h���e>[Acu=ll�:�D���~�S7i������FW�����+��"e�")����E����������	�9l.����ycgP�_kK�"����]$C$G���~�R��_Wi!�LLQ:!G����8�+Y���m�z�2X4���4�n���i�S�?S�iXI=�h�$���mek�8�l��;�7�I?q�OJ��lG��R�5tig{�=aRU6������Cv��b�p�<cdd]������N���*	��k����)�1��,�-�]�#4��:c���i#La|x��kmm��&y�����I�c���TC�������S��W��f[�����v%�������2��|51��jvR�NC���tI�����������OHd"l�q�q��~^�E<�!��g��[.S�����5�o�=c��A
�-��n4�H��n�"�P��E }4��UgY��@#K�/���@��V�H�k��?�\��i=$o���9ilTZ���U���
�B7�O��(|�<����K�������7�co��xeY�������T8h����X�������z�
�79��N��bCD	�ncP��#�����p�-u���6� ����!H����L[�����|�P�IQh���V�I�I�4�����#����+VI�{��B[��2H�o
���n=�(~Sxrb�:J�����`A�^�v���������+a��yw�vy_8�	;m<����R�xQ��������Fr������}c�_H��;�Y��<��06�J��K�D^����K�<%9gj/�y��9���D	�&�-��T6<����^l���l��|[6��U�N����s���0F�D�X�I���{M����J�^���	�����Vs�	5h����>��
�N:�Qb��/��67���
�?0���i%%L�/o	(���W��zY��
����z�V/5h��)���)����
	?���_�9]�v{���,O1�I�6��>��g3/$�CM�lC��~z4q<`�W��P�b�nu���:�8�w�,����$�/F����
�y�O����h��o[M5H1�q��� ����"B�4�.�D�l5t]+�Of����4�oT�~z��02@��X!����/����7fjj�V_�
c�z�����j��iII��ZG�{Q������Q�*U<k���X`��1:,��[&W�(�������jYj)��ig����\Vr��Tl�>�b,]�4��������-o����K�s���>��p��Wb
������&�I7�M��#M�0#�|�!s����Xw�C���P�*�s2%�aM$��x�K��A�]Q9��K*x�I����:@QAu���0��[��)r��6���1���������wY��m4���<f�������N�aS#���x��UG��1�t���F�tW-�6�l��S=����;����?�%���t�K���,��>�0��DI����0��?��X�v?��%;���������U���#��=n;RgF�vcu)N��55�������������P?f-e����_r���)����W$K
���n�����mT�������������8K��j�y����j�4SI8���1C�=
�1��
.����9������W
��������D]������U�/��Z�W��%�;��vA����^�r���J><s�`���R`�>$T��5�
@����[
�x�O�L�F��q��[�Vo�NX�����wF�2N���/��m�p�Z�Ws�������}�t���Q�l�������������/���C��3��p}sjJ/$K�,��f�733G��\����/�G![���e��w�3��,A�����Y���x�N-f14�rUJ*���7~w�hWO*�s^t�32�[�<	8O�f���N�M��Ny�;�������| g@%���:�e�k����r����:���sm��vq�-��*�#�����
��2�K����{{�n���`�B���pg)�a����2��%-���_`���v�Gk"8~c�Vz�l����������E�7z[	b�;<��������s�"������%y�>���D����w�e�p�[��6O	.�B�Vh��@��S�
m"�f�[UK�Y��S4a�EJ�!��g�[��+_YY����#���H����})\�*��[
�s&�Ej	�h]bYd!(=������\�lS����fTP������>����q���:��GB�U�6�.��"vu��gO�t��`u����C
8��W����.l(����:����k�z�������\\��[)?� ��v�W��n�j��x������$�B�L����c��h�v����{z"$����\�%'�G?����jZ\T0��2��~��.2SDu��twc,�E�^��_�#a��3���9������X����a\2��������W���K�S�:��"P~F��f�jx+������
,��e�y��O���3��������/���������{pB�E���E*�<U&K:X�\:��g�W������^�:
TY�i
��yr-%��*$�Z��$n����R�y9�jhHEN�6���u;������E��9�����W��n�'f2���!���,<[��|~G,������W������|�����R2g/�Dm�'4������	����q���T�Dv$�+\��s>�P����m&�H�����p��<��$9������NU�+Bt]�0=?w/��I�!u�O�g�~'aqy��.�T�
���Mya�nd�J��KUDU���^B4;��X�J�y�Q��(���NKu�p���M�b%��F,��.�v%>�DS������Z8�U�X7��-pTu1��K]-[3����s��U`kqqZJ�B�+����d�B�>�$�������"|�/�bm2c?P�c�Y+������Q�p�S�JiFE�,�������L�C�����<��W�q:��m4���_3�hlW���)���d���q������C�N~�z�EE�z ��W���5	w����77����c�������h\������:XH�.}�A-.G���t�� 9;���C �`�
�r������\�up���U�rL��a���3���XN�!�+������vj��@"79��h�H����c���QG
��	H���y#"�������SL�
��^�_r�?x4t�,F�BE�"f������������E���l�a��$�^��������DA'�-"�}��:��I���k������'�W��/�^Tp�JR���$�x�V5�� �h��I>3�6i���BC%����`�D��#�VkY������+�s���Fa����_��t�yy���v��@w�1�Q�P���- r�j�.���""Z��d�Sz����l���Z�X��\�1=M��~�D]S��v�{]`��![��H��inm����j"H��^4%���RT7:i1�8+��9��U�k���g���5��-�*�j�Dk��*H�����q����UaLQ�/��HH_���� �J���}�:��vjp��,�#^X������]
���6W�
S<}rd��\��n����������U��
MT'=*q_B	dE3�'��������[���{q���Wj�}�S`��3������"���������*>#�B��cF>0�nuc*	��"�U�#.���
��pcM�%(����	�D0�MT/�9���r<�U��Y���C�V3���;�I�{�7P�zF�qU��Y�������u����}����?��Lq<w$��d�WNi!��"�'���=�*��0'��}�nl�ow�QE�>�&sp�C(=Gz����@5�qDs�i��s����[�r�r���P#ru_unT������/��	������:U�<��Kk���Z�a��v�0�-���''� ��muj�pT�����-;y�����ep�|D����{I6V��M�1�f�;��j��:�����J�b�j�b����T��-�B�z�]���j�c��9O�w�����
�+�!�6>���|H,����Rp1���vu'�;��.����an��Q�� �P:�K���a�tF�P��^�'��$����l�~!����N�]q���a����[�E46��4V+$������?C��\7��;\`�!�]%��Sh�t�|dmD����u�����/b��+�<��0^��l4��iL��(���nuU�����wg1������5��K��fvN��nl9�g�2TJ���`���&�Q[�w�9��v�����IN��+��X�������k=�4�����������������������%��%�ei���L�(�D�o,#����V�����$�[�F<�Z�������^��;����psu�N����8�R�����"�b��-����if��3�&bhp��A����o�)�zT'F����w������^��gC,�������1=.��M�?��������*�G����*:�[��O���^���f��p���~����95%��_���G�G{;'{�
Ml�S�6�B���H�s�uU3G���]�9�������rT}�"��W�BL~�[����[c��{��p���)RDc�%C����DC�&!�N�a,�-+��p$u� �6����7��fc���s�b�e�
������c	h�;�c�v������}�UNzED:+S��Il���� �l���t�L&vF4m���Y?�.�~���|�;�D,��bBX�,r����+���$�F�-��A��8�s���p2Z
��� ��\�4����W������3d�BA����:b);��U�M�A<U�"��\B��4f��]����I6�=;�7U���-]
�7��^��J�%�8I!hWWI'��~�u���6��W�y��8��T���33$cz���7���J:�L?�"�6����	�ps��U6��m����Gk�t�E�#.U�b�>���2&��������F�����M�!�p�����1�SvdM�f����'�\T�`Y�4\;NI?k�;I��������A�]��
2�zFK�yg���3�5~��P.�}�����q�{i�@p���si-�����J=E������'�-~�?e�����^��
s�w�E �Q]��g�[��`�&����d��6��%O�����Y7��T{-J8�6,���ouaE��Q��"���������&0�E2�o�y��Z�V�
�+��N_��pou�p>X��8�1}�0�_�+\�����U�p�����o���{�p�N G��=��.
w����vP���;c�1�j����c��%_����]��kc��<W=��9�55j/�Je��3�L8���*�3��B�=&��i�
e�L,	������H:�������u���
o:�����'|�c�es\�{�qPO����NTJR��]:�d��4������
�kv���#qY���x\T���B��~�@B��z�.�Y��9^b����[LLoK�t��|������@FR^A�f\X�F�`���K�r�`E���1#-���(��d��W�n���gE)�u�Q���H]`2�(���u�����6���
<e���B����+aS���5j�:������z��A{��oSq`��0��e2u�%�;��K���.�u������g����\4�u2�)u!��h7��K2��(KH���Z���-t%2S�����n����Mh��Z�o ���P&��%GY{�#d��0>��L	M65����"6��alc�o�,r�����'6�f�h��y?u��J����5e�I�����"�R�('W��L7U��L'd�F�712��H��'F�B��$��%SMP=���*����c��J�]���B�I�������2�M�u�X�s.o�!D��H��z=�,���d��@%���sz-C����H�)\]X$�,���-lH��!�Rg2�K�>�����n�j��9%��%��x���g8�i�<�
�B<�P~J&;��hk^hk���rZ������#��s�D���������%1�R�)�j���D.
�+*;��t�3���S��@H��y�n[B�q��m,L��Y��#�&1��6�G�%(+hl�,���H_F6(��Hv��v��_�5iQ��v~�c����o�����g���/�B�;q��Z�MI
2�gNLj���C��ro�����pk�����+yk'�].�e�1��a!*���U�f��{��(��,6�=S���N��� 5g����_}�QH��l�	�,�b�G*8�����V�R7�O���bc�xN"��z!��WY�7��BqOJ9R�t��r�`PY�G���T�v�Fy���-:�_��0�RI�-���aD!,	D�A��KTy�,�U�L��sT=�Z6�vz��+�����"��&/DH��#E��~��O8��M(Brv,�����8��M�%
=|����.���>����>���(�F����J]A~�q+�x
��p}4��q�i�J:��}~�hbl7���K���ym%��E%D�K3p@V�<�����|���J�����Q������-���8������x'#pS��K��H�V��������O
�x��K��G�����B���k��:p`�6w��bt@�:l���u}�{��>�U���A���[a�s��!���2k��"�s~��ue��2������:�T�P�|�Eg�]MV��2Km� ��qGl*����H<E����lkS���;G{�4������T�/�P�>5�j�K]�����:�^|5;��d�1zM!�h�X?��5�J��#�r*�(A�����pk��$N'!����BH"=�D�?��P
��$"���R)E<2w/�j�3��P����L���7docN*���3�d<������
���p��x�o?eSqw�o�E
A����5h.\�F,.�����x*��MBPy.U(���+��D�c7V��8��EIN/�,KUo��opZT���%)5��Zj����+A�����������������y����]0��O@J��<��'��a�,����KMs�O���v�"kv���}N��!g{GG�G�dw�wZi;�Ow_����=�;z�spJ���8��n~Q�+�`�6L}�a������&o8b��sj.��������)���Vf'�����p����\HN�M<b;���<���3��Wa}eu��"@�����A�$Y�����_�"9�s��F�h��(��2TcF���(�yC	a��u����+M�_@Ai/�*G�?����k���:��Q8��[��*��������5�H�G,]+���%�_��1���.0����6��J��Yo�-js
�U��}o���[	��H�`����c�b�f��C�E��4���){���M���[}_�]�]���"�6(�.����oS_L\�@��#�����c��b�"V�)�;�h�P����]�#��_���x*}�:�B	S�o����
���}G5*hb��(�Z�o,W����%<T~e�u�POx=�����i`��;�L��w����7<�~��f��'��>����o@NC"�t���q��l�T8����k���*������A�$&�L������ae?F�X��=S�q��7((YU�� ng�G'�3�3�������N�:Pe� ���A�>����F_���5,�:���Y�]����IC;����:"�L��!p����c�n��my%��wj
*sh;�������X~@���Cj��~�N
���L�Z����U-����&h�V�(�Q[��3j'�B��.���n-��n�%�3O�H�����n�Y���3�T��+�����-,n���	���&;���Y��G�3�E���C��YzT��\Uh~��_��"�m�'��6$���6d���:��l��Q�)��,��&Fh��r��@T����t^���b����������C:+�@
��l�)t�T��n5+���e^L�,���DuT��(�'oi�pG[��lV^g��R�3��#���k9��5�-0�����C6A�o�����q�t���\���K6�\��WM�f����C?���Ny����\�jW�C���%\�l���=��������.P�#�r���s
�u���b4_U�4X0w@N�H���"BA����"4p�H�
�%a�n����D�|�	M`,�/��������A��K��d���j&6�����V�v��/�u|�F��p�]�9:m4n%�!��Xi�n�6��S��=�������j|dd�y�o�Fu�-���[��X4��g_�����k/���k��� 	�y�h�W@��Kx�2�c��8���\�g��Hm�75�� ,������hn�w����7��e��V���Z��B6P�Zk<��JU0�D�R�7���d�p�M����
�U'��xH<����L�������qk��#�,h����IF��_$���jS�T�A�-m�Y��*�����Wq�<���>=���r�����B��&�m�M2ur?=@ZY������zQ������J�o�a�.%��
a��K�Y��{�N��U�sf�
����m{i����Y�E��%|!$��G�����V�E#aq���P������2W6"Li>�t2W��/��J{3Ac�es��?�1+/��x�k��6|�a�����Dz�������S����4@�m�R3�CG��������;$�i�8WnD:�R�[rF1�H����%���p�\�8X�@��|1��{}X�����|��	��a�u�&N����X�UGH�Z5��j���%��=���o
�d U�����V4���:'�*YV�Q�����u��i]�)��6�	8��4���d��j�cxNQ���_��O�
�=>w$���M~9�=�I�c���k���x�����O�~Ds+�k����"xJF�� !s0y��^����MF��>�u{N7�q%��9�o!9��OF�r����R�O���1���S��w�`�I<l�$�;������s��\���u>o��"/�R�.ag��`
T���J�^��)��^�)?f�4rQ���	)���@J�x9��3�;D�8���%��y#f�X�,wR�����H��fHq�q�^��\&��L	��>g�dy�W.J�u��F�K��La�1kL����'���-�@U<(���>�2Y1��H��.o�d�qE�L=a'2S?9����Q����c]_%y+������&�&��=�*�u�m���Gp����iq]}+\���;��z�����#tnt@8q���&p�������}��*�j�)[����0cD�<��
�����x�KH��!�A��:h��q:B�2�����g�W!��1��>�J�i����e�����o����-�(����;��H��TU[g>	�>�/�|g'W�;�����r�������RA�� g����,�*��D�0v�!v��������~��D^J�P[
P�e�1���h4W�C��.�=�\�Z�jqBX���M�e�G#�X[�������FE�h&S=�Dn���3'G��@���g��n�L/T�
��+�;���+�lpK{�d��(��e
aIzMG(W�a5:*��s"LJi0}U;)%�p�r�P.��/Gr�I�R�s��c���^?M���]�fJ�I�R%9���XM����:�i�
�|���2B������O�Y�b�.��X*�c�nB�Zqv
�-lW�9.R�gN�)���A�#L<������K�T��[I�t�F�I>[$^��pK0@_������e��5G6��>�K�����qV��a����
j�`:���0��m��!!� �kFp�3�MK�����9���~�A����W��k$	i�u"�j��_�l>z�#�(�x��)< 	��I����	.�������| 0���d4��YYn���Uc�|,���t������5�� ��n7�H��ypL�&?\�)1�T[k#���v�S�\C�\������k������i�@O�O�������>������BV����.����5[������q�I���s|��y���o/�'..j�O��Z�)�{,��<�6��0`4��Mt�7�h3�`d����!��s�b�~'>��f�o�
g�~A�/~�z���7)�0�L��~a&F��:p���V��0���0�|�����ZPpmm�������~�T�� ��YA2���sQ�zt!����<�a�9)*�4���@l�N����Ph���1����k�����V����Y��W��F�����L�����EQsd��	���
��a���u��)-�R�*(���J�~+Zu��uma���������+�~���dn�Y� �u���%�~��GTQ^<�r��SPZT�����.���5����l/�'@����cj��'@��|nm}H��/�����*E����������H� ��B�T�YL����s�)���?x��bJ�(�G�e��6���������/�UL�75���\�h�m�,�fr�>��J,v�*d>	.?��(���@�����5P-�8��S�0���(j?_�J�fbjw	����=Ps;y� �����	3��@��pH��|B�(��W���
���9)���<e���q�LB���QV]
������nA&6�:��q��#���*������:T�:Wa:s9=i�N
�B�W>m�'T�q_P�*�!o�Y�A�H��FM�����ZY��������F����M�j�!E���s��|���.���
�@����uP��0|A&�+Q'9�IC�W�mQ�D�yb�����L�/��^d��(���}��f6V�r����������vu�"�c�)@���N��9_��U�xV�9��g�Z�|��X��^�������Q&a
C[q��\�&��,����h��{�zq�v���������0��L;!:�Q�'�w�r>�w)__���WM��\�����Q���&����5���
K�oo�s��N-�:�$��P��������3A�����;1�-�	J����]�n?d0�y}kc$d�E��.���Us����UE�3�������Ke��,�����~w�A����|h7���������nY�"�(W_J�vu�2.�Q{.3�U�'	/����_��&)[��y�����Hi~y������,UQ�Fq���6����Z�,Q���M���5	E��'�X�5���Jw"�d"�XK&(�k�F;zm�����x�}o��������p��LU	o�~��W����8�������������;g��U��u��}N��R�X��`tB���!!�(�) �E�b3+����(��Ut�K�mV3IG�%�{��P��f���M�[v|9���^�IdH@��T�T��9��Q��E>X]���f��vi0���L�������;~{��I��&��$��A�n;���������-���*����~k&�O�~n�5e����N�8L��Y�,��C�7����,W%�)���(m���*�����XC�cAN|*�r$����{���>z�H���#�3�J�}��0�I����U[�V�����T�K��Vwl� :�p��c�-~�������c8�����IX�*��u)dI���7�&�E���a~��!7c��j�%����xa�����f�����3�nm�Z#A�����eq6��.V���u���O����Kg��C&�������j�����^o�}�VS	���rV�2B�0R��J����Qf��X� �)i��9b�qf�{��,A��w����uE������w���d�
g�}��R'�$���*�O}�djk���IY�RHn���<�G�{���k��8lzF����n�ur���w����������=��+�?�����p�������{5�.�\���m4#b676W`��F1��
Et"-V��������4dq�.���5��*NZ��Oz��P�8C���&�g���l?����SW�r�
Fd�
�I��.��PM&)p����TS�~Mz�b���b��nNJ_P�/g��e��t��%9���4�YOdKOf~��@�8����Z�O
��6�N���ZL\yc5��%�;���T�s����;GT���'������RtE�����T��"�������%D�-C���e��\���/��|�B29C����S!2��	���,s =�.���P*�u���m���c"g7��Li0�X�f�X��S���{�Qu��i��J�[�_���bH�GA��6�2=���N�.�Y�(	#���5FS�E/�����.�U�=��>S��
�0���x����|��J����
b����
S���Gm�+@R�����)v�k3������%���y�(�0�?������Q�X�{����Ib���&����1����BA��� �����5%�tZ�+Mm=]�EN��It�
����m��e"u���.�T�a��zL�`�Yt���������X)��d x��#�=lWU�8jSq����J`�N��N�y}|����F2JT�%=j�{����o4P��+�|�T�L�*8l����E]��#N=��
k�j�J�bn8��b�R�L-������1�}-.������/>�n�p�/�o����1T������(1�jJ9������\��/��X}��>��>Xp��:�!�JR�r�v\��K��������m�n��iuC���gE�K.	W��j�p�2^����U����M�&V`A���BR������K?X�1�'����Y�M(v�`~KiXH�VN��f��_���|m��$:��R���G��K��7M��3�V�����Y2�?k'���A��/�L�DC�i�6�	�|�M`&X?>n ������S�/����J��Xq��'j������E�@+>��
�b^����s��f����r�
|����?/-	�Gh�@J�))EH��Q�E�zw�I��Z�]�q(���)���P��[��=%�R��E�&�P������!4���b�+V�)E�|�n�V:3'�$��R_�L/��YG��
�F����yv������G��R�Hk8tB�X*X2(u����
f�����?�6n�;8u�'S��PI�Z�9�l�c}5�b�K�*�K?���I���hOb�=l�K��"��I��=��@��A��Q��Q��=����ln�%w�|E\����40)����T��6�(��s�7�p�fR�l�H)�%r@�p{{�G�a������(ftU;C{>o;��b�P�-��L/
�/\�)(�4�s�-����QEu��W�G��t$�~~K��0�l��+��G�����.����z��]�!�
.v8�ec���l���u���.���j����$d���|�v��|�0���t�M��H:�L!rIpU�����I��F���^�����m�����~P'��4Z���i���M��GE�p6H���>��bA�A�)� 5�W��/�0��xt$s1��u��I���=+n=���Tt�*�������0������Y�AW����m�'4���*����Th>Q�I�ad<u�;��v�R���=�K�G�������5�]P?����<$������^�ZH.3�1��C.�:�����q��HJJ�vgK9�lP0��q��S6@����l�]��x��|V�^�����|#�M�����C5����Lu����"�)g�s�}Q1P��\q�f���N��Q>j���t4��K�z%�abD,#�������������������'u�������6V67=]A��k+�(�Q���G��7X�W��PTQ�Y�RUR����$]�}�"�����<�o�L�����x6c_��Z�9h���0!�v����gAq�^�3���j���QLN����?�z���=�AS�G/i�*ta�E��*v���(@��<��H	9�l����-;�_ �����P������������J9�����"�(��������^d,�Ti�����-Ia��8�`�62r�?c��6�n��3���i[#<�W�A�]��Y�Sg��86����E�����3zV1��2]'m�������D:Y�_�^��24u�t�}�fF����Qm�����Cx�i Z"�H|�[Q�����
�����33�%��+@��/�gI�@�K|�������PM��@?�L��M>�%��3��������T�E{�zI��bX�W����!"H�{��Z.��'��5W� W��33��T�La��/�gH��g�x����}��y���C�P{iB���Oq�G
PVqE�C�����d��(�����Tv��YX�Q\�g��U^Z6�x���x�I%�`Y�N��N&�K�0��bZ`I���1P����!�����<S'���$�������H��6'+���m eM��g/����a����9eTF�FY���Tn�����R(U��F{�J��B��G7�/�lt�&d�F���7�01
���i�]_rTJI�x��|�0"�HS����u�)�����#���TQbWi6��	��0�X�d"�e��H���UP�)�N�-���K*�$n�l�����r���]Y�~T�����T���"`�|Xb<�;�����R���S>�%���~^cF�������'�c�u)FH�@H*�Y�t��(��Vb�t��8��T���AQ-�'a�l����:�O�&����p5�u����������<�Z����Be�1�xP��k8Q� ZN;q�9��P�[7�R�[��nt���D��4����F�WEsMg ��o���T��E�,��\��h��M`����x�H�#���fHx���e��P��O���K�CE�pl[x���%�(�
���Sl;���$�tN�Sz��T��Y�I���'2_��� �������A n[�a�>�1R�;�5��J�_���'Th�;��C0Y�����\�b�%
�������aDv�E�i<�����������Bz��_L��r0�g��[����x�+�%�&q�w�GdG��d
H��
{<���]����n�Us��
��7@���#tgd6E-�k��(L*���
q:�q�J��5���r�%>Bq�DJ����q��k��(�=z�2�xXx�1� �����SuHE2��*��7�*�r���<��`���}�����h�_#u���%�"=�|;�����5���ZzI��^<���I�� ;����6��������8��V������e���,n�v��sA��h��\d�C	������p�[V�pm��X�����VlfU���7� ��(���A��D������^Ru�us�� ���4��e;iX��v/24��1%�I����(��2��>� �NG�fL�����Y\]�&�B�?:wH����4�"7��b�4?*�#�k^1%e��)�(
�me�� vE~J���h��*4=nh����Y��)J���D�Vd���������� �v���}#�[H��-�P<���P�
�J���K,����1 }���*F+�EY�J"*�7�]��T=�+��uH	��r���ZX22v����foH��l2f�(J�u��#������O��R06bu[k|��('��R��S)�g���A���t��T���NW�(�$��hm����P>���Os��1}���$x�����_��&~���oa{����B����04s=,1b��A���k����b/�+S����!R���
?�;h5�k�A�%�t����y�f<�����M�1@j����������������I�9���I�0�<[�$�Q_[	uS�c��,�*?�Q�rAI���,���v0#;�O��� xwAP��6	��K:��JdT��N���Bvhb�������LE�52����:��V�a�56��%�m
����8���M���=R�K��F���+mk�X������)Fcv�x�A�18��u��:�
���v�#�(���DG�	c�(��2��&^��
sq?�2GU��X����������P)��S�
�����At�^���|�	U/��DJAY��*Z�W���I'�oL)PZ6*B�,#�Aoj�1Qx|�v�*S�~f�����m����1+nt�tc�� wn��*����F�h�W���[��	2r,*H�#b��U]�7xx��Vb�SG3f���V�n�2#u\6����2G
���5>��XYi���F��w�bv���K��+�Z�u����5���ufE����|�McM�\l3��h����/_M�nt}z�������(7d���$�q��w7��T�)��m|e����.�}��D5��.-�����l����X�2
Pa;�oR,D�bE ��.�?Le-�.4�8k����=j���YwubZ�����E�>
P�
���["-��Fc
H�Z]1O����g*�W��|�D1JV�r�6^���N�^�������*#����;��wEKgc���~2�����z2���?&m
D�7#~sd'����F�V�9����+�
�Y����s�}w�sHY>K]��D�����>"���7��Vp����U���
���V��6{�g�h{�������c��:`�FI��W�j�������	e��m��{�m�������0����"k��9��,.��y�u{��NpG����L���`r��R�(~Zi������-R��(y�� R�f�Y�3"��]��~�Zq���
��s{����:��t~b�JGQ�N��dP�g��{�����������6r������5C`��,�P$j��q��!�t��+��������A^����<"|��g�e��[�Z�8���Oywm�K-:��	�������j��[�{��Z�d��*0�X��[�\	{&���gZ�T��`���x����t��sI�R	xi9�1�9��d?[!Y�}o4lZ�h{��~�����k;�@�}�e&�^Lkn�C�gc<��5��ln����R�������'G;o�kp�\����^[)���������^���IG��p\�^�UDUHI���Ja���jG�P���j����������Zf��gt�A����v�~�Xw+%�����2���s������r	���/�vH;8����4��\A�@)#s��C�Q���b`��m�Gr���l���)�s�R�j#�c�Z{idq�W���A������>}9L:����Q���������C�	�@�������&Mc��]T�M>�$�Y2�.�_����/9�;h�;����:�n�7��{;�N���y��������������&�_�q��=(���b:��G�r���i�^!�qF�
S3`�����u^��
�Z��|@w]}:��Pvuee=\]����=:iO3A��a�~S�f(��e]9;k��s�`�����X��2(�%&�����.�A��[kZ��Dy������c?�c;������A��h���������������
�����M���)XR�k��Q/�����Ty����u��[W��%���?@�S�V��U@�i��AI���B.�/�&f������	!a�)
�h�X"]G	"s��rk�����+7��N�SE���.��7x�\�InOY����%������a$n�)��}�:��I�#�!T-B �Y�W�J���;
��=��_h��1������1f���l�o�[�|_Zi���8-���������5��C����D�]1�����<�'��|	?33K���2g���j�������&�L��
�"����U�l���������E+��o�n\��R�z����H�T�&jS�8]uP�u����g��-��=D[���f�����- �+J�*2tH����'����
�f�Q*<�R55��A�����!v�i����"���TZHJm����T����7��O���@j'����QWJQ=l����b|��i�=�T�����i����|�=|�f��d���.9������{y��X��p���>��m���8�fyU��f=�$��3��D�sY,��$~���)����L}��'[tl�W|�Gz
��R������s�@��S����i�5g������Y'c7�,���j�`��4e��3;�;�?�2��Y����L����g�sw��D��H<�c|h��\
7�b����s�������q�j�����������������Hf�z�Q�����S/�|&�����2H������x�Hn2P� ��T�c�"�-9��;mxP� ]�W�����a���=la��U�Y�jAg7�R��R��@���W��9/��r`������������|{��3�2��
�2�������f�6������u�^��qv�*�[���_Y���I�\H�Xu#u����J���d���x}x�������2�J9������
�L���ng1��\���J����X�ELR�<����I�B�v�������=��Qz�;�tK@�*�(���-�v�������MV�S�	���x��3�9}�s���2}������CH�o��%3:�����~�e���2l#
b�EL��V8�)����^M���c�5�3:C��?ln`��M1m��,�@��a���Q�k���o���_�I�������A�-��1����r���+�6J��Q���JQ�4��l��a;�gU�c��5�$#ix�#D��@2F?K�ePmO�:�U*�;fm`��M��v*����rWVVC�g����"Z���,/����e}�$��@K'M?��u;4��	B	��V�EP�P`�.LK�s��h������K��C�R��4���7�����XnV�k,�������
����I�\���������
!����H������Aj�$������*-MFz� ��zCZ/vp-�}@~V���.9ia?��h0�(��Z���B`��l�;��.�`�E`Z��Yw�;T'On�tC�<FBe�������T[�>k��� t{�*%�t�����V(���U�}h�����uK���y�����������c$���~�o*gk����3P���t����(��=���2P���hN�/F@@�e�X���{��j(3����kej�W?_F�w>���t�<��WW0h\��S/��n#E����Y�c�I�(|�T��-���5g�F���w?=��9�9:}��w���QK�Y�W ���0�-����C�����B�J�zFW6�V��Qv�� ��\��F����
�[���"�'t���:���08#\0�=|�'k�{��_��q�8@MY������c|��Oq���W���� ����� /4��y�r����ApJ�O���y��3���{�������!��3�@���Xc�G9�'��"RL�-�Q6�!�UW�[o��yy�U������%�z���~�}Nw��m��������o
���&�����0��V2���.���#�r�"�����js��?���a�`����9���8���/��S������q��)����{������u�s����%�/�:"�J���q�>E��|@�F!�������q�.S����b�{�<�[.���k��T�YPwi�1>�X]���+b�?��.Rh������S�����
��F� F_T�s�k8��|T[mA��w\�TG{����5��n�[�-��]9��=��}'����o��KK����R��w���0��3.�����TE���Q��b���w����h��O�
�������v������L4�����Ke$�\s�cRD��wv:���6�8�r�������1�Qj6�}&��$[��2|XH<��m��[��]����{_c�P������]�*�Rw/
�NLeU
M�T��?�)���p���s����-BHl�����C�Rx�nz���tuy�fG�pO�n�:��`�Gd���;m �����>r�1gi����P��@���|IDFdpa�B.um����C ���J^�����S����|/�^�|p�]����i��r
��v�s�Zr�
���Ol|�rV����*=|�Kr������������������yT��n~Q�;�;��=)S?�4������ht���7y��3�d����w���D���/�_<M��g)��?��r�>#�$<��az}����T)�>�4K1Q�T�y�	����[��OnE��&���~���t�@�'K��T��/`h�:<�X���� ������%	g������
��8vO�_�j)v�:r`�f8RF�c��P3��C����|><?OZ	�b���'?<��`�p�K���~����Q4����{����|x��y��652���E�s������.�{���?�#��������(�Rv��b@�a�=�����>	p���6��>
l��v�?P.0<���H�#99���c��z�L�T|��z�������d��BA��%�q�bg���r���[h�dws��E�'ZS�����	����P��)�9���y���~,&!`&������D��B���H�'�9B� �?���\�*�S#/�jf-������ 14�D#.{���/���o�"�5|��w���{C����.�3V�d6��q�#F<C����M�	���>��Ew(���� C���'#c~r(��|T��K����������?9�=�2�v0�FOVc1�z_�\����J����lVG&���.��_[�(<*Z����'��T���p<
o
��kX��l*b��/$���a�m�9���RF��_L����B�S����8��X�z��BHPF�2�3����&"���D��]`�9@Ex������2wk[�����pZ6���+l�8��Xm�>N��5�;0�����������������<�3��B�OT�!������hZP	������Eq��w1���_;������D`��8+��������_���{G��KVSK���K2"6�f$����I]�q:���rA������{T������D+��������f�Xm<1�qP���m>�����3(k�i�e}iUWS�B�-��?��Mb��@sA
+��s;�)���y��,?��pk<�������:���P"�B���)����}�Xp���3���� E�q�T�WJ�d��5{�`�"Ua@��X��V���� ���=�G��	����+pk,��s�wX���0ZluK�'�[�H�5����AZY#�e�<��$#�=Q0�@$�� _�>��9:E�`�a�d����!��P%Tu��JAG:W�V�I�(����@��D��N��o�U'H�
����X6
.����M��3uVS5����i�����}{����Ip��r���	�,�Z������������k���<��\Z��M3�Xb�a!��q�>�,��`��������?��Q�,�5��TH����*���'`�r��8e5��$��)�Y����){)���J�l��X���� ���!�i]BgL����P"mW�6�����IZ������y����XF�����������Zd���%���
�?��,��Mt���������-V��l��~�3W
L��h1��������6c��21���3/�c���QF	��E����9��Ep^c����=��-�p�5���J��Eu�n�������M�������O�����E���Qbg1zz�����c������\:9�y�~L������?XN]�jz�s�5�����[���!��{��Pc����N��������?�v�����S,�\������T�g9�n�?��_�h���� �N{)������Q�W���B�����4�������=.Lr�e
��W��N��d�NE�R�P��X_7�N��q�<�7j��k��*p�u�z�m�,sj��Z�Fv����FaA��_)^K��������j;T����~!�����i&��0w>�
zT�A�x*-�z#W������������c��G������ox�R�����g������y��G�G8M� /��}�a9����|~�r{���qD�;Y_[Y
�ku)�����Q����)��<1IHh���m��vS��Q��B���'t���I|r�3��=C��2�he�:[��bkL�e��L��#Ea�(�+So+��"���oUl�6�_Fmn*�(:�)T��7��"���u�������q�m>l]J\�U��31�?h�+�y�Z!4�]3��
g?�����T�nUU=�6���(
^�S.��l}j�,t�� 2��e
�:+W�P'��F�Gq�i����&�K�����\��	�����(X���t��H,)z�A|���9x��|����Y���,F7����5 �HD�=���x��}4�H�>3S>������OznD��8��Z]��R;&�\�f��#U���(���"]E�������%��1n��cZ[���vY���$1���������(_W)\avt����U��(��:�!	��YA�u�?�f�`���t����g��!�o�����
���-�g��3��k��������6�0�/�2��^t~��,��Y�%� ��Cc9�4�e.���XcV\�W����1���{C0?��@�{s���u���7����(BL�Z�5��"\�v��"��������A�3?as����;B������]��y�5�>�u��B�8~�V?[!0���A���dDl�,�D�����Xclw�K~�'v����s��������Nk<����5aVSlq��*[�A�E�r�{�-���R����X9�w�W�[�jsS��Q�=��q�Q�d�*�i�����pP#�cB-}%Vi�\�~o������N��v������������?��<*S	c�b�{��6�F�X��8�t�������������K�����/	F�+�^BE8���;��-�K}@���M��������p�t����*x�%j=J���<���Vr����7J9<�|�,~�>������������������i�j���������$UW�|�o��v������uR�����&e^$��bO,�P���ZcK0�ll:mu%�4������P��?���E�++_Sh�g�0�$�����iJ�gW����1w��~���������r%��m3�$�U<B'7g�Pw�c6�<9'��M�m���Ue��j�x���u��d<�z�M�+��d��9�I�,&�UL�����I{�l����?����F$����4��S����]1�vblz��[�o�W��\�/���>S\����ra���Yr*����Fz2��uq�����)�����w?��������������U.l�Am=�:���V��A�U
��?Q�����R�:�N��6m��v�����s<3W;�"]���<���#�"����#���4������D�C\�h��}5��(�`��%���[��2�'���H��
(/���Tv�x����E�Bn�X���T��%a�<_������>:|�=�{�����wz����"&�{�b�j���B9*�X��>�`p^��PA�<�-N�V��9�����k�_��%k��keO.S!�>������V�)���`-�{0X��
����(��E��>G����t�\���3w$���{���:�'����},--�j�~U�H��r�����32�
W���r�))$�
��,��i��KD�6����"�������-��`�f��
�}T�x�����B8�j�Tx�>��Y%���i�������NqS������ySW��
=s6<��"m2wd��1��?^ ���N&���}���
/�}�or�[��e��Z�	`X����f�B<�V}��X7V%\f�O�}8}���5m gziY�������9�E7�;���Fc@����/����Zk�pc}�.�5�M�?Q������l��l6��8���#Q�^}x�Yo�����tz)����������z]�b�Sj��(E��Vs,i;�U���#U�Q�Y���p��rW����m*]�^A�E��J��z�L��"��9�$�PW8w����I� �`x����}�X��,V�x�f�t��
>�GBA�1�7~@�p���6��i��L5��3��5��k��P�\id����o��U>�����6������������Zm��O���[[[������lmm����������V�������2�Fk�E�#=�b�����Y}���T�d'F�?�p���/b��bx}����jT?9Q�0N�Ma����+�$�*$�"�#����/����&������(���_� �o4W��������fX�\Y��4����Ja������h�a'�����Y�|H�hn����X�Rwl8����t�-9i�s�S�S_�g�������k���YMJ;I7��C����s&���o�j�������JB��Z�*+!�����&�7P){V�4��p�bv������x7�aw�r��U������8.�
Mde�#�x�<1���4k�4L��ha������������}�������U�?YQI�V<�$o�x�[��`P"(BJ�x|��Y���k;M�p�XD(�yk��y ���-���M�9��J:�{d��$5Q�n��)��9]"~�S����o��w\Q1*�:�{��R���#�����*���;��X�������T������S��&�E���Zy^�K���==��m��S5�X�$���8���
@}1c���t�tN5�A�Z���L�s�s��aA���Q:A?�oa����t �RR��g�E���
x��%�����&zCl�c��s�6�e0�� ����F�
�yKOyJk���q(��!j�7� �����KKap��j^�RK{�{���3�*K��]���Yo��`i�1��|����m����7�"����`�q^�����"�*���#��#o�� Dr�X"�$D����q�'���ae7BwPwo]��V ���0�<�N(�\a]v ��E>��"d��>�Kv/Y.H�S�<��dB�Ln��S���2>x���vp����IX~���;�Z��V��z�y*3bW������`U�z�3�BP���=�����������q��~|*�S��u@��?�}���l��U@�5�Y�R;�6�v���6������.��.5	�2�|��1�$��b��I�-~yH��f�)����
�C�*�[�9A���
.���U���\����A��j��
^I���N-$vZ���5����{��,j
S
Po��p��]�j�_���&�qT[e����a�i�����������G����%��W����5� mZmq��*����^:vm�b���{����Fj�%��
F�i��-���Q�,[�BW
k��lI���b�gk��a���*x���4N�@i�?��g��6c7�+���2�[T��B#��r��q�����0�@M�8��+{�:���UI���w��/X�:��6�7���'��&G��!���*�hOP�V���[�/d��$���-���bJ�b��������I�<Ik��������3������9���_o��_p������f�E��:�a�����"��\9���n�PeP��.76EC��o�A��l�fA�
O(�l�~r�[��}?�W�G������A�9K[I{��X��cu��6��V������
��LK��@O{�UQ:�(�����P���dV��j���X��G��H��y�
j�8"B�����7A
���9������T��t����p���W�d�<~���8m����3l��|Z��U�J:*��z���������M@h�Q��
���j>m��XF���H�w8����s��t@mJ6�������{<�7?�����/����`� �4�"�=�:�n���k��3u�
5���8?�;��+����{�b]���/!��� g�c�*�x����m����5�T9g����Y�5�W�kn�.���;�}3�������f�]����w'���5\�{�VN���g3S��:G���3�H��v~�$��&�����.�WV7F��:�1�0��M�Zuf�q�9��k8	$�%�iS�r�;m9���5�d�X��W�_{���s�;�(7r	l�6%{�g�d}XnQ�O��^
��667�ZL`n/x�r��Jl�		�p'JU�4������U��IWin�^$6�v�{8*1{32tn�����W\`�:a�r��Bn'���+�M����{"���1�������I'b�W����6�g�`�
���m@K7
��,B7��t��-�pu��d�����u|�+1��L�����e������fe�?]=��Rz�����0��PJ#D��U����H���;^��Zx��	��I3�l�Y�e�(����E�AE�
z����b�b���sJ��f�Zw��K�O���S�K%�<�����F$���Sl�����!������x�+)����x������*��M��(��
�t����E�����	���F3\[k>|:���AT{!S�*~U��������#�=��[�1,�H�r���B{��������R�����*��t�0[�m������5���b���>�������OB1�Lo}%�0B��tF��z��:���u���c���l/rJC?�������+�zc��`.Z���k����R�Og-Z}d�$�<-k���F��V	u����j��m�?���A��Mt�2�����b4�5Z���*�}�G;�M)���~nV���|R�S��*�P������p�d��fp����SL���#�E�_��q�(e�q�7��jml��]���~\CV�5,�P_�!�Z4��+���v)�����2�G�k3v���|'R:�On����&R��JT�&QNT�_5�9Lw��drn�������8��(��kase��XI+�LR���t��K��Cz�����c�:��Lo��L���u��#h�B��SZ������SR�
+��������D����K����ON�����=���	����e�������d���������t�:C��u���4Zh��9�F7�p����[��:������:�y�����N�\�a5W�w�U��*�7�[�����%�������p���B���G���}���I��Do����Z'���=����K���)[��H�|O�����m�5����S�p�s
6�Kb�0�V����TEp���O��71Yr�O��4,������g��s�Tlmma���?����\�[�&�[h���53�Ei��&���|��������F�Ud��o��_lH�D}���u�����#X'��d�&r���uk��Y&pcVE&�]���l|.�J�L����4�u�-V�L�����%��q|����������5.����m��h��h6��v_h����	S���_��m6���c��K��Hd\��~)�����PBe�� ��A�3l������D�?�a��//�}|����
�����M}
���	�=��+v��L��kr[�mT�zs���U?:0G������������ku@��J���L���TS�^�+L`�Q�mYu�=�)��-����0(W�m~|YIe�=r1E/a>���w��t�hl��N������)����A@��@p@��ee����n�r(Fp�i�9`P��L�~�1������������q������������k����Z.jE]�z�v�y���k�^ti�W{{'{���K���K���Y*�M�U�l���N�D=�������i��x/(�M��A��
��p��M��*L~��m7;9����-L��E?����B6���Q���/�������� ��N�[+a�4F��z#���P�'��T��{��366�L���D�V���nIx#�bF�^Vx���;J��������F�[}�-���%�m z��C<���d����^qoL��V��r,~��|�@W�g�,��#��?�����X���um�6��������o�r}�a�.��������+{�����@_�L�L�w�%R�I�+;\nm�i��3����m�)mgx����b\���)Xs
���J��](�����u��c>����wq_6�[�VF�L�.�e�������$:��\�!�x
x�Z�aa_����{�J&�����g�I-����)w,/&��[0�+V2�Kz���������'L�yU��1���Z��D=��,��6������0��eBb������9|�,�r�s��c��x����{3b�@�+�v����8!��S�\�Y��Yf�BE%)�3d_��WiRp����2��$E9�--8� ����K@P�����2q�0
Wla���(K2|��z�_b�����P������Y�a\%9�6|�m�gF���t�=����>��e������c�>{���z��F������y�IQ���V ���s#��C�B������>X2��f�����U4�?4�����Io�q
7;\w���q�����������������V�.�,x�����Q��!5n���U;���O-�/�����/���K�����Z���`�N�Z���#���,�C��g����/���O[���^��M��a��AP�!��t�E������������Ua�C�3�
�@|��L7j5�X������0�;�<�>����_?�����cW���e���MrPk)���`9�������d������6V��ace#l4�`e/�9���]6���"`\����si�^�� ����=a�M�k���V�&��)���� ���C��������Tc��~;9�� #0�im�������$�.R6�@
��#o���������uc��d'�M�O�`qw��������Gi"�RS����k��u���~0�%S@�
/u
��R
�n��A����`-gX��R�rA� �C3���D�`*T�]���F��r�^����pu�
G44���l�����@.�Z�q���4�:����^|= �.4�������i5	/)F��b{�
{-VA���\���LX�>�����o�w��7�\&�I���^��O�����|g����U�������-�A�A�\�oy��s�XY7�4l�=��2J�%��u�
��po����<�����E"�G��
J�:>x�nY�qa�+1��O"~���+\.�79���)_��z��z�WNYY�h�U����"�}LA���~�a"���`1
a�x��8�S|eq���ap�����I�	����.)aC�,��{�{�	�
���;��1�)�a[�e�����r�A���P�&b5��.����~r,r���?�Y�������*���b[c��g�V�
@��������E���"���������,���wK2��C�^�3l��Q��c�T���ti�U#��V�������>h^����p�_;�)��x~
��Y��m���7���d A�<����@�t>���e��G������m5%K�[���v�.�E�]�S�5��t�}9I��Mq�r��@s<�;��=	^?��=8�q�hOtY�����sX~�;����#l^I=�(�%L�e�Z�}�o�@���=�a�L�*�m&�	+�/g��Z*gV��9��
R�=���X����H��{���ZX��N���
�F�������`��l��q��o;G���:�qff�zee������c�%hX�J��� ��l8�k�<�Fq���'{Gowd�55��8�E�Wn����K���������+n
A�s�Hj|}�\_Ax�1J:�zQ�|�����]�h�P��ic��I4���AK��9��Mp����1y�����N�>sPM�,OB�����Y��,9Q
g
��:W�w|���c���D��W�`�������I{fgj�����|�����fJ�����AZ,���=�p�k��$*�}��������E:V��v/��R}���A���vQ�s��|O�9>��
����8%G.�|K����^1@�?d(h
{ ���n�g�,�!���,.�x�r��x��r�����Z���Y_=/Ckn�
���N����������A_���uP����n�������S,S���`��X�9�+A������y�������z�_��Pg���������r�`�M���ba?jL!P����.
�������=��)�h�M�����������=G��]�cR(�:�5D
�9����� ��Ze7�y'�E�h3������_�>�KA�������J�h�x���q�d�ks�Z�/��Jq���)Nh�X������P�0�"�P�L����#�:�X��H��H�y>|�o��R�4&Y��zD���V������b�#=�;���#Ql��3W����n!
Yn�����<P3Pnk�����7�'�������&��"��&z��AV\�<�G���-��P4�v����M�{iR��1��N���v����u�D�t��~6PO��Zn*�n-�~�p��B�e�Q��y;�:���u�&�xip��X��EP�N���A0_K�������Q�M�y�����L�S���	���*k�Q���tH$5���v�,
>I�W��t(�=i��Dl5N+u��*�� ���#��xE+��D�|� x�H���WG��15��<��0(���f\��w�b�3���y� ���Y�/�<!���$��n�Y��	�Eg�* !�������Gb��7O�o�E`�E��M�a�1K4�.��'������"�W��f���"8x�|A}��Tw�G6���F�2�9��C�����~t3�RK;N~��q�o�������Z��08�|����B��S0�%�Po����+2�����oV�7��nY){da�8����l��g�$/T�7�T���c���	�t����syP��NTi���O���s�,�j��3��kZ-
e�Q|3�5�^*�9{�v)�9
�%<G�����Z�4pI�C�[�dI�u"I��r��V�����A'���Y�tca	�6��ST�a����.q`��j�kxhx��F�5���5�Y����+���#���7h�����Fa�����Ln@�����-���=����-�**�
�::�<a��1��bv�Q�V��?R�"s�� e&���or�M�<p�1�����a^��Q���Zh����P�gIN���m}v���H���9���8�=P�����-�	������\�u��UP'���z�,��P��`�g������h��I)��f��j�����b�MN���5�
�S�I�v����d���,�x-�A���7���q��W��\���7p~[���Z3��aO"��J���]�~�y=5Z�)���}
/�N�h����-8N>�g)��`Ai�k]���� �q#*-�_��`N&����9W4�T�}������`e�"���,�\�w�wR���'�c4�e��g��)����E��Z�}�'����j��U��G>�1X:��l��>���0���VE����f���["C(m���pR�yr�Ip�r'/=��.O�0��:��}�������-�V��������p�����_��;:��i���{�r���oTT�Z��9��`��������2�c���@,�D/qx�	���	<y;4p�1���6��v���%
0��S����c2��(��P����iOf���X	���l��G���R��1B{��x�,�#�������A;�A:��L��1]��S��vV�GY����<[�.UY5�~�!���"|�m���b�1������x��gt���(�g�X�U�,P�y��A�P$���cB��i��e0V�ze%�����+�0#�1
�{��o��i�1k8���0h�iZA���������
mJ�ja��\O�{q"y�HiE�����?8��>��8gck�Q�X]���,#�-�-,3+��&�]T��Ly|e��m�����~��d)�3�F*�ZJ]���Dg�IJH����i{>��D��_H����Q����������{����N~v��v�n)Y�Mt�7I��-��]�JWq��� �&����;OX+��^L����0:z���>P��sj��g:['���T^��^D�1��I���_��L�����Np�Qg������N-�;������f@������x�>{F�/������!>y��P�-�{��ay ��o`.�P�O}A����W�@����x}��~��)]�S�E~�~��)/���P�����U�5�"7	��Opq�	�JA���K)�=t��d�����LF3��X.X���e1&��K�Dd&8y�8�{�T�rJ�|��F#��*������c�u|9�����a�k4;QC��o��4�yLM�����(��Y9E�]��N�t�p���CC;�Y�6q�3n9z��R�o�&=y������c|.'a
-�a�"�r����r�#IH����xe���T/3��u��FOu��5�]$i�-�������:K3\[��,��G	7����C�H"�e��ea��_q��^������P�u�|'���I}I�B�W����5�c�T�?�O��T�}e��J��3,�'��L�=�O�QY�=�+C����[��NS��0��cD��;����g�oq�Z���h���^rMu@�3L��Z���$��t0�?Tee�v���7�|n���=��,��q�~�"xy���V������(����������7Q�!X��<�,i�d�#�m�-k���h��/�P��e~en�� ��,�)-����a������pG�Q�������,�QG&���&��������e�%�<<Jp
G���F3�k��Ar����}�@�u{��z��� J:b'a�f7EC�2*��6E���������s@�,na��6���dN��Vgg��uJ�&����k�2���O�{�d������|�;���9��D�[_L~���l~~CF��TE�|��>Tv�,���������M�cX��2����:��P��4����<�O3�j�����2U������h�&��i��4TC����~�9Xo�F�1��H��h���F�c|���6�	wY��*�p����%�Q�T�{���>vU%��#8�|���6IB����:�t��J��%�!T�]K:�El`+�����������
ffj����^�� 2��"+y{�������L�A��}����]�_��P=������n�86��b\�{�:W	�zS�
�\����S�O���LV9��}�)��=��K��(���/rw�9��2�_���X��?w�&���k�j��d�-l�����z���5�0���e����ho�������QF�x)_��$�t���'{�*9��N,�$��0Ml���`�=��
�Qb2��Y.�v���Yk����k����&�Rxq���~��Js���C����U6�o���M����|�w�� ��1���C�)Ub���c�	<������c��������@%V�4F�=�4�+�L�3%��r�.��E�?Nj�4_�}��8 ���8�)O�7�����-C��C�unr-�����������Z�����(>��)��[��It13�����<F���j�h���AL1�����&��������/G/E[����������"h�7v���n9��)�x��f��/(WF97��������CP�Z�2�]��ah�l8�L�~�S�����`��+���
m}V�@��������|��*��#�KR^.CVw�K�#���F�l���_�^;����H����#!L�j3�J�&�fIii*4
�>G�j ��I���M��I��|
�U����T%=����e{8�-���t=�Ls��������jJR{�s��\V�v3�g������/��?�"������S�e� r1L�K�[��H���7�(��=�4�\eX
����)�u��q`���SI��S���`w:���2��`��%�TjA�[!/�������{�������LF���?8[41$�%�
V��dI2��%�������w�\ p-��T,�R�x��������#M;�zS���w6��b2���g���{�{+���������������gs.���-�5����
�~y�7����zc�Q��1j-�\_�r�����EY�	&�(c	`���	��;��hKae*��~\P���8�%�����|�����t�BXHR��>�s�;� ���c��������Ge��1�����h���e����W����#D�@o���L��<�������W�v�Z\F�d��$����5��gk��6e~��)����G��`���f��D��ut���r�f��'J��M;!������M��I������q�������^����U�+��������K������g^��QnN�)��!�Vh�|s$VP��2��eh�f��?��"p�;�J-�wQ�
<������s4�%��W�J4�|�K3GZ����D����i�nic�dQ>�������	�\�y��s�8_5�dIq��d��E�6�3a����>�����;�_H�n��f��{~�{�`=���c�z��y������"������+{���,O�6�L���b�8Y�cv��p�M���Py�T9�N�^��/�A
���}���/d�b���N��!�F�I����M)��l��F��#�i"�y�Zw��b���#��E�%��������v�����w��*���0����h�2s����g8H:�2\&/���z}�+ka}e��������J���,�Y�Tg��;������)����8��!o�C�!���O�6&��$�/�'���K3
&�s�e{�u�n�+��32�)�����H�'GB�DQe���T�4N.��8�0�
����Q�����������l��Q,�z~T@-��:�����t/�os
� 
*�E�
;�_��)�,F�lb(]vG�O�������i��a�6���)$�!�m�Ok�C���!T���Ozp�C���73���@~����|�`���a�.b��4� =��Dl�����C|0�2����C�e�X�L���M?K[*��n�o�� .�n0�������;�I��L��5�������h�EJJn:X�q�g��pX��|�1�������i��J����?��N��f�����t�"��;d�XQ9N,9Bc`R���D7�����,������w��8n����x|����w�%�������j������uM���w����N���^��!-)�����
<^�<��'�[��H��&�Z�	�tRU�AZn�h�;�o3�'��!��-�������k�Z�I������m3x��#���h�]�����V��n��
������F�te���F��7���ZCn(��%�&���x�6;9�wa	�$��R�e�0r�����y(���x�Y�y���6|�{@��W����ql@�{���&l����`7���Y1Z���F�& '���=
�Mp&���al��m����L��
7�J*��$��7�O��o�}0\q��@������k���������iO��>mL{��q4����J+oL��7�������*���(u��7�����v��z���{G�!��nTr��J�<���������pe�������zy�>�����O���15���Ex/z����NX��s(2�<��G?��Q��S�&?�	4�	�A��Q����p����=��d���s�9�za��g�&>��S������m�aj���q<]���4h�� $i�
���������"����P:�J����pW��$��������I��{��ZGPWJc���p*1��M�P;sV�Y�f(Es�fh��?����������Q���B��(|��������������<)�k�E�.��	K�����e�?�U.@�~���������t7�[�5������0���n���Y&A<�P��ivj0���t�s?����o����~ �f���&��R�T%��)ZMu���RO����uE[�i�	�����:�n�w�.��N����8�>#�'�'�'q"=�9|q"}�����i��?���_�HO��8������D��5D�Zq�D�G�����P�1G�eN[o�}P��^s��n
+�����7j��vN=e5��kKk8�x�i�=u��R��o����O���,.�����OX�B��8�`��O�w|J���;��\�?��uOl�����aZ���#\^��D����L��\n��{�NgZ�juv;TY�5��B�+5���*ar^��yl�A��������e013n}0M�I��$]��n$�6�Pv�[���Ns{��o�����>��G�I���<�AM�;��}P���������G�s�]�����?-�a�y���B��<(�z����-���qY�����6��.O��>�����2Md�m�G�����������[<�>����z�(����D�&����>�Z����O�>���pM�?F?�:�w�����������������:o�y�:?pp�@�y�"�U��{�?���JWE9���N�[����?��x����P�i������Cp���EJ��khQ�����/3��,L~#c\&����qk��� ����j�Ut},(�% h
@z��[r:�L�i9��x�z��(p��?�)����u����8���!��y;D(Z��������[1����4g�S����Ku����X���������$����H�mK��XN�y�j�`����M�S���@D|�^��
�����f���y� <����N����V�x<�D�e��4J�������L�m	��������F{��4��S?X�G�e���m/�8@�2��X�?JO���_�6�p+A�=��+[�����,`�h�o4�:�;��Rhqw�VAh���`����id�(<mB3�=e`�t��uq87��:e2t�<x�����`���`�d��mp������h��������=�u��uf����� M���������F�[��u�A�����?}�������0���\�Y@%Y�,��Yt���(�i�����;G{S]������	U��}A���B�<8:<8x����8d�G���)��OG��P��|��K4������b��'wg@~�gy��0��� ��b���O'A�{^�O	/3������=Qe8���4o]��a'��]�Xo��jX_��{��y��b����)�2��a<���"��`e������y���%x��7n���q���0z�.�[����$&d��i/���S�R����/2���<lxf����?�J����'�G��6�?T�	�]�	�t��Lr������?��zQ�C�k�C����3P��k��
,5����7������jmc������ 	�]s���������Mm��$��hw�=��|�����o��c����7���C�NId�m �xBz3�|�gi7\&��A+��K%|3`���G1��y6�Oz��:7�YlFg78E����tp	�R�fI���
�|���c`i
Z��������.?v����g�]�J;_��T^nt�vK����x��_k��[|����0	k�~4�GC�j��Yk���	���ik��������E����(������2K�.�����>e�k$8���W����4�O��������>5)G=5���&��~��#��}���	�*�q�����J��0]��Y��I��K>����[���|�����Or����?�-�K~�;>�	�E	�;�R�p��U���%�?�b�������0�zm��z��S3n��e4����$)�/�Ai:|=< ����,m���^�0��Wt0���gJf��[�Q���`����m����.�����J�����{Nk��/���@ �F�T>l�b�\i&=4qcr�,�?8�Z��p����A�{��V�]��]C�`:�������M��R��zx^�����YJ�#���Ae����������y�5R���P�k�����gB#��������%�}�/Az6��R���pR������%��@�aj����U������������?����<���~	w��gn�i�y��+�E�_h���p�����v�Tx��^��C�n�t6��B�n�r����D�A'��
CAb ��)�e�?�Oy�6��J�%�#4H�i!Xi���
�A'����O���4P�~�"��[���*��q�u�SQ�J�#[��&����2���Ymm31
#29Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Alvaro Herrera (#28)
Re: foreign key locks, 2nd attempt

On 15.01.2012 06:49, Alvaro Herrera wrote:

--- 164,178 ----
#define HEAP_HASVARWIDTH		0x0002	/* has variable-width attribute(s) */
#define HEAP_HASEXTERNAL		0x0004	/* has external stored attribute(s) */
#define HEAP_HASOID				0x0008	/* has an object-id field */
! #define HEAP_XMAX_KEYSHR_LOCK	0x0010	/* xmax is a key-shared locker */
#define HEAP_COMBOCID			0x0020	/* t_cid is a combo cid */
#define HEAP_XMAX_EXCL_LOCK		0x0040	/* xmax is exclusive locker */
! #define HEAP_XMAX_IS_NOT_UPDATE	0x0080	/* xmax, if valid, is only a locker.
! 										 * Note this is not set unless
! 										 * XMAX_IS_MULTI is also set. */
!
! #define HEAP_LOCK_BITS	(HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_IS_NOT_UPDATE | \
! 						 HEAP_XMAX_KEYSHR_LOCK)
#define HEAP_XMIN_COMMITTED		0x0100	/* t_xmin committed */
#define HEAP_XMIN_INVALID		0x0200	/* t_xmin invalid/aborted */
#define HEAP_XMAX_COMMITTED		0x0400	/* t_xmax committed */

HEAP_XMAX_IS_NOT_UPDATE is a pretty opaque name for that. From the name,
I'd say that a DELETE would set that, but the comment says it has to do
with locking. After going through all the combinations in my mind, I
think I finally understood it: HEAP_XMAX_IS_NOT_UPDATE is set if xmax is
a multi-xact, that represent only locking xids, no updates. How about
calling it "HEAP_XMAX_LOCK_ONLY", and setting it whenever whether or not
xmax is a multi-xid?

- I haven't done anything about exposing FOR KEY UPDATE to the SQL
level. There clearly isn't consensus about exposing this; in fact
there isn't consensus on exposing FOR KEY SHARE, but I haven't
changed that from the previous patch, either.

I think it would be useful to expose it. Not that anyone should be using
them in an application (or would it be useful?), but I feel it could
make testing significantly easier.

- pg_upgrade bits are missing.

I guess we'll need to rewrite pg_multixact contents in pg_upgrade. Is
the page format backwards-compatible?

Why are you renaming HeapTupleHeaderGetXmax() into
HeapTupleHeaderGetRawXmax()? Any current callers of
HeapTupleHeaderGetXmax() should already check that HEAP_XMAX_IS_MULTI is
not set.

--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com

#30Alvaro Herrera
alvherre@commandprompt.com
In reply to: Heikki Linnakangas (#29)
Re: foreign key locks, 2nd attempt

Excerpts from Heikki Linnakangas's message of lun ene 16 16:17:42 -0300 2012:

On 15.01.2012 06:49, Alvaro Herrera wrote:

--- 164,178 ----
#define HEAP_HASVARWIDTH        0x0002    /* has variable-width attribute(s) */
#define HEAP_HASEXTERNAL        0x0004    /* has external stored attribute(s) */
#define HEAP_HASOID                0x0008    /* has an object-id field */
! #define HEAP_XMAX_KEYSHR_LOCK    0x0010    /* xmax is a key-shared locker */
#define HEAP_COMBOCID            0x0020    /* t_cid is a combo cid */
#define HEAP_XMAX_EXCL_LOCK        0x0040    /* xmax is exclusive locker */
! #define HEAP_XMAX_IS_NOT_UPDATE    0x0080    /* xmax, if valid, is only a locker.
!                                          * Note this is not set unless
!                                          * XMAX_IS_MULTI is also set. */
!
! #define HEAP_LOCK_BITS    (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_IS_NOT_UPDATE | \
!                          HEAP_XMAX_KEYSHR_LOCK)
#define HEAP_XMIN_COMMITTED        0x0100    /* t_xmin committed */
#define HEAP_XMIN_INVALID        0x0200    /* t_xmin invalid/aborted */
#define HEAP_XMAX_COMMITTED        0x0400    /* t_xmax committed */

HEAP_XMAX_IS_NOT_UPDATE is a pretty opaque name for that. From the name,
I'd say that a DELETE would set that, but the comment says it has to do
with locking. After going through all the combinations in my mind, I
think I finally understood it: HEAP_XMAX_IS_NOT_UPDATE is set if xmax is
a multi-xact, that represent only locking xids, no updates. How about
calling it "HEAP_XMAX_LOCK_ONLY", and setting it whenever whether or not
xmax is a multi-xid?

Hm, sounds like a good idea. Will do.

- I haven't done anything about exposing FOR KEY UPDATE to the SQL
level. There clearly isn't consensus about exposing this; in fact
there isn't consensus on exposing FOR KEY SHARE, but I haven't
changed that from the previous patch, either.

I think it would be useful to expose it. Not that anyone should be using
them in an application (or would it be useful?), but I feel it could
make testing significantly easier.

Okay, two votes in favor; I'll go do that too.

- pg_upgrade bits are missing.

I guess we'll need to rewrite pg_multixact contents in pg_upgrade. Is
the page format backwards-compatible?

It's not.

I haven't worked out what pg_upgrade needs to do, honestly. I think we
should just not copy old pg_multixact files when upgrading across this
patch. I was initially thinking that pg_multixact should return the
empty set if requested members of a multi that preceded the freeze
point. That way, I thought, we would just never try to access a page
originated in the older version (assuming the freeze point is set to
"current" whenever pg_upgrade runs). However, as things currently
stand, accessing an old multi raises an error. So maybe we need a
scheme a bit more complex to handle this.

Why are you renaming HeapTupleHeaderGetXmax() into
HeapTupleHeaderGetRawXmax()? Any current callers of
HeapTupleHeaderGetXmax() should already check that HEAP_XMAX_IS_MULTI is
not set.

I had this vague impression that it'd be better to break existing
callers so that they ensure they decide between
HeapTupleHeaderGetRawXmax and HeapTupleHeaderGetUpdateXid. Noah
suggested changing the macro name, too. It's up to each caller to
decide what's the sematics they want. Most want the latter; and callers
outside core are more likely to want that one. If we kept the old name,
they would get the wrong value.

If we want to keep the original name, it's the same to me.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#31Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Alvaro Herrera (#30)
Re: foreign key locks, 2nd attempt

On 16.01.2012 21:52, Alvaro Herrera wrote:

Excerpts from Heikki Linnakangas's message of lun ene 16 16:17:42 -0300 2012:

On 15.01.2012 06:49, Alvaro Herrera wrote:

- pg_upgrade bits are missing.

I guess we'll need to rewrite pg_multixact contents in pg_upgrade. Is
the page format backwards-compatible?

It's not.

I haven't worked out what pg_upgrade needs to do, honestly. I think we
should just not copy old pg_multixact files when upgrading across this
patch.

Sorry, I meant whether the *data* page format is backwards-compatible?
the multixact page format clearly isn't.

I was initially thinking that pg_multixact should return the
empty set if requested members of a multi that preceded the freeze
point. That way, I thought, we would just never try to access a page
originated in the older version (assuming the freeze point is set to
"current" whenever pg_upgrade runs). However, as things currently
stand, accessing an old multi raises an error. So maybe we need a
scheme a bit more complex to handle this.

Hmm, could we create new multixact files filled with zeros, covering the
range that was valid in the old cluster?

--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com

#32Noah Misch
noah@leadboat.com
In reply to: Alvaro Herrera (#28)
Re: foreign key locks, 2nd attempt

On Sun, Jan 15, 2012 at 01:49:54AM -0300, Alvaro Herrera wrote:

- I'm not sure that the multixact truncation code is sane on
checkpoints. It might be that I need to tweak more the pg_control info
we keep about truncation. The whole truncation thing needs more
testing, too.

My largest outstanding concern involves the possibility of MultiXactId
wraparound. From my last review:

This raises a notable formal hazard: it's possible to burn through the
MultiXactId space faster than the regular TransactionId space. We could get
into a situation where pg_clog is covering 2B xids, and yet we need >4B
MultiXactId to cover that period. We had better at least notice this and
halt, if not have autovacuum actively prevent it.

(That should have been 2B rather than 4B, since MultiXactId uses the same
2B-in-past, 2B-in-future behavior as regular xids.)

Are we willing to guess that this will "never" happen and make recovery
minimally possible? If so, we could have GetNewMultiXactId() grow defenses
similar to GetNewTransactionId() and leave it at that. If not, we need to
involve autovacuum.

The other remaining high-level thing is to have key_attrs contain only columns
actually referenced by FKs.

- Go over Noah's two reviews again and see if I missed anything; also
make sure I haven't missed anything from other reviewers.

There are some, yes.

*** a/src/backend/access/heap/heapam.c
--- b/src/backend/access/heap/heapam.c

***************
*** 2773,2783 **** l2:
}
else if (result == HeapTupleBeingUpdated && wait)
{
! TransactionId xwait;
uint16 infomask;

/* must copy state data before unlocking buffer */
! xwait = HeapTupleHeaderGetXmax(oldtup.t_data);
infomask = oldtup.t_data->t_infomask;

LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
--- 2848,2871 ----
}
else if (result == HeapTupleBeingUpdated && wait)
{
! 		TransactionId	xwait;
uint16		infomask;
+ 		bool		none_remain = false;

Nothing can ever set this variable to anything different. It seems that
keep_xact == InvalidTransactionId substitutes well enough, though.

/*
* We may overwrite if previous xmax aborted, or if it committed but
! * only locked the tuple without updating it, or if we are going to
! * keep it around in Xmax.
*/

The second possibility is just a subset of the third.

! if (TransactionIdIsValid(keep_xmax) ||
! none_remain ||
! (oldtup.t_data->t_infomask & HEAP_XMAX_INVALID))
result = HeapTupleMayBeUpdated;
else
result = HeapTupleUpdated;

***************
*** 3314,3323 **** heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum,
*/
static bool
HeapSatisfiesHOTUpdate(Relation relation, Bitmapset *hot_attrs,
! HeapTuple oldtup, HeapTuple newtup)
{
int attrnum;

while ((attrnum = bms_first_member(hot_attrs)) >= 0)
{
/* Adjust for system attributes */
--- 3537,3549 ----
*/
static bool
HeapSatisfiesHOTUpdate(Relation relation, Bitmapset *hot_attrs,
! 					   HeapTuple oldtup, HeapTuple newtup, bool empty_okay)
{
int			attrnum;
+ 	if (!empty_okay && bms_is_empty(hot_attrs))
+ 		return false;

When a table contains no key attributes, it seems arbitrary whether we call
the key revoked or not. What is the motivation for this change?

! /*
! * If we're requesting KeyShare, and there's no update present, we
! * don't need to wait. Even if there is an update, we can still
! * continue if the key hasn't been modified.
! *
! * However, if there are updates, we need to walk the update chain
! * to mark future versions of the row as locked, too. That way, if
! * somebody deletes that future version, we're protected against the
! * key going away. This locking of future versions could block
! * momentarily, if a concurrent transaction is deleting a key; or it
! * could return a value to the effect that the transaction deleting the
! * key has already committed. So we do this before re-locking the
! * buffer; otherwise this would be prone to deadlocks. Note that the TID
! * we're locking was grabbed before we unlocked the buffer. For it to
! * change while we're not looking, the other properties we're testing
! * for below after re-locking the buffer would also change, in which
! * case we would restart this loop above.
! */
! if ((mode == LockTupleKeyShare) &&
! (HeapTupleHeaderInfomaskIsOnlyLocked(infomask) ||
! !(infomask2 & HEAP_UPDATE_KEY_REVOKED)))

Isn't the OnlyLocked test redundant, a subset of the !KEY_REVOKED test?

{
! /* if there are updates, follow the update chain */
! if (!HeapTupleHeaderInfomaskIsOnlyLocked(infomask))
! {
! HTSU_Result res;
!
! res = heap_lock_updated_tuple(relation, tid,
! GetCurrentTransactionId(),
! mode);
! if (res != HeapTupleMayBeUpdated)
! {
! result = res;
! /* recovery code expects to have buffer lock held */
! LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
! goto failed;
! }
! }
!
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);

/*
! 			 * Make sure it's still an appropriate lock, else start over.
*/
! 			if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
! 				(tuple->t_data->t_infomask2 & HEAP_UPDATE_KEY_REVOKED))
goto l3;
+ 			require_sleep = false;
+ 
+ 			/*
+ 			 * Note we allow Xmax to change here; other updaters/lockers could
+ 			 * have modified it before we grabbed the buffer lock.  However,
+ 			 * this is not a problem, because with the recheck we just did we
+ 			 * ensure that they still don't conflict with the lock we want.
+ 			 */

If an updater has appeared in the meantime, don't we need to go back and lock
along its update chain?

}

+ 		/*
+ 		 * If we're requesting Share, we can similarly avoid sleeping if
+ 		 * there's no update and no exclusive lock present.
+ 		 */
+ 		if (mode == LockTupleShare &&
+ 			(infomask & (HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_IS_NOT_UPDATE)) &&
+ 			!(infomask & HEAP_XMAX_EXCL_LOCK))
+ 		{
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);

/*
! * Make sure it's still an appropriate lock, else start over. See
! * above about allowing xmax to change.
*/

I agree that it should be safe here, though.

! if (!(tuple->t_data->t_infomask &
! (HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_IS_NOT_UPDATE)) ||
! (tuple->t_data->t_infomask & HEAP_XMAX_EXCL_LOCK))
goto l3;
+ require_sleep = false;
+ }

! elog(ERROR, "invalid lock mode in heap_tuple_lock");

"heap_lock_tuple" in that message.

! /*
! * Make sure there is no forward chain link in t_ctid. Note that in the
! * cases where the tuple has been updated, we must not overwrite t_ctid,
! * because it was set by the updater. Moreover, if the tuple has been
! * updated, we need to follow the update chain to lock the new versions
! * of the tuple as well.
! *
! * FIXME -- not 100% sure of the implications of this.
! */
! if (HeapTupleHeaderInfomaskIsOnlyLocked(new_infomask))
! tuple->t_data->t_ctid = *tid;

This seems right.

+ /*
+  * Given an original set of Xmax and infomask, and a transaction acquiring a
+  * new lock of some mode, compute the new Xmax and corresponding infomask to
+  * use on the tuple.
+  *
+  * Note that this might have side effects such as creating a new MultiXactId.
+  *
+  * Most callers will have called HeapTupleSatisfiesUpdate before this function;
+  * that will have set the HEAP_XMAX_INVALID bit if the xmax was a MultiXactId
+  * but it was not running anymore. There is a race condition, which is that the
+  * MultiXactId may have finished since then, but that uncommon case is handled
+  * within MultiXactIdExpand.
+  *
+  * There is a similar race condition possible when the old xmax was a regular
+  * TransactionId.  We test TransactionIdIsInProgress again just to narrow the
+  * window, but it's still possible to end up creating an unnecessary
+  * MultiXactId.  Fortunately this is harmless.
+  */
+ static void
+ compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
+ 						  TransactionId add_to_xmax, LockTupleMode mode,
+ 						  TransactionId *result_xmax, uint16 *result_infomask)
+ {
+ 	TransactionId	new_xmax;
+ 	uint16			new_infomask = old_infomask;
+ 
+ 	if (old_infomask & (HEAP_XMAX_INVALID | HEAP_XMAX_COMMITTED))
+ 	{
+ 		/*
+ 		 * No previous locker, or it already finished; we just insert our own
+ 		 * TransactionId.
+ 		 */
+ 		switch (mode)
+ 		{
+ 			case LockTupleKeyShare:
+ 				new_xmax = add_to_xmax;
+ 				new_infomask |= HEAP_XMAX_KEYSHR_LOCK;
+ 				break;
+ 			case LockTupleShare:
+ 				/* need a multixact here in any case */
+ 				new_xmax = MultiXactIdCreateSingleton(add_to_xmax, MultiXactStatusForShare);
+ 				new_infomask |= GetMultiXactIdHintBits(new_xmax);
+ 				break;
+ 			case LockTupleUpdate:
+ 				new_infomask |= HEAP_XMAX_EXCL_LOCK;
+ 				new_xmax = xmax;

Shouldn't that be "new_xmax = add_to_xmax"?

+ 				break;
+ 			default:
+ 				elog(ERROR, "invalid lock mode");
+ 				new_xmax = InvalidTransactionId;	/* keep compiler quiet */
+ 		}
+ 		/* no other updater; just add myself */
+ 	}
+ 	else if (old_infomask & HEAP_XMAX_IS_MULTI)
+ 	{
+ 		MultiXactStatus		new_mxact_status;
+ 
+ 		new_mxact_status = get_mxact_status_for_tuplelock(mode);
+ 		/*
+ 		 * If the XMAX is already a MultiXactId, then we need to
+ 		 * expand it to include our own TransactionId.
+ 		 */
+ 		new_xmax = MultiXactIdExpand((MultiXactId) xmax, add_to_xmax, new_mxact_status);
+ 		new_infomask |= GetMultiXactIdHintBits(new_xmax);
+ 	}
+ 	else if (TransactionIdIsInProgress(xmax))
+ 	{
+ 		/*
+ 		 * If the XMAX is a valid, in-progress TransactionId, then we need to
+ 		 * create a new MultiXactId that includes both the old locker and our
+ 		 * own TransactionId.
+ 		 */
+ 		MultiXactStatus		status;
+ 		MultiXactStatus		new_mxact_status;
+ 
+ 		new_mxact_status = get_mxact_status_for_tuplelock(mode);
+ 
+ 		if (old_infomask & HEAP_XMAX_EXCL_LOCK)
+ 			status = MultiXactStatusForUpdate;
+ 		else if (old_infomask & HEAP_XMAX_KEYSHR_LOCK)
+ 			status = MultiXactStatusForKeyShare;
+ 		else
+ 		{
+ 			status = MultiXactStatusUpdate;
+ 		}
+ 
+ 		/* FIXME need to verify the KEY_REVOKED bit, and block if it's set */
+ 
+ 		new_xmax = MultiXactIdCreate(xmax, status, add_to_xmax, new_mxact_status);
+ 		new_infomask |= GetMultiXactIdHintBits(new_xmax);
+ 		/* FIXME -- we need to add bits to the infomask here! */
+ 	}
+ 	else if (mode == LockTupleShare)
+ 	{
+ 		MultiXactStatus		new_mxact_status;
+ 
+ 		/*
+ 		 * There's no hint bit for FOR SHARE, so we need a multixact
+ 		 * here no matter what.
+ 		 */
+ 		new_mxact_status = get_mxact_status_for_tuplelock(mode);
+ 		new_xmax = MultiXactIdCreateSingleton(add_to_xmax, new_mxact_status);
+ 		new_infomask |= GetMultiXactIdHintBits(new_xmax);
+ 	}

If you remove the conditional block above, the next conditional block will
handle it fine.

+ 	else
+ 	{
+ 		/*
+ 		 * Can get here iff the updating transaction was running when the
+ 		 * infomask was extracted from the tuple, but finished before
+ 		 * TransactionIdIsInProgress got to run.  Treat it like there's no
+ 		 * locker in the tuple.
+ 		 */
+ 		switch (mode)
+ 		{
+ 			case LockTupleKeyShare:
+ 				new_infomask |= HEAP_XMAX_KEYSHR_LOCK;
+ 				new_xmax = xmax;

Shouldn't that be add_to_xmax?

+ 				break;
+ 			case LockTupleShare:
+ 				/* need a multixact here in any case */
+ 				new_xmax = MultiXactIdCreateSingleton(add_to_xmax, MultiXactStatusForShare);
+ 				new_infomask |= GetMultiXactIdHintBits(new_xmax);
+ 				break;
+ 			case LockTupleUpdate:
+ 				new_infomask |= HEAP_XMAX_EXCL_LOCK;
+ 				new_xmax = xmax;
+ 				break;
+ 			default:
+ 				elog(ERROR, "invalid lock mode");
+ 				new_xmax = InvalidTransactionId;	/* keep compiler quiet */
+ 		}

Can you rearrange conditional flow to avoid having two copies of this switch
statement?

+ 	}
+ 
+ 	/* must unset the XMAX_INVALID bit */
+ 	new_infomask &= ~HEAP_XMAX_INVALID;
+ 
+ 	*result_infomask = new_infomask;
+ 	*result_xmax = new_xmax;
+ }
+ 
+ static HTSU_Result
+ heap_lock_updated_tuple(Relation rel, ItemPointer tid, TransactionId xid,
+ 						LockTupleMode mode)
+ {

This function could use a comment.

+ 	ItemPointerData	tupid;
+ 	HeapTupleData	mytup;
+ 	Buffer			buf;
+ 	uint16			new_infomask,
+ 					old_infomask;
+ 	TransactionId	xmax,
+ 					new_xmax;
+ 
+ 	ItemPointerCopy(tid, &tupid);
+ 
+ restart:
+ 	new_infomask = 0;
+ 	new_xmax = InvalidTransactionId;
+ 	ItemPointerCopy(&tupid, &(mytup.t_self));
+ 
+ l5:
+ 	if (!heap_fetch(rel, SnapshotAny, &mytup, &buf, false, NULL))
+ 		elog(ERROR, "unable to fetch updated version of tuple");
+ 
+ 	/*
+ 	 * XXX we do not lock this tuple here; the theory is that it's sufficient
+ 	 * with the buffer lock we're about to grab.  Any other code must be able
+ 	 * to cope with tuple lock specifics changing while they don't hold buffer
+ 	 * lock anyway.
+ 	 */
+ 
+ 	/*
+ 	 * We've got a more recent (updated) version of a tuple we locked.
+ 	 * We need to propagate the lock to it; here we don't sleep at all
+ 	 * or try to check visibility, we just inconditionally mark it as
+ 	 * locked by us.  We only need to ensure we have buffer lock.
+ 	 */
+ 	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+ 
+ 	old_infomask = mytup.t_data->t_infomask;
+ 	xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
+ 
+ 	if (!(old_infomask & HEAP_XMAX_INVALID) &&
+ 		 (mytup.t_data->t_infomask & HEAP_UPDATE_KEY_REVOKED))
+ 	{
+ 		TransactionId	xmax;
+ 
+ 		xmax = HeapTupleHeaderGetUpdateXid(mytup.t_data);
+ 		if (TransactionIdIsCurrentTransactionId(xmax))
+ 		{
+ 			UnlockReleaseBuffer(buf);
+ 			return HeapTupleSelfUpdated;
+ 		}

Is reaching this code indeed possible, with cursors or something?

+ 		else if (TransactionIdIsInProgress(xmax))
+ 		{
+ 			UnlockReleaseBuffer(buf);
+ 			XactLockTableWait(xmax);
+ 			goto l5;

What about just unlocking the buffer here and moving "l5" to after the
heap_fetch()? We should not need to refetch.

+ 		}
+ 		else if (TransactionIdDidAbort(xmax))
+ 			;	/* okay to proceed */
+ 		else if (TransactionIdDidCommit(xmax))
+ 		{
+ 			UnlockReleaseBuffer(buf);
+ 			return HeapTupleUpdated;
+ 		}
+ 	}
+ 
+ 	compute_new_xmax_infomask(xmax, old_infomask, xid, mode,
+ 							  &new_xmax, &new_infomask);
+ 
+ 	START_CRIT_SECTION();
+ 
+ 	/* And set them. */
+ 	HeapTupleHeaderSetXmax(mytup.t_data, new_xmax);
+ 	mytup.t_data->t_infomask = new_infomask;
+ 
+ 	MarkBufferDirty(buf);
+ 
+ 	/*
+ 	 * FIXME XLOG stuff goes here.  Is it really necessary to have this, or
+ 	 * would it be sufficient to just WAL log the original tuple lock, and have
+ 	 * the replay code follow the update chain too?
+ 	 */

A single WAL record referencing the root tuple and a PageSetLSN()/PageSetTLI()
on all affected pages should be sufficient. However, that requires a critical
section from here until the writing of that WAL record. As the code stands,
plenty can fail in the meantime.

+ 
+ 	END_CRIT_SECTION();
+ 
+ 	LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+ 
+ 	/* found end of update chain? */
+ 	/* FIXME -- ISTM we must also check that Xmin in the new tuple matches
+ 	 * updating Xid of the old, as other routines do. */

Agreed. I suggest mimicing heap_get_latest_tid() here.

+ 	if (ItemPointerEquals(&(mytup.t_self), &(mytup.t_data->t_ctid)))
+ 	{
+ 		ReleaseBuffer(buf);
+ 		return HeapTupleMayBeUpdated;
+ 	}
+ 
+ 	/* tail recursion */
+ 	ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid);

You normally need at least a shared buffer content lock to read t_ctid. Any
concurrent updater who changes t_ctid after we released the lock will also be
copying the lock we just added, so there would be no need to continue up the
chain. So, if you copied t_ctid outside of any content lock and then verified
the xmax/xmin match per above, it might be fine. However, I wouldn't use that
cleverness to merely shave a couple of instructions from the locked region.

+ 	ReleaseBuffer(buf);
+ 	goto restart;
+ }

/*
* The tuple might be marked either XMAX_INVALID or XMAX_COMMITTED
! * + LOCKED, possibly with IS_MULTI too. Normalize to INVALID just
! * to be sure no one gets confused. Also get rid of the
! * HEAP_UPDATE_KEY_REVOKED bit.
*/
! tuple->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_LOCK_BITS |
! HEAP_XMAX_IS_MULTI);
! tuple->t_infomask &= ~HEAP_LOCK_BITS;

The most recent line is redundant with its predecessor.

*** a/src/backend/access/transam/multixact.c
--- b/src/backend/access/transam/multixact.c

+ #define MULTIXACT_DEBUG

Omit the above line.

+ /*
* MultiXactIdCreate
* Construct a MultiXactId representing two TransactionIds.
*
! * The two XIDs must be different, or be requesting different lock modes.

Why is it not sufficient to store the strongest type for a particular xid?

***************
*** 775,786 **** RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,

prev_pageno = -1;

! for (i = 0; i < nxids; i++, offset++)
{
TransactionId *memberptr;

pageno = MXOffsetToMemberPage(offset);
! entryno = MXOffsetToMemberEntry(offset);

if (pageno != prev_pageno)
{
--- 768,792 ----

prev_pageno = -1;

! 	for (i = 0; i < nmembers; i++, offset++)
{
TransactionId *memberptr;
+ 		uint32	   *flagsptr;
+ 		uint32		flagsval;
+ 		int			bshift;
+ 		int			flagsoff;
+ 		int			memberoff;
+ 
+ 		if (members[i].xid < 900)
+ 			abort();

Leftover from testing?

***************
*** 1222,1236 **** mXactCachePut(MultiXactId multi, int nxids, TransactionId *xids)

#ifdef MULTIXACT_DEBUG
static char *
! mxid_to_string(MultiXactId multi, int nxids, TransactionId *xids)
{
! char *str = palloc(15 * (nxids + 1) + 4);
int i;

! snprintf(str, 47, "%u %d[%u", multi, nxids, xids[0]);

! for (i = 1; i < nxids; i++)
! snprintf(str + strlen(str), 17, ", %u", xids[i]);

strcat(str, "]");
return str;
--- 1285,1327 ----

#ifdef MULTIXACT_DEBUG
static char *
! mxstatus_to_string(MultiXactStatus status)
{
! switch (status)
! {
! case MultiXactStatusForKeyShare:
! return "keysh";
! case MultiXactStatusForShare:
! return "sh";
! case MultiXactStatusForUpdate:
! return "forupd";
! case MultiXactStatusUpdate:
! return "upd";
! case MultiXactStatusKeyUpdate:
! return "keyup";
! default:
! elog(ERROR, "unrecognized multixact status %d", status);
! return "";
! }
! }
!
! static char *
! mxid_to_string(MultiXactId multi, int nmembers, MultiXactMember *members)
! {
! static char *str = NULL;
int i;

! if (str != NULL)
! pfree(str);
!
! str = MemoryContextAlloc(TopMemoryContext, 15 * (nmembers + 1) + 4);

! snprintf(str, 47, "%u %d[%u (%s)", multi, nmembers, members[0].xid,
! mxstatus_to_string(members[0].status));
!
! for (i = 1; i < nmembers; i++)
! snprintf(str + strlen(str), 17, ", %u (%s)", members[i].xid,
! mxstatus_to_string(members[i].status));

This could truncate: 10 chars from %u, 6 from %s, 5 constant chars.

How about using a StringInfoData instead?

strcat(str, "]");
return str;

*** a/src/backend/utils/time/combocid.c
--- b/src/backend/utils/time/combocid.c
***************
*** 118,126 **** HeapTupleHeaderGetCmax(HeapTupleHeader tup)
{
CommandId	cid = HeapTupleHeaderGetRawCommandId(tup);

/* We do not store cmax when locking a tuple */
! Assert(!(tup->t_infomask & (HEAP_MOVED | HEAP_IS_LOCKED)));
! Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tup)));

if (tup->t_infomask & HEAP_COMBOCID)
return GetRealCmax(cid);
--- 118,128 ----
{
CommandId	cid = HeapTupleHeaderGetRawCommandId(tup);

+ Assert(!(tup->t_infomask & HEAP_MOVED));
/* We do not store cmax when locking a tuple */

The comment is deceptive now.

! Assert(!HeapTupleHeaderIsOnlyLocked(tup));
! Assert((tup->t_infomask & HEAP_XMAX_IS_MULTI) ||
! TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tup)));

How about the more-specific
"Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tup)))"
in place of both of these asserts?

if (tup->t_infomask & HEAP_COMBOCID)
return GetRealCmax(cid);
*** a/src/backend/utils/time/tqual.c
--- b/src/backend/utils/time/tqual.c

I still haven't reviewed the tqual.c changes in detail, but I see that is has
several FIXMEs.

*** a/src/test/isolation/isolationtester.c
--- b/src/test/isolation/isolationtester.c
***************
*** 395,401 **** run_named_permutations(TestSpec * testspec)
Permutation *p = testspec->permutations[i];
Step	  **steps;
! 		if (p->nsteps != nallsteps)
{
fprintf(stderr, "invalid number of steps in permutation %d\n", i + 1);
exit_nicely();
--- 395,401 ----
Permutation *p = testspec->permutations[i];
Step	  **steps;
! 		if (p->nsteps > nallsteps)
{
fprintf(stderr, "invalid number of steps in permutation %d\n", i + 1);
exit_nicely();
***************
*** 565,570 **** run_permutation(TestSpec * testspec, int nsteps, Step ** steps)
--- 565,571 ----
* steps from this session can run until it is unblocked, but it
* can only be unblocked by running steps from other sessions.
*/
+ 			fflush(stdout);
fprintf(stderr, "invalid permutation detected\n");

/* Cancel the waiting statement from this session. */

Why these isolationtester.c changes?

Thanks,
nm

#33Noah Misch
noah@leadboat.com
In reply to: Alvaro Herrera (#30)
Re: foreign key locks, 2nd attempt

On Mon, Jan 16, 2012 at 04:52:36PM -0300, Alvaro Herrera wrote:

Excerpts from Heikki Linnakangas's message of lun ene 16 16:17:42 -0300 2012:

On 15.01.2012 06:49, Alvaro Herrera wrote:

--- 164,178 ----
#define HEAP_HASVARWIDTH        0x0002    /* has variable-width attribute(s) */
#define HEAP_HASEXTERNAL        0x0004    /* has external stored attribute(s) */
#define HEAP_HASOID                0x0008    /* has an object-id field */
! #define HEAP_XMAX_KEYSHR_LOCK    0x0010    /* xmax is a key-shared locker */
#define HEAP_COMBOCID            0x0020    /* t_cid is a combo cid */
#define HEAP_XMAX_EXCL_LOCK        0x0040    /* xmax is exclusive locker */
! #define HEAP_XMAX_IS_NOT_UPDATE    0x0080    /* xmax, if valid, is only a locker.
!                                          * Note this is not set unless
!                                          * XMAX_IS_MULTI is also set. */
!
! #define HEAP_LOCK_BITS    (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_IS_NOT_UPDATE | \
!                          HEAP_XMAX_KEYSHR_LOCK)
#define HEAP_XMIN_COMMITTED        0x0100    /* t_xmin committed */
#define HEAP_XMIN_INVALID        0x0200    /* t_xmin invalid/aborted */
#define HEAP_XMAX_COMMITTED        0x0400    /* t_xmax committed */

HEAP_XMAX_IS_NOT_UPDATE is a pretty opaque name for that. From the name,
I'd say that a DELETE would set that, but the comment says it has to do
with locking. After going through all the combinations in my mind, I
think I finally understood it: HEAP_XMAX_IS_NOT_UPDATE is set if xmax is
a multi-xact, that represent only locking xids, no updates. How about
calling it "HEAP_XMAX_LOCK_ONLY", and setting it whenever whether or not
xmax is a multi-xid?

Hm, sounds like a good idea. Will do.

+1

Why are you renaming HeapTupleHeaderGetXmax() into
HeapTupleHeaderGetRawXmax()? Any current callers of
HeapTupleHeaderGetXmax() should already check that HEAP_XMAX_IS_MULTI is
not set.

I had this vague impression that it'd be better to break existing
callers so that they ensure they decide between
HeapTupleHeaderGetRawXmax and HeapTupleHeaderGetUpdateXid. Noah
suggested changing the macro name, too. It's up to each caller to
decide what's the sematics they want. Most want the latter; and callers
outside core are more likely to want that one. If we kept the old name,
they would get the wrong value.

My previous suggestion was to have both macros:

#define HeapTupleHeaderGetXmax(tup) \
( \
AssertMacro(!((tup)->t_infomask & HEAP_XMAX_IS_MULTI)), \
HeapTupleHeaderGetRawXmax(tup) \
)

#define HeapTupleHeaderGetRawXmax(tup) \
( \
(tup)->t_choice.t_heap.t_xmax \
)

No strong preference, though.

#34Alvaro Herrera
alvherre@commandprompt.com
In reply to: Heikki Linnakangas (#31)
Re: foreign key locks, 2nd attempt

Excerpts from Heikki Linnakangas's message of mar ene 17 03:21:28 -0300 2012:

On 16.01.2012 21:52, Alvaro Herrera wrote:

Excerpts from Heikki Linnakangas's message of lun ene 16 16:17:42 -0300 2012:

On 15.01.2012 06:49, Alvaro Herrera wrote:

- pg_upgrade bits are missing.

I guess we'll need to rewrite pg_multixact contents in pg_upgrade. Is
the page format backwards-compatible?

It's not.

I haven't worked out what pg_upgrade needs to do, honestly. I think we
should just not copy old pg_multixact files when upgrading across this
patch.

Sorry, I meant whether the *data* page format is backwards-compatible?
the multixact page format clearly isn't.

It's supposed to be, yes. The HEAP_XMAX_IS_NOT_UPDATE bit (now renamed)
was chosen so that it'd take the place of the old HEAP_XMAX_SHARE_LOCK
bit, so any pages with that bit set should continue to work similarly.
The other infomask bits I used were previously unused.

I was initially thinking that pg_multixact should return the
empty set if requested members of a multi that preceded the freeze
point. That way, I thought, we would just never try to access a page
originated in the older version (assuming the freeze point is set to
"current" whenever pg_upgrade runs). However, as things currently
stand, accessing an old multi raises an error. So maybe we need a
scheme a bit more complex to handle this.

Hmm, could we create new multixact files filled with zeros, covering the
range that was valid in the old cluster?

Hm, we could do something like that I guess. I'm not sure that just
zeroes is the right pattern, but it should be something simple.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#35Noah Misch
noah@leadboat.com
In reply to: Alvaro Herrera (#34)
Re: foreign key locks, 2nd attempt

On Wed, Jan 18, 2012 at 05:18:31PM -0300, Alvaro Herrera wrote:

Excerpts from Heikki Linnakangas's message of mar ene 17 03:21:28 -0300 2012:

On 16.01.2012 21:52, Alvaro Herrera wrote:

I was initially thinking that pg_multixact should return the
empty set if requested members of a multi that preceded the freeze
point. That way, I thought, we would just never try to access a page
originated in the older version (assuming the freeze point is set to
"current" whenever pg_upgrade runs). However, as things currently
stand, accessing an old multi raises an error. So maybe we need a
scheme a bit more complex to handle this.

Hmm, could we create new multixact files filled with zeros, covering the
range that was valid in the old cluster?

Hm, we could do something like that I guess. I'm not sure that just
zeroes is the right pattern, but it should be something simple.

PostgreSQL 9.1 can have all ~4B MultiXactId on disk at any given time.

We could silently ignore the lookup miss when HEAP_XMAX_LOCK_ONLY is also set.
That makes existing data files acceptable while still catching data loss
scenarios going forward. (It's tempting to be stricter when we know the
cluster data files originated in PostgreSQL 9.2+, but I'm not sure whether
that's worth its weight.)

#36Alvaro Herrera
alvherre@commandprompt.com
In reply to: Noah Misch (#32)
1 attachment(s)
Re: foreign key locks, 2nd attempt

This version of the patch fixes most of the problems pointed out by this
review. There are a bunch of relatively minor items that need
addressing yet, but I wanted to throw this out in case anyone is
interested in giving this some testing or more feedback.

The biggest item remaining is the point you raised about multixactid
wraparound. This is closely related to multixact truncation and the way
checkpoints are to be handled. If we think that MultiXactId wraparound
is possible, and we need to involve autovacuum to keep it at bay, then I
think the only way to make that work is to add another column to
pg_class so that each table's oldest multixact is tracked, same as we do
with relfrozenxid for Xids. If we do that, I think we can do away with
most of the MultiXactTruncate junk I added -- things would become a lot
simpler. The cost would be bloating pg_class a bit more. Are we okay
with paying that cost? I asked this question some months ago and I
decided that I would not add the column, but I am starting to lean the
other way. I would like some opinions on this.

You asked two questions about WAL-logging locks: one was about the level
of detail we log for each lock we grab; the other was about
heap_xlog_update logging the right info. AFAICS, the main thing that
makes detailed WAL logging necessary is hot standbys. That is, the
standby must have all the locking info so that concurrent transactions
are similarly locked as in the master ... or am I wrong in that? (ISTM
this means I need to fix heap_xlog_update so that it faithfully
registers the lock info we're storing, not just the current Xid).

You also asked about heap_update and TOAST; in particular, do we need to
re-check locking info after we've unlocked the buffer for toasting and
finding free space? I believe the current version handles this, but I
haven't tested it.

Some open questions:

* Do we need some extra flag bits for each multi?

* how to deal with heap_update in the 'nowait' case?

* multixact.c cache
Do we need some updates to that?

* multis with multiple members per Xid ??

* MultiXactIdCreate
*        Construct a MultiXactId representing two TransactionIds.
*
- * The two XIDs must be different.
+ * The two XIDs must be different, or be requesting different lock modes.

Why is it not sufficient to store the strongest type for a particular xid?
In this version, I've moved MultiXactIdWait to heapam.c. This makes
multixact largely unaware of the meaning of the flag bits stored with
each multi. I believe we can fix this problem, but not at this level,
but rather in heap_lock_tuple.

* Columns that are part of the key
Noah thinks the set of columns should only consider those actually referenced
by keys, not those that *could* be referenced.

Also, in a table without columns, are all columns part of the key, or is the
key the empty set? I changed HeapSatisfiesHOTUpdate but that seems arbitrary.

Need more code changes for the following:

* pg_upgrade issues are still open
There are two things here. One is what to do when migrating from an old
version that only has HEAP_XMAX_SHARED_LOCK into a new one. The other is
what we need to do from 9.2 into the future (need to copy pg_multixact
contents).

* export FOR KEY UPDATE lock mode in SQL

* Ensure that MultiXactIdIsValid is sane.

* heap_lock_updated_tuple needs WAL logging.

git diff --stat:

contrib/pageinspect/heapfuncs.c | 2 +-
contrib/pgrowlocks/Makefile | 2 +-
contrib/pgrowlocks/pgrowlocks--1.0--1.1.sql | 17 +
contrib/pgrowlocks/pgrowlocks--1.0.sql | 15 -
contrib/pgrowlocks/pgrowlocks--1.1.sql | 15 +
contrib/pgrowlocks/pgrowlocks.c | 133 ++-
contrib/pgrowlocks/pgrowlocks.control | 2 +-
doc/src/sgml/pgrowlocks.sgml | 14 +-
doc/src/sgml/ref/select.sgml | 117 +-
src/backend/access/common/heaptuple.c | 2 +-
src/backend/access/heap/heapam.c | 1524 ++++++++++++++++----
src/backend/access/heap/pruneheap.c | 10 +-
src/backend/access/heap/rewriteheap.c | 6 +-
src/backend/access/transam/multixact.c | 1436 ++++++++++---------
src/backend/access/transam/twophase_rmgr.c | 4 -
src/backend/access/transam/xact.c | 3 -
src/backend/access/transam/xlog.c | 14 +-
src/backend/catalog/index.c | 4 +-
src/backend/commands/analyze.c | 3 +-
src/backend/commands/cluster.c | 2 +-
src/backend/commands/sequence.c | 3 +-
src/backend/commands/trigger.c | 2 +-
src/backend/commands/vacuum.c | 2 +-
src/backend/executor/execMain.c | 7 +-
src/backend/executor/nodeLockRows.c | 20 +-
src/backend/nodes/copyfuncs.c | 4 +-
src/backend/nodes/equalfuncs.c | 4 +-
src/backend/nodes/outfuncs.c | 4 +-
src/backend/nodes/readfuncs.c | 2 +-
src/backend/optimizer/plan/initsplan.c | 6 +-
src/backend/optimizer/plan/planner.c | 24 +-
src/backend/parser/analyze.c | 24 +-
src/backend/parser/gram.y | 12 +-
src/backend/rewrite/rewriteHandler.c | 26 +-
src/backend/storage/lmgr/predicate.c | 4 +-
src/backend/tcop/utility.c | 40 +-
src/backend/utils/adt/ri_triggers.c | 41 +-
src/backend/utils/adt/ruleutils.c | 26 +-
src/backend/utils/cache/relcache.c | 23 +-
src/backend/utils/time/combocid.c | 5 +-
src/backend/utils/time/tqual.c | 356 ++++-
src/bin/pg_resetxlog/pg_resetxlog.c | 33 +-
src/include/access/heapam.h | 16 +-
src/include/access/htup.h | 66 +-
src/include/access/multixact.h | 68 +-
src/include/access/twophase_rmgr.h | 3 +-
src/include/catalog/pg_control.h | 6 +-
src/include/nodes/execnodes.h | 8 +-
src/include/nodes/parsenodes.h | 34 +-
src/include/nodes/plannodes.h | 9 +-
src/include/parser/analyze.h | 2 +-
src/include/utils/rel.h | 1 +
src/include/utils/relcache.h | 2 +-
src/test/isolation/expected/fk-contention.out | 3 +-
src/test/isolation/expected/fk-deadlock.out | 34 +-
src/test/isolation/expected/fk-deadlock2.out | 68 +-
src/test/isolation/expected/fk-deadlock2_1.out | 75 +-
src/test/isolation/expected/fk-deadlock2_2.out | 105 ++
src/test/isolation/expected/fk-deadlock_1.out | 44 +-
src/test/isolation/expected/fk-deadlock_2.out | 67 +
src/test/isolation/expected/fk-delete-insert.out | 41 +
src/test/isolation/expected/lock-update-delete.out | 65 +
.../isolation/expected/lock-update-traversal.out | 18 +
src/test/isolation/isolation_schedule | 2 +
src/test/isolation/isolationtester.c | 3 +-
src/test/isolation/specs/fk-deadlock2.spec | 16 +-
src/test/isolation/specs/lock-update-delete.spec | 38 +
.../isolation/specs/lock-update-traversal.spec | 32 +
68 files changed, 3323 insertions(+), 1496 deletions(-)

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

Attachments:

fklocks-6.patch.gzapplication/x-gzip; name=fklocks-6.patch.gzDownload
�o�Ofklocks-6.patch�=�s�6�?k�
����c��|EN�8��h��^I����h`��P$KP>6������)�p�l�L2i+����<@}��1�u7��_�#:d<s��%��`�b��[�V#sG=��������4�����}������������'��������T*�8��b��T_���8�|�X�L�`�K��az9�{��`��yE�1�6����v�l���C��GEP����U��2����� zc�1�^@�^��
���"��D\��y��'������q=�	�?y�H@j�_��^�w��lJ��l����0�m��k��T����`��u��~�~���p�
�dD��n:�5e��O��[�>�<'�t����?�����PI����:��
������~o?��Vk����?���qQ�����W���cr�m��^�����'G��j����YR�u��0mL`
����/	����������q.�BUU��4�(����2��
���
G�OyD��<JD������LpHL�K�����Sr�i��Z���{�s��R����%�����`!��U��?�<Q(��Z�����szFLh�^Tv��!�,0BcV6�}Bb�t�w`4�9=���>%���S��������5#�;���5�	'a��Q�;���5��{���}a��!���aD$�q��G�}�k�lu��_NQd�#A�8t���aF��;��tI��;=��a������v%g��W'��[��(c�#t���|�e��n��>��pp%>Djvf4������V��������*_���K������t2?���F���L/	>+B����Et���������re�-���2eD��A������y�N�����������,���*���g{S%��0�I<v	�>i��d���n��G��L�y}Y-��
�;L��T��`<�C>\��M>���RiL��B
��^2P�lL����T6'�Dr��.!2���Z:[V�~,6�����x3B��!�����IVp�/���'\��b�w/��e��9I��P��#�&�S�?b���](W*pR�85|�d���v8�/����utpim��@	�������+U�q��/������29�����8�:(`�F4 'Ni��3��������T�e����j����������������.���"PdoU����[2%'����q���Wl�P�r9L���������"�b yP�Vp�������%�L�u��Ok������_���Z?jR{1
��i{�1������zD�G��Gp�o�s�-��6T*�hpK��)z\�����.�U>�f4x���
x
�x�~�-���^�����M�]��� �m�sRkE�h������tN; ��u��'����q#���*�&�+S� ��IVHc�h����*
��qi�[	��HQ�1��.�E~'�j���b|U�
�O*���)t�[`$s���,�1�*}�������S3�YN�g#$!4���ddN0��}J?���M� ���\��������Hh��2>L��q7kdl�M�a�P*YA�J��)�%��F�	j��t�9W1�Xe�Z���E]�<���g�������cB��!�l1W}:�T��x0`q5�a����VGF����dz�v���olZ;����J)����� 4������%0���hmA���M�/�����y<Rq������+����j�91<��O��&�k�����	a]��G��
����T�C/��Elr�dIr�i�"���Y9����q�
nzBa��r
;5����,Od.��
L��;2�A��w�����t���7��c�L�[~3��'�%�Zp������|��T0�*rW���*G�46
�t�R�<&f����k,���A?
�E�������5����]pI���i���+�]�����V�(��fh.���6/Q�[����`�s�E6��r�����n�	�0s��Ox�f�����\�\,������oY�l��������J%����_R�������c59�!E��Kr�AK��|�
G<��4b�2Q��jU&&�U?W�tN�Y��6�wF&u
�@ ������kq}[��������$1Y��e�?AF�#�787d
c0�o!d�|����9��
��T�N��%hw�Q�H9�H�9�;���,��^_[s��v������(���&!����-=O3��XE�}8�g����h�j�LA��4����Px����i4��vK�x���q�n���8����^�����C����d������o�������X�`|���D�\_��\3"��y�;y$�BJ�e'5l��45�oS@�%b7	V}�4}\�3�L��p��j:���������>t0pa/ ��w<'���k�1�|(f�����d�xt{l@��>��'6V�(�c��#�\��&��?�_x<��
;�=O�=���N�t�w���F�q_t�������|[&�]o��2�mn:[�-3y'-�!P��$%��.�%����A����k.�:�����c����a�M~��������3��KD����)��\��������}����{���[�#����B\�!a��2�����d&f��W�T�o6�R��)$'J:!e-R\�R�@Y)�H�j���[|Y��^K�)Ex�o���IeL�����d�.�d���g .2��a��y�0`��//��+�����T��/J�0��<�pW�����iZ
:�����e$>/���/�>_-R��*K��s��(9n/Y<����3}���t�*e�������
����M�����R���l5��t[���yG����������O���RV���.�������"C�,����{�L�H��w�����#�Ap�q���r����|"�S`�GG�Vo,(�"�w�s� ��������;x��;��D�|�9�8�2�V���Of���8?;��Qy,A�uz4o9�����9duu(0'�o����Q=R��ntS����c����H��(,U�`R[�������+H�,5�r�??W��_[�}/JP��?i:��>2��q���9���BY�I��B$�iO���<!��u@
J�u�s��)��}���8e�\a�s�n�|���{�����w�'�d���z���OV��Ha�!��H��rU��Xtb��jj������F��K��i_M����qIU��iU�P�rf���)�3U�o�������4v�|9����t��!;B.)$�YV�9�*r�+�Cz�e=��%Q�d,����������O�/=`'���e�6(xsl��?�����]�3�� ���IK
Bf(G���Q�;I�*OE�j���b_]�O��Nd����_���8��X�W@��4�w���v.�L�ML6��f��|��G���0N0���$6��WL����O��smw6XD�.n�0\��Cn���X��M�p�;u��O;���v�/~�WR29+�d�N?�5����Z�j��y����7��I)h��>�=K����EVW`�����~(���~��	���(&���;\�h��s�pX5�����Q���'z�5H���Xu}b����i�j�,��S�,u���N����\���M�����B�v����&�	Pg�h�?�{�2�Xq
�'��PMDc�C*��I�yb�-o?�lZu��H-��%��������R�i6�f���2���tKw�~8��	�S�]�Fs�i��=L��!��i�[L�eV���pv;�x����j�lW�
�|fI��il9;���P���\)���������H��A�����!dg��o>L�!�o"~lm;;��z�a�C����d:�,rl���������(�Moll;�����������
��z��]��p��R�_�5`VM1�&���%����Mi������&Z��`����%�	�P������,e���
��a<z����k��-+{�j)gE�iNg��'��FsgK�n#"�X��X$0l�g�p�m�7dn	�g��M4,�������i>�H(����������j����������6P;t��r��Z�Z�q�'[���:���#���`R��$r�\!�i��/�Y�j����zu�8RN|sD�v�~�L���3<�{qk�I�8���iH��p<�D&i �nek�����2A"�)���K�xHL�	p�dE�Df�:5 ���U����Xo4U���s���'�����,J�x���@]�DS���=,|k�K�R��2�&Gz|�/G�@rY��O��hk�{I�!���>��Pn������SB�{N��1
&b1�J�����|��t0O�uL�IR�0R�F�����(,���=�s#x��,�6#��:��'/ym�(��Ply��qV����
T#
�=�'q1����D*O���iH�=�y�w���{����U3a��3>$7�"��@iN}x�|�:
�y�&�P]V�@d5�����%���8�lu��|��4J]���uMy��@��� ��O�f��q�:����I�- ��4@U= ����o�i�������).kg����rY��������*	��R]8�1�4�p	O���*���� U

f���3� ��
5�(����h����b+f��Fx,���>�!�h�D ����<���4�`�pO�k5��P������wH�8���b|Q7������$69���*���a��E0����x�t(i�1����{����r����M��		����h��a�m8[���8P��8��Rgf�V�(�����/,��Op�Ego����V7�8����]�R�&@\�*��0<J���i�����U
Lf3\��� �V�R&R���/J��P���8��� �+w��2����];���wU�;����Gd�����j�P�I) c��,���N����28��S��X0���U����OA&vk�����Q�y��}�1��3d��(`IZ�����`�6��L�%�#�oY�f����7��p���a���C�x�M@���i�\#�.�a�����v$i)��6���0��/��S1������N|���`���Q�{/�kZ�X��
�q�d��f}}�����'�K(��Z��3��{�X^d����S>�H���[D"|���[���\E;�)�����I��Baz�lE�Xl�s�F���q�#;��W�*Z���J�d�����1�e���c�\9\'�>M
�)�]MI����` ��U�/@�b�X��%%��w��-	A|� V����v���U+/�!J����%����<�x���y���g>\��;�Z�Yec�o�����&	��0j�Qp��������I�0jN��M7�2	�M*��X�&��i@�[�#>�L�W�B����k�g|���Z�n�v�)H��_�c���Ct��N���<��k���6O��A�����+w�[����_�9}M����[93#^&B�7�Z����o��d��	)!\�fbQ1�+yZ9���%���u�a�.�-����{��L_u�$P��[��d9�O��,$��}�3��^�6�;;�G����w��S��A�d'E� ��`��h@q��xg_�^���������fe��S<c�^���u�/Z/�'��r1(�c3�9iXB�?���D����HH(��bI��
��I��r_��P�K�]E��%w/���������J&�G�S��{pN����5��9������i��G���+�4�1�l$������?���N�D�bo���D6!�r������,��lHo�8P���1�
��=?��H��i��w�e����Z:�}��4Q�T�KcO��ByvS-fu��3k�u����Ag-���	�U�%���z��o�<�>j��50L[�v�l�%E���C�	����:�t�T*�`.Xr��Tz��9���`�f����y�K��(���*��r��)4!��?{�����������8�-$�P�8����������sgrx �0&	@ZRf�}�Om�
.Z�$���%���������ry ��c��&_�a)�N���*%��n7c��/��N�M�gt��������;����y!��N����*����f6l��y��I����d� ��G���h��t�w���������h��%b�
���;����j�*�����������hK�n��
�-a	�M!���3����OMc�&9]Y�<.��e���)��Zym(BHX���!c�(8z�!*��z��8h �q�}��q���k~����#CJ!=&��V R\T���JW53�YA>K����	vV�B����;������K�^������n�y���i��>�v>T�x?��}	��[V�'�M:���������?w{�U���mp�����ta�6����� �8��hERp(�4��
}<{����L@�lR�z���gI�KG�"���F�M�����%�Z�H���(K���DeUF�����G1onhK:�K!\Cyp{}{���Y��"������'q}
��W�H/�&�	(�1��!*����E�1���t@��	JV������nn4��
���y<���h4�:1���w����q?��q�a(\�-�����$�Z����p��R3T/�Z}��q��+���������	C�����<���@W�)��eu+���
08�_��Y���b�*��/����N���w(�7��U���b���_:Y:H~��`i"wn�Jx@�2�|M:p�>|����6�>���5�MG?�!�'-�kD�����.�1������`|k��'�E\;�E%��S3�x
�`G��b��3=@��0�7 Z'���T��B����������#����44�68��#�i������Q���OXf��t�A|Ih��	�'���ab6 N�R��t��^
��=���J�hm�7����'��������������m����vn
�$�Y��"���A��?���u���mQehq8�n��k�����-�h��	�K���(��l�-|�.`���]=�Q��'������a��;��V���Y���<n8�[�����w�#-�'h'�5�����0�9%���\�W���������~������C����j�s���O���AR�B��,,N�h0����v���@������x��c
��O�}�Y/�H�
a��a����_�'��l����9����G�j%�rTw'��B=4�-�{��8>`�,?����t�1��7R�R��������HQ����u��6�C���R�O���!�������t4]�i=@�����pH�N�R��Eg�|�����,�r_'������B:�+*�(;����|���`Lb�y��;��������Ja
��U�C�TOb�0 Qg]���e�/�8d�$$%�)���V5�~)��;������K�������wY�n�6�C�6E�s��.�����~���e~���V��h�ru��|j�7��Rf�7����'�f��FJzvF-��n��3��G�����;��B�Q�U�D��p�6AZ����1/���]�5:K/��X���y�&�Tl������xV�{���e�����h1���O�w�"^����
���
���F&���~m�9����)A�ww>�u���o���:���ky�jSa��0K�7]�P�E������`���r2���U]#��U������H�(>��(f{g3���-C��&��������
�Uxd:X{�V����������B��%E�'��0�ro��� ��&��p1PE��,5�jV!�7?��:x���S���KK���3�N���#�+*��#�%thg_Q��V�v��n��/Gi5h�D^�9���Wt�o���X*?�!k��n�Mp������42��� U��
|@����t}@���4���M��@�����H���g�����g� �4��6�����u�[�����#A'�o���)�]= }%� ��Q���q��H�H�G���ts3����M�(�O
�=�:X.F�Z�Vh���!,��VP�����M���q6���}����jz���K�����+�{�����?B��m,�����������p�0_|��rW��K�9�0@�����J
N	LC
�?5X�~�~���������y4�����l
[������`���=6�s�����?;�����Z�O`w���1�P��^j��E�~4E�* �B�L�qF����MDIC����,��Ue�F�&�x��t/J*=wIj��&m�l����Q%��Oq|:�K_R?�Z����}v�
�NG�p�
#��"����09;-� ��q�Pl���f�7�W�������5O���%_�
�m5�jv'��	�c��6��+���8O{9D\�D;��C�	.}���%��m�1m����:�]���f�����3�J��%��+hr#��T�y���WL?�����4��p^�j�J�x��������n5��������}���������n�m�/b��������EU3�]�������V�(����.ti]t[��1�{q<��%=����aEE�~���'�t6�����q�o�Jq�K���U4�XzUF^�t:U/��g��k�a{@JDgN�SU��xqc��,DOj9�PVyw|���U�z��P�6������w�-���tC}������z���K���%d�t�������N{��[wC��<���#���Q���l�ml��*�K������a��tU��"��OOw�������o�81+8>Nb�p���Q��������i{3�������S�o��DUG��V��%�e�U�b7y�Q�Mnl���%����|u��fQ~��E�3EDR��*M��%:M3+,Y.�S�xD�� �Q��-/Z�g�	�u;�#[G.�"����H�������b.i����uC&�fHgc����V���w��~���A��*2�@�?{��ZE����� O>��f~lL��c����r-����Um��c_l��g`��
�\���j-�,"ZoE���-[M�S~�[rs��l�rc5����{�6QM��p
��=x�?��H���0���E���Y5���������e�$�����?����--�>.H�W4�+4���(��7���c�����9_�R������x�R.W����������o��t[��m�����`�n�]Q�S�x�s�i���U�St���~36�]��Y��m�?��Y2���(b[�F�����lk�9�������f��cf��\q0�g�-��������h�rI��������O�ig���E����1�������{���r�����g^�rz��
8����}��I
�������JI~������Hz����[;���IJ}��K�����+�
�ok,DM����B����C|���gp������������1���I�������!������������#5�M1�����6��s���.,�!��o$i���4Wl�v���F��7u{m�M����o["Vu�h�O�yL�����#��n 	��z��@�y����x^�A�|�����?i�/��(gQB�s��tP����sz�x�f�V��ot�J�#��"����
h��h���X;s�E/�B\D\
}���2�0��7���@g���.0�� E�I�2B�aj�V�@���6P
X�V�W>�M�A��:v;�Q��p�f.��Y�I�C��/[�[����/��q(����>�WU���~���Fr�����4�3�`��Sr��T�0TL"s9��Ok.�e=��|(�tB�f�s>��S ��8��`�F�C�C�@9@I���
���#\�x�}�@���r$�������]Y�Z./��
q`����Q���]^$=D��;c��(c�Qc��X��RHt�
�Mi_v�a��EPH�H]���@
J_r��kd��`�������B���q�)�qu�����J�����1��"�
�����hq��Q��eX�U�n2��Z�(���\6R.M)(Y�5�m���]C�������qU.��%���ME}z�jwH�d�� �MA��[)�o��1�~��z��<�K�����_��n��'@D�)�F��b�t��D������������J�����B|D�>a�@�?^g�ql���	I�k��P���U�5Ou��yC�Y��jZ��qNW�.*��������5��[[-K&�}}�������)����S�[M�B��Z�+�c��
g�1y�����AJQ�L�QuG��X�`���@�B�u���)�2��:�����&j�M��5��FyT��Q�ng���xy��*(��-{�?q����x,���G�����NwtY��]�����lI��h�F��d0v,jR>)�m��� �x�������o�@����m��S�$9>ae����!��"s��q�Lf�R��r2R=I�i����|l.R��h�Q�j-PQ���/uyk
C��
�a��48��W/yp�M'�m�p�t�<�Y�3�j���U8���I�A)��8Qp<U7	�E��H@�.@��Ib5��J�'F��j�45�f���'Yw^�|e�1������n��X�@�c�������dG��
w���
��6����oJ�x�!t�J����R�������P�"�RzCx���1���9��[jH|�������0Su$�!9�w���1�~"p�%�yT���6��A��9��U?���j
��;���%C'H!M�S}��	�4H��$G�����J�<���K��W����%<��Yo�� 0�DB.T}���0^z��U�s���h�8?�PGj
��sL��*�G��>�#.$1VK��Q��]*�X����*����&��^bZm�>�X[�nm�
�
�<+��]�9�oz�_�Y�E�����F�M�B�NY%� i�Q��������'��jq ��	�^�c����!�#�}:��r�+�b��E���)~�������������<�E�	_F4�����!��Y3���@��4�7���@Sk0"���8��n�A�q>8~A����	~f��kK�?+�y��#rR��t��EKY%����	&r��K8sjE��E���1v] ���@��d��{�S�� ���ICs<�$���D�T���9�J��X�K��VT�3�����"	R��i�p��������)$�h����*�����.���y�����/������!.�e�ei���S;��T=5O�n�����V�~So5���#b��9"b7"v�M�%���A���V���e���cE<�����j�M��=���K��67��z����M�~S�����/�HS����k�Hg��H/���R�iQ|U�-���/^i�yj��V�`4l�o�\#EQ��q��X�QF��4��Nk<��2�2��"tx6�2
�(�`���W��hwpi}P����&i������o��(��%8G���Ml�OIO�� iX���~��(v'�6��b�VP�s�iy��s���%�W�+m>T�;ju%F�x�
��#��fZT�~0�C��+E������a�h������)�)��x�O,Z�"B}C������Cf���/ynq�!,��t8�Cs�#RC���;�$�^�7��/z�����7c��*�R$��$�4i��)����V���s�X��/��.v��U�*�o�l�2�C���E@�O��*�T�/�VH���6�����\��s���J�r�V�����&����P�r����d^��q���?���8b����.~�9�K~F-�7��%W�S���e��Y;�];�i��k�oG��Z{U��i�R���v�%W5�zQ-��l���Z_�����?z�g�n�}�y37,��N,�;3q��q�f����sk�,��+#yMj��g�7������������S~W��F�h�jK��6w��|�Y�+=<o���J�R�f���u��i?o�eq\�!r��1E h86��
076o������Z��������~vr��BUM�b��m�p,�}t1��CF�jB	p��ZW�C��X#�gt!��7VQ]M4������<�&�*(�`�(�?GQ%�d�
_/buOB����w#�����v�vH��f��wC��.��x@�}�^�{r�����}�1�\{��&���J(�=��1=t)t�5��t�f7�
K�R���y�������0&>�-8��N��o���}���Fo�WM�c���*������f�J�Oz�������_����V?�05��'����&@���O�=Xf��)i���j�X��Zl���5E��1+b�Kxm��O���nI&Y���+��R��D4�[��Yo�������{m��dv���[�n��*`��tf�tv�[$���td)U2�)G��v��	+�J:`p�7����@�p�I��e9�DE�k7��:z ��p'Nl#�
z�|�0�E����F��q�Zue,�<8KI~��B�\U.gv�eY�/�*Q��gDc��x�&���QN��n�y�4�_V+��
����qr3}&��THu���������3�kb9(xV��3F�v������ycE����Y��k5m�������bd9Sy��Z
���,f	�����~��@�L2��bt}�U�z��>��������$�l��I�����2�fi%���$�k0[`&�l����33�t$��0��x�E��A-$'E����u��SF!m0=�a�� �|s�^�����ISU6�/���c�XG1^�p�N?�@�r��~E)�r����m�mYWo��������/j��((^��q3#����	i�����+T�3���x���fMV6�b�<��!?��Y.��a&����/����hs}{�V)l\"s�����EW����X�O0;U)��*]u#WKc�f���g�������
�&}O0T�F)�]G'�,f+��V�l�6��A_�:����(ix����M��
������_�X5��fs=l6�2�_��"!��t���[����Xo�]�)z�?
���K0��86.�b��P��F}3lnXQ��Tm���-&���:
���a�5@����r2M���K��;%|��V�}�W�j6��8��h����{;�������w]�78i}�
�}��d��������!5P���L�
_��j�1�vZ�$�Gm���z)��Y�n%cW��R`e]�������1b9���MT6�;����I�pz�")o�i��F\�$o���
���3���b��4�
����)�t3��'���N����9�r>��:�/A�U����Z���$��f4
�q���S�8%�}��}�|��/��vc� �	��ar�/c��2�M�n�IV^t|���T�O:���h�Yo�1o��hD����[�
����y�s5w�dB�a�������/�Q�"���L��v�-E[?a����nS���W
��������r�#|D�gq�{H��F�y��E��HZ�"��������a���"Q�tJE���;����~���`V=�`<��tB�#�C�E8�
��D
���"���OB�aX5�g$�r�O������A3�L���l����������
��?I��|b����������@�J�1)����@�s�!EnA��j�`���j�j{�@7P3����<5P���^zR9�@��Y�d�L���O_�O���v\��O�����R�v4�4���Q}=�x��h �<�UB���,g�akT��m�����V>E!�/���L:����
0>66����/O�Nj�z}l���D�i�`��)c���/���|�,(�p���m��P'��b��k�0�������o�V��'E]���O}����x�v*���Q�;l�!������`E�o������G_
������G������]:
�S�K!���2�/l��d>��?)
1,3��7��+Zxzm�K�c-�w9�g��S�q�Cu�SE���^��gS��>"���l����Da��d���d?�-��<00@J��G��R��
�MUO���@��)+d�2��b�k�s�����j�Lu���x��0���!rr��%��|cSqT�G���1�D��wr����9p>��}���G�/�Bo*+�m��#F�-����c&m 70{Y��pT�����W�p��T�������9�\h!f5M�-�[���,{>m�uYL��&���b�/��Krg�m���c���%;<�1�i�4�=�E�Q��m�#k�Kw��*�R;R�����b���-!�)�[����k�>q��.Ti��j�q�IlMu2w���4U�mP�nQ����fB^�s�������@!x�k����Z9X���A��8G�`W�t����F~k^��G��z���$�\� 	�����r!��s��z ]�#�eo��S��0m���g2}�{���?�����+��7��{=����UZI����7�q��"3�*y�x����t�<���3�C����=t�^�*E>��-�����Y�0��������bi���{�j���}�m��y�!��������L���^�>����7�?>4�{��u�_���gB�T��d]Z��^|�
�I�N�����T�[}p���T�]bB�����T�;!4#�Hq%c�&6@\5�*��"��A_}��P=
]!����Jq��)����`\D.�$�h+>j���G���T��8,�P�.��$@5i�)�T�������S�He'7���|����WHi��4����9`� /�w�o{��� ����I�A���H�/���^���/���^��2���o[�����Xf�{�~������R�}���6�=��f,Ep��A-IP	.�"L�~�����V�W�xyUL�<V�O+��������y��
������j8�;�I�7��/�K)�}7���B�5/C.� ���H</�"%N���T�EL���T8y��I�����f��#��W2�����HT��~��4���@7y����J��������B�hW�[����.��
��EU���OE!n�j���������V&�l �U��g�nn;��N����Z
����`	D���L����)y�1'����p��087��'97~�V�d�����7�TX���q�R �������.=X{^���5'������#h����B]J	%�\���%	����������_��ZqRg%���!��`�`h�LB�s��-�����1�����o����G>}�����G|���B���w�Y��>
��.��)��8w�.��
���I2��*�g��(�����?�t2((��z��w��A��NS`%g �l�^CY�a�����]��q�\p �W��q�b��#�cwj_
�S+ +)TU������Ep��Z�.��`��Q#��P>v����_�iabT]Dx��P`��)�H�����\��aQ����3��:,��b���-��#=9x�������������e��q,�������kE���Q�����W�lc�/H��*a��A�2)'v��d�B��-U�(O��]d��)pX� ����	
��d���t��3S1s��X����2\��T�������������,�r��C�::
����� ?��m��RA�Z��I����P�����s��Q?A�U�z.���J��k�U��%�VRe�������eD����a����v��.���8�q*����Qe�����KZJ��]��7�0{�����{���%%�t`��
?��"?���-����J���r��D�E9�a�W@k���`U�o��D���x<�c�����/L0�'{c��N�mk�!vu�U�Z��hj�j��!���4D*�"e����G,��+2c��C�����7�cO��r�!�0g��RP,�L���m!�"V%���bu�=����%_U{�����=���.���^��~Khn��6��]ID
�2q�4*�M�8����C*#��m���8KK��E���
u}�l����a@����3��n��q��������������{e�8��F��L��A��9�6�0W�>�������X�P)�e��,CA)ZE�6�&sK�$Z���������X<���|���{�yc')6�W�:�~g�9���J�B-0�:TCR�T�Zh#
]O�ht���0��-�U����u.�g��\t\���1,J��$E�E���R`A���,.��Sj
���6����*7�m�A��[P�!#B����TA�J��-������@���!}2K�M1��0�X*im�3����5"gb8`�Z>���T�/8y�3@�I���ui`�:fa?��,��N��~��wv�&*�m/L�F�p!�P�F/��bKX������'��T�dE.-Lw�2��b����������]��������^���=����|�(���v�����4_���GG�uU�Pu,�0
;������!-O���	TO8K8�������{����Oj��Y����,� <J�RT���Y�*��(�Jf.�}�Y@K�\�I����H_��}]Z��`��)��X���M�pP
=��N�C2�Q��"��
��3)�%@�d��5u�	�>�����upg��X.�l$�tr��p�gn����y=b��-�����s��+:�+Vl����b�J�P��1���\c��y�z�q�-+�kG~��kM$���#�t7�U��LQ�����^��m������]��3\�L�W[
�p�k�I~�Q�M��=�#��r���!D
M't�!w6��@s���A��fI�����t1���3!���t`W����4�S�w��LbM"!kt���1V�6��J��
�r�S7��v'������7�w�������6�T�z��
����?e�j��l�O��<�����{3�;��-��z����R������*a:9eY��{��`W�FWqy��D�_8�#��)��d��H���wc�����AV�g~����/\���xZ�H��5�b��qF�[Y���5�9����?S��S�HG�:R����;��`�(�|Z]t]�=3g��~n�K��
]V���6jj�}������q�p�*'Q�>��'�2����To&�V�6���N&�D/q#G�����aY�#��U$QM� 8�����S��D������W�.oL�RpF���s;P}58P%�8tJ8q��c�ZJPv��8�y���6�v���2����s��854�D�N����S/����L��1{/��2��Y�j�Y����#��^���\,�����^-s4~�B�o�7vl4���b�(#� w�_�;u(ov�����h��rld���Jt��
����Ji�~("D��I<�+\���-�P��������$������U�)��/u�6����zQ��A���k���E|�B;.R��S���r�]�2!�1��V�2W;^5��pa�W�Vz<�5�����Tl��*�`��N���VJ�T����*������@{��r��
"lD�����<Q�V���
l�K�8`�����q�"����
����f.����Z�L�H'5�l�bx>���$�D"���`������,��s���(
?���S�����E���L�\�$�����{�P2�-G�,���]6�(����(���U����l6H�����x�����Q��BY����������]��3?H�nm��DX
3�b��2w����(�P:w@w�����$m*dx�"VvR��aH�I`�1$������B���d��dy^cDPq�����,� V�8���r�h��?�[����:bR@�ezz<@P1)!I��;��0�=��)���vj�x89�0l�D�+���0��zR��bE�#�Yd���y�q|��q"�TQ�����6���������x��(#I��m�W'��A�+\u�w�y��>��cg�M|��R)��R%��1��b�����-tF�V
������K�%��(�F?'Z����t%�*��SMS+f�P��A<nlU%�(%h�U
8$��p�*�<�|����V�O��9D�����G����RV�������������s��i�������������D���*��1���j��^l�C��WZV^�B��2����"��1�/1r�8R2@_��P��gc����C�])M���c��jQ:��tmB<�wq������I{����}��wrp��f0t7��
0�7S�s��vj����4��� �	��`_�GTp���b:}�rY�&��&B
omM�c*�������F��5;SQ���ax�	<6pBc�i�8��`_��9U�[`������n��xg"���z�jY�������x?\���R��b�P�����X�BH3�5;�D�L0���U���!.��3y��0��d��(�E�����l��:���}(�����0�Te������Lx�����&���`��QD~q7�e�
P'��
�����ON�C�:��h:���������Y��R�����@��$"6%
���>=����A��u�� �T���Q�E��@��H�n.���j������.���)���p1���e|�$s�dN��0��H��JO�ko�#*]��k��%�v�M�5�K�V����[�c��:�2P�V���N+�e�6~����p��~M+amVS���&	b�3����T�o,b���	��I��I�����X�e�P��9lq<^�����@������n!�Yh�kp�8o��h�o��F���|�U����6��gr�?�Z�K{!x���W/������4���}�&3wj|����&3wjg�Tvk���,����r3z�'&c/i���f��UTo�OP�����^���=T-�QLTHF�^�I�2��x��-����x"�F:��pcs�i������Gf^
S��{�*a�Qr��	Wm�@^mR�_�����.Xpz�=F�n{��a�$�R�q�es��}��-&�������[
O�\����)o
6��qT�t�����9���������;}�b�2������>�V{A$�r6��=} � �4���<������m���,s��k��������bv�m������������������*�rim�:�����I�:+�l�-	]�	�)���:E"�:�����r��0��$��EE���(���F��%+�{��cm���C�?��������JY�� V�"�w���hc�^u����d�8>l�'*��oJ.����u]6�G���
W�XoSO�a��S�[�p*���BR�=Cq���X]��$>�*�B���_k�%�19;F����REE�����	:�X�yi�����7^h�����n}Q����9d4�����L��K
]d��7F�H�Pkj��\f������J9�Q�'u'eo��r�xi�xg���X�Z��4�"���)EB0~�)a	!m�H�����������8����W���J���W^(�f���/J(��$:E��y�#��w���x������`
����t����������-��~|���1h�J�{S���\P�#T1��0,�(���}M�������n�6`_��p�Qw�<�*�b�z[�j�F���X�!����`�T���TO�a���LX6�arj�����+-Q�rI,-�"A�������1	to)�I���>u�����Q���)���I�T�����3uY��`,�to"-���h�_���,4&�'������	�@�b�\��f�J��N�������5�Y��2;��z�
���5V~��9��kl��D:�@��S���v��=����b
}\+O���B4�_2���t�h<YS�����t��Lk� �����}-DuI�8�M��"�iP^���o���!5Iy��X��hp�}���1S"�0=1x!�C�����X����E,�R|��������@�@���y?6"AK��gY�7�6%��K��jE?��x=�)���F@��UW&B����/���������.Zn�k1���m����d���:\|l���"�]�yd#g�R�C0��<�;N5�n[�Xs��73�>�U���*��0/��.x:����3�z��us�cb{������MS�tV��j��z���m����C~�����!���919�=�������mPd��*�N�G.V���L�|��������o����}~��������z:Ve���<0�S��@�����,
��r��y"�������>|N���������6N������b��Etqf@�:���v3Yx�����b;&<)����i�KFX�`�|����0Vs����A;�%S���[M�����
]�Vt�y��������d5~PAI/��X���W��`?	��qWJW*	q�>�B"Q�,xUVai�P+�z��,���-���W%�������5|C��e+:��g�BZ��>�O���a��	+��gs�l�b`R��Z�O�C�����W�h���`�&�X���x������a~��v��e����H!�7���w�^��u�����8��|�����\�p%
�=A�?��1��./�����
$����`Jw�I�F���:]q��D�$%[� g(J���3�0;T��&�.�8=��T>�#C��@�Bzcw��K��Gb��6i���@Z��@3��k���\��`��.�L��/-�U�ab�
-�gJ��J3���w���7�GM`�D�\o�-$��?��^=�tn(���UY�+�/lN�����~^G�����Ct���)��W�ie7{�r��,/�RxfB�R�F�h���1���?�\�+`]*�_�������1\%�����7l^7T+��P���u�=�ch��"X��A����M�����"Ul���Ta�b���t��w_'�j�b����U<��I{� {U�|�s������{�W/��p^>^����{T�D���v��d�p)7G���I<�QX�XP��bX	�U7��f����`#���R�"P�X�EI�+QRJ�BrT��c"�#��6/�:����j����G��N�����U�V���lv�����M:g���X]������$P�q~1
���
�&*�����w�^��g���E�ubLp�A�Hi����'�)��+(H�
�������H�h�K&�M��3z�B�-D��.+���^'!�X@G���2z5��WH
�����q^#.n6F������f���p�@~?[F�PXh!���(_(��Z��T�.��V���2��,�U��H4P�4X��a�1Md�����xo���PV�\c%;}�OV<�;�Rl��&f%�@����|�fc}�n�o��;n�������^m��<�i/`���7�����u��r�P���K~%kK=�B�N%�+�	2�} � z��z<��q���H9�r{�����F����R	n}����J�\�}�����>Krg`�R�kHb���k��T�FS����o����D�`(J�����o9L=-</��:q:�H`�&W	e�����XaOO��DF��s����s}�:�q��*G ��#���pK�'����s�{3b��G��\WN�#?��	���K
�� ���DEl"��������V�T9=j�1�aK?T���Y��`8�EQS���e@����W=h��Rx������/k��&*(�jF�
D"�&
<�Q5��9�8�T��<��x�e�2&S���
�'9���j.;@P$p��s^8:��k�P��J5�)���n(�����Ht��>H)4�#]g� _�a���}�~�N��BI���_W@�x��\���9�U���|t������W��*���
�����;��i+��b��Vw������d�d�%]qA��+�!x>o���J5(��k�#C_�v�0��<��R�h�/�[[ �w����D�u�n�������r��$H6�lp���lx\M�5�W�,���&�{e��T$R�n������
�p�\����X��jM������c�D0y�e���K���0��M�Q�"�]��w�������k?~�8�>��l��/UI2���%��l@#�Yv����*���}Z�7�S���R��x�:K�3���
Q/>[�W���TQ��JU!����Q�Ofo�Fu�Ga�c��������9f�F/����mNY*v��^;+�vE����<e']=�	�;��K�q[a(��'pG�i�fzP��e�JR�f�9n��)A�4�K�e(�3
���D�8`'�{���69��Y`��@���C�'��*�
#��rx��9��OW�t8�ac��{��S�R�L�s]y�7����?.�bO��a���f����!��M/y����%nH�w�s:�5��[��;O7 ���A��*��_�����|�$hvcE��������$>��Y�T���r��t�Q�mS_���&�1��&���O#�"���s,C���3>���+-��MU&B�#�coBVH�9[���8u��-�����k=�aI���,xw���=>��;���~<x�=w�E�_�AW�T�`�2s��	����a��zm����1u�s�"��\��<b�
UC�
��������F�>����%
�&�Ga�+<P$��@�\����0�����"�XW�I�>��s���a������"T���i��7�����)&�6^�<�i���\���A�9H��J*F
����H5��W']
j�]U����5�^���prtr�FIJ(��]T����D(�">�:��[����-�t�|�1_h�.U������a���9����e�[���"k���3b}4P�FL`u]|�$JY�l�����u�����5��VH�6wU��I{Z*���T�%]��\��X�{J��:x������ZEu�j� �q?*��V��+���������q.�-�4���Z^�.���[�+�'g�,TFE�u��:�C�c����B���C1q�
��Eu�S�n�������$i���f�J�
"T�K}V%��=�f��T�!��Q�i�<�t�����&v����B�Y�a$o�)��
���!u/�E9�)C79�����`�����O�Y���m'�4���X:�*d���P8$�%����>��ELj^N�dGX��$�us+|;������~�:�`���:��7�`��hn�a�V��|	�j����zs��n�7�}�]O���?����X���c�)�0�����WX�stD��J�L���,�SM���B'8B��Q�@#N���d��k�����J�����~��|a=�h���bP����-���-�7��~W�K|�V+�h�lX�������v���Uz~w�����{�����_"�x���xz7_8���r	��P����* R�--����'�^P�Ow���^�T�����V���}����E?�R�Hk������U$-v��@Z!���E:��_<���;`�Z���i���n��6�b��\���Qo���z�ZV��gC�J`�pp,�Z^�����)X�XH���8��B]S$u�c�������3[���y�%�PbF4�b�����4���Vss�k8<�.:p��<���!��������c]�����@2�-��}	=e��S^��������R��/��j6����m���M������J��@���V $gU�/uvl����x���K��
��FUm���f �7��j�$x(����L�S:���`%o��Ym��_m7�h��8xX���x�f�nn�H
�?�R��e��p��S��,���}���/;F�sss3���6J��w�bb������No���= ~���[+5rv��<a�\^�p���_`�K�yC�i�X�<o(cTMz�
�1�[�m�{<A�oB�����<�����Z����5���a6���j���L-����F���n�Uz�M|Z��ns�Y�J����j���,M��4���G��R`{tN�	��q&Q��F�����q�����7kd�N����
3*�Z��m����DSq����"���w����W�����a3���~�|,!��h���������v�^������$�"�:�i_�)�2������w����h�YH���43x�"��=R�/e�"����Bs��;�	C���^�Z�0K���D��Q��zr��ri���X���<}d���b��!D����k���O�M<�F)}�/=0Y*�T�=
�N�Q(?��p^�:5�j{�U�o�Y��+�j����>x��g���V+�v<�7�J�����?~���yY�����z;x���,�T��TC��o��X�qkKe�P����NSK�;v�����)D��\��2JWmk������*�G_&y`$|��K���.�����:�}#�8YW���b@�%��p����o���2G����~��������`�	����|�$D$�N��/
K�I=�%�*o�%���0��Tt����T
��$��I��
9X"�����/��:*��o�������������:K�'c7(��m������_��+V�
����H����zJ����ms�	���7xn+/�o8���o�e�5w�����9i��m�/���������(���F&��������r��a��1E�.�v^�B5,���K�]n����W4)���4v=��#�y'*4�
�'�'a�,L�X�1�&������}J�/�p�>0�P?STz0�L��WG�0�8B,�AL����L#�60��s��C$h��`s���r�9����8�|�%��@"�������6��02�U(��DIz�0���`��X�H@*}P�0W��d�.v4��f;�E��+��D\���o�!�����D��c��_F���)����Ff������W�x8���
2�b�{���X�u)�����<�����]�:�B
�k������d��QsR8`�WV%^0���G	|���!
���"�JQJ�0��8��,	�v�=
'.�L���FF�{0YT���j�r������##$���JP��/+��d�`��`�"�P���%�������C,�L�R2�'	�YK�e����f^OZ+K�]�V�04���6���yF�z���1�_�u<b��^|����3">
�gK���l\��H^��7���p��$�yWX���{��^�v:N``���-P�"v��^��?�����-/v��r��Tn6��<�����Q���;#z��z�+��o��[l���+�**	Rx?��)Hy<b���V�}]f�PU��[�z)�YQ�<~���D���s�$�f_�����{'�����G������H��s��v��=����}�����7'������U
��=H���j�K:	U�N:a~Xg��+>�,�q�Q D4.�9��BP1��l<��4�:���1�������,r~Cw�
�]Z���\#�A39����QL,[��!�8_����%���|y�o�WP�[S�{�z�(X���z�����I��	����u���p)8h���������4�	�Y�����(�.�������_��9IyK�i,��p�c�R.M�����=�9��x�e��H�\�y�r��|
����P���e�xfA��%�,����g��iLTL��w�DE;�A��D���0���;d��3��f��y2G>����yCf
nm/�zB�z#2\.E�F]]��h�Zi->��u��y<i�I���K�D;I���~���b��@:iP����$C�H�:#U��-=������HU���+�7[�j��������de%><�C<|�2�4�n@%�/�� �����"��Q�w��s�X4Dk��`�S�L�����/�'�|H�6_j���`��<]N�xt�9o-?��;q	�l���8D �X��;�mYu6�(pp>J���di�|4!����4��O��*�B�B����������@�9P�a0d�f�CYs�7ZSDW�hP&���z����7���G
>���.,,4��|��=�|���}h�Q��~�~_��g�������u�<A���mr����
t�@�e"�[ar����q�������-��*����U��]"{�^�%�2Q��N$���&Q5k0��������i�H��(X��@K�F�������������0?CG�
��)� ��1zF�G�\Ti�7Tfy$�@�$aR���A��\�5��gj��i|�|����pT�9�k�O�$��law�����,S�;�����"9+�k.�����>4����I�`�c�*o��=�rb�{Yv�:3��$[�����^���12�������\�*2���T�oO��(�A�!g�QG�V>���Xa�C���E$�^�h��=�)B�����J�|H4n�:�$�K4R���m��(��A*��T��YjN�$f���|��#���~?�&0���}P����z������?�1���u@���@��`�^0L�����^�WD��1����	j������t�/Io<Lz��C?�j�xH��i�?o�j��OG��"2,W�)��t;!x�g�!���!|$���0��X>ry��w�~Q�pJ�:@���aE�Iz�����4�5c�"��:���#+`-��{�U@��=;�;E<��
��E��~�}Y�4s�7���hP��#<I�@�2*-�/V8��r�2s\��,h�>����B�ube���S=<t��`x����
��9e������DyF��+�!���`-��M\0��Y� `Y|pI���Qih^Nf������v���(�(x�5����}Y��DO3P"� ����j��s���\������>5s���x�L2�<�T\&+����H_���LE���4�L/��T,���%�p���[/F'�q�_�� ��Xq���*��jl�������
�����'1Q{��e	�Ez�:�R�K�p#�E�q�!sDb��q�{P<����%�_����i����Fr�tg1���Q)�J�� BP��i`6@���`�2��RW��+�,V=�NR;�c|�+�/��
�~J�A����C�aT:��t6&��L(���uhA�	���I\�|�V4��^lf3V\�6���KJ��2U�+I�,��h�,��D�Io?
����4��W6C��HX!����e?��>������������Y����z<���)^�f�$4��)/h^�5������+6�E��g�A�X2��zQ�AA�����/��U��K����N��L���D��4����F���i����z������M�u|��H�O�~'Za�r'�q�W�������xW��9��K�F%�Oy��
}Q�gzva�cz��j�y���`�?L;%�]^Z{�	����c�N����*��J�J�?�a*�c������������E�3���<�H0V���@Fg��YM6]����d���(�"K����%tt�@-")�'a��a�|�I���1�� 
;#�z��J�8�Z��E@*�;�s_05�y���_v�`B���S��w����>��>����K](��S����e�VG����2:�	��o��"� ��@Z|�h�����c���ED���P�����9`a�����<�Rj��6�t�z�B�L�X���n������I�����h.����
pXt:\����?��',,����A	���.�����y��;��^Kk=��$�<�����C?U�6�r6l-U������
>sA
�G!�#�\]~(m?�S�rD���0~����\��1�@Xd�r�|O���N��y�%A�2�8}o|�^����>��s��ya���J�P�?+��9e�}��"a��Fk$�C��dZc��_.�{t���Tt��$,P��R�Q�	}��}�����v�!��V���3�a��e�v��*�E�c�G�*�QU��+�/�<�b'��ly=�}��=��GA	
����H5OO��Z|~p���K���jb8o�)\�0�����Vg��FNGS��[�H��.��i���W��`Fm��8�yz�	����Jy��2|��=!0�H�P]���n|:>oc��f-
O��R@5[�wo�
�2��M������:���!�x�E�6�����l��:z?Ui��Iy�Q�F3����v�����5l�Ta��������N:1E����!%��/fH2L�Q}L�d�K�DD��o����xd��}�����KZ�����+���>�}
k=}�tGxx��=�U�����������3�$��!�5�m/��������8��=J�9����m"����`wg3F��`�.��� 2���?��H��1�����u=>[���(6�M��mTw`�?��!�b���"����m� <���g����2������H��H`�^��,o�����+1^ow?P�k�K��=����d(Oe���7���xz{�������/��(;�3W��Q5�*10���,J��z�C&�Fww-f{�g���\��QA�-:U~'����77��V�Q��T}��N�d��4J\n�jTPh]�
&<����K���T
c/i���g�������������f�H�OX*��XSb���y��
�u�f�f�0�s��������fgAPyeh���En�s��#�W��y�bL��4������bt�������BA����I��\<~�����q/��[�
1����iAj%��^�/���F��:!Ue�7~Q�.����K!�����hz���w�J�NS�y��X�m�Na(������0B���Svxa�o���C�wV�w����[�+:�gI�gy���^�{��Q/��z�}=<V�A1�	,�I�{��+�O'v��}G�Z���jqR�N����\�{�&��t��)�{������w����������V�YL%�z�/��n�S'n`8�*�+X�u�4��'�r�F�%�uT�N�DR���%F,r���]
�C|�h�������p��.j_5�*�����,=��}E�`����q4�����W\i_��(��y�]���y�������5�sU��%u!g�3;���'��(�f��m���ke���T�)xH
+�sU"T���s����S�^^mt����Ijq#��O� ��7c��f�P*���So"�_��lVf��C�y�
D:��,)�G}'��_�N����1��JG�C3h#U>�/E%	����&K������OU�Z [`�lo�r��S�)w18T�����.��T������KbF���
����s��S�Q]>j����O����^��T�p��9
�s?Q[H=v��V����
���"[����X����}�z�Q�����+�x1�)���a-��Rnmi��&b���^�n���������|�D"�vqS�h��NY��j{�H���4f
�`��k;�@p�s����$u��K�F��+�+%�*E\"xsx���QeCt ��_$C��'QZ�����/UT<<"U��`ob�C~9�5�0���j;�9�9�xd\�A:"�"�\K_�d"�~�te����6EQ�_�c���|��?J������������J�P��5�%��,T����A��F��.
���R�����OeI�y���~_����W#t�S��(�8j�A��J�\��N��f��^*`�����lq�����cuD�T��(�4O1LIQq���zX�kh`��d`�N�i���Q:T��Q)����G�q-�n��m��F�vK'�w2�C���(�>�Q\�o(Q�"�X��]3�N[u5J1q���������!}�i v`�w���D!�S��G��
=�5�yA���$���2��)���Y55��X�7��/{�x��3������i��'�������a��uk����m|����u�f=P9l\��+d�*1�bz��*�J%��l���C��4�0O�����;�@��o��:����]����,LRd>����"�����)W���G��}n��Hl���Z�������(�'�JU�iz���=��H_��p���k�9��33����g0��U)�~
��t���6PLm��
[<�RqC�4��r1
�rP7$C@U7 ���`�6K��&�%)_g���-/�k��15E�	��WD>�
��9�.�0��g5uV��Y%:���-���
.4��[��<v��wi��?�m��KF��8	?�[��R���������lg{��m�Fv�F�A~��������	.��6���Z7LD���Pm8M�w=w�`����)c)��=e>��5Vr�t�|Q��'�T~��\N�� ��	��u9���?F=��r�����%��O���,�T���d��h��3L�F��;�zD��.�������z��A�*Q_�m�a�����������TkJ�����3S9F��G��||��V���&���J�$�(������_���NP��b_}��A���N�i����Q��LS?�����M�a�D%����&b$	-!U�2��E���A������������'m��~����y�4�h�[;s��tO��)����1\����7G�d����	�'�c��Ro����[3��b���w����,f���s�Y/��\aAE���\�=F�$Ka@��y/=%�%��e�pW�{�nGO��A/���(X�#��
���E9�x�Hpzbx���O���3���:��s���nq�w���yL!�q���r
~�����:\�g%X��������J�f�RV�V,�h)Y=~�`���D�|d	[z�����[N�;+T���*�
�Hd^ K���>��{+������TF�����s�u�����ce�y0�F\���
M�(x����9��.,��I#�(��(�<�k.�]�2����|i��G��_�q����:��u�K(��u��F��{t.�X�@�F���s^!���
y��ZiD�����q���:%k����5t7w�
�7s��fme�p��:�T�������z}��	������&��RtMec����t���������
qk�+7��N��8�G��,��p���$�2B��h�������#d�4�'����/d��o����%�TE�����Ez�(���Xw?�J* h|��Di��Pw�����O+���k�7����>���i�����)d,t
����c.\��h���O%��n	-`U�����~����qF�|��0x�s{����w�?�/yh������A��o�KzF.%�4BY.�Hz�^�tWh�����0}�%W,1�d-`r�;FBP]����~�JE����0�&�I�kbF��?���s>X���
^"�����b����,<[>��G�������W����(l=GZ��z)��WX������kH>� ���o&��`yn�1Q��(�
����$�0�0T�-D����r����Y���:�&�w
���
^�k�����EN,�'a;j�N�g�|gq��pn]���	��
>0w7xV��BAUNev����"H�0�3*aN���i�Q�4��H'HD��+�}0ah�6�����(MP�'��j��V.B�6$���My��e[f�0Z�?���Z���Z���Q�,8��i��Y�����k"�)_<��Z��$��@��q{��2����E��E�4v����Y��3�!�[3y
�Cj���e�b��y��ah���m�����b�����R�6��`�qL���|g��h���*z[i���l=��"�R����k���O�tO�6���$�#e�s�~�Z����1���>�1��#Z�)}�m����c��1����0"�0D����"����Hzc�������sL���6{�q!R�A���#H���)D��*"3���g����`�T���Yrh�9�SF+V)'��B���������(�|aK��?�D��XB/5��	�b4,4��c�[a�f�O9e��$L�S�r*BNo���H�`mR|��$�7n�[k.���;�X}�L\V�����>+�[����sc7��fx����R�T�)7������8��L�����9�j�[�����
V�z~��+�,�)��1���y.*?}������Yr�V�pv����i�z1������D���k4S�T��O*��}����J���_'#���Wm��p�!�������F��%���-G�cA����zP�dk����dZ)��}��\�.�+:��o��kz��V�&��)�<b'IPz�/�5+4PL���
+�^��M~m:���oO+�b+:@1��l��u;���Zmu��e�uqU��
�{H�����6���zmz5^��%����R��l������t��\�$TC�w�Y��q��^����s���<%%/W6&�S|(O%�9�8���R�V�8+{�/?��>��bW��*	I�L�|,U#�~���t���(�zX����[f��;V�]�]��� �Y����9T�W[
�7��M��I�NS�"��7P	�?�z��f������	�Q2.��2dQ����j]����Az�������u��xa�}��;!�}���@��g�c���L�&l4��L���Pb�677�������%�J�8���xi��n�4oGsR��+,s�pFO�r�=����EO��4������������I�x����Mmi��veE�#���-�&�5�@�w7�;Yr����b�A�7������M9�on�,�dz���^�������5�> iV�H���op�i�eQ}N=�M����2��A��y�1q)�=���h�V=�
����q�k|x��sq������H{N�m�������!���WiT(]����*�mZ���0�������6v>=?D������<xUX������7��EN����Gn�|/���>����9���hU�w]�����/�S
�-��Q�-�D2m��yQ8?���\�]Q��C���$h��!e*���lq?e��N��x������F�U�,F���������3`���F��'��Uk����
p��dP4A���<Z��ytP�2�(}�bX%��hA��a�]D�\'s���%�Q
�D�@+�Jr4��\�#��$�Z��;Z�
9��0sP���Cag����}�|�r��p�@n����7����n�OTq@�1���J0���c[}��r��t�6�������e���m[�p���%�����J]�9�<����;[%������-?e:^��X����-,���1�m���ZAQ%F�_|XR	D	[�d�Z83O�v��9{��*mH~$��_,���u�S������P+x���I>����fW��DH��h�,� �b�\�lAa��r.a���q�c|��
-�dw��=���i���4���;�y��m�'�c�f~����p����������	�2���X�7��|���+����,���5��/��c���9^���o�[�D�N]7�F��m���r��������nPA��kQe�6Y,���k�?�r�!2���b�d����,B�p�y�,��Z��iJ�����p�L�p)X	�K��)}���a�#\h����a>w������o����,�H�a�@r{���i"�?Z':�s����*=�����I��J����i�Cy�Bp�x�d�6)����Kd�\$=B��Y��_�8)�A���A���/��pA	�[�By������
�[�	���/n�����,�/z�F}t�qH�
W���O���Z���c��v�n77���OO�e��s�q���%	9��)D4��\f�|S|��7�5�x��F+lmJv�M��N����=� p(�%��2�����������a)�+(	�ue���� �����$��H`��WX���Y9�`iIa���:`��h$ rR��	|67������P�]����<e'����������6�1
j�:,�����,i� K<������O(I�L|�}��xG:�X���c�%�Q�|��e�^��s��Ta�hp����%�$�|���,��>;����@���0.�'�(�]���
-]Jg��������#S"B'�"��r��
M�ZrbO�J���A.nW�Fr�{{��V�rb�!���<�8��o)o�r�'�[�zr5�(�����~��8!o6�����LF��?31��3���M�6���-Ee!��AJ_���D)D�q�h��C7a��	3�+u����s���!��<R�1W�o
6�� w<
T�=����C�P�`��9���$��{z�7'�H��%������\Eu^��6�mC{��.-�X'ip�ba�m�����A�#���0z�� ���@a[�B�T�$��S��|��M-x��q>�,�T��<��C2��t���{�%�Ms���AapI)��H��N���S��6z��/��G��1�=����u"�	BF-��|V����(h��,��H�'���Hw��'��_�
YQ������O�x����oND�������F����b����������.N���d&���B�I�h���tpN���7��v15Zn`-(�N�Gj�)C���\�t�rg7(;������Z��^<8]X�@�X��B���P�f�z����e�?����$��>Y�Ap�09�-�:�7$�a���	�,H������>�p����uc���w�E6u�g�)~�BC0(V�PTy3�+�kG���3g���L��*��u64��t�.�]�#n���RR1��������N��0$�����x�
���%&Y����������;^�Y�S'��p��1���'��m?�dx����.V����cE	 �u��|�z�(>_E}T���*�7|���Q�h^�Y`A~��(8x
��0|���1q�)�JI�r?A��!���Lj86��m��M;���]]�
�p��Q�P����/��c��$��X�Z9wJ��[{�_�9���N�KS0����yI���^n0�"�~#-�3�%DUe}i��
c�f�6cM�.W�BiA����[� ���%;~Q!�y�!��O��"���
�H�Es�W����v�+_� ���v���(W6�|�E�NV���+V��@�"��GXp�E��X<�U-N��5����;��\zF�i��_����jF-(�!����7��A|�8��e/1*M���f��>l��adTN�����6i5��V�r(��!,�z��+/�CI�����P���B8�+�Z�'���S-tf-=����|?U���n,I��u�����W����]g<�S������;���
E�����w}]�/�qU�7lO�Z,�$���R8�
�n�1��%|��K�d�(��eA�e���
~��"��a��Tqg�ad8oR���:��>��FX
��z��_��B.^�������=�9#��T�����=o�t����l���N)�f�$��C�<�����#)��P��:d������������7����lI�C���5Q+~`J��S�F~GC�|�JV�{���$����3�SY�q*����}Fy!������,��s?:��)R_�����`�p�����>
B��8+�����La�K�%���TFl,����M�"%lgc3��2W���/��4k����������fs�f�Q��	�[�� *�1�AW��RLD����"���jmi���PAPQ�����&�R	43�-����n7�����{���T��(�Tk`�f>�2��c��v����E��5���K
��im�;;���]Mob�vu�u9>��������D.�l��J�bsee�8Y�Z@V)�����vi�!������'�x��oQ<���{��=P���, �W20D$A��0�.�eP���Y���2�`$
+>�j��_}]��7
�vWN�2k�k��D�L��w������M�??8y�{�#�g��Mw�K7�������
�cs����/\��������A��W�F���I�d���F[�u��s,x9hi|vOi�`*��R�QC%�_�v���$���`x�Xil�E@��Lg����}�i�j/����!�������Qv^��'
}9]T���p���}>�~�1���}�-�$z�NuA%V�Kj~a��_+���b7�P��'�F�MxJ^&�{���0�h�	���*
`��������4_�����$KD�O�4��x��_q��x������+��_i�&��BD�}L�>��~�8���(��<��K��k�`����,*��q������/d�DL�$�v�c����-Ot�9����9���Z�����C�*���^�����6��p��"_���S��T�F��!PM�_���7�o��Z�X���,�O����KDu�Y�����m8���P&+��f^i����������/�5�>����Rj��*DV��r�4��=����?�!���5�Q���~+L��/�����]^4��wv�>��yh<�0���Q���|H�c|����uS������(����&H�M��74�*���+v�a�����U��
A��40�s`�%aZX���#7�������X�D��pQ�����{��E�xx-��lJ��@�V���x�A�#�e����q�����oE�����;��w��s�r��l������(�?�VM���/���j��m�xO�|�"L���>���U�Q���G+�����,L���/,�\Q��V����7�$n��+�>Dn;���geI�A0#���o��=��Kj�.���6v!l:��k�P�.=f���(����(o���!������){��v��)�q�*dp�VY�,�n��ckvog��#��w�e�cR�Q�0���W�blJ���rlV�XE�A����|���U�����g'�M7�t;�b�Q���f�bc��#�{A��X�sa�m�)��%��:�~��^$�S�K�����0�{MU�e���z�Q~���j`sNOBW���t�A�����*�dQQ��/��9�h������X$���-JsUv^�1��N�9�'�M�q���SYo�&35��}|���H����������9G\��^<r3�}���wg��M~>����,����#I���Y`��o.�P���]NyC�K�=�UtN��(F�M�f�"�h9�X*����`��r��6���3�����:�������B�2���O���u�z���
���Bv���Zt����/z�u��U;T��������
�	6`�[��v0�Y��+y���d��j�c��)�����4�1����'����C|�_<|:�I�c��a���C�W�aw����&?������9�����KH�t>@������IWPt�t�2d�]u��C�u!�7<\3jID���Y�������Y����3�QC]\T*�&S�<���M��G���%��o�#�����s[��k���������^�s��F�G]��AU�����$��_��#����o�hx��t���I:t>�x��U��]Tu���&� �|nZo�H<���"��5�I
P����f��g��"��F�"C��y�q�!_b�g�}J�=�}8U�c����"��]Oo��.l?6f����/� 8WOu�)^�c��tV,�
��[)�q����������u%��r~���}���(�Z�������J:p�L�Va�������$=�-����&AY.�����xD��+,���z�:��d^�8
'�O�[�!X�:4��L}��e����U�e�1�7���D9G���U��`���%�.@��Ut�K�R��e�BOs��n�Ms�z����uD��S��_;�����/[��O����I>�����-N��
k'?��a�x�]��^7��P�<r{-��l�����M��������z�x���G�ow������7��]]\bo�?��L+n�:����S7�G�4!�,t�g%�LjCO������h���w.���_���}��P�XY/���~�Sry�j�_PZ�b�^��������S���}��,)U���[��`I)���c�'$�~s���j�.g��1�����j�\F]�!Q���$@����������#�.��jU�6��e��?9���@�.P�f�X��R�#u�e�
�����v��s�J���E@��5K��A�k�m^8D_�>p��2�Tz��;$3����-�X)�����W�
2�DZm��q&�����#Gr��=�,�����xX����)3�0�X���Q0�D�<��]*�!!�`�k�~+���_����%UB���i�!�����R	�%����2�7�`��TY���\�R�[.%�RW�<�9����@�Xs���Wq����d7"����GU�]�'&��w������^�@j�-3S���S���5��Y�l������I����T�P��d#�o�]����	��`�vS��i�;���e��,U�����#��}2���r.D��25���@}S�_7"=����%r����
�79^-)�����;(�/�VD�q��0�-j8R���K�,�*�6�Fs���������5���)�]��(�����=�g[F��	�9>������.�v��\�H�4mt\-��=1���1R4��ut���x&�����/�'�)��R�@��^�*���yv���|����|�t���#�����/�$�E>�M]m���U�f%��YzIp����X�� PM�r9�-�r�X�����Ewe�0T�������pjiEX 5N '.��>�
��x��t���~���$b`�<�@b6_k��K���y�6#��J�%�Ot'�V���/�|�,�R�*�@x�;���YE�5>-�P�Wt��P���w�Jgn%Xd�	,�U���U�|��G�	/����
�U��������o��.+:��M8�����3�����Sj��g T{C�Lfs���T�x�����������Ky�>��u.SF�������2�
�
���pn l���S�S.��Y+`����K)tt�&-����b��8�Q/��*���y�M+o�+X�P�������>��Omx!����'P[�����Y���(���04�(�>_}��q21���{r�n�OP@��b]WJT�!�(�Bru�h��*9����M ������y��Q�WM'd��XcUnP�����w1��P��_��.�(�B`����*U�,WQ��r:^Y�;�
�[y�i����/�$It����� S$R{�:����NM��P��������0��U]��~HQ�/`��/_��g�w�^ �IKF���I��jX�Q_�/Kg6�t���d(7BvybQi��r�|88���CR���(b�Q�?�a3+9{����X��B��������!������O���N��j�T<�Y<�q���K\�L
K��o,�Q�i�k��,���8��1�@��xH
e��&.�,��:2h.�{�zi�f��+�7�`s]��M����_+���`�O�����iBX�s�f�)L����KM�&��D����j��kCO�x�t���A�"�U��k������c�bu�_m-�F�������;[W�cE�������i���rU!�$�`��ts�%�=�����~:J>rYJ��g`���/���E�QQJ�6DT�����;��I�
�I"�coW��Y���?O�S1����_��x���Y���zc�O�����#��$K�E��Ds���MB�/�"�LcU�2D��tD����b�F@���V�i{��G��v�]J����9��e���f.t#<1����*�0�U2��9w�?kq���[k<F��R�pY��p�d�1:��PbAH/�b
�t��X������d��w�7B3����'���*�}n0�M������q|1u�����������������*s2��A
4����Zw�HY+&d�G���F:]��s�q�:/~Lp�m"�Kj�nt��5�/�t5����;��]���6L��n��^w�����;��01C��Z��z���6i��2��DJQ��/]�"���p��/�z����1'>�\]�����{���>�"�$SyF�g���-��'�b6�WM��}����N������SC�����4[�J�9��3��p�U�KK���&��M�*��E�����*���8����P��fR��RFt�B��,�.i�`x/J!5��~�.�����Q����:��q��0�$PUMBn����/����
�H��?AL�Ur�)��e-�
��w��T�rB�2Z��Y��{��8&�S5jJ�(Z���������%�R�����uI���tp� C�djN��(P3�"L�
hH�G��O��d~jjR��BY�RDn�t�)a����Yz��*5��Wc44��Z�������|����,����{?�=<xs�~q�f�����^l� ���"r���������XT8��g�
�f�JwS��Q�r�DT�;v�(P+R��OZ�P��CU��f�g���=����%���q��Ur�I����X$T�;?T4����v,V����y����%�r�z���6VAQ��
��
�g[��/w��4��������~���O�f�x���7�t�!���������`�]�� .z�+����E���I��f���U)^E+�N�U���"����t�%��/C�������#�����x��er*�HC'�Bt>��J�������3"�4��(_�b1��]vS5��c��*��`�h�rW���=������H�t�|%�-���x�0$Z�� �B�V�^��^����w�a��F����h
5����P�������b����'P�e]�P��M����%o�=��:��<,�}_b�4_�Q�S����,5$s���j�iqb]lI�*�@�&�c�����B7�j8�:���^���g��8��nm���U�_&�`�m�����M;
k�ZSE�^hn��>,z��L���:JX�L:R���"O+�����7��S����o%V�{U	:��{*r�����F��Tl�����h�;��S{$��G��S�I&M���d@e�����F�Xq����"��JK_�g�}Q�)���_��:$,TI�#'@����[LY�����{U��0���	�jzc�e��'��[�����'w��z�8���
��WKF��M�P��#�8���:���7b��t��������)�LH�R������:.�a��]V����B���6�g�����W�"�����K+EY5��"�O���&������
�b������ocY,�[
��������N_=,���bq
������|���h�����Zi��v��D�*�8[��e��������.~f����-|v�����	^I��E����WC�i�r�����L'S�O�[���j�
=��+�����J*1����b���s*����/�u��2��h��X6��V������te�����%��=��z%�(��4����mO�7M�"w\-�2�H8���Q�O(L_�-�������G���"cS��T8�� m�`d���C��.��r����`��pB�J��-����������A��cr�+��.��@�j�+�z��K�"���	c	�'�tP*'^�f$����?l�vn�[�u�O�����<��sW����j��J�HU���r���t2��z3����g��g&p/�-���zY~!+�,�I�_�~*���X���!�����sy�����%W��Lu:Q7�{gDop�zR���H)�%r�8���p�:�^�:�j�b��U������U?��`��r_l)(�4p�-����Q�E���#V_:�R?����o����i���#�|�qzY���=���k��|�X\�]�3�^]��
j����|*)��e���z�>��mj2���A$%�������b�����I��c�z�!="�<�GTw}���QQf=��X.=c����w�����"��9����E�R� ����}��� �Wx�:�����:����d�6N=�����r��$H�ylI�b�q]���]���^�\-.���%��K��<.[EVZ��D�&y,����:G�{0�.�.����9,���6r	�`~���yH^U�6!��x��'�\z�c=�\��9l�E��<��	�8��}�-0���@�;��N��
�Mz�*9��c�������*m�7������Q���������h�"W$��L�q����/*� �����im�,��.�'��X"p�����$4lE��e�YA�HO�ALv	����aY(����q��PG�/�����b���T����i��9��F�J���J�`1_mC�D-|g���*D&l�v���_��f{��U"~	%�������*}P����B�^�	���^6::Hk�(7�X�%9�F19�77��������M������Q�
���(@�Q��o���B��a@������Q
�|��W=���>�$��u���R.������]9�!"��xY,��{�E��	�!�����H.-j ��q������t�s�j��O��^x�Z
���O�V>uJO�e&7|���Vhz�MO+��7KWI������k;yN��W����L9�f����V4��b$[eG���rna���2��N4x'�3Bd t�����=I}(�l����%M�,�U������B��c�H2Uw6���d�,����j���>4���P������>�K�l�����k�\&��5Q;��#6[��	<LQ�:/{�����4x���x}��%��b0kC��s��c���*�X���!�����0	a�+��G��S��/da��e��I�W����#�h-��N��^���t���B�^#��)���zZ�����]���A�����j�%M2��).-i}�l��jr2"����
4�����Y��83D�����2�5�#�x��r�\�����(��Z�S��.��c���]��*�:	����er����,����v}�qJ)��b�?���(XbM]�
��%�g�w�7������J��N�>�`fp��$3�"���tolv
�#2X�e7��k�"��q#�`������u�+�e���W�T�1[��y�9AEn/"Z��%q����h�nS��_��'�&�s���!`,(�w�t>fB��:[����A�O���rF����J�C{��tdv�m�w�B}J�nI�\d�&��oUg��U���MT9R����������xGJd5��q(A�ZAM.���^���"���aq)�-�E�:4[z� �`VOO��
���������;�@!��U������x%����7	������[7���b��2�t�N�t)�7>�Kt�R�O�"/�y@���'Xx��{�xv�%!#swN�Sz��	*��4��f�v��Y2_��9���������F�n[���}��
au�1j<�/%myt{���`6�8�dYV��Fr4����t�./�����n�v��B.j�Vw��w�����~6���R�<+?���o}���{fm�� �����H�8���O��^{������V�xI7��%�j�z�k�t��N!������z&��k�8��F���+��r�D�����kb����+�
�U�3�I�����)���l/��,^��<U�T�6��P�Zy����8|T���w-Q����F-�2���~-"���x�����`��ljx����u���7����8��8�>��������DsH_z@~��%t�a\.
�.�dI;�]�/�]u8��9�P;<�XxS9��g��z�u;��y����m�fQe��v��$�o:J#���wx��S�T���5
�^2uXt�s��`�TP,V�Fe?YX
^��q��3��)-�d����
�����i��o�TT~���R�z���C����[��FP���I����'�]{�~-+��l�6g��a����������Oi"��^�f��
�_X8����aksVB)��)Q�f���3�~�T�~|�v��d��������� ��0��!v��)��7�����(KRI>���:c������q�r)X\���B��j`�h��	���*�!C�d�Q.�l
���N�
/Z��P?�}K8XD�-s����L�J�Gw������T����f���T}0iw%�1��^"��v�F����r���a������){�$� ����yK9�%i�wJ<��z��7
��&@@G�)��:;������|w�n,��b�2-	KrI�'��Qq��Cv�������K&90����E�ro������qv
�K�{t�G7���6}������$�~�5��������������_K�A����5����nS���B�|��4r@���z�x���\�,���� ���*�K�!���{��n.�Q�~XC\}�-r���O�}�ft����Q�n�lo��u3a���,�C�M@]VL%�r@IA�d��k49eo�i�)��o��U�f����&�yZMJ)���#���.�G�eeI#�t�������8���k��o���.u�r8?%��/S*c8	:��L��"J�mi_e�L��2��!�)4��[u�[�I<�	��4����(�n��L[�y�z+�1Vpf������~�aNR��������j@��x"��X�5����,���0����3��o�2���51�*����G�@���k��,�kZ�5�#���@K�2�|:���)��07b`R���@9�w�����nJW��Mc����	h��ll�Y�7��*!2���TR(��OH�T� ����#�������t2c�<�u�,�&2OKb�,�Sk�m����9��S�h���N���>W:xa��^���e��_��WT���.U�4v��1Zs��z\���A�����U��O�N7����
!����L��4S���6o�K? ���������1���{��p���N Y�Q��?��S�_p�BeE!C, UV�����k���{pvi��q��Nngd<~��?���L����[%'���� ��-��|�T���@pMN��JQ2�P���`xoG^
C8$�B%�F�wP��@"�%KgbwM�~6��Jm6o�&qx���K��f�oN��������U��
MQ�����DF���}v���r���sh����y��`^/�0q[9r��Q���M�����:�7��<�~�+c�mP^?�l���U�j������z(�,3ev�D��c��m�%�(�-���^�f�5�C��[3��I�M��S���z}}�B�Q�7�~V-~�
?�
i���,�XK��
)Q��'7�-q��G�~kB����o��b�I�,�N	?yh�KWH���y+mE�08���A9$"(Vm�x��������T�C�'��c��-�2�'J��%�\�}�Q���g�L���������o��H��Z�u�$(t-�t�<_����5]k;��g����y�����)6�6�"�{*����p�mc�������?����]���eT�~	������f�~��y����o�.�R;4?������D�g
��z�<3��IOC���Z�S�oU3����.R�2��n�g[CS&����t����2�ae��<��#������P1�:nl�����#����e��gYE�p�E���[��F�1�z�n�f[C��{z=���k��s���n�R�L�����^���wj~*��l��N��B�}��OY�m��;�o�8�����|�����9z��������b�<z
kv<�Hz�V5A5��Mh�Z�:3�X�� uj�����1$]
Gc���kYjUX�F���c}{{�����3�<�����69�!�\e�TE�?��SKd9[��$��$����i�S����9X0�?D�O�����Y*�#��8�-�J���{�S�yo��"�nnU�8��8>?�^3Lm[0{|��_���^Jk��C��5��h�Z;����Fx�Tt�����������l"�q!�o��c����k�b���A�v!����S��2�.#��'��TJ�4nv�1��g�J�v��>t��1��S>>����dtS�<B�f���U��!=�+l��h�k��
Z���-�eU�rVv�����\>�;���U����#�z��8h��r y�_��{���A���q��FAkXH���#�-�����.*�6}>Nz��h��5%���D�����2>��a��P?������:�����g������/9{xS��������G���o��I�9f��r��W����:�_�q��}?�*�-���+�=N�r>��+�<.h�V�����e�K"��"k{��Y��������d�����"Y��D�,�f�0�P$�1-��5]�$����h4�tu��X[��I)
������WI>�N1�xi��9�#$TF=��<��y��������\z�}��������L���_������=L
�vF�
������O����^�~�x�6�@�m�V��U��i�������)D�9�e���A@��,%	!d�9E�cA�����
��Qn�p�"B<EG��!��
����%�/��3���`k8�~�*����.���"�U]�W����z	HD���Ae�����s���s�����n���
��jO81��x����i�;1;�;�����6����i��%^��/q�a ����&>���<f?��9����������DJ��p$��#f���{�������6��	d8��TU���s�|�M���������7Q?����Ra���h�>u����6�:n_�B�#��������������f������0��
��D]���q���Y���z2�(��
�i���#��@���C��?e��Q���j���(���HL7��-����C�+��Z���Nn ���#�����"�=��8�n�=�T�����<�Y�����~}pr��Bd�l��]}����{x��X��p�����T��i��4�����z?���eX������I���G_SF��/{tl�W<�-m� ���u����s���6(@����q�Y��i���PX'c78;���j8`�av�=;�{g�h�'lS��k���������Nx!�ki"�6>2�j5���x��e[f?8��*���UIF	�_������8K���Y����bU����WI_'���LJX���a�a7HG+��H�6���b�b��b�q��-9���]�!T�V����b���8>Sw��8���r��N�
f���bT_����+_���g��I92u�a��X�w���'_�����b�a���� ������w�Z�z��o+b2z��������8����~e�;�.�2����j7D���C��rXd�3��xyx�{�b�d
%$"%���z�Z�CK�z������,�j(+?-�����Z��gB"����A��������>G��k��K,�T������2Mw����y��I��57��"�����e�������G?���'�oi��pV��
,�|�����p�@��_kYg����Zt�&�
�GJ���xP�#��1f��������������6�A�E��q���SI���o���_������s����`��5��	�9������)j�gQ��P���K|�?^���3��1�����4<Z�Q"@E c�%i�:��'z�y���!�~{��n,�/l����b�^%w}��y�;e�����-e�x�pm�iW�F���������Rk��;qA(��
<=�v*�O������
S��s���P�T�=�F��7������(6�k��.������V����qx~��~Si��__7,�+[$���Xm��Vj�&1�����2��MZz� ��zC"Zz8�'�E~\���>]��|��h����j]��������k�����%D(�"0�m������'�C:�e#���`�p�LO��H�[c]=�
#�t���au�VQ�O����s���s��l[���^��-D&�����vaz?FS��������n|�{#�8yH�������x@�=�p�<<�5z��/&������Q��=N4F�B����Z�[����P���?�(�����:�a�:�������0RD*A8��1
����`O
xa����������w�������W�G����^�2*����
q�f����g���v8F�����
���V����$Exp>����x7���	��MkC��A:�|fmh�s�28-�exs�b_��� �����bW��(�2"����U����?�^��_u���N:�X;��d�h}7r�]��6=>U�z3��O5j�������'�7����6�D�l���l�0�"Q�9-�V�4`�;�����c�)>/O����~�
��Y�����5��vu�iV��t<�v��&~��B��R2��g��/��U�|I���N� ����~������|��j����CM�V;����_����������&�8��o�����mg�/��C
S��?��- n�������
J�����J�����i�6S��@���s������k��T�YP�vh�6>�h�����x����%
}uv����
4����B%���/������8��|\[MA����\[{����5��~�k����
�r�����vzsk#��V��"�����|���19�3.V����LE���Vh�b���wx��}t��'v��������v���8����X��[��e�5�0&�F�I�
��������RSh�o5�`(6�+�	�}:��~082�w	Q�	+�������Vc���}��yKE^@�F�A��	�Y!���*K�oKAp�i�X`��Od���n�w�������X��Oc����*]^\���<	�T�#X��(�C$��/��0�r*����A�M����8���J%A�����!�|@!.u?	����?	h����I���Z[��NcN���`�����w�^������mF0������@/SA�q#���Oy�jq�u�����H���=yGV�I������G'�/�tPM���y��������2�3���`{I�!�'����%������Y�.��q2x����KRwe=M��W�R���tk6UJ�g�������;
DOU�TCX�,�D��8��2������2�Vxr+r�����1�8��1.<Y"G��X�������d��p���2
*v�������|L�`>�E�����\�!H��NF�`�#e�af������<d��Z�������P�7a6�������|�������`d1�A<��
d�Zl;��V�"���z������171�����s-@����.^�0x\���������vZ�ODI�{
�=�������4��L��@��y��}������?���H��c]��.���c��)��������U4�.KM�V��8"����%�.r.��
W��n�Q��u)=Q�hLi�c��&4B;��^@F�S���q^_��j��G|&Yf�Y,���dH-T���"��d���+h��f����@`���c5��}����� 1��T#.F��/��m�o��,��5|��w���t�;,X%}(S�)>f�&�.Io&��s�^�`����3Nve�$���$e\7�K>+�-�S������_�G�_���j��
4�$���g_hO�����L�����&����U4�D:�h�nG9e=�
�����&���hl4C�!QxZ���^O���$o(�x�<�7bX.*z	�T���_H������J��OQk��s�������cV
7�pe�Z��[��p��BHPGD���|�zL3�ql����7��l�b�����������juM�����]?Mn�U�v�h�#�1"���&]��������B���>�}�j��?o�?��.�p�+�^i��Qh�J�������oc����>v�^���������qF�!]'���w�����������U�L�@|IG�I�+�S&}tN�= h$��L��t;3��n����Zvj���FyOk��6���� ���VL��������A����F�xS�q�%���dM��@C��X6��3/��Rq�����N��'������c��[>XH�Kj_h1��A���_"xO�WJ������loQ�w�ex�T�-<�fw@�!�/r/$t��_�u��o���k�H�S!|$��`+=[9w1��5}`6��oW��Q�<1��E[��U��:���T,�����<�D)����c�r-���/E���Ar��\���6TG	�n���t�sp��u:F�[b��^I�`��%VY�P �74JtB?E�4�`��ap��[?c:�z�����V�,�|��!���9�y��$�L9z�	����Y.�A��;g?�,���c���<������Y�f`��t�	��\�������"�������]��I���P�h����"z����l��84H�q>�c�U_R(����z1�~�����!���!�&�;���0�1^����1iS�#)���bM'����N22�����e�k�/XN����������3k,��"�%Z/�Vc�sJ�}�:�r|ya�F���+�|�\������`T��]�Nq�QZ�`[0nR��\p����q�Y�QX��Z�,�u(:=��B��=�K�"dy,��M���[�)�[��<����u��R��Q��z��14a����x��T��(��==�A��
���ij|��KA'G������������+��������o�l,+���n������;�Q�9�J
�w�^��:����_����(���POd�x�������
�GX�%���Cg��Q{�"���1���g>��+&�aU����Y��qS\���,{X�PY|��]��?���]�l�;���V��B;.�gBx����!p����m�8s0��ZC���tvB���%k��28J�
�O���}D��QW��L3�oG��v�k4 d����d����K�\ya�����x��{R�crp����@�3�W\���Y��GG�M�Y@��}�f9;�x��
|}�rs����E�3Y�Xo����H�]�T�b�����r��!����v���K��*�b�<����L�������N���0���p���j���Kg��BcG��*Q��o��#��[[�]�Q��
 ��b
��E����fPd�b�N}����"�������k�L0z&����Jj�fM��;[]�����~z�7`�s����v���Y�q<�m���@�>5�J�:s� 2���:+W�P'��,�Q�{~������R5<"�*��gB!i3�3
�G�:� K���a���cNn�
�8�c�����<������b��`$"[����n<��}t�H��3��������nD��1���\��R��.�KKx�_��T)O>��c@��,3-���|���i��o���G���o���a�����D�*����J�
���$�����2J����0�}"H�SW��nF���.��������|A��os;����9W���?�����5�S���P�������x��1�%06�n�d3th<������Zk�����U?;��gqg�g|[�too����M�����EH�Q����s@���>zV$�����[>���fbln���|W�~����W�l��T��8��N!��?H�_�P�h��� ��|6&6��l�^�b��B�1�;$�����CZ�z��L��jfrO�5�Y|����1L��9�8�V����(���x��?��t�h�������7[����R�DTs&|g��Re/���9:�j�`}L�D�b�������oO��}��>���s������/���(��XL���]��L5mD�X�N�p*V��M�����G���O��%o���N���_������}F�sEp����7~���m�P�:�pm<Jz���{�

���e��q�A~#9��E����V>��V�q����c���������}�����i��+�E��_��[I����"�_���U���	�$���j��:eY$+g]�}��b)��`�|��X���
xF��J�it^Ek��B����4F���C�=����0���:�	S����f'��`�	�~��KK3�
���Ny���S��$�U<B;��D���������C����om�!��dYU�]�V�����n^���,Ou����&��zu���l��}��*���tJ���=���������O�67!yOt���U���=�kg���=������������[$�9�oA{D�0_����,iK�6?~`UK�A��
�uaG��?��\������wo�N��w��?Vu�����P�X7�;Y2;0WX/� ��?����H~h�m��v�����9���N������8��i�@����Ab
��?g��g.���8��
t����UQ&A��U��S[�qy�M��HF������&,����N}�����O��-|���P�X���!/�"���G����7���u��V�;��J�gQ��=NG�Su���	^�l�2,��F0:��x�5O~�S������3�o���������)���Z��k�7�ZQ@Qy��*=%���Z���m-e���p�_�����"���[��Zi�����-���}���C4�1<��c���`uu�Ch��1"�~�58����tX�!\Q�J�HS$���,�����CD�6����"��@H��B��Y��
\MZ�C��U��']/1���
�3���C��!�j�U
-}�&���/�o����R<�<op�k�����Y<,�&sF�l�o��Y�	Z�����W�������:�2._*l�Z��v}��,p�O8Ul56�����B��c����km�i9�K�c�"=�������/�y��X�_���2X��[�2��xIk�nmn���f>��/���|��8[���6[ss���q�\}t�]o����+t)����q�N�D�^�X����9F����K��Y��c�HfR�v}#�n���r�<�6�-y�K�%��z��j��L����%�,�P�T8s����I� �`d����0}�Z���V�d�v�t�`
��\M\��d���Gc����U����%�d�������lR����zVf�qy���+E�'���@v���N����"�Z;MX�[���e����������';;[a}}��Gg(��f��]Q����T�o��`i1�}�O��Y}����j��L�H%�	�v�T��V���`6�7U�����@p���pm���x}k�d�^E$*��D-\��?�q!�m`�[��9I��)���AE�j5��_s�����������5����z����_��~�^LM���������j��C�
����4P��t�%'�vc?E���j<�_����~�!�f5I(�%�d���UG5�L�z�"a�v�����J�C.-j�J�;.�����
T���C(
M18\8����cv�����{�7av��������H��.�Mde�#9x{�1���4����>����+o��ON�~����������0(�����x�Y����7|!�A��+��M��B&(���ta�D(�y1�U�j!���-����7��TP�Q���gYQE���������������SB����Q��9�X�XaLboUE(�����`X ���	����#���T*���xDh�oU���v�XvB+�k{�j����'��,�1U���!$!��P��yN�B�H��|�z�;���9aj���0��C�z���	��!�t�~���"M���@JP���� I�U^qw���1�����s����
"����J/��a�#o�� Oi,�����7F�:�}{��x�|5^,I]j)o`�?���������6l�f�K�����gY�����y�%	/���y(�����&U|.������"xL�+���@�4d�s�A��',��ad��w�v�\��V����8�<�JF��\".;���5�"������
�%������3���&$�����+��J�/����.p0��0#	��i �#�!,Z�

R_��D03�61
����u$
����A�:u��������W��8�������s��clm����9��#���6�-}�kL��vmD����m^-3�m��]�S
5	�2�|��1c$��b��I�-~yH������5�P�f��^�y�?�����B��;�F�-�#��9�=�l�]�+�wv�����n���N;z�`�j�E�����Y_N���Um��o�`x��D:�*����,x>Nz]=���s���
�����$B#�}����I�)PXe���
���j3�=���[z�j������Vk�����������5@��j����4H��2[�|
���kj���=��������>�`����z��OQ��:�L�J�c?5���a��ne%D#{�8h��$�x������-EFp������!��d516�V�������1�}���YQ�,\]m��%��������`Q��H�c25O��>�|%� ����#f���N����E��8R�Hx��f�y2�z�al���� t`�9s����PeP���46�������gn7L���a�O(�l��e���|>���*����~s�v������w��x���Z�g��
����@Ow�St]�F!���6����tV��3
^�z�2�#�������8��"B���������F���?���KPG��������_P�A��	�����A�7�����iq���T,T:Zy$���������#�(��G=ZTX.���-�P(�]�	kd��.�A���U�H�����4�W��~�:U�.��1p��,����w�#y�g;�Is��*���z��6��?KF1�`Y�5x�z�}���O��`� ���O��-�6�q����j������}Sz�	f�������:9XZr�x������4f�����S��������`>�{t����������F��d�xS���K�S$��v��|_WX����^Q]�sn��U{�:���Y���@n _��6�)��iO�o����a'KEH0�~�u����������!�'�@�8�b�E|T\�3��n����������@� ^����[�zF�Q8%T8M`l�2�����u���Ab��qo����73C�Z��^z���Ty���+v;�4�Y�i*���������2����,=Gp&��a~�#�"��>��f��X�Xv�t�P��"�vT���e�6������D��=���_,�_:�Y�z l�P�������)�G
MZ�����4A����j�������;���p���5��O�>y�WL��a�u�b�X<_����U�*�Z�E�n0y������%�?�6�;�c�3�������|��{0?#����&<Yx�o�=��B���f�l�J|w>�����������^��o��?��?���D��T����������V����'�E�����b�{���W�=C/���\KpD��[s
0f��^�!t�(RdO�zk;�\�����?����J�61,�d�tb(���n
���cX�����y������1�Bos=�'�C��T"�L�x��&���uj�S����_�@1�������6��fc���.�����wu��`��[�	���I4���E[��pk�r�Y��Zx-��z�$]+��n�������1�{��)������v�{.��v&�Rp�����L���n��1U����{g���9r���>���tSLv�L�E�_o�q��eq�MO�w�������X�;F�iY����B}��k��_��x4�*�L\��>�cx�96�Q��D���#�=������Hk&���D�b�,��*������V�����������
�J��������y��T&)g���������a=k�&��y�OK	���Z.��XI�:��	�l���9��������sr�
/�����b������gq ������!}�w{7��#bThfH�:����TV�S4Nr���t�2���u�wl3|-��B���5��h�>V��o���T��9�/m�A�k4>��t���6����kVm��$]����k���!w��/m�l�;�/*�n�{����?c��@�hm���C��i����}�w��Z��S��]�%����t_��lg���|Y1��w���`��$���-Q���OHU���Y�ffK��w���OEk{;l��X�Z<;;;X m����h�6��N������
�Z�$���vk���i~�����m���V�Sd�����?}�K�D}��
�u������O���$�D0���QH�k�R2����N��.�����mr�z	����g���+�f5������4�a���rWr�?�s�����l�� 66Ax����d6��vWd����	S���f)V�0l��5��L���Hd���9���4�PoBe��(��Q��fXJ1CY��\������"���/��m��m��!4�1�N��1�^���nj�_���nb��[[ ]����/��X�[���#poK�'��:�f��mn�W.�i^��y��f��(���*��Rj�V��C(~��l�_S�G�qO����L�wz2G�Y�,�z��S���.���9����,���6�jT���u��~�r8Fp���9PP2�B�n�1������������������������s����W��\T���
����W�z���_���?�/����6��g���o�[���]���	p@���)M��<�S�9%��1������f�B5��-���[h�v3�W?���U�.���g_#{�+,���
=�_��!���pC���#�2��dL��z#������o���;���2���M���L�6���vIx�bA�^Vx���[j������H����z_;�3�KD�;t���\Y��j�����Az��(�S�`ST��/���z��]�������R�����_xbUNHT����M�������������n/�
kq�p��>N��5��t�t	��E:�a"��up(�Zw"U��pk_hc��Y�O�N�Ni;�
�g���������Z�\�W:�o��nv�>)Q���x(�k�Q\�yw����oe������l�p���z��f6�K�!�o����7,'�=��T=�'�u�_�������`6������o^���Z�k���������=������9y�w������)������yW1�I��r��[��/X�a"�����iO'6��D�i9����R�Lxk���� b��@�+���}qB�|�������3�":E%��2d^0�eR����E4�I%�r�Y:�=A<H���|��`�[������;T������QNV��Z���-�
T�2�B1����*A}��q����������R��S]X�w����!f'g�����������W���^���h��y/���}�hV�w=��?������P8:vv��@z���7,|��h�����^���^e2X���d���^z�������wZ���8��`���x�r�	��E����������c[�>8�"��;�E�:������]������|)
��3hQ+|����Q��7~�|�����sp�\�}�l���&���s�/��u�\�������!l�{X���W��|z��|x���*|w�~fXt#*�^//��Z
�����t8��C�<������'�O���O.-a�������$qP���Q���k�������N��4��ac�5#6���F�N�U����,�>�a#OI'�����>�������p�L���Aw8���D�O��<7�<x,���
�U�������
3��Y-������O�mt2�#g�j��)^������C�0xw6��������:�'t	kq���|H=������O�W�z��@z��������Z$��rP����T��4���A��������jq(��M�b�Q��(���e")s����5�����q���f3ln����g����%������j�"�|x�&��>�^�W#b���������V� ����S��lO����&�>�����&����'�c��[��]0�
'I~���W� VwO37��������xs�n���uE/�)��A�A���oy��}�Z����� �4��L�r��p���G5bsPo����$�����E���O~�k� �}�6x��z���7VR�M�e����/p����K$���"����'���z�z#������y\��&B4��-�z�D���J������v�����p�����������t�b���D
�\����;�CX.<�k���p��P���m���>>'4�mb��5���1����tAw-I��!��d�z�c��8n�/��`"�-�5���`��,����:<���[�?��4������4;�nU:0vH2����x-�t�u�]���W/,����W��	�����9���u��o������b-�G��/�Q<.|��}2���'9H�^�mz�i`S:��_�0��#�
~����������ps�
����A�m������}9i��M��Z���r<���w�<<
~��{p�������z?��4��������#����=��<�(��K�����~�!�~Fwo��������0��,age���7VC��J�;�w�C�3G~�z���z"���^~���������������!�/�d8��~�=�i����'?,,�_���7p.�<�e	�V.���"�F�,9��Z���Q�h�������W����x^�
��+(��z���%u�R�D�nO�wF+�	�>�$��>MF6����w�1Jz�{Q�|�����=�h�S�`��qa�$:N������g�'��~�]�p�:43���y����X����w����^r�N�u����M��#������
�n�������B�3�����/�����������e��@[,���z��<*V���ET^X���X ���6�)�uT~o���:���82dWw�.
�w�����<@��u�p~B<'D*!��!Ng�����'���_�>����.�W}:�2��6T��ye���/��������t��h_���"����������,�k�����I�����D�/_��	e6�%���,�q�q22���<��Wu&aq�mX�	�+
������y�������z�_���O�@���9��-�r�_�,/��L`?��Y1�]�M�]�pM���+*���8Ro�nh�-�8�dD�:4�����hVyO��u�i|1,
�9���y�(�AD�up������.�������"���j��c��u'��s���i@l��Z��	��Es-Y�WIQ��=����|
�7�u�����N�J��%��K �ep�������%��*]�c'8��o������r6�(du`��M��*�f&���,��2jn_h/}Q'��=�b�����}���!�i�W�����`�� G(�&�b��#�Kd�/~
�f�3q���Q���CM���4����6�����gA�*�"�A��T^�s����?�z��<����i�����i���x[����,�,�$v�z*,�I
���c�m�g�4�L��B��A�iO"�m��0�(pD x��;����1vT��c�����D�w������A�6<�I�G
�=a���C�0O�����q3eB�)�
X������b<3��0��2w�&�����Tl�yb�;�X
�&:ue8���'�v��n;Y����������f#��*pq�]���� �F�1�*5���������^��o9=�����A�,9<
�������pLv�?UT���Q|=X
p������
c����5��FIw�����Z��":���sc%�
0�6������9���D�EpX��Nr���gqV��9m�h��R1bA��2�Yc��	Z�3��.�5��z�g+��0�^k�i�mH+��!�
�v$PS��&Y���rB�1	J�l�EX�c�<��7�������\��F���k�7�m\����M���*6�*S�u�>�=�d0j-,����Fq_eZ�e������ca��"+���/W���s"���u���(v��A'���#�������_�X�/Sm�,a�@q�u@7b3=�i�����k�W>m���z��z����~�q��So�������n/,�E�H���kn��
}���\�����VKu��U�#���z���P��h&g��/���d��	6x��4�-_	���s����0�qW����H�����U$#�f�(�cI���~|�_�!��2;����?�	\z���P
�%��x ���WMt���f$�zdj4�64�����FmdZ5�Z��	�����G
f1�@�4��Z+��n��F^w,�)Z�������E�^<t������B���Z
��,��2������%�{'��A2iK`:�/��<�XR7�8+����Y-��'`���h��e��G�2�09��b��=|K���B���C)I�����%��ra0����(��s�*��:y�Y������R���h��^�=��;�u��]���7�^�_����g����|�t����5z������iT���R0k�+�������%����_dq,Mp��po�ka���ZA	��v���0f�L��������q6�@�Lt
�(Ee�E�c��%����a\�����G�+@�����~:|�M��$H~x��^�9�.��H�����a�Mr��./ �~L;��m����k�_�z�{�����*w��?D����N����MB�.N���t���
��tgt���7�J;�i	`�Q2����S��e7,e9��LM��D/�LE�`���z�oe5�W4aA�m�6v�����6������)��������$S�- WJ h���\w�[�#y�DmE��v��u�=,�(K�����G}���*������� nS��'����b����L��<8x!�S1��������3�F.�Vj}Z`��N��<��g�������T�{!��G�~�O�G��_����|�C�{t��w��*N�`k�Nln�Y�Q��6��}�4���8�:Z�s������2��AY�������9���{���qR��Y����vB�c�d;�1���?���E{�������,U�;�G���g��X���[��z������c�|�@���������C�w���-�~����`�4��rA�6��~�^Q�f��`���`��V��-���+���S�E��>������`m�mV�~yz��B^>�8�E=$�7�6���-`14��I*���o/����s�i��9��4��H�7�(�p�}���[-;�������Q9�.��nz9��L��%J^�u���$o����Aq���N��F��<.g�����Y�h�R]���)6��N��x�
G�g��M�d����&��zQA�����"��vwR0,<�'���y�C�H�"��1<�~n�l�xPk��a.������;x%�����/,l�ygw���W��Q�y;��^>�;��G��D�`=���#'0A��-�\4L����d�����;��6�����q�d+��lF��ds�_oz|���X���P��JGsS�~o�����Jo�am��l�G�������u���a�\�o��#x��k����|����G���o^�9���]�R�zE������P�����M�.�"��U������{�'���/�f���r���|�Wd/��N#D�+��vG����[���E���_���4?���IxN$�N�����������
��~Ri�]��1�1#�!�M�Gx��Ro�o��99:|��i�������N��zf����{�v�����b��[��n��Q�	a4+��e|��!SM��k���3�?�n4��<�6N������d���~5
��gX��m9Sl��K�Q_?O+�WV>���$��H-�Z$�C/`�%!W���n�'����Ke�7�J_D�V�1V���2Y�B�����E$����P?���b�I�@�<��_����]�����x�<���"��Nz�}����h�Q0��+���!N�$���D����`-��;��������{c}3����{��i���������C�BQ�hx�^��uJ$9����:����u����StB��(NA
1����&Y�����DG@Q�t��AIy�9���;�=����_u�!���/$#���;t��.Qv,_(���i$��eS����c8no{��B7EIO�8����%��p�{��A�e�+���@�����#�+]�j��V{g��]���Iu���Z�� ��;�=�
2�Sh��������>��D�L~n���+�j�5]��P�r���e�K��d�eD8r�F��a7�!!{,/Y���F�W�pxw�[�����s��Z4?����T�/}�|�Vo!ZK�H
]�C��+�J�t���Xd����^���OP�1>����6Q	
�Zq��r�
#w�$�rj���'�O�p�bVUj0:��)z���G�`]����`��#�VT"����Z �!�����VV���w�^������B������K]�|Ut��#o������?�/���z0����"3���������9�lOa��J"ae&/�e�����s���'������%�z?�_^�����/H�d�xY��HP}��Q����y`���	D=:��-�OF��k������
L���������3�0C��������h���?�S��?Qa�I��p��N��*4�q�&�'�)���c;g��z��:jL�=c�a�����z���;����)5�|)������0�V�*x�&�KZ"��XU5on���-F���l����[~����U�b	!K�B��3hm�hk$4�D�.��^�x�o^I��Lb�IS�0��W4D.�$�Sbk-G��<�)�X��rR��$���
)��}*	F�e&x��)x"����1\�$hPQ�:G_�~�0T.�=�zhD]!<$��Y���IrT7{ ao`'���6&�mMf��(���YG�6�a��C��HKB���4@�>��mD��9�y��D�nJL�3}�D�g��������'��I����F��x/;�JD�������}��
��!bv4r>��v��;F�J)_hB[��>��,t�C���|����������������r�'Z~s��d�M/��-R,GuF��H�)0�����*+M�
#��e���v�!�?D�&�e�N�b���~v��)�6�8���<o����'�i��A\�������#�W=�#�et��1=�r��5YOh����c���"��/���q�_PL7�G����
���+�C'AG�e��>�������{���<�t�[�Fq���2^#�p�/- w�$�����J.9�Zq����A7�ZX��b�C���]	~In����S�$if�������w����Zk�s���x*���� Vv���������'��}f�A,.#�_
qQIJ>��:��`���[��xxozn�7�����r_���=�w#/���V��z�`�7�j�wFm����^��*�+mQFt�	���G
S:gI���^A��8g��s7WPt�p<H`�z����i��|��1za yHi��D�+��������w�����?:(���Q�+�w�����q{�����X"4L������b,��>9x�5���r�1r�!A�Fg�a�{�x��+�3oL�����E���no.��t��N�������w�~$X�w��	������f�GL��p�(o����`�T?t��r�h���z_yA�O�����dxF]����ku��	��v"��
;�o��
R�r��,.�R�����>�� ���E}*p�+�����#,u�<JW��v��p�m=<<@�bO���tK�4�������w��,�����f���UK�������5���dtW�_B�����3�J�����l:�8��\,[i?��������}(5�/�L���H�/�p��}����O�5�F��_c�:Yj�v�z�t!q(t^j���v>���E:��<��O�8��XS��q��;#qD��6	�;�.�S�{V�0 ���5�U<Lm��k������ ���At���f����o������0��'��c���+��x���58L^�����M�oP���Q'Q/�,4���?�n[�g�3�a��@ai�*�����mBg�#�+�_�6&�m$B��N~�a���aD,�{�u�[�Y�z��}Pp$����+QTY|�-����9�'B�R�W�80�f�z�2{�1a�G�k�"@%�m@������O�X�?���)T�8��WW�h�#�"�������bt9�$�R�}�t�=��O����6�ls����"�������>�3�����IN~��2���s��o��[�O���>�S2
=�z����'��������IH�Q"&���=s���1��i���fiG������?�������O���! �Z����Z|��4qw���
z`������G�y���-�%%�C�����w��mI�o���C�HrB&�/N@N��Q���Aqw0(�:�)�"���|����"�%�KQ����8���{g�<���xL��XC��<~����|����{���y��}�bL|o"=�����@�p���n����dh�����o�r��=���;����ONN��N��s��|��BN������_��7Ec�q{�e����YR�H��\<R@V2i%���
�D��6C!�N��8{�wb�����91<���f��������,�;�w6b���'������� �'<����5i�r��Q�I4mn^��w����T6f+������� ������AH�1�%8�0d��M>m��?\�	������J�Uh��a{5���ut@	h&c*@���<��@Si��h%�����-�������MA�8�qy�udM�'9���86���)0F�3�������6/2(�������[�a�@���w��>2���ruu��ps%��>����P�X���y=����O������c<�&��Q����/����}&g�O���x��2s��a�A�
�7�f���=��w����{�s#�zCs����B ��,�]I`�{�R�n|�u�+�C�v>�A����A!CP����V}8��17?�����!�E|8�b.�g��)���CW����P��v-��b����
��n���Ck��A;�d|�
iW�����C�X%����K�����>4�OW���b�!�FsI>����SH�3m���j;�������!�u�y5����v�l�T��Da
��t���@��T=�2S���k���2��������c�-T�D��
V�M)�W�i�����K�/}`�-x[�J�|r`dZQ<��1��P�j�>t���^
��(Q�D�:�K�<H��>����������|e��t��~���$'����>qzNw�j����|v��E����E��_$���E���V��}i��������sE�/�E�f�a_D��������z��(�,7�,_(�^�G���_�������<7���~�~#Ne{��������T���}k�K�g<�b�����tL���*_�l7�Q�Rr;���68���}�p������d���������[����6���[|m��&2'���H[��&,{>u��O���u���O���*�d��l�|������0�+xz~�V!����2qm��O\T`b��^L������0/6�\<�3�e��u�R" _�)/�2^��WmR����������NF��4k7�Q������Z���r�r�2Fhe�ng�V�rL����-MF��V3�qfK��R�K���m�6�W]������������2�����3c�p���6���u_���j��Q������]��6����;U���3n�D7�L��������9���lvg%Xv����u>��s�W�9b�����+�Uo~`T���y�"5�Din��kZ���j�������X:����`-��Huo�����Z�	Oy��/����:
����V[]������UC�5�<���J6i)�7K������u���eY1�YW�

��������<�vyHo��U������xs���k�b��r����D*U�@�
+��]/��#����QMd������q����U��ip���V�B�rtq���,O�$��<;N��!@@���Z���.i
[����A��zS<2e��Li
o-������vRAe	$tg�c�%��96:�N�,g��������NzX��B��,��
"�6��G��M�n�fX�����L�WHmH�yL���'��/��l!�����X,���F�l���).�T������M��?o��������_����U��������{���������>��{��� I�lr��U��G��1m4���x��3D�?6�d���)�����Ys�&!�[�v���/4��z@~��An��}-��������<Q����(��dp}uu�{��"a������;w����
"vS.��Z�DBv�<�+!�7��3?}v���+>T���D4��������U<JY>�Lg���K2�����o���ub���]������;�)�~�
�����2����RY�[��a����]��`�+}U�Ge�L�<�8�%H�J4N���'�0��2\��<��x�L��6��]�6�������
*2�u�&|p�!��?y�_w������W���}QO���98�F����&`���5	,���n���F!$t��I��K���X9����@����b�?����H����-�//�
<����M�	����M������������)�Sm��
=@����!8S�d�i z��z�OF���Dw�Dxb[v��ozn���g�yc�%������J���.2��Mh|R���;Kx���S�7y���;�\�@+y���}�w���'���0=�[�-r�1X���	%W��I�m�x)_��davN_2~�'����Z�����e�?�5��}�@�	8�.�is�\23C=7�C����>���N5bq_����T��
���-�r�dJZ(J��XD��b�J�"��d�(&�H�,�MJ��|��1.�L�?3RjX�]�f{#t\��2%WQ����V%W!�L�U4�25�U��T�2:���B	I9#7��d�������/���X$�!��+p0J
D�hZ�����D���	�� �<��C�CB��@X7�w�c�+�B��J���7���Y�[���w������]m��_������c�����RD�-�b����n���H^�+�KMb�v1r�-%�z�=�$C�O����	_q��?���q���]`�}�Q+`9�����������
%>��$S��V�v�|gwF�:�/A������i��;E��T���a����"mI�M�n~��|?xD�������zNE)*_J5���$m�j���|Ub~bfjb�tb���1^W\��:H8[��6mJ��/Mf��f%���d�QLx��[l��+l�l����*EjSs�U��y���.�*�Asj?�1���Y�;�����u�^��R��m����
V�����
����[B�8� a�j7m��C8F3��n��tT!-k�:{��S�)������!e�Lk57��4��h�
#37Alvaro Herrera
alvherre@commandprompt.com
In reply to: Alvaro Herrera (#36)
Re: foreign key locks, 2nd attempt

Excerpts from Alvaro Herrera's message of mar ene 24 15:47:16 -0300 2012:

Need more code changes for the following:

* export FOR KEY UPDATE lock mode in SQL

While doing this, I realized that there's an open item here regarding a
transaction that locks a tuple, and then in an aborted savepoint deletes
it. As things stand, what happens is that the original tuple lock is
forgotten entirely, which was one of the things I wanted to fix (and in
fact is fixed for all other cases AFAICS). So what we need is to be
able to store a MultiXactId that includes a member for KeyUpdate locks,
which will represent an UPDATE that touches key columns as well as
DELETEs. That closes the hole. However, the problem with this is that
we have no more bits left in the flag bitmask, which is another concern
you had raised. I chose the easy way out and added a full byte of flags
per transaction.

This means that we now have 1636 xacts per members page rather than
1900+, but I'm not too concerned about that. (We could cut back to 4
flag bits per xact -- but then, having some room for future growth is
probably a good plan anyway).

So DELETEs can also create multis. I'm going to submit an updated patch
shortly.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#38Alvaro Herrera
alvherre@commandprompt.com
In reply to: Alvaro Herrera (#37)
1 attachment(s)
Re: foreign key locks, 2nd attempt

Excerpts from Alvaro Herrera's message of jue ene 26 16:03:02 -0300 2012:

Excerpts from Alvaro Herrera's message of mar ene 24 15:47:16 -0300 2012:

Need more code changes for the following:

* export FOR KEY UPDATE lock mode in SQL

While doing this, I realized that there's an open item here regarding a
transaction that locks a tuple, and then in an aborted savepoint deletes
it. As things stand, what happens is that the original tuple lock is
forgotten entirely, which was one of the things I wanted to fix (and in
fact is fixed for all other cases AFAICS). So what we need is to be
able to store a MultiXactId that includes a member for KeyUpdate locks,
which will represent an UPDATE that touches key columns as well as
DELETEs. That closes the hole. However, the problem with this is that
we have no more bits left in the flag bitmask, which is another concern
you had raised. I chose the easy way out and added a full byte of flags
per transaction.

This means that we now have 1636 xacts per members page rather than
1900+, but I'm not too concerned about that. (We could cut back to 4
flag bits per xact -- but then, having some room for future growth is
probably a good plan anyway).

So DELETEs can also create multis. I'm going to submit an updated patch
shortly.

... and here it is. The main change here is that FOR KEY UPDATE is
supported, and multis can now represent both FOR KEY UPDATE as well as
DELETEs and UPDATEs that change the key values. I have enlarged the
status bits for each member of a multi to eight, as mentioned above.
We're currently using only three (and not completely -- we currently
have only six states to represent), so that gives us a lot of room for
growth.

(Looking at this code, I have the impression that either moving
MultiXactIdWait to heapam.c was a mistake, or defining MultiXactStatus
in multixact.h is the mistake.)

Other than that and that it's based on current master, this is pretty
much the same as version 6 of the patch.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

Attachments:

fklocks-7.patch.gzapplication/x-gzip; name=fklocks-7.patch.gzDownload
#39Noah Misch
noah@leadboat.com
In reply to: Alvaro Herrera (#36)
Re: foreign key locks, 2nd attempt

On Tue, Jan 24, 2012 at 03:47:16PM -0300, Alvaro Herrera wrote:

The biggest item remaining is the point you raised about multixactid
wraparound. This is closely related to multixact truncation and the way
checkpoints are to be handled. If we think that MultiXactId wraparound
is possible, and we need to involve autovacuum to keep it at bay, then I

To prove it possible, we need prove there exists some sequence of operations
consuming N xids and M > N multixactids. Have N transactions key-lock N-1
rows apiece, then have each of them key-lock one of the rows locked by each
other transaction. This consumes N xids and N(N-1) multixactids. I believe
you could construct a workload with N! multixact usage, too.

Existence proofs are one thing, real workloads another. My unsubstantiated
guess is that multixactid use will not overtake xid use in bulk on a workload
not specifically tailored to do so. So, I think it's enough to notice it,
refuse to assign a new multixactid, and tell the user to clear the condition
with a VACUUM FREEZE of all databases. Other opinions would indeed be useful.

think the only way to make that work is to add another column to
pg_class so that each table's oldest multixact is tracked, same as we do
with relfrozenxid for Xids. If we do that, I think we can do away with
most of the MultiXactTruncate junk I added -- things would become a lot
simpler. The cost would be bloating pg_class a bit more. Are we okay
with paying that cost? I asked this question some months ago and I
decided that I would not add the column, but I am starting to lean the
other way. I would like some opinions on this.

That's not the only way; autovacuum could just accelerate normal freezing to
advance the multixactid horizon indirectly. Your proposal could make it far
more efficient, though.

You asked two questions about WAL-logging locks: one was about the level
of detail we log for each lock we grab; the other was about
heap_xlog_update logging the right info. AFAICS, the main thing that
makes detailed WAL logging necessary is hot standbys. That is, the
standby must have all the locking info so that concurrent transactions
are similarly locked as in the master ... or am I wrong in that? (ISTM
this means I need to fix heap_xlog_update so that it faithfully
registers the lock info we're storing, not just the current Xid).

Standby servers do not need accurate tuple locks, because it's not possible to
wait on a tuple lock during recovery. (By the way, the question about log
detail was just from my own curiosity. We don't especially need an answer to
move forward with this patch, unless you want one.)

* Columns that are part of the key
Noah thinks the set of columns should only consider those actually referenced
by keys, not those that *could* be referenced.

Well, do you disagree? To me it's low-hanging fruit, because it isolates the
UPDATE-time overhead of this patch to FK-referenced tables rather than all
tables having a PK or PK-like index (often just "all tables").

Also, in a table without columns, are all columns part of the key, or is the
key the empty set? I changed HeapSatisfiesHOTUpdate but that seems arbitrary.

It does seem arbitrary. What led you to switch in a later version?

Thanks,
nm

#40Robert Haas
robertmhaas@gmail.com
In reply to: Noah Misch (#39)
Re: foreign key locks, 2nd attempt

On Mon, Jan 30, 2012 at 6:48 PM, Noah Misch <noah@leadboat.com> wrote:

On Tue, Jan 24, 2012 at 03:47:16PM -0300, Alvaro Herrera wrote:

The biggest item remaining is the point you raised about multixactid
wraparound.  This is closely related to multixact truncation and the way
checkpoints are to be handled.  If we think that MultiXactId wraparound
is possible, and we need to involve autovacuum to keep it at bay, then I

To prove it possible, we need prove there exists some sequence of operations
consuming N xids and M > N multixactids.  Have N transactions key-lock N-1
rows apiece, then have each of them key-lock one of the rows locked by each
other transaction.  This consumes N xids and N(N-1) multixactids.  I believe
you could construct a workload with N! multixact usage, too.

Existence proofs are one thing, real workloads another.  My unsubstantiated
guess is that multixactid use will not overtake xid use in bulk on a workload
not specifically tailored to do so.  So, I think it's enough to notice it,
refuse to assign a new multixactid, and tell the user to clear the condition
with a VACUUM FREEZE of all databases.  Other opinions would indeed be useful.

I suspect you are right that it is unlikely, but OTOH that sounds like
an extremely painful recovery procedure. We probably don't need to
put a ton of thought into handling this case as efficiently as
possible, but I think we would do well to avoid situations that could
lead to, basically, a full-cluster shutdown. If that happens to one
of my customers I expect to lose the customer.

I have a couple of other concerns about this patch:

1. I think it's probably fair to assume that this is going to be a
huge win in cases where it avoids deadlocks or lock waits. But is
there a worst case where we don't avoid that but still add a lot of
extra multi-xact lookups? What's the worst case we can imagine and
how pathological does the workload have to be to tickle that case?

2. What algorithm did we end up using do fix the set of key columns,
and is there any user configuration that can or needs to happen there?
Do we handle cleanly the case where the set of key columns is changed
by DDL?

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

#41Alvaro Herrera
alvherre@commandprompt.com
In reply to: Robert Haas (#40)
Re: foreign key locks, 2nd attempt

Excerpts from Robert Haas's message of mar ene 31 10:17:40 -0300 2012:

I suspect you are right that it is unlikely, but OTOH that sounds like
an extremely painful recovery procedure. We probably don't need to
put a ton of thought into handling this case as efficiently as
possible, but I think we would do well to avoid situations that could
lead to, basically, a full-cluster shutdown. If that happens to one
of my customers I expect to lose the customer.

Okay, so the worst case here is really bad and we should do something
about it. Are you okay with a new pg_class column of type xid? The
advantage is not only that we would be able to track it with high
precision; we would also get rid of a lot of code in which I have little
confidence.

I have a couple of other concerns about this patch:

1. I think it's probably fair to assume that this is going to be a
huge win in cases where it avoids deadlocks or lock waits. But is
there a worst case where we don't avoid that but still add a lot of
extra multi-xact lookups? What's the worst case we can imagine and
how pathological does the workload have to be to tickle that case?

Hm. I haven't really thought about this. There are some code paths
that now have to resolve Multixacts that previously did not; things like
vacuum. I don't think there's any case in which we previously did not
block and now block, but there might be things that got slower without
blocking. One thing that definitely got slower is use of SELECT FOR
SHARE. (This command previously used hint bits to mark the row as
locked; now it is always going to create a multixact). However, I
expect that with foreign keys switching to FOR KEY SHARE, the use of FOR
SHARE is going to decline, maybe disappear completely, so it shouldn't
be a problem.

2. What algorithm did we end up using do fix the set of key columns,
and is there any user configuration that can or needs to happen there?

Currently we just use all columns indexed by unique indexes (excluding
expressional and partial ones). Furthermore we consider "key column"
all columns in a table without unique indexes. Noah disagrees with this
choice; he says we should drop this last point, and that we should relax
the first to "columns actually used by foreign key constraints". I
expect that this is a rather simple change.

Currently there's nothing that the user can do to add more columns to
the set considered (other than creating more unique indexes, of course).
We discussed having an ALTER TABLE command to do it, but this isn't seen
as essential.

Do we handle cleanly the case where the set of key columns is changed
by DDL?

Hmm, I remember thinking about this at some point, but now I'm not 100%
sure. I think it doesn't matter due to multis being so ephemeral. Let
me try and figure it out.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#42Robert Haas
robertmhaas@gmail.com
In reply to: Alvaro Herrera (#41)
Re: foreign key locks, 2nd attempt

On Tue, Jan 31, 2012 at 9:19 AM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Excerpts from Robert Haas's message of mar ene 31 10:17:40 -0300 2012:

I suspect you are right that it is unlikely, but OTOH that sounds like
an extremely painful recovery procedure.  We probably don't need to
put a ton of thought into handling this case as efficiently as
possible, but I think we would do well to avoid situations that could
lead to, basically, a full-cluster shutdown.  If that happens to one
of my customers I expect to lose the customer.

Okay, so the worst case here is really bad and we should do something
about it.  Are you okay with a new pg_class column of type xid?  The
advantage is not only that we would be able to track it with high
precision; we would also get rid of a lot of code in which I have little
confidence.

I think it's butt-ugly, but it's only slightly uglier than
relfrozenxid which we're already stuck with. The slight amount of
additional ugliness is that you're going to use an XID column to store
a uint4 that is not an XID - but I don't have a great idea how to fix
that. You could mislabel it as an OID or a (signed) int4, but I'm not
sure that either of those is any better. We could also create an mxid
data type, but that seems like it might be overkill.

I have a couple of other concerns about this patch:

1. I think it's probably fair to assume that this is going to be a
huge win in cases where it avoids deadlocks or lock waits.  But is
there a worst case where we don't avoid that but still add a lot of
extra multi-xact lookups?  What's the worst case we can imagine and
how pathological does the workload have to be to tickle that case?

Hm.  I haven't really thought about this.  There are some code paths
that now have to resolve Multixacts that previously did not; things like
vacuum.  I don't think there's any case in which we previously did not
block and now block, but there might be things that got slower without
blocking.  One thing that definitely got slower is use of SELECT FOR
SHARE.  (This command previously used hint bits to mark the row as
locked; now it is always going to create a multixact).  However, I
expect that with foreign keys switching to FOR KEY SHARE, the use of FOR
SHARE is going to decline, maybe disappear completely, so it shouldn't
be a problem.

What about SELECT FOR UPDATE? That's a pretty common case, I think.
If that's now going to force a multixact to get created and
additionally force multixact lookups when the row is subsequently
examined, that seems, well, actually pretty scary at first glance.
SELECT FOR UPDATE is fairly expensive as it stands, and is commonly
used.

2. What algorithm did we end up using do fix the set of key columns,
and is there any user configuration that can or needs to happen there?

Currently we just use all columns indexed by unique indexes (excluding
expressional and partial ones).  Furthermore we consider "key column"
all columns in a table without unique indexes. Noah disagrees with this
choice; he says we should drop this last point, and that we should relax
the first to "columns actually used by foreign key constraints".  I
expect that this is a rather simple change.

Why the special case for tables without unique indexes? Like Noah, I
don't see the point. Unless there's some trade-off I'm not seeing, we
should want the number of key columns to be as minimal as possible, so
that as many updates as possible can use the "cheap" path that doesn't
involve locking the whole tuple.

 Do we handle cleanly the case where the set of key columns is changed
by DDL?

Hmm, I remember thinking about this at some point, but now I'm not 100%
sure.  I think it doesn't matter due to multis being so ephemeral.  Let
me try and figure it out.

I thought part of the point here was that multixacts aren't so
ephemeral any more: they're going to stick around until the table gets
frozen. I'm worried that's going to turn out to be a problem somehow.
With respect to this particular issue, what I'm worried about is
something like this:

1. Transaction A begins.
2. Transaction B begins and does some updating or locking of table T,
and then commits.
3. Transaction C begins and does DDL on table T, acquiring
AccessExclusiveLock while it does so, and changes the set of key
columns. It then commits.
4A.Transaction A now accesses table T
and/or
4B. Transaction D begins and accesses table T.

At step 4, A and/or D have an up-to-date relcache entries that
correctly describes the current set of key columns in T. But the work
done by transaction B was done with a different set of key columns
(could be more or less), and A and/or D mustn't get confused on that
basis. Also, in the case of A, there is the further possibility that
A's snapshot can't see B as committed yet (even though C subsequently
held an AccessExclusiveLock on the table).

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

#43Alvaro Herrera
alvherre@commandprompt.com
In reply to: Robert Haas (#42)
Re: foreign key locks, 2nd attempt

Excerpts from Robert Haas's message of mar ene 31 13:18:30 -0300 2012:

On Tue, Jan 31, 2012 at 9:19 AM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Excerpts from Robert Haas's message of mar ene 31 10:17:40 -0300 2012:

I suspect you are right that it is unlikely, but OTOH that sounds like
an extremely painful recovery procedure.  We probably don't need to
put a ton of thought into handling this case as efficiently as
possible, but I think we would do well to avoid situations that could
lead to, basically, a full-cluster shutdown.  If that happens to one
of my customers I expect to lose the customer.

Okay, so the worst case here is really bad and we should do something
about it.  Are you okay with a new pg_class column of type xid?  The
advantage is not only that we would be able to track it with high
precision; we would also get rid of a lot of code in which I have little
confidence.

I think it's butt-ugly, but it's only slightly uglier than
relfrozenxid which we're already stuck with. The slight amount of
additional ugliness is that you're going to use an XID column to store
a uint4 that is not an XID - but I don't have a great idea how to fix
that. You could mislabel it as an OID or a (signed) int4, but I'm not
sure that either of those is any better. We could also create an mxid
data type, but that seems like it might be overkill.

Well, we're already storing a multixact in Xmax, so it's not like we
don't assume that we can store multis in space normally reserved for
Xids. What I've been wondering is not how ugly it is, but rather of the
fact that we're bloating pg_class some more.

1. I think it's probably fair to assume that this is going to be a
huge win in cases where it avoids deadlocks or lock waits.  But is
there a worst case where we don't avoid that but still add a lot of
extra multi-xact lookups?  What's the worst case we can imagine and
how pathological does the workload have to be to tickle that case?

Hm.  I haven't really thought about this.  There are some code paths
that now have to resolve Multixacts that previously did not; things like
vacuum.  I don't think there's any case in which we previously did not
block and now block, but there might be things that got slower without
blocking.  One thing that definitely got slower is use of SELECT FOR
SHARE.  (This command previously used hint bits to mark the row as
locked; now it is always going to create a multixact).  However, I
expect that with foreign keys switching to FOR KEY SHARE, the use of FOR
SHARE is going to decline, maybe disappear completely, so it shouldn't
be a problem.

What about SELECT FOR UPDATE? That's a pretty common case, I think.
If that's now going to force a multixact to get created and
additionally force multixact lookups when the row is subsequently
examined, that seems, well, actually pretty scary at first glance.
SELECT FOR UPDATE is fairly expensive as it stands, and is commonly
used.

SELECT FOR UPDATE is still going to work without a multi in the simple
cases. The case where it's different is when somebody else grabs a KEY
SHARE lock on the same tuple; it's now going to get a multi, where it
previously blocked. So other transactions later checking the tuple will
have a bit of a larger cost. That's okay considering that it meant
the other transaction did not have to wait anymore.

2. What algorithm did we end up using do fix the set of key columns,
and is there any user configuration that can or needs to happen there?

Currently we just use all columns indexed by unique indexes (excluding
expressional and partial ones).  Furthermore we consider "key column"
all columns in a table without unique indexes. Noah disagrees with this
choice; he says we should drop this last point, and that we should relax
the first to "columns actually used by foreign key constraints".  I
expect that this is a rather simple change.

Why the special case for tables without unique indexes? Like Noah, I
don't see the point. Unless there's some trade-off I'm not seeing, we
should want the number of key columns to be as minimal as possible, so
that as many updates as possible can use the "cheap" path that doesn't
involve locking the whole tuple.

No trade-off. I just thought it was safer: my thought was that if
there's no nominated key column, the safer bet was that any of them
could have been. But then, in reality there cannot be any foreign key
here anyway. I'll revert that bit.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#44Robert Haas
robertmhaas@gmail.com
In reply to: Alvaro Herrera (#43)
Re: foreign key locks, 2nd attempt

On Tue, Jan 31, 2012 at 11:58 AM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Well, we're already storing a multixact in Xmax, so it's not like we
don't assume that we can store multis in space normally reserved for
Xids.  What I've been wondering is not how ugly it is, but rather of the
fact that we're bloating pg_class some more.

I don't think another 4 bytes in pg_class is that big a deal. We
don't do relcache rebuilds frequently enough for that to really matter
much. The bigger cost of this patch seems to me to be that we're
going to have to carry around multi-xact IDs for a long time, and
probably fsync and/or WAL-log them moreso than now. I'm not sure how
much you've worried about that, but a new column in pg_class seems
like line noise by comparison.

What about SELECT FOR UPDATE?  That's a pretty common case, I think.
If that's now going to force a multixact to get created and
additionally force multixact lookups when the row is subsequently
examined, that seems, well, actually pretty scary at first glance.
SELECT FOR UPDATE is fairly expensive as it stands, and is commonly
used.

SELECT FOR UPDATE is still going to work without a multi in the simple
cases.  The case where it's different is when somebody else grabs a KEY
SHARE lock on the same tuple; it's now going to get a multi, where it
previously blocked.  So other transactions later checking the tuple will
have a bit of a larger cost.  That's okay considering that it meant
the other transaction did not have to wait anymore.

OK. I assume that the different treatment of SELECT FOR SHARE is due
to lack of bit space?

2. What algorithm did we end up using do fix the set of key columns,
and is there any user configuration that can or needs to happen there?

Currently we just use all columns indexed by unique indexes (excluding
expressional and partial ones).  Furthermore we consider "key column"
all columns in a table without unique indexes.  Noah disagrees with this
choice; he says we should drop this last point, and that we should relax
the first to "columns actually used by foreign key constraints".  I
expect that this is a rather simple change.

Why the special case for tables without unique indexes?  Like Noah, I
don't see the point.  Unless there's some trade-off I'm not seeing, we
should want the number of key columns to be as minimal as possible, so
that as many updates as possible can use the "cheap" path that doesn't
involve locking the whole tuple.

No trade-off.  I just thought it was safer: my thought was that if
there's no nominated key column, the safer bet was that any of them
could have been.  But then, in reality there cannot be any foreign key
here anyway.  I'll revert that bit.

OK.

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

#45Alvaro Herrera
alvherre@commandprompt.com
In reply to: Robert Haas (#44)
1 attachment(s)
Re: foreign key locks, 2nd attempt

Excerpts from Robert Haas's message of mar ene 31 14:12:10 -0300 2012:

On Tue, Jan 31, 2012 at 11:58 AM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Well, we're already storing a multixact in Xmax, so it's not like we
don't assume that we can store multis in space normally reserved for
Xids.  What I've been wondering is not how ugly it is, but rather of the
fact that we're bloating pg_class some more.

I don't think another 4 bytes in pg_class is that big a deal. We
don't do relcache rebuilds frequently enough for that to really matter
much. The bigger cost of this patch seems to me to be that we're
going to have to carry around multi-xact IDs for a long time, and
probably fsync and/or WAL-log them moreso than now. I'm not sure how
much you've worried about that, but a new column in pg_class seems
like line noise by comparison.

I'm not too worried by either fsyncing or WAL logging, because those
costs are only going to be paid when a multixact is used at all; if we
avoid having to wait for an arbitrary length of time at some point, then
it doesn't matter than we some things are bit slower. I worry about a
new pg_class column because it's going to be paid by everyone, whether
they use multixacts or not.

But, having never heard anybody stand against this proposal, I'll go do
that.

What about SELECT FOR UPDATE?  That's a pretty common case, I think.
If that's now going to force a multixact to get created and
additionally force multixact lookups when the row is subsequently
examined, that seems, well, actually pretty scary at first glance.
SELECT FOR UPDATE is fairly expensive as it stands, and is commonly
used.

SELECT FOR UPDATE is still going to work without a multi in the simple
cases.  The case where it's different is when somebody else grabs a KEY
SHARE lock on the same tuple; it's now going to get a multi, where it
previously blocked.  So other transactions later checking the tuple will
have a bit of a larger cost.  That's okay considering that it meant
the other transaction did not have to wait anymore.

OK. I assume that the different treatment of SELECT FOR SHARE is due
to lack of bit space?

Yes. I gave preference for SELECT FOR UPDATE and SELECT FOR KEY SHARE
because those are presumably going to be used much more frequently than
SELECT FOR SHARE; one because it's part of the standard and there are
plenty of use cases; the other because we're going to use it internally
very frequently.

Now, perhaps we could fix that (i.e. have a separate hint bit for SELECT
FOR SHARE), but I don't think it's justified.

In the meantime, here's an updated version which fixes some funny
border cases, mostly involving locks acquired in aborted
subtransactions. Interestingly, it seems to me the code in heapam.c is
now clearer than before.

The other bit about columns to be considered keys isn't yet changed in
this version.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

Attachments:

fklocks-8.patch.gzapplication/x-gzip; name=fklocks-8.patch.gzDownload
� (O�=iw�H���_Q���@,��+�s'�vl?�����pd�0�I���t����m^&��O��Tu��K�����3b�,�
vU��k�\�S+�
�������_��*�Z8�Y����v��o��@ppg�YHG�|��ryv�k���;/��_)�J����'�W0�7�
lT�L~I�6L/# �0������LgL����0����
|l��Q\;bnY�UO�
0o�l�1o����t��"�g�������Cr�����Z
�����
@r��$��A��B��x�nJ�����?6`J[�
>W�/�ST�<K�u��8����^���94k�9#��t���K}}~|y��_���>����/��[J]\���[o{��.�0IMyB�{��k�j}m}������]��>����}	�
l: ��V����.\qt~v�~)�����4
��>��)L���%Y_����;v��B��K�P�D�1���U��% �z��P,o�;&s�(����!a�poX`p��		�pHM�|b&9��{������z��S����`�
	=�x�
�`!��5����,�(��Z�����s~AthI�(��6CZ-0Ba�7�}F����n�h�9��@�>%���Q*��������#�;s��Bb�h���\y�#G��0���A�������� �����\�����49�G���^�e���}%B�O9��������V��s�%�V���by^>�����\�^��n=5����Oa��������-rD��N��7���������&F}KYA1��*_%��U�z�(-���B:��'>%=@-�K����f�n���V��nuie��+c��2V���K�;����yk)���{��n��t��E��Qy���4�7e���Sc+0��N=* �����<g<r9f�_��	d��(V�?`:si�t��Q��Ri}��-���R}��B�F�@��1}C�T�����)[������M�l^��9?��w�zS��EJXIX`�`�hf�<W�7C0)R����E�5
]P�2N2�������/��gW*��>9�<=|%,3��=�&&�`?��{�2�D�O��F��o�!���)~!�����[� /.ONZ���������NK�|X��/*�,	�����������h�
��k`:�V��R�T��~L�
A�d����;E�eA�V" !Yy����RcY�^�J'��
��U�UM���Vobd���@���,(1��.`����l�H���]!�#X�^{����[CJ�q�R�S<M���7��ux���������XE�3��fm���JW��+�� ��Z��3��]��L��;;�i�EE>w�H�+DN1�����Af����Gq������������H���M!��M��wQ�j/'/�������t����B�E���|���Io�?���g4�����
�x�^�-���_��
��-����@"��}R�kE���]����N��R�]<Z8�:hx"+���e��<�pS���d���.Y�$R!P���+��
�zD���H���@���quU�G�
�OJ���It�'�>�4����E�]�����+f�trb�7��z<Bb�)2��������/�����_O��F4�7�8:�����0!r�.��f�|�	_������4���B�de�����R��*1LP�W��;L���*b���.u��|��_�/Ky���cJ���$�+?�9.�D S���M�������B��
���u/�����!?��ib@�g���y�
f^�t�d�����$��P��SdS���P�`����6'/�,�(Q���8��<	�@3$c��f��4T�dF�H��[R7=,��o��a�I����_�����f�\$�:��#aJ@������1hp�N�`s:5���6VN#��!N-���]�����g�,��2+�W�g#�_e���jn�� �`)��.�h������Ta�Dn��MHO�t���I�=��|*11��7���f������E�Mb���u��-�'��N����-7N��S9p:)K�Jb�Fd.�'AF	�*Y��*�!s]��z�l�e���nXh
I9Gn(V����C�29%�"w��?�D����a����D�]������|�"EW�t����F��S��Y����m��k�aw�W��U�V�2Oi�
��v��.c"�m�O���
[c�������bf��q��Ip�
���2Q.v
�e)�W�3��|��c[**KwD�r~v�g����������J�Aq�?��������CW^8$W,����w�\�Y����Q9����U�4����U ���9�G�8��/�L���@������kv��\��y�G:W��03U{��?�AFwG��������A�%�{cI��K[q��[�����z����~��8Ud�f�w��nM��Yc}�h�7������#;j��
=b���%/�2�
�d�C�G5]��H�D�+Oq�|7[��U�{%O�A@i����������vN����z�+c��D��x�E�l5�6���F��1�W�`y�q�J On�fHn(���!@w��p�&�C/����9�L"@x�LoC,1o��rmi:���^���hl~���"��~2�����\��`��c�<�o�M��@��D�h��HD��@�M&���A�����S�c���o�C�w��aW6j�3�;��{�JlXb"��G��Z<2����+�h{V���z�$e�����������5}�.���Z�7�}D��[�����>&�n��8�o	��jr���W���7����';#�:�A�S�WK^��f���D}�&>�����s �i�j��jN�U
��,=Y��k��8������j�-���T�`����NHY�g��b�����Sv���]�Y��^
�)1E�+�i���<}��2v���'����8qc����*[�<��"^)��������H���J$3����Z�$#����(-�4HZ�������o����e�R|�gA5�k%���kL363�Bu�%����L��T���A�S�Z�L����w������n��u�#����)��������p~F��J�;�EE<����� ��dV���G��L�H��w�����#�A�����b����|!�#`�''�Vo,(�,�w�s� ��.y�y����N��n`"k����Nb�/+dX��^��#./����o"���Z�'�����1;K�A����"sv���
�%����������1�h�A����]�e� ��
m����L\A8d�I	��S�����?~F�w����wv���jA��8m	��l�;�P�(�T��!bW�r��@H{�A )������Hj��G|?r����S�A��
,��`����P�j�����"��>�~�A�D�G0�k��<�����P�b��k���P����{'s1������*��U9�*r�Y^�G%�]kn&��0���B�D�������U���Qo�<��+�	:����g��* N�g"%���9��`�OMkH�nsjK�0����QW�/=`'���f�6Hx�
����2�_�z�0k�1�&'7�qHk�LM���$FB�����^��5�����0�fd�L��7���!Jh��1gL��L��K(��vMcwc;��F�+��bsi�?��`#�T��
d�~�L2�xy���L%��{>XD��'��s���!�~�D�xK|,���_,���q��<��?�!@�yE��(su�_SH��(��E����:1��I�~�
_��jU��B]���z��D�?�\x�7�	jz���>�*4A�h8������L���Z���@8���:�k�<�O�{�[*W)��F!����m����6vw�r�	����T
���f���`���@���N�:�)���p�&[��e�?F>D����&F���.�g+Q
���"z<#���S���~]>���.���������N7w��c}��>����n���������fbd1
�������7��H�U����h��3sbN��m4?H�yl���Zc�hnl}���z{������F�in4@���	=?�����-����{���T��]h4-/�l��v~������=�o�
���$R��t��4"���+���+��;�����U��%������8���*n��5��\�|$,s�& M���.���?���M�&Wo2����r3���
��������B�������>K���u��?kmfb
��+J����Q��<�a���bi|���������`#��!��%�������4�]�&s�t�LZ�����)�4r82�LI�,P�'d/���%=����q�Zr����~�����v�p6�k���@d*����5v��������s��G�'�L�������*I�z�������N�����@tr�Vl��=4�f��I�(�Hz���Zc�������H�&����qC��H��>x��2�BT�!�O�Of��u�1����a��FQu���@<����Go-���L����dZ�P�
a���4�]j�xFH��'&d�V`)5.�3���|!8��^X����18vr�r�d���hU�Y�~�JID����k���\�F �t��43UX�r�x�P�/���`���/&�4N��z���h�A(�H�f*���C�M��X���MQ���G�^W<�i�.M��^�P����������6�U�It���X����e�.>E�@3hc�LG�!E�����t����0�y�P�����	��&(_O5�����(��-0	�O����x�D4�'_X�nO�Z#"o���j��m�RoA�X�!��O�i�n����`����k46���"��b�ca~D�3����" �G�@��������2�����$3�
�wN�WSvE1iWB��@S��@���a	t�MZ��X"���(�eP'���ZtbOj6�����d���	r=`�|xU���d��9��r���(Xh���i:u���p�>�Q��9d�e�P�>�,����iz�Y����6�l��l4�F��,��1`��B9]�*\x��EMnZ�%��w&2�9	A��F��x}i���m1kr�0�6�" p'x�P3?����MG��Ct���;�)T-]�j�j>.���#�Z��q/����0�UA>1z�S���
�;���!Klz�F��w���Y��9w7D-c{�G���7��+�T\��� p������L���h&N�1�"�|����gY�@�J�"����&1��TXZ"j1�X2�R�����3�[5������?��[�����A����q)>����)�HG�B�UL�o�v�AR���L��]�&p0\0��k��2�A&��r>��x��B������ W���pMNb�Zk����I�i3�y��
T���	3�;�)��#X!�x
�h��dj�X�,�\4$_�
�H�j+�'�C e��
0H��'f�Vl��������[l|�������1��z-��"lx�~�	H�De���� 2���u4+G�^�2J�
���H��Ot|Q�%.G�U�N|���`SvJ���7�k&IWO��C$Ce�[�����=�X�4�$�	�-gi7���<����S���I���[D"|���{���h�v�$�����H�1�4�8�(���<���V�o:���Y�+xe-�G�G�Urcs����cV�9
�L:\#>kM��)���A���� ��U�/�k�%�������
M�@@���&���7����%��~MC��J*���a�d<��>�{�_�(��A���
h(��1�T��\��F����%_%�da��6�9*m��T����M�K���N�F�z�[�<���3�70O$�l�?��n�r�H���xc������
��57���z������>�T������|��G�;��1��������:���q�pN��j�K�<�����yWW�?��_E�{�HF��n��q�/6�N��o�V!PmI�VI����?{:S�)
6N���A:u���������`��)�@n�P��"����	-�7�S�;���C�����)��z�����c����������B\�N�[!���s\�w�����������O�R'D�|�39M#�`,��^1Sd��*\_����tO�6��3��b���O���s\��{���z����o^5kS�6A�V�R����n6���w���	��-J�Y��m��o�n�"�\��gD�u�M������S�[�N�������M8*	�x���w��'��K��1>@q_�����y���cpr��*f�-t|6�2w��Y��P+��.^����kmA�.�GXQ��UC�d�"���u���@�J�iE=F��k8��I�2gW�����u���M����`P$,��XW�6y���%�3��hP�U,�����@��Q�.x�Ls��Mh�����YXG�!��J7����)q�Q|�5��a��HO�+����DO,U��9.c���ry ��c��&_�a5�N���*%��nw�F#_��}�:�j��u'�1*���v�
1<��\������G�>0����>��I��xR$7�C M������v^I���oA):�{ei���&Ru��*���s�b��}�|���.V�2>_,�;�6u���@����ZBg!*���jMR��4�|s��	�S,�k����Ca�ZGd��� DY���PQ�t����d���G_�u+
�z���'JG����2�=���B�V���Y�
�Y|�'EH��V_Bw�?���t^����&B5�]��.P)y;�Q��T���P����6r��U�iQ�N��?:}JV��`���DKukl\+��(�]��X�>#(^ �x�&ZH��A2�����A������#�^0���^#���Y�������f���R+�,�V�f�#�)��Q�%��#R�m�q���7���9��������[��|�S��6�������3��=[(�H7�h=�3��S��DLO�];x�@W{�`����
=_[����W67~A�K��p<lyD+�0c
NL���>~�2��*T�;x�Xl]�5��S�L���

��9�U��(�;WY	��>w��I?�`��g\~��3t����]q/�n{����X6���uKf��Vx<�|�G��v2�f�C��Yi1�4��j�
.��^:�d���N��	�U��LG�7�X>����=t��Qq� �6(�m::�I	1��z!�2cZ@���JZ�y�Cz��omS�=��KG�X���Yy$�����0�v��/�+<�,�Q	�
�����-D`N!��&9?�:���58o���W�gP	��D���X`B^�?��R*��S0�n:�.��aB�	�!y��
������6�]R�3~���o�e�v������'����T�������K`	��!S]�X�(��fv����o�)�m�c��r66,���[T��\����5�����D��GG�"u!��s��U�	,]�������+����U��=N�]���C.c�S95v�Ls��mg���)��6���c�e�B���ld��������yaf%c�/Osw�-���!-Y�1D��1�an�A����{����a<��(S�M*�W!��%��1
���?m���{�~M��>��)V��)��8�����Z824L ��+w�y�O����`%����TKu����l��v��J��?(�?�����)������.L)f���E]���7������0�Y����P��{{�+E�v��DS������a�����/�_	�E�48N���|��L�svF	��\�p��f6�5E�)�q_'������WU:�,�p�pt>A_>%������3rwqw����5](=�bn����zX�Q�:���^-�~9��!3$�C|�WO�*���
e��L��.[ozOWR��e����,~5�},��������z�?�-������E����,=)���i'�5�JG"��~D�K�['X[�>����`�����K�'J����:���z	����m�s��@���d1�	�ja�-�{�.��?M!L�����K�<�/����f��w�Mc
}]��,�C�������lm�]wJ�P:�
�EV�+����E��Hw ��?w.����r�nqx��Z��d���k���r��X0��W]��	�5]8��Uq���_�H�@>��(fkT-���&�����������F��?U��w���[L��*�t�"hF	�ZA�U�z}"�K��j
�U�U�2S�d��?�i�����7��L��^���o�����+���:N���O�����E�ZZ58=��U1�3/Gn5h�D^��O��7s`���X*?�m���Z��Jk�"���y�>H�r���Y'��%]4Vxk/I�|�����i5,�s'�5��n�����l�@��wH��?*�{f��q]�:�4���P0o�#
�WH_q>r�S���!��d��0|���Qu��p���L?rg|�4��BW�����
������l�x2�V�s(�	!�s'M���������^����Gp/�,�"�~���_t�����nT���Ac��vp����4���c!wU�)�d�H�Y�n����H<TRp�"j0����������oN����|����oW����uJ����f/�c�8.�2������C�j�?������F��{)Q.�s��
����Q����8��&�{Gg�����PU�s��"���N����s���Wqjr��*��o���i^#��������S��D���g���>uxg�0�)�<��S�"����bJ�@�����F�ZY��'�^�D|{��a��t��	T��iq���8 �)ozx@t�Q�&�O��G�XB����|��Y���c�.J���J}��Cq�,����r�\+����S�?D�������h%�\=�K��o���[��\87�6�����Y���t�8�{�X�x���s�e���aM���{5��V�U������O����T�j+�	2&��Tl��I��+>����Z~e��p��q�c���_��;"�����������6���7M-��{��M��`��-w�m��r���H6��������HPat=pl�yVa	��d,��^
ai�q�.�h+�X������5���"��(bG��P��4E;p�2�*�2"��z��� �^��2��R"�8s���,eo����r@���s�}�}��x��=���2�o��o�l���{o���$��[�/M|���"3D��x_��Mw�8;0b�;SwD��Z���#���Q����Q	?�.�UVY1N�p�5��J�5���M�)��^j9����V�)NL|���.�'���(��O�f�V�[�����_^��;E��6���������)N����p7�j�f��n�C��]o9���fV~����3E����*%��%<MFV�_<�<?�T��:"�L��,SF�|����"�G|�������+x�F��U}^�TV&sIs��o�2�4��t6��%�CX���G��u�S�6�k��]���3@�������������lP���^�wO[�S���}���6��M���m*{�H��T���"�&������)����Ne�}��|�{�=J��&����Q��������!�fe�$0[��HC%_E5i���`�_���z6l�j��u�{�������|{��t�Zv�A�C"$��r��q������14��4��_O�5�����#4���a�j!�q(x�Li;��c�#w8��A"T�w�f�	���+�����#+f���P�\w�t�u-�����O�8+��y��l����$���'��rE�]��uz��r�X������~:B.~,��E`�q�2M��r�V<���e��ZO��o�&����s�/^-`i����d�B�f������M�����[uS6�LsOy��3�9����������f��q��~d��oU��ctb����������\I�ou���#X��Eg�.�J(��{��������'�������":([��J}�Y��"�Nj���_��(%����[U;��.���	�n���#�l� {�xE�4�D�|[bqk2��T7�����]�o��~wi�7t�<%�qB�����{�����hii?y�[�+i��*�,j��l�O5�6��x.l�wLR������M������w����J�U�0n������������0$X�y����B�����M�[�VI�o��6_H���q23Py�X+Q����s��\)��k��Vq�����<��R���/��U���%( �(�[����t�U�u�|7��2���K,C��1-N�q/��9�i^HoA�E��{"[!��VA����]
�>�Txf�
IHH�8#�y�H�� ^�:����o��ptj[����E����u�g����e�����Iw��Z��j5u ���`�O��)�PVP8|�O�;��=��|,%�*��>
O��$��t^��e�Q�@�9S`S�������E@����H�pQ@+c�swvQ=b���H�7�I��<V�R���I���xy��;�L��A2�l&����]|CA{T:�`jb 7���_� ����*���C\�!��R�\D*LFc,'@`�5���W*��}�h�b$����A<�P�#->�0F�]�����d7b2-q8a���y���bk����Z����D��[U%��t}>�e��o�E�z��wHRh��;�MF-�[���V��?��d���<�K3���Y�;�x���g@D�	Y�y�:�t��D�����������JQ���^�}B%�Pb`R�?^gt�p���[
I�3����3&�U�>Mt���(��Gq���j�&)���]���j��;fkM`���mK&I�H������z�l�� 9ZX�M������O����f��B�SLGb����x�P�c>�|�D���.����H@���:���~���D���|�o�V��%X������#����'<^:�2*(~��+�9����|(����[�Z��]5�w��KS����
���e&����W�I��(�I_�J�l����������>G�!RN���P�T�N�
���

�.8k%��!KA���X�$)-��B =�	��P=��u0&p���F����z��������K5��f��0��<8������7Up���,�m�0I��C<�l����A)o��^p<!Q
�EF�X�=/@��	T���I��].��&>�Tq�x�]����<_YqDm������",����~o�U]w}�#����
4�7�m����6�w���7�-�w�k����������(�,�RzC8@d��K�S���	��[jHt�@������1�Zu$�D)�w���1�#>�%�yT��6����l�T��U?���JX���w��KFh'�O��AW|���6���$���W��Di*����w��#�I59JaT3������46����T&����T�a������ ���hq~�?��;��/��"�U��
����\�f��Zu�V�[��&�,��*����&��^��m�>C�[�Rn5]���!����.#l]�7;������L��D��
>R��)��_$��p��SU�U?i��c'Q
"���)�;�~�0t1�}���L�r�pH��>�Vy:9�/R���q9������=��	!F���:1�b�:��;k�|}���c�FH`j�9�igY��q���_�`��4Pj��k������<��9)�P2X����R��H�S���%�9�Y�i`������c��? Z3Gr�Oz�S�*��,��9�FO�w�S�����p5O|���|�+���|�1��rA��\O��K8DNL��c��F�O4��R~�5���������2�~�s��s�~Y����2�j�4�l�3�T��h=5OB~�����V�~Wo5����������7{��
c����H�����������cE�����D�j�^��=���eQL7��F�@�?T��7�n*�����4�q��?X�t����2l8+��g�W���=������'�\s�	F�f���5R5�&G�����`�u��LJ���#�,-S��B�g��G-uU��������%m�;B�;ZW�1��E����h������������$
����q��D���Yd�
jp�:-�qN�;�W�+m���w��Jxx��*AGp~�$ )�`&�Z��3��%�%�gr��^+�4z���}S�h
���*�X9R7D��*
���7�%����^���2dCXV�	qX�26`G�����$�^�7�IN�z�����7#�����%��$�4i��)���Pu���s�X�����v��U���o�l�2�C�R�b�'�N��L*Y�w���j{��]]�J����n�^%E�Z+eV�Z�Jxiz�Q�m�{U2/Q�����XEEd��\��D����%?�
�;��%W�s���e��Y;�];�i��k�o���Z�$��n%���*z[i��K�>j���Z,/��W9������1J����k�p�b�faX���X8wf�,l�|��u3c���Y�9WFR��������%�-���������00���-��l5�����[�L�b��E3G}WJ��=:�5���O�i�lE%"�O[T��c����q�y��/������m���5���+�:�j���nk��J�����m2�U�H�����%�/����dDxyc��V�D�nr����#�{��-�B	�����s*�����U�^�"�T��V���,%V�����!E�t��>W�U��|��d@�}�^�{r�����}�1�\{��-��.~*�=icz2�R�k��nxVPG������[�WVaL|>7��|Zq���z���=d��z����\5i�i��X�;���Y*�>�1���{�~�s�#?[�L�<Tw�Z����^Lgk���},@�n�3��=�;��856��Z~��"���K1���6f��iLa��$���p��K.�^"��z�Q�0��v{���^[�"����-q���-g��=���Ih�n�M�����A�n
���AQ;a���KEZ08�
�I"{�
��u��Zy��EX���������.��'�E��1�,l���
�=}�5��+���L������#�G\���=\n�v`w>_�]��N����Z���U��V�PPx,�e�B.��i�&�<#���N7uM,������n|��o����g��u����%�Q�.���%�o>����������N�	�x��_g��A�)�<����� ���M[D���	`F(�M��_f����	A����v�[�����U�2o0����b2�w���icE���&l� 9M��b�7������������r�Las �����
g��l[D�e�,N�}xl�SA#��S���PV��@2�/M�EH"��M��P"�.���>B�������U�z����`{�N���}k��y0dU�<S�����K��	�T7���kNjr���$6{�3�g����� ��e�����\h�D����'b��-�*77���c��w3����~�@{�oL�]"p�A�pf��)�kR�}@�q���u����^o��oo�u���u�o6@
Pb�K�%�Z}xa����aX^�1�#���D�W$+�'����4��d��;yR�/�t�������W�@��Y����V�}������&Y^�rE����������������[L���5��u���k���4��tR��������F�eq�������M��{oG|�O���.R����>
&}.gfU�,�GAP g}Ey5\��Q�K���q%��y�\?���[ddjm���T��A6�B��t��0;k���w/����w��AiE�)��4��t��
��g����Q5�h_vM*\ ���u;�^OY$�4��W���m��^!rI	�������~�V����T�4�o�v��hnj�09��RT��� �kSe��
���I<��e
��h�C�K��3v�c�]�In�[���:~'����}���(�J;6��X���uaH�@q?�po:mY���FXs�n�1�O,oQ��6^����'S���eTD jz�;AI}(^V�P�ck)���N�w:��D_�Q�g�=O%�S�#�>��C�C:��7B����,�������wttxT	�|�%����c)���yG�n�(�=�1��
Fc��A/�x�=t������Pi�0r���J(�N�,T���d���I �
���y�qhf.��N��e����]��b�,����<���v}o��b����D%� �������_��IIm��l����������AU$�(Z�C:��@�	p�	an���������|�*|�K7�=���3�<B^P���7k��(kQ��t�X&�����|*0O_;�M~�'{��l}D�v��dt-�-���t=y����*�U����Q�jY��V�h�DZ�ER�=L:���
�����+��<�r�U����X����?M�	p����/�}J5T�
,���������������������]D�<_����dW�����Z!�eum�M:���S����"O��"�������	����Sv��S��J�������eV���6+����x���������=����
����0����A�r�@��\����_0~%=�~�X�t��z���B{z�pt(c�qV9�4�;�trj{�����tJt2���F�����O&rA��00�:VX�'�Z��Oq�0	�e������ZY�x��&�VTO����1��)����	��k�������j�Yu���x��G�C��!rr
�[�����O��������YAn����{a��*�x��������Wm�7�>�n����D�9���+ej�e0K_��q�������(�w��NKR�!��o�Z��7�[���ka����nC1�"��6!<��|�_���m���b��/9����s���Y�����v>��_��W�����b.D�03�T`�		O:���z��������fw���T'�q��lMsQ��Y�f��sj.�1>�x}?��(���zK���_+�d��!x����ST��z)���8 �5-	,�ig=�����k�8�[���++��r\�����&�t����5�'P�Nu�B�� ��^��u��~`�\�q�IG/�^���.�����3Wi%�����u��^�����m��c���(y
������n����{o��"
�f	�`�O}
�7�.��S�������v����|�gn�m?=/=��n�>Si5<�h&���A������?6�k�
��_�.7��e�c)��V���@|��3�'g�),�V�<������1!�]��hT$@!�#�Nr�x�&2�s5�(��E�_���-#���E��M��Le=B�`,���N%m>Ai���4��r��"*�e��a����&M�3���H��")=c�T~�O7i�c=�(��Ff��>����j�9��������;:��"����p����-r8����,20��E!��%�[b����/pO����$��m�!u�f�<��%�97x�%	
A4W���/7��y�*��U/�*����r�iu1��z[���(Ze�����P�sG<���,�����w�,�����4���bH��@�/�*J����>Ua	��
N��n�E@e~#�Y�����$���������T�.��
[����iTq0S��Y`�A`��-f��^�B	iW�[������a1�����f�����������U-*4�f+�	��S?+���wB�,�V�����3#���U�b�5N�I�
s�zm���������e?�������{t���-�)�s#Xz�N��`��V�Td(p>����������<�`��%�'�h��S�����W���K�N
��Q�������_��ZvRg9������`�`h�XB��x����[���0������)�X]'�|�*L������\��Bra���X�����T�~W� B����{6�/�&�@���M����S*y��/�v�AA��=��5�[_�Nu�+�()���Z�U��1W�V�k�� ��k��
UQ]l`�Ef�|�N�K�~�Ad%UT�G/s,Q+�\W�9��p�P�O$����&���Ww��U���5��q�h�bb��)���nX��`�������;��/��wK�X=�����^��9X���u��4���]�v���Q���T
'V�����my�S��oMa��Ed$R6�#$��P�[�W�he��5G$���@��R*�c&(G�Das��s�L��}�r�J�
���T�������������,����C�::2����o?=��
m�t9#F�����Y��|aE-����JB�J�
���z���vPb_\�Lf�I��*���	�7!�.�i]
#tfH��Kv�,����Sy�l&��S��^$�]:�r�.,c��y���������C��-))��5_������<Y�:����\� I�K�^�2'V���)Vt,�A�(���f�}0\
^��)����o������u-1����W�K��I�AH^��"�� �4J��>$��b���f�!�]��3�}����a\{R���jI-E����|R�
i�;�*�t-��J���x�/���S��[������������j��[��n�r��9��A��/7O���DTU��1�2"�����	U.�.���/�*���1���U'2;O�W��D���WU/��W�n���vNW�����3�C���������xP���T��\�Eg,s��2w`�������Q����>b�������S��D,��ux�q��A��������k���G�t�S��H|v�Z���$U�L�*�B��X���]9���� /�s�<�%���|��L�bQ��&!(J-����`3z���gq�W�R�p�]�)��U��am���r��R��t�|�u�W��k)=&������
���y�n�0����\�v+����n�9��������j~�!����M����kK��9��f�#�a?n��pv�&*�m�L�.0�Jb)_ ���ila�a8O[��N�VHRQ����4+��.=�����CZ���.X�v�9�|/�p'���.���i;���w�H���r?��|�L��r�H�����*���r�����x��������4T����r�������,k	3-������U
��?��Hm06�am��YJ�Qa��NF���H_�F}�pM��7O��f��������z�9^�#�����([<�d���V��X|����(���KJ��*�k�����.���2{;�+v+��~��cln���nx}c�r��H��������IIe��I������WS��}�"����g�~#��u���]���3��R��%.!�M�0�� 5�(K���������{�K���y��Ts3���T���t1����3a��44v`Wh��4�S!x��LlM"&�k��~�foR���g����n�%>�NU!_@����- �����oZ�����j��t�\�M^�7��3=���
~�o�`H����A�i�=KCe����9b��������CT$��$}�%��T_�HC����{?C72�,];��-rSM;��7��Y�����v�t��ne-S���X�p�w������#3��I��B���c�	�d�eh>t�u(�_0�Q?7�E�}���������QS���D���,���Y8��$rx���G��#T����F��h6kN>j ��(����Yf����,�Z�J,���&C�q�5p}G

%����2�����������%���������W_�UA>�N��H2�h��0��qD��FS�p
�x	gu�Y���T�tf�����������)�w�;���P2����x�6�^/�;s����e�Y.�Bn
�q?���2G��o�66*�f���Z� �2.r7�E�S��f��\��-`�������h�D{.1�N�*,C�&J*��������C1d9��x�_�����3<G@�}j��G|nUc���K�&�mV�R��]['�,��h\���Bu�z����p^�2!�1��V{3�:^7��pa�W�z<��hc�qa �*�(�2#�kV!�iUT2�>��)����q�T��W"v;������B7n`+_�
a����0���58��(�5o��`3���(�$�1���1�O,|		��.�P<�4����`��3�o����"V���6�T$Y t��!{�11W�\I�v�����"����F@u�.h���H�8��>U����l6�����1y��7�����
2��2��Gc{�
	�4��?T�nm��D0�4�dU�i�}S��t����{��Ra��J$0�r���p�����L��� Y�=mMlf4H��5F�a'�h�>����IV��c6�GvKc��(����<����d�>
�bRB�T{w�a� ��Q�]v����pr lf�-V!�P�`�H�'u_�*VtCB�EF��@��0���6�: @*�p48�(_�	8��[��P�Q�����/Ol��2X��x�Z�.�O}�/������4�r9�Q�Dhdn7qM��������Q��MIfLk,��~N�w�����T�N�*�^���K������c��
IF)As�R�a!N�SV��������D�
}"l�!:���7]�]z�n��x�v�|�g_|�g��4
/��oM3U�PP������-��rp�IB_k�Gt#`8����F/��!H�+��4E�)���Ot$� 1�a~A���#���
5{6V
��L?�����<m=
(����M�&�s~������w��O��{�'��%FV�6��[[���%���J�L�h�\I�g����P����*���Cv���,����Z)[�u�R����rLe��<Tx������R�Ny�:)�����q���B�5�l2GWc�aN�A-�'��f�b�k��y4�F"<��z��s���+{����/�x���-h��~T���)1o\E�����s��9F�����U	�A��;����,�*�U���p$V��x4�V��]����������kKy8'������xY=�:���-8��Z�>�_1��[{������Pu&��r��1���X�w�������
��t�I�x��T����3�P����=�a'xY������)�_�Hd5��0�6�/c��,��}ETY���8�&��1��9�S^�i^�\�D��O�.w�l�O��������5P���0Z~f#A1����u�CN1I��A��/�E%�����
eOR�������f�JW���
�q[���Og��_����y��|�u:.]��Y�#P���1�qe����m��y�i������V��l���%��)6Kb8�Z2���bv'��S 0��>����������Cs �Q���v�i����F�gH�g��.������Y���[ �6�Ye\PLel�	|�<�4�Z�5��o2�~���.M�ja������{��Nu��;���
���M2�,����|3z�g&c/ia���.�����g��\x��}�.���:���s�zo��~���a��^�Ky���e�9Z�y<{$^A��y��
����]��`Y2h��5�v��qBlY�XF!�&�����W�Y�b�����Og����C
�T�c��~�����RN�F�h�����N�&��M����I�O���"����M�p��G��s���8���y=����e�����9��C�����j�o��W�w��fx��]�aN�{��=��s8�Y��R�hm�_��|��J�X_�+4�<_�+��8�l��jI�"�����	������)G���1���3�������N�Kjm��_�b_�j_�'{���/���������3�`5�OJNU��\���C����r!��h]pU�l�#T��#x�g� �~�1l\�T?p�@���/�DlmQ��Ls�D<1�6�����4\�.mMrs����gM�5����1������=a��QPC��]��:�\�<��u^y7��EQ��o�CF����Xe�?�h;a�����������#�y�����4������g~?6���`�kXo��������I]��[�z05���2g)�A���`�T_��
$�V���3
f�6<Q������f�[ur������Q#m�s
��5�#U=!eSKN����;C>gvBc��^,V�b����GW����F��\�����(\E�h#�`��_J��g�O�
b��:gF��=�Z�hJcU���T?������}B���G0��M���61��=��y��
�J&m
���S$�=�����tXN	����<bE�����#����H�Q8����7��������wM"���!1��������������n�����;k:��S�6���H���E�� �"�82���%�W$�ls���YL��1�uu���9�3n�����s4+F#�jht��s������8�s�@ZVN,��r������p}��8��Q��H�hIO�dK�l�k<��;g
���U��p�*q�M~�r�C�����,;�)KB7����9�����	L��D.���$����a���2�{�QX����N�6E������N�V������vO�1ZCd� &��FP��]7W���lm�����������Qb��x�������S��3hGk�9�)|�n?��;\�o��p��Y�p����s`����hu��M���>�)�zF �g��o������ibvxX��@/ ����hU�b��	�D[	T���a�+���yl�V4�u��"`r��wwn�������e��3�(]�B���eV���8�#v=+�������
t�E�����k��;;���/Ye+�M;�����}r9���y������-�����/��M���\s1{o2C���\���-+
�����]l��4���*(��J�k|�Y�\lO����g��sQ�}:�C����5�����/,�����y#����=(�Y�NG�H�,
e��`�x7����9��m��&����E� ��x����wb����mkypL�.��z�.`��DAH �!0����*�j���N�MMY:�o0F�j�#���76X�X��?Z���m��[[��E��|�TX�����9����
RI<���JVT5,��$���e?�e���7��`����	�&a#�������]1��a-w�u�G�{�*�T���9�����]��2���ZM|���.uo��+r"W<��U��A���.AS<�T�y"��s���!�Z��8�'L���]lr3��9�����y�� '�l��=��cs�?�������J0W�@��C�4I-�HNV�������cS;��9<���5G7&X�]mUagLw���%�]�z�x��CV����J����l��'���<Bt��q �����&�Z>h����0^���-��g��������������|l'�d6O���	�����O�N�y�J��S�z���HH~��Mr�����3���I�����KKd��x����Sg�EAeV<Y�������(����
M
�0,����$9g����$������ip����1~A��-7|�k�O�����m��z{�U��[�d�ID�
�\%�v�����nM%�����oO�����I��7� RgB����W������d�C@0���
Y
��,*->�����O�Rr��z�R����MNJ_s�a��2��O���j�=��	���9�"�.4 �������D��>�����$�N��V�ri�5,;������7U���%��J���UR���&T�
���B���S���y:
k�m��v���x����E���c�NNS�D
��52�;�C���#B
���q���x���*d��V�]S�?����h�X�Q��c$�M��p�D����Awh�Br��BC��T&P[��8��2�����Z�+�.��#�z�+�x���B
����>�J�mvxN�l��$�.:��i���K["M��S6?[�B(����/�
��F�n�Z���v��X��0��_%�3��_��d����i�V��R�g'P�''��d(H���U�!�q??����VU�D�$�
�����x�D3���T���8 9��������1
�}�t��V6A�R��t����;O�=�����3��b��e�L���.P(3�	��u��D�c6F/�����Zec����8[F��5d�i�e��������]*�RUT+�L*�*����g��W`� `]�bS��$��dI_�7��1*n/X�S����"�[�L��)�YHa���$3���V[���������
�I�0�R�������6^�8*\"k�X�0L%|��@���UO�P�SI ��ZB�Mu�
���V0��"M�F
6���/'��<	t��*%�V�^
�]�"Z%�����>w�0]�����q���_�QB�4%::|�=�OT��.�(�{.D
s�aBW�Ro��K�ND�\����`�{+K�z���VY�����q2�\pT���!����d�'(�1��Xa)�'�����x�{3b��E���kK��
��]s���c�Z�O�"��S���P����U�^{f)�B�����^/���Q|�_��U���f��>|�a��Co��A#�����x�D�M)_���d�#�{2R���(��U���`D�P������i'`]qVDAP�p��s^8+����P��J�3
B���n(�����yv�$?J�H�#]�� {�1�j@��>j?r'k�a&�q���g���%����ethN#�*
�m5_���{
=�"����V��D��sQ���BKQ.'^�auG~Nk8l���H6��k�����2�:[�7�O�������)��(�/�[M���Y)
oy�$��lT6���"9���&�I6��&V
:SY�F7�!K.��c*@��6���T���C����*���S)rI!]�����Z�2`U�^:!L^BX#����C1`���8g�R��JP��d%��&q���s0��������D��[�r��������/�i��Cn��������w�*���3Kb�����������k��0��!��?�l�d�i�E	�U#}�����e�������:RyE�������J/��[��iy��~�;�e���t���gP��M5B��_�Gc��}|�����`��%�}�	;�������7u��V�2�:��\Y� yo����!��G:j������g����"���N,�AK~m\�@�"
��b$E�O�zU�������3R
��k ;���z�������i���=`��������q�4���a�^��9=\s��5��#4/"���m��la�0k8���:)&t}:O|>Ht\�qY������)h�!&��F����S�$\t�^����X����i�m�����|?2A�i��t'D��?�����[|��7u[>�S�-��Y8	5�_��wh�L�����s��M)���<c�|y=?���y�$��+].`u!1I��
��Y��x��s|��!?�����}����b:Lb5�H?�m�{�	��]^$�>�8N����.,(-;�k�%-�+t�W��@x����[���L�Q�pi�X�e@��L 6Xu�'�=<Bu����sAAi�)nC�s����6������a��{l�T��*{."���u5�>,1�k
e���0#�������t�����F�R�1_�����V�l����y�Y��OA�:X���|�gy�3��j��:����vR2�����\��.��$��7%!�Z�GN���E��� O�BO�s0����k�����a��Q��L�8�V�R�Dh�19
��`�P'�}{��*D�
Y_ph;R�D�G�
t�����o��G��-�h0Q��]�R2��Fp��)A���J�d�*���L�N�@����n�����W��������5��rx��A�#�G����?]��E^���z1op���U�t"�\YQfGFjv��Gf�w�,���Jh��nz��2�����h(�b��<yN\������'��O�����s�\�0t1N%D�uAY�\AF�U	�w�����2�(��D�;v�t����'��j���H>S�M[��C��4�r�S�nr���gy�"����e�[����2N�Y�c+�tJU�����P����B_r����EDj^!�d��gq���VpYP���y�v�~�z��U���V�M~47	�1���J	BP��1�0x��Y���l��
�V����z>���Q��*�c�)"3�����7�G3>��n�o���e1k��U�2����Tr�8����^�lB]0G]@^��cx�}��S4PH\��	��]�6��nQ��8��2^�k�6+��M�]���6��L�~�K�@�F|����[�\��1�=3�r���0h���p��P8�S����<iE��Q��L���W�vV�:{�����\}A���N��]��
�����y�52c*X�xsJ�dYxckk���2�����v���]Uo� @�����T- ��X���q���[����`/�{��e@��z�Q��_�.o;/!m���m4jYB"\�?1s�hJi�Cla�����^\�����1g����d
������������,�[��ft��F��e���/z��m6�������]��y���������w`�Vf� s2�>��K�G6<L��3�F�F�����Yp�����5���v��x�z����)�Oi���O%���3f���c"	��r*�������?���F���l0������KK������w�v�Y*���Q�����/�����'��NV������h�J��.���+��K\�D�I�AJ���T4�Bn�m��Ox�[��"�q�t�._9�U|&�*���7/��������Z��Z.��.�/��N+{�N����y��b�������A�����BzAd�o�+�!8:O�
�@D�$���,�F��L��o�D��o�2��) �, �w;�������=4��@�,*�SY�k�W�C?����:��@$���7���=��$f��P����y���SK%��bJ��fYA)�M1������f�o���6�z���{����v��t���JY'��^��z>Kf5�6��H�|���'�!���
/��"�����wh�dza��yA����^�&C�W�f��B�e)��]�f�^�l:o7J;��1�b����Q0��K�NC������C�q9�+��H�_�$��#�<�>���+xJg�d,�F�u�G�����4�f<.�������l��gxwY�p`!���}��$P��7;�{�ct8��z��l���7����Q�o&�n;��+v���o�o-@�����)��������?f��D�.��������rV��w���|K�!R�O�������	nU7+�[��-e��t����hnm���E�������a�vO����}����|��%�}��be�R�"i�����6���x�}��ZF�����G}S��zn��U\�LV�XBT�q
M�vEu�����LD$�p=u�%pm=�`��u���p4D��Z�v�t������F
I#n��6a�8 R���7���6J�1���LT����BQ|��"�F���o��R����hM?�.�Y#c����{��pD��su4
.����%��H��LH�'����k��{��|����������c��E����������l�$�������o����'D"���"��Wj�As����,$�}��E��db�G�-b7WQ���O8/`�y8��������h����,�&��q����G&[[��M���Up�9~(%�L���&�R��>J��L�xp!^�:a����^e\.ujI�����N��7��5�L���o�3�ne�U�������0���_���E;%��8��[�v�>�b8��z�\��������z��l�zj���X0��#����;vr�������pReu��RM��)�8L/��dop�u���4��X	D�������K�4���en��=��1���G���=I���2G����~�������+A�D~����|�*���kS����s�rRO3rh����d�`{e�����u`��fR�j�7&��*��c�W��F��|�(�V���_7P`S�����_Uj,�Q��<���wI:>E�����\���V{	.�����?R�p�����&\2
���s���c��a0�q2��~e%rg������Oy|v������?��!���=]OC��e���(�d����D�)�ii'�a���a=����d���:�4)���i�4v=��S?&y'*4��0c�j��]M1�&����e+��O�Z����G&��gJ7��Ip���=�Z�i�gqt8[��.�&�r=~��=�?$'@/�>�G1�S!�_�Io���a����F��
����/�]b�9�	���`��|�h�C��~����k?�-
a`Cp�;a��6)��~f0����NL?����T���]u����*��8����qZ�������igt�e�
�S�/[�j!�h�%�{�
z�y<���9���<L��v���tIN�=��D�Vw��BrHj�^)����D�������D�<����n�FF�1#����%wr��kS�TFH4#o�l#;SV.��$���)(A�<C&��q�)�X���%s��1�#��`�N4c�z�Z���%�Qi��@�����?2�H�.?��=���M@�������m?#��0 z6�}�M
�����&��V�J���qu��c���l��q�K�1������~�	+p.��W������`��k��(��M�B/���x��T�+������^3�!_X�wj�-6��S^,t�����f�<�F]���.G�P�A��^����tG?'^R4�����y�{3�������������������X���9g�:���u<D�����������~V�0�����am��<y�����������*��1�e�t!�KFj�����+<t�{dKjZ�d7��xO*,r~Gw��s*��CF��5�3�?��z?C?�������Zx�.�e`�����=k������R�3�F�����{o_�M��E���������_D1b�
������� 7�)�)O��`<���������_��9IxK�i,��p�c�R�����`����[��.e[`�h.�<s%�m:����f�y^�2�)<3����qf��F�3��,&*�s�;`������b��?3Ki�
y�2�?[[�T�4OB��lp��!3��]Q�6��I����]��U����W��4h�����8��cM<�!hz���h(NSB����6\4PZ[�n��
�`Re����z����zu{+��w� 3�����W���81	^���D$�$�"	fO��K�R4rH6��u*&
�+>�4��0�I��9�E�M!�t�?"�Z��.��k�����a�0��eL��# �CN#)��&HM���a��k��y}g��{M�
'��7�����
�'M�"A�~�a���d�0�,�1�=���LM�U�HG��T�k�fAa?��8#3�hX���F&R~�pii�U�����������A�ZQK��}�s�#�Z���Myg9X
j�GR��s��\g 2�������F���������P�U������mj��;7g�bj��������B��C��	�i���$��`
�}���{��x�K��+��<k^�P<�i���r����3t��@��2	�T��g�nd�E�{C
�G�TG� %�C��X�n4b�g��1d|�|����p���k�HL�$��lfw������S�;�����">��k!E����>����>I�`�c��o��=�qb�{Yv�:s��4�-�g�V����$Ld|=�.2A��`PTs�/���k��h5LSPZ��kt���i���*p��8%Z��	q�b�Q��� ��v�%��	>$j6L�������%z�V�"�[�#��bF"�L���T�?�������1��r���)7g~�d=�X��%�@P���%j���K����]_<�����{	������y1(�����W�iZ��x��Z�,�:EC��@M#
����y-�$���S^D��=#��$�����7�@�D3�������X�����_�����P�2cX����acQ;]:��P ��:���$Q��60%�t)��F���u0�K����"�G���!Q����,q��"4}2����$Z |j������R��
��,wF�X7PR(�NlM`�4����5=�0i�"���N)/��;�w"Q��������Z,���`��	-Z�S>��H���Qi�>^J6�Z�C�?a�^tj�n�k�Q�|���;Z��9(m�E{o�R��s1��a�%DO�������!
�������p���^�d%5m�=����%S�0�,
!���jy��q��R����W]������)�zGb�!�Z���?(�fNN|6 v�/(gOb��!hc���-�GT�on����	�����e��^~���t=/���<��Y������ku�tg���U)�J�� �BZd����u��+p���e�'���
b��Y�z���vj��8HW�_T/�	9�����/<tz��lL�A�P@x����+��#q
�k��)��b3���*�����5��L��JRa�\ V�
����������|���+����HX!��~�
�?����|���;����`��h��u��Q=�k��oP�R���W4/����_��t����v����^��6P�
� �(����=�����bT{	K����H�y�T"w@�$p������Q����=�4F��C�������)3����(N������
�2�Fw�f�R���x��R��Y�l25*}������J<��[�B1�1�
j����/��I�"���K3���A���p���K�d�� �+
*!��@��Gpi��k}�|�![��qNZ\Y�)�N����8<+�f�KC���6��x��2�����]BGg�"���mb�l5��p������+���1N]�H�Hr�bh�" Z�����gL�c9������)�I`N��9����B3�QJPU[M�+#-�:.���e�*GJ�p�Uet.�/��HmE�hzg����-����Y�b�)��4##�>~���9s��:�����W,x��� �m���<�~�������q�B�9
�}�>�\���+��� ��:�t�2��;dW-c���d
w�����<�����O�:XZ�J�%��������X9����##����QEN�(����3��d+�>�������Gx�Q�H�UQ���#&�w�-���b�X23FU�'.����
0�8}�|�^����>�MR��ya����kR�m+��9e�}��"a��Fk$�C��eZc��_.���t�7��a����(oc�\�mW������>�c����v���
a%Hx>��f]���PU���`,XZCb%���<*y��v����z`��W�8�����|�a��+��3������+X�p,�L�y��L�j���j1:u8
}:n=#];`9_k@A��~��af�����������o?�UP�u����
�qG�����Bt���y�~��rZ������
7��J�nZ��o��+���B(��%�������gc�����jB�4�S2��C��fdaZ�����#]K��\`��������N:1Y ���!e��/f*��Q}L��!����*�5���;�����fw��w>�����c�S�?,�3}0!��z������f:�!{�+�O�#Zy����w���I��;C�kn�^��|��y�q T{��SB���2Dn-7�2�����'���\<�[=�c��%$5e�~�cf�k��z|���)�k����Z/���V$C6��ni� ����-��rV9�>D�}q�;��o�����"�+Y����������E�n����v�C8��
x����]����nq���#�;��.���dTBo���i8:�3���^4�"1����-r��z[@&�Gww-�{����\����@��:U� ����76+�f�Q��T}��N�����s\n�jT�i]�
�<��/�K�����]�i���g�������������f=�L����)��x�)�����:tSKG�p�x��a7�I&�N}��Ml�a�Uj�scD���*��
^����LF�k��b���((p�Q�PED���{�lLb�]������j/���t�� &�����-H)g����%|V����@"��L��/�����vF���V���o���-�e]S@�w�
���jo#[p��d@������!�`�n���k�h���?�}g�qk����pQ���x�z�W�`��^fw�:�^������������$��]
���S����#_,E��K�8)z�a��w��^��I����cJ�'�~N&����Z�vz8;-)+��fT�?�
G��������?w�����z�X=�%�I����$-IyU�4����w���25��!��{�r���n��9��L����S����
G�i/���(r���(���d�`�����J�*{�\�w�����*9c?�T�g�EuT��B��gvxs�OH�{t���\������*�^R���\U����Z�2$,�����W[�9��mw�Z\����gR�i��	� >9����So"��;��feVT��/�.P ���MN!>�;Y�(��u
?�����W:��C)��?��R�d	��zb���x-�&X [Ru�6�!�rEy
�bp���3?�b��o�������LbF���
��X��9K����&��RO�'uZhmh/�n�y�I�9
�s?Q[H=`�F����%��r-�������&�n��k����7�'�^Y���B�]xkA�rkKK�w	����Ru�ZiTk��jW"������M�(�	b9����*fn=m���s��T���{m��p�������}�X���t5v��V��K�'�NU6�2�?�E<��x{�uxN��0P
�f=,D�kC�8!��0T��Z�&�vT�� <G� G��r�2H�V���@&��WNWV��9MjS�>�>��
��������_|��hnT-;����<'O�2D�P��X�
��G��UZh}�i��a8Yz�\�D����uh�m�E5B�8��������F
�Q-�d-�*��noV6��l9z5��oAU@I���|"��s���(�*Q�����	�))*����������:q�!J��0M$�A)��$�M�#����<��t[�F�q����;"V�]�\�(��w���@i�r������'��8�p_��X��������4;��;��bMz���v�U������� �P}�TB}���Xz�����\�oT7*V��^>�����>��"�Zp���<k�%��f��h���-<�e�ynAlk��Y�TI[lR$�U��]%\�*�L`S������,U=�g7+�%��:|�{y��L���%,}����`�;]E[�Y���|��]�E h�'O�)c��:I��	�#�G�Y�������8���J)�i!z��&=���H_��p����k�9��33����g0��*�s?�zI2\+�
S[����O�n��"��\�����PP�
d?��%��f�IYj�q��_++��1mL!g�����Og��b��K,
1�Y�F���E�����noN��mp�����5���t�s���In��^�0�7�I����(���</�X�v7��e;[M0���e;��A������^2g��f�$R��Z�0�7B��4{������W�g�%/�w��@x#�X���U��E���S�Qjr9���F(&�'��K����$&��.�/���}�(�Y2RI3`���B�=�0Yz�/�D��9����+�FN����o@��}e�-c�_U\��}[�}���5�[�����9�PAJ��G��tr��V���&����r�$��(��#� ��jO��(�w���S� h�y���S{��(RCf����E����0)S�>�����+��*Z��"DU�������fm��C�e��6��f�f�)��^���*����iF�?%�z�(��9���G��YZ:�'S�$N�?�m�z�������f��l�c�H8�����tn0��Q�*,��{�������[�d��<�%����D����J�b�����6��+�`dQWa�r�(�8�0����Kn�����)������N��\�y��'��+`��AQz����\���������WA�Y
��)$xDu�C,4�Z������UK)ZEJ��A��G6�(�%�$�X���;|������3��*Um��J��u���07�����*���~��9�>��sG��4��w�z��$��X�q�1�0�B���2��&��B����p�1��"J>O����"�����4_o��}��d\rg����x���5Co�����@��6�2��k.������B��F<��C�0�C�#8�0c����N��8r�m
��]����p�Y[�B.b�N6��s���� ��^kdB!�~��Ip�]S�Xvy�#�}�bf��\��!a�x����Tg��s!�%�N�"��T�~��~���@w����QTR��c��n�ai ��*����=��HO!%����GxCI�����(M�i��uBi�i��a��&R����Z��1M^2��>�|N����y���F$�'���SIp��[BX�6��������o�� �G�������a����������9��/q�������B�K�6�P�
:����V���@�@�S����+����
0��� !��TPb�I?z�B|M�{x��$�51#}��Z��9,wL����yZ�����c��^�YG���N��#Vbr�gv��^�����#-��
������b[�����kH>�!���o&��Jyj�1Q��*�2����$�0�0T�-D����r���c.P���:��&�u����
^�k�����EN��'a;j�N�g�|�q��pn]���	��
>0w7xV��BAUNev���p]D�a���9*��)F��#�"��c�T����]��g�J�X4A/��?(�E�[�Yd�
!��&lb�G/�X�e����s��
�5;8�%X����0�����G]8�!�&�_��#_�E�O�����B)�-�YT/\tIcW)�(���[��2�5����=�f��+Z�+V;�'jVl2�-;_��Xl�T�B�\_���{�2�Y�����M�<U�n-�z����\W�bR�p-���i��	����0�$w�x��X�vp�5f0����#?�0@vD�������z���}"FD��H���R�Wq��qoBURP;��|#i�����fO"�#�@�<���R)�0�(�TEd�t���'�i��e0v���E�,94��'�K���d�����P
��Hz{�0����/'cQ�2��KM��A1�����:3�����2Fz&��D9!�+6���H�`AR|��4�7n�[k!���;�X}�B\V�����>+�[����sc7��fx����R�T�)7�����
�^��m�Yo�p��~5��{�|�lW+�=?O���������gJ�<��>VE�NE��9O�_8�U�	��4X���T���Q���l�5�D*��'a�>_v��$�G~����������Fe�.������f��l�m�bD�Q�X������+�?=��M�5�R��i2
�m���b��N��t��V�5��]�bE�/��I���h�

������v"��G��N������
���Gl6������p�U��:����:��(����=$�F��%�M7!���^�^��pm	z������.���oR}�A8��+������,Y��8��}/����9�ZE����*��9>�&�H����s�x�J����<���FQ��Y�+��#��$L&u:�����DL^�@R8�zX����[f��;Q�]����� �Y����9T�W[��7*��&���n��jE�so���=����J�U�������)B(���1d��B������Y�����uU�U
�|	���$�/wBd����|��<��=#,�%��M����6��u����mnnV6�5'��!�J�8���xi��Uin7nGsR���*sipFON��|�,t���y�A7�Ry��d����{��>��=�?<(��<k��Q��=q�j
�i"XC��zw��3�OU>�A@�0(�fs�����)���M���L���[���V�z��_g��f����Zy�O����\����3��1�8Om��<�@�=/�#&���z���n���E�6��F�gz�<���X��������f�X�OJ�I��ZR�F���u�����������a)p��o3`����A��m���W�
�������=��:��)8H.�~����F}U�G��<':�����+cg�����`J���:�����H����W��c=}L���-Z�-'A�P��;���SF��T���qX����lmT���l����q�����?��`
h��~b�NP��X{��9�YD��Q��	�G��m���/�UrL��	F��p��$b�O�:��7�a��hQ�*��.W��`�� ������B��7����j����?-��*B�O�sn�������������[��3UP~���L��Q���>EF����C���w��;b���w��z�R%�n7tIf3o���ZShN9�6i!��VGI'"�}�u��#�
�_�-WN@�������{�M)���
#�/��	����f�����1��mg���GP
����GR*��}
\�?eJ8M�@��	��������C�\��r��z`�>L����T��|�]����DZ������G�������������D�=M-�����9MK:�mI@azv6Arw�[���W����Y �k��_�5�~W^rL�.=��J��X��~]7����������i5��7{�5�z��,�YU�U��/�Hl�L�"`���"���?�T��:��7O^��T*����
�5VS�/�O�&~XV�Z�0��3�����L���|
��-��V�Yi5�H��C��������8�a����!�3�InT�����D]1z.I��j��o�3"�Cx,F�&��i�*�2/�	W�4�fR�~a����E2��\+���$�O"UL��
���:^�l�7�n�&������k��I8N�h@������*�t�$}��X�F{��j[����u���8�c~I:��&�$!��>������|l������/3�hUZ���zSv��!��/4�
iI��a�=��`c�G�jQJ
J���������� ��]�d��L;��2���=+g#XEZR�����e���f����fek�dR/�6B�_jy���;]!/
nU+-�Qrc�@uxw�.!��A�x��?'D8	�0����:����
���s�19�q��I�#�,��V�muw]���Z����\I��bf"�.}�R�n
?a �pL����^8�]W,2P#�4Z����.#�C���!G���2��vUn����6t��D-��k#�\������g�V�U�j��� $O^�9�D�-eJVO����'QO.���6?��t���F�771��H��&&�:aNz����f��}KQ���F�R��q�J2�a\�5���
�d��jM�8.��1-{D:���|�y�[����s!�4�t�3�0> T��H�?��A���==�'��%������]
uQ��Vk���k�����4
�[,-�%�hD�� ��G�I_�=�}���d ����i�b*|����G>gx�<��8/I�H�wS�C�1�v��e�=�����w���JpI���H���N���S��6z��(�"Fk��:��X+��{�'1�,�.�Ym+f"o���2�r"U�����"�
���;~�7dE���|��>���������g�����LA��j.��
9CF���7�]��#.��L�{S7}�`��I����0x-(n���b�	���Z�������R�����D�"��nB�������Z��^48_X�@�X��L�����f�z����y�>����$��>Y�Q��0��-�:�7$q^��	��H�����>�������tc���w�E6s�g�)~�BC0hT��Ry3��"��E%g��W���w��fN���]T�,G������`�-����%/�"�`H�����q��	WW�M�G���Eo�og�����gN��,4s!6b��O8|��.~Ba�*j�����Z�U%���m�������\t�Q����l�d�	�>B
8�y}g�2�����)����?*����)%=B�p���#��Fc0���@��-L7�PJewm	6���NcD�Bq�����r����ub�j��)l
�mr~������8).1��-���%��j�{���W�������p�PS���mz7�]�1�����\
�m��+��n�J,go�,�e�d�
j��?���0|��?$��#R���^e������|y��C��	���Z����(<u�k��(]��F��0��Y.����I�jq�,�9<�T�����s��HK���6��TsZhA� ���T�������78�-{��e�����ta�1�Yl
#�rx����I�vg�r(.�!��z���~/�CN�������/�B8y(�Z�g���S-tf-=����|?U5���n,I��u���F�����5���d��x�o?�SIw:o�UJf�e�1L��2�&_�7��m����X�I�(���B�����S��G�6��.9���qJ/��(Q���O�[#0�	9�^�����Tx�?��)7�-��F�U�b���g��SR�����|��jjO#���?��#g�}O���4-q��l��S���vA7	��8Oi����H��.��v��~�{�j��p�wt���M����X?=/��X�SS,E���0�;����P�*��$����^\��Ney��,O�
W�K����^GS�������(>�H�ZoVj���E������� T����!~������P��Q�Ia���nQ��	�4� R��76*���ri�y��
r3��8�	���g���5���5�zfk�n5��4�]nHQQ�
R�X�V���%J�B�0�CQ��'LC��K%���7�7�[�U�lom���7�N�-\�LK�&���.��>�xt�rG7�,
����H�){���lo�������H���|��S}�Q���\B�ji�������q��5��&��a3Q�R�!�������)�9��G/8`��pT^��K�L�I��?���[�T�@C�qa���8���]4��
�/���q�^�+�
=�������J&N���Tu��
���_����9����w�����P�*����
�c}�q�Up!���^��j���ZZ�7:�6N����6r�� ����#���AK��{JF�b�!*%5TB��kg���C,b	�g��f_& C���q&#�`P�O7�]M�%���%%S�V�4��*A��������jH8��G��>�?]��t����W�~���R)�%5�0��o��W���w��S_��
�=%/����;�<��#.�m�&l����-8�r �	�����/����c�)yb�@�/8u5<u�V�[Ou��~��d�&��Bd�}L�>��~�$���(��4��K}�kb����,*M'Q���� ��c��J�$�v�S�S���-�t�9����9���Z�����C�*���^�����6��p�"_���S��\�F���!PM�_��d�0�o��Z�X�r^�'��w�%�:

�,����m8���P&+��f^h�����������/�5�>�r��RjH��J��� �0��F����5���9d�p�?j�7�o��]��{<zj��M����u���t�(��4ft��,=����=�z�������g9�~���	�sS������Hc��J�����cI�D���*BF� �GQ��9���/",�����X(�l����X�(��pa�����J{��E�xx-��lr����
��	��NG��j
��������.�[�w�io7��r7��,�{x������!����?��:�����d.F-�T,����/^X�U}g�"|�`8KK���/�������m�I�B�pqcO�v����C����|�Q��Is2������	�Sp�XV�t�����Z��C�����oW��@������v��Z��w�'�e/�!G���M*��E[ee����?�-d��[wp	�������J�a��5H�c��T{c�*���^
Gx�3]��X�U�>;�n�����0+����$5�`/6Z���z����[l�X^/���@��z5K�7�0|�R�K�a����}�w����6�6�}����������t���=4��UJ���">	_�s��<&�e��Uo�HX�[0������c����O,��5��K�����MP��c�U������gvz��5�v9��Kw��Gn.��~���l�����G����9[�6|����ntB����3�"�q�S�P2�c�t���$�Qm��!���Z	6���"|<a��R����p�o&�7��]G2�������������;�[�Q/���5|S�]�"����p�#z��c��d])e������L��V�e�9�l;T|��f���X�5O/�q�"(��E/����������5E?>s���������iO�����e����S�>5������d(������@]B�������tMz�BZ���!������������SK"��/���N8������]:�����:���Rq�6�����P���o:,=���wX�p��|k���MZ��mV
 �+��d>�����x%��/!u=:U���SF�G�}�.�����_�e���r�:<��K'�������*vY���>������i��"�tg�w����4'�<E��B��N��[Y�	o��1W�#9��8�S���~���_������xk��ta��1M��0�����x��L�s���l`�U`^�JI�s�N��E<7ON����3q��8����e��)h�������j�*��H�#�2�>K�k���8�n���T'0����w��I��1���p��$����-	�B{&������1|�i�����2�����v�v���A~�*S�?�l�����.$��Et�K�R��e�JOs��n�Ms�
z����uD��Sv�_���t�/[��O��E�q:�F'��;�#�N~>|����^;�g]5u�P�<q{���l�����K�����E��z�x��wG{�v�����������.�����j��Ns������
q�`��G�$�LjCO'�����h��w.e��_�N�}��P�XY������)��G5�/(�E1a/���������S��8}��,	U|��[��`I)���#�'D~�s�,�Z�>e��)�������\]�Q���$@���������&c����jN�6��y��?9�����.4�n�X��R�cu�e�
�����v�����:�Q�X�tL������C�P��[H��y�z��?��!��pN5�m)��j���>�pe��m�i$��h}�F����/>r$������Z�p4��<��5yF]	0����zF�����Gx�K�.$�Ly��o��|��~����JH�r6
0d����@*z0�y�^���,>Y�*�1"��T#��$�R����%�2}Zd�5���ye�l6�
Av#���/�pT}�%xb����o���(����f�27�-�;���]�o�����
�i2R�p"���B��"��
��l������6a�L�n��:
t'���,u�%a�~�5y$��O��U�e~H�]&&w=�o
��&�A�����D���R���&��%�Z9��x��E�UU�����p�4(�pH)JR.�R^
���)6Z�;�&����w�� w��_T�1
8��2"�O�������G��n�����O�F�U��-�3��s E��
��t���X���E����#E�9C� ���M����]�=pA�����_��y$����p�3=�~m����E�_��x��v�^��	nlT}$�T��\�C����#��rr8u�]$��15+=>����ZZH�S��Kb��O(D�x,����h��j?D���?1�l�y$1�o5��%GX�<j��|%�d�'�F+~���:��<����2*����Na�bVQ�F�O#����(T.q�]����[��j�u�Amm�9��9E!!C��g��<@�A:��6x��������N����Z�m� �)���Sj��� T{C�Lf�
LfS*{�L\�p��EY��<m�?�:�#�M�bQ	jY||�O��dq876�����)V�,��_f����::n����?�W�����W	��*���y�
+o�+X��R�U��}.����6����jQ�p���2���d��E��k�T1N&�z��qO�����	
h�V���J��4$�QH��|BY%����h�@��K��a����6��N��5���l��[���bb�����z]t5PD��4*u��U<.Y.����4t���w�� ��n+2��i_PH*��n�Y�A�H��Vu��E��Y������AU#``����5����_��A_�4Y�����5@���8G�Z�f������F����uT�F�.oB,*IcS��g��u`hC�aB��EL�,j��:lfb�#g�>?\+q�	�Ww1�6<d���_��{���9W-���1������R��gjX��~cv�zL3_�/�f�V���|������P��n�2������K��%^Z�'�
�MD4���P�I�s��[!q?������Y9������YAw
����:z�!��c��������������:]�(kP��uE��:f%�a��X��+�W[K�Q��+�����nN]��_��E�Ms-����
�%a����X��D���Q��
,��|��q�����PX<�p\�~��E���<�R��!���|���L� R�M�{���=��>��y�����
�h~�f����
V��E��}�����\��� Y�`/L�'���Co�|�@�hf��J�!R��#��%�5�����O�S�=�����Rr���h�M��g=0���������Q�����>��{t�Y�3���Z�1�?��`���lL��%������rBzQ�!��Q�����kJ�{�x#�13���z���k����3�tM��j�%+��b2�&��i���u7O9��iU�d���hj�6��(��VL�x�3�g�t�8m�26�Xu^���:)�D��z���-k�8^��j&(o��w��l'�m��=�����h)�13q��ab�,������qg��oV�yt����x_��5DfE�p�z����1'>�\]�����{���>z�D�)?#�3�I�}��a��b1�����������R�K��Fg��!t�b��#�-~�������ce
��KK��jhnJT9�.R�<��V1^�&���_���5����2�g�euI��{Q
�!���u���D=��>�����4���&��%���jr��]F |)�Lel�E��b������H	�.k�W�5���k�����a�B����f����1���QR�F�rD�plf�m��,Q���X-�^�KZD �$�3�%�Wp��G��	a�n@CJ>)l~*�$�SS�j����"r��KO	s���%���R�xy5FCS�������^������G{�����������������������&{�Y��t�5�D�4��@�[�l8��D�Y���������du���F�!���@"
���x���<Tc�P�h�������}��1e� u�$'u�����*9���?�S�	U,�*���*���KR;+~
V{��ur���w9�=�H�d������>(�/�dkw��4�pJ[�Z�Fw���A'���Y%��M�F5u�!�o����8��`�]�� .z������E���I��f���U�^E+�N�U���B���F�p�W��'[�:r����"�������p 
��
�8��2P(1G�#�������T'J�|y��XLBt�M�pj�I�h2�
�y�]]Pbc0�k�2�vD����+�o�~���!�
l���J���Vu�&�Y\��b��������CU�
�zbW��&���@�/�u�L5��/>�	�K�Z{$�u�9yX��4���i�����%��Y�K�3���v�������UJ��M�2���W�np�p`uTV��2�_��cqX�&����U�_&!c�m�6@t6�v��+���P����S}X�d���+Zat����t���?�E��V�Q�(�o2��;r�G�J����t x��T��LW�C��)�Jw�=k%�jg�g��D^_	�xoNm'�4%.C���>�;\�;�`�`�k"�L�*18,}���Ee��F��~!����P%y���Vn1d���7{�U����VW5&G���q����[nepV�-�-O�
���qT��[��\-S6aC9��J����1_���|��>�~}1>��N�u<@
�(HP��X��L�U��l,��l�zq�L�+LqpA*n�]I����U��+���$~�mRom�AmnxE�����D,~���n)�VgJ��;}���o��5X�Rz2�����.�#j�=�En����l�F��K!���u�3�T��o���xl���J"-��Ve�bjL���p����}`:���~zD��V�n��T\�E,TR���(.�H����S���x�����Q��+��M~Nt`��R:XU���>���������#�| '�����N������=�4���p���`#�P(?|�3D�>�0}q���J..�>���M�fRq�TK��!t���b�������:��y��	�*I"��Wd����cO,y'��eZ.,N���)�A��x�y�����t&c '�9����A��\��'�0#�$���aC�s+����}2���(�S��:w�zl��}�T�T�
|)�� }I'Sj��c���x�Hx�����^��@/��B"d����2��+�O%����c�>D\t}.�����J���N'�FQ�����.UO*��}	e����\��Qg��]��YMS��������r��������L��-���}�����}`>*���1r��KGR��� XX����j��]��'=����w�
���l���Pp�p�������=w������VPY�������Q����W�����&����R��z��t�v�Qr����)Iu�Q/1�G� �����N�O�"xT�Y�$��F��4�����������i;9`���T,�q�2g'������%^#��/f2B��e6�$�s���SO�n0��\�)	�IY���f\��p�)���!W����m�'<���*��V��Vh>Q�I�aj<��������E�s�Gm��tk��gg��\����g�q�WGD��M	u���	$���D�&g7`~��,�4yB,5�@_g��h�q����c��Y��&�a�z�)��i�xsW�6��mn�������T�}^�A4�B�+kk��$Ee]�e�������6w�YX��fq,8�G����r��"F�2��� Q�'� &��L���,��P�8_@������M�����<�=ogk/n�4�Q��o���'X�W���5Q3�Y(�*�
�	0�������l�����q�W?���S�d���C|�>�[qg`�Z/����qo4::Hk�(�
7�X�99�F19�77����o��M�������(L/h5��Q��<��H)9�l�)���-3��������z�A&:}I�_�z+��\R)G����r�CDy���UW;����E�C*
��Y�\Z�&@���vQ6n�-����<����f)4���|U���O�>uJO�e&7|����iz�MO��7KWq������k;yN��7����L9�f����V4��a$[aG���|na���2��N8x'�3Fd t�����=I}(�l����%M�,�U������*��&��T���S���)�L~�����
|+���Kr�CQ-s�����.=F��.e\k�2!��q��9]�����dO�a���y�=E�<��S����_�)����
i/!0�Y���Q4D�
��b��LS`K��$�1��;;vO�r����a��ez&9^t3�.����|�;��zYV�����
�{��i��8R�yq
"Rw��&��K���f�4�0��X.k}�l��hr2"����
4�����EP�:3D�+���2�5�#�x��p�\�����(��Z�s��.��c���]�+*�:	�I#����5�Y@�L������R�� ���Q������8�K��f�4o�5���%^�z����}
-����mIfhEfm�����|Gd���n���vERx7�FJ�����W��&+����c�a�zs���^D��s��;'�ts��d���`�OlMD%���gC�XP��$�|$����jl���I_<
��e���+��G�����=�YP$�I(!�%	r���$��U�i�W��7Q�H�f�b��n������j��%�P�����(\������E(����R�[��uh��DA��������GE9pMU�w�Bn��F}o�u�JF���#o�1�Q�+�nd����IF�x��:e��H��L/��K�?E�p�Y���ud��`�U������a�����9�N�%&�p�����!�aNf�|�[�X���f�Nj��m1
\K�����������������5����L`�,<��eY�?����Z��M/��Hz�#����-r���[�Y���2��}���K%�,��t������:�`��y.�����#��dH/>�z�q_z���Xi�9-����k������[8�t�V��C�1�T�����,�I��8v_��Q[�r�b����&���+�\��&����*��G�g��d����5y�6){mL'������q���y��*S���-eF-�2���~-"���x�����h��lj8�KK�:Q����^REdYt_��������DsH_z@~�1�%t�a�
�
�.�dI;�]�/�]u8��9�P;<+XxS9��g��z�u;��y����m�fYe��v��$�o:r#���wx��S'T�,�5
��3uXtOR�,`�TP,V��y?YX
^��q��3���-�d����
#��3��iF�o�TT~���R�z���+r�i�h+��HM�����O����ZV,���m�6��h�G�5���C���D
��:b��\����t
��������=tS�H+F������z��3Pu��i����-d��zV�Kg�B���G�����UW��>��@�
�Q�H�)�7��Y���+�s��:����8�V���N8��TI
�B%cv�r�fkH��wzTx�������y�[
��"�n��O�d:Z>�+e�<�>�Z�uWO6�GL����H�+I�Q8��b�hm��N(�m2&)+�L���������;��7������P~g��oW[x/��I�4:"L�����������U����P���$',�%��0�G=�Y��������`8��q~�d��y��^d-���N���/��N��~p�������0��o76a�6j������u�~�\���m?	��A��JN��/�����w� ��6E?2H)���|M#����Y��Y1����ep�A$#���
��@�*��^�����2���kh�+���E�]�������.�F�dT��[�z���LY�g(+�4�P{@SPW�S���PR�5�%�|�MN��sZuJ%�[�sM��Y������E�V�R��*��u��w����D���,iD�n4��3�u�%�m5����S������ere�A'��>�}%�t[�WY8�);�yi�iM�yF����I��	��4���S�(�n��\[�y�z+�1Vpf������~�aNR-�����o�j@��x"��X&%��w�Q�K�@�F�{�D����� V
��T|�� [�5��3�.=1�4�i���2����Z���������L������<3�A��Nt�,uS�"��o����O@�
'`c��j���	��� -g�B��F�p%�*�\�7��������������x,n��<+������aN��n~j|��HNQ�~�;s0�G{X\i���z�k���#xz��^Qq�~[.8i���c���������r��W���k�v�nH)Ec0B*�)��i���ym�K? ���������1��U0�.��R-��@�n%���]'X��B���B�X@��P!����������F�$������x0���KV��%$���FN~a��H|[�Z�y������@pMN���(]E���`xoG^
C8$�B%���wP��@"�%KgbwM�~6YGu���
���E��.�/����9q�a�^�gT�+4E�{����d<���5���y�+���&��Ets(z�y�|��m�t�i�^m5M�����:�7��<�~�+c�m�_?�ln��4sV
��S:cT�x�e��N�(�~�;����m��d�Y����k��D�|�@�rk�]<��>v�Z�R���FR�W�M��W����/�B�+��u7�R�eC�_d5��M�EK��������o(��[�D-�yRw��S�&�[����<7o��(�x[:(�D�����?�������R}��F�0��[�VO(�6u�r��	F'W�������^�W���#������.A��a���:�����Z����h�C[u��^��t������A�7�F�c,����k���o}%���Mr��C�����g?�(T`P����1�|��]��[oVbXr��������8s������|������6�w>����/��7tQV���h�^�5�(�����B����Vn<c�6G&�&�i�Uz�
#�����������`���F�x
����|khj���2��+�������XY��%������c�QQ����������&��YF�y�U4
�YDi}�5��U6�`po6*����4��^�}�Z�L��n���-�ve���i�t &<�P�v^6Z��P#��w�������1��;c����NK,�����<_����A�����!��Q��Mo�a�ju��=�����4iUg���*���)���F����%qWc��-x�Z!�����j#�A%���5������v����g���W��:.mJ;��%�
�%�<�n��|a���IG���)�R���,,�B�?@�K�G�0��($�8}/�r�������yo�1/�onE�8D�8:?����l���|��e���>Jk�m���Mi�A����~Z�v�Jh���'G;�%�D�C������e��x���[F��	L�W��� �	����S.����A�?"he��3���������)�J�~��A7��L�to�rd�!���LHR���+��*G���-���k�������2������J#������/2��V"'o���|W�7���et�S�hC�c�^yyd��_z��!��;�c������!
���RR�)��O��M`p+��Sn!�� ��"PMk]I��1 ��yv]��.3K�������}mN|����{�������t�cv�(O�wu��������|��W���&u�O���h��%z���%
=�z��[4����,RO-�v:|=Kw^~����l�ZEs�&�|�E�I�tc<%��d��.�2����p<����@%�R(1zR�Gc:o�tl�b3��N/LS��H��z�{yl^�2�A���i�������?D%d����%�������=L
�vF�������O����V��V��T/�6�@�m�V��U��i�������)D�9�e��f��*qmMB��S
���,V��	((/����4A�{
��+D�F+Y'e�/��3��)`k8�1~�&����.���"6WM�W����z1HD��\R��[Y~�%WI���|as7_j��v����No����4�������vsSy��md�4l������K�k%��Y��Pg�������u����\"��P8������~�9:�?�}��@p�������W�T(�����c�
o�J��A��J��f��	!�F*1!(�]r��������W���H|5k=�����'x�>��Y��"����Y�P�I��M*G�>��E�~EO& �!��S0-���m�DN�XQE�N|j�?��Y�-D1#U
r��t�M�/�|9�L&��s�[�%�����2�H)��������	�^d�9�tS-������3��{�������+�]��v	�/����]Pb�7�9vz?��1�����i�5]7�7*�fU���+=��2�:�;v|��K�H@T��e����
�'��mXD+���`��vQ���Q�h8�;�;+ �:���
��S�F����:A	��3!��g�Q��=
��L������xxs~/g���BH�Z�JG������F���d8h����������r��a�q��W��=����vN�u|sVBo(�X%x���U��Y��>�:��{zd�
���u4V��/�����s�b�F�1|K�6�N!���.�����{�c�N:�x9B�5��k�.�����E���:}�E�sR�M�c�5����q���8z���$F��[e���k��z�6*5�D���������c[��I4�~�������l�G�A#,`����������g�f�A����U�prXQ�><
��{�s��N�J\[.��g����'{��n��Py:b�b�j�����w�lV�M����wf��#W�2gy��&�������h$���i*�l��f���q���Z������k�#�V�Vi���3:��N�#������\������J$�p^1�V�"!���Pi����D��aK8C�6��Q����PMq����|��:�����-u�	�/
�_D��9��
z��K�������;�r�n���$R�����I���m��U���0�
Fpr=��^��������ow�~l�z�j=������~K����W��������MA���@TP��#�%����V�~�0���z��`&�iI��xyyc�Z�MU/��
�����2���H�V�=���B�I���3�<x1��H�Tj�?$��sQ��RG���a&u�N����\���������5��D���-���?��Wr����O�����7���/�Gc�ve"%����>GK�,�{�N�����$�����/�Q�����������z�&��Q�����	i^���XZ6�piz'�L����4}Q�V.���Q�,l��� �
(2���j����$(�Z;t��2��R�"���`|���g:CW����#�w�hZ*��^���[�����Ov����-������2����=~��~WX�fb�[�����������bI��(�6�L�[��^>�N��;8�g�E~Z*���>i�|�W�x����J]��J��&�k����bD�v|Eh*tG������%�������3=)�%e���I<w��84�;�����n��m�,���,giL�BE�}K���1�f�W7|�\�jV������V����~��7u���u��p�����e�����<��l$��m=X���:t�
�
��#���'��\�@�}��sd%�:�������;�6~9�@$eW����$l�Jy���)���>��|]xa����ZrZ���{������7;G���{o^�@��
�z%o��|�A�3�|;� ~���r`��y+n�Yf��Y38_��D��hmW���MkC��A�:�|fmh�s�28-�e88|�'c�suA��w���@W������m�|�_`/}�/:���A��u��F^2�#�M����IO�M��A�W��S
��{�����+�M�o�����+���(����%����J���G�x+���z����S.�e��E/{�sv��K�s�]�o�E�?��g�~��_�7	E�1���=V�5���,��H"�|�"����J������������{�5![����<<|�'������n�����w<����o3���^�c�f��7�@��u�?.a��'���h��d���~��$K������B?��s���p�w�_s����b���Ck��QE�Z�4�r���.Q������Wm���7�*)���}Q�������~���j
Z���H���>���p��_��:����.(���<��o�7��M��Q-E��� |�YB�h��FI2&SQ"��Z�x�����������N�b���Q�r ['���9���� ���*�}�"�B�LJ�������1����$�ZP�����'( �t �	*�`�x�yU��g��x/#�����K���O��X
�<��"�"�E
� �E�`��=��*K��� 8Fl����l�fxb7����
��\���d"����f��h�./���I���r��!��Y8
&C$��/�F��0���-�3�g}��#}�2��a��|�� 1�x�me*�Rg����s���x�'�N�������ln��WKK��^�����'O��~���6C��;�^t��h���`�h!��oOy�J�h�AS>�%}no��=Yk'��������*�����������S���G��tR}\.\�7�.��w���������f^l���4Q�_�J�1�����|35���k�z��6���NN)��-�(���������9�[	(#xJ�M�[�p�1
Q ���;��
a�I�5\�$J*�Q�H���\P ��}Z7
{c��[-�'�d��qE���:��B�t/�}��[
���.��\S�84G-���q�){�����^��[�m��`^��)�$c�$��/@�1��E�v8����0������&)R��k��J�)Z1��#���'�0�|�����Vm�*���:%�)�L��`�t��>q��I����E� �
��n�%�]�!G,��%���y�����*N��k��� S�.
'^&S�&���tUC��e\���������;:����>���;QbH-Y�����Q}_|����.�ud�Q&#Q�Fe�R.����M��I�)u����X|�M����m�_�����E�i�Oe�a���mk*?�Rq%�[ �b*hL�Df�i}�����kz$���1QYl�i��z=�bbB������D��H��� ���H7���1�������hk�%��:�jH���:A���zm��Qt���aG?G�_A�q<b*�'�id��8XT�����c����c8ch�R�����q����;��#hvO�_���#���# ���<�P����
��o�����Y���F�X�����}29M��x����C0^&C������R�m�j}������T� !+�����Q�L�-�jW�W ��_������FI�;O&��s�UN���!���0������t_��o��ns�����?�J�;q��:�p2�]u��%����h�]H_������\���"� j*�r?��>�x���&�L���1%#��wN#�3�����>el��Di���(��,�L^�Q$�16s!%E�8+R���ZV����y��O��:�{k-J�K��J����Q���3�)�T�9��Q���������34�uz�aQ�.��y�+kK��#�>��,:�G�}��P��($�Q2�(@U��{f:II��f���s�K`�C�.���Z�@���%Z:;p�z���Sr	sEb�c���n;~#��]Y)�J������j����j������-1[s��	�g#��<C�'_��$���8}��������X��P�_E;�N��������������k-=:a��wazg���)���st9��u��
	��#����������M�k�"����8J��"�cI�"$Q8���+��<���w��JZT�s���)f'8�#S��08IK8���D&�L�J8��8��(2�����������00�G�3�.N(�b�#X�s�=V�#�d���j�&��������e&���n`@��L�)%Sb�Jr��������w �h|8��l��X|x�v�����j���	���J������j��d�Z�)?h�����?:|�.x���_�3&N*����M����O��Ol�s~��)8<?E��<1`���w�?��\����/<�KI�p�o!�|�����
��XN4�r������c[�w�fJ�����y�Yf*��(�X�A��:y���T���S��!���]�`*��]����{q7��"I1n�3�%93a
Xw*����m/>������e�����I��g��E����J����s!Sxy���k3�7P�ld�A7��g[�����uSE�y*
t�HpX5�n��p��+�!&�-;p�O��N��T�V�3�9=�#�-�T�:�/[�i?��~+�wm��s^Q��r��"�l��_��(�L\�y����d�����@��9&����A�.U���q�������?h���|�g���S$��8�M����PZ�CFkU*A&����AQI�]<+��E���F��ptm���g��1J�������~����q��k�I��������W��v��c���%g���D�~�V���a�z�Q��3
Jx��
���T�D�S�V�L��|g�I��^B���}�*h@�e/���6�S�C�}�zAx�����)�\���y��d:}-�6����#8�,$�����J�Q���DR����Z�N�Q�~�-�����������1�y-��';���lU����O�q��N|vL��������#��q�B�g_y�&iLp�J�����m���%�U��,�9�s����_b��D���r�����tS\�'�
���2��?��*�qTy2����
�3M
�{��la�e�#������q��Cf��W$�L(e����y'��U`
y���*A��T@�j_h1�(�b�:*nL�WA�����f��j1_)��f.wL�~q�5��y_��YJ�����������(�4��G���D^DC�����>��+W�}/�O;o��?�����{������;~�?����{���a!��#d[X ��O�+Z�?	Y`M��\e!�|����47��lU��3���|�^���?�{%��������� ��������J��>:|��qmr�+?2���6D���*���/�6��e�O�[��j������+�<����X
���Z�����-�t���g�����G�����&5[!��c�,0v�-���Vqn%2t���]���1���H��!)��e'f$���/�qt���
�>"���V��Y�M�:��(�:��1�V>�U�T1��d�L�[,]�1��AJ���'|K�p��]��(1��9�����N� /����1!���`t����F
B�����)�rR!��J@g�]���(iKr���P7:
�;���0NqX��M2
�IDhM`�����7<�������lv����4nD��:��Jp�D���:�8��;c��(/���/�������<�U���lT����!�&�+����������8�g��f"�bL-�[	O��N������%+����'(�����k,��"t��F/���9%+\�mx�d�T�N�X���0���
�|�)��"T�TcF�pf�����\2�Q�1���{��r'g�Z��b�����4��������n���u��"dy������EN�������l��Q�_�.N%��}��]m4*�C.�`����������(�3�=#^	�d�����2��+��N��������
���7���
�s��l,�����"'u��s���F
��@�����ys|����r���#@�6X>����6�_j��4���j�������p�$��-?�J-.6���	n�_�'h22W<s����������n�� �p�S)5��9�/R��@�>~��������Yi���h�Jfa�S��D)(��#��y��`��#��1�u
"�������U���j��7�>��E���h�-z��d�Oi����b����|�qB�S*+��JK�}F0�>Ex�h��zl�h��u*�r@�?��A��zj`	y�|w����#�&�, �
>a��x���}}�rs����E�3�lUl�-{G��/E)��)SF��c��Jw�=�����T�:!�~0A��`���.�j�:��g�W���;�%�����zg�UQ��������C���D��v�C�RV\�������i`�^:b���Ar���r�=���!���������U�=�<����W������w�j
,�,.����1�c~�?K�4x�8U�/�O�vh�m�S�-����B�10 w@�E���%�G"l�e�(���O��_@(�e�������/n�g	w����,�#��_�	6�)�>�7��w�X���o��M	A������>"��]�R���g��%�����4��c�}1�_����h����S�G2�e��`�>(M_���8�����3#�$*O�eM������.<`�����!L/�w�:��1cz�s�Lshq����w�6��Jm�V]h�(�����q��M8���
�>���
�}
�`����AM�z��F�t���b\����K�A���>���oU�
�Rq"�)�}`���JhOl��-���Z��Uz��D(���������T?�����k��t��de��}�����Z������)c�17�g����f<�����O
 �p2r��GV��a��f�#�����)�]0l��O�A��r�<�C�b��k]�/��5�-:����8�������a�^tJ:7�5��Qi�
���FcLGe�z>#j���l�a�����Q������aZm��`�>ZV?�u���G�������9������qC�}������ic�����&-2rR1��|���N6E���s9#��sFp�0��JY{;%�t������H.ce��Hg�X�<h����,��Xns&�T����9I��?���%��5�S�cO��2�0%'�d���`�f���P�s����m_�����Zk��m�Z4��3QW~wF\~��D%[!?,���U��������H�a�������(�S����%�{�,��R�<S��������������pT����g8��M������/(J4�!b��|>6���\������`���Nr>��'Q���� R��������vk6����5S��WLq��z���L��(<��{�����*y��W��M}���jmW-U���Ja���(��W�(�������d\"��S,@��v����t��v����f�}���wp���W{;����,�(��R����b�f�!
�'X�~���h��^����w�6�PI+�?3.s��?h�;:��h������d��x���}I0�%� �����D���\Qf7���/���t7k�?T�N2\���^<���L����?v���~z#9��E��[M��-�o^��}b�ZY�Vf<j���Z�prh�y5�u4��A\��[N�.��"L���Y'{S��+��Wm���L�E7mY<
�gX�����J����w1<;r;	���l�2�v:L���i.�j��x����&����8�%�o��S4���G�ys�l�������;����s�?7�L���Gh�������1w�|,��������Y�U�(�2a�S�-P �0�S\�`�wA�s�:�fl��}f�t����z�u0�r3:�T1���������S��]�j@���Cf��t�|I�}n�P�o�*o���
����2:/j��z�����x��G���������V�Qns��~�ct��8����58A{����2_I����&�(���!X���?z�#�^��Q|�����#�C�[N��������q��>.1+��88q��*7K}|+���2�����/\������Lb$C��,�6�4���Y������3Pe��"�{��H&>�s/����p
=y6�i�����?��B�uB�j�����y�|��K�5N�><x���o������nVs=����I2���� �G��GI��V)s8a��������F�������3�o����������R�{-t@X�w�h��c�(���o6U����n���������.�;�[.����k��\���s�4sOfm��?�����������;�����_�����\q�[�Z���o���S�[
�E�Y�z���FQ:L`��m��H�[��,i���}����5P=�_�~���Sk�����[V����
Q���*m�>N�n���L ���I�k)\��<������'�����<��&����#�m�M�@7A�9�K��57�@d�k�K/._�l�Z�V����f�C|���(�Y��4U�q��zl���~�Y%��g~i��Y�W�,��}r��~a�"���7a�x����xIk�Uinn����>���pV��s��f���0���<?����G[�ze��r�� ���v7������YI��{ �(:���c��9+�vl���B���mT����Z�_f��%�t�����V�Z�	U4��D��*�
g�P��i$�Lp^r��Q+���J�L�j��neL��Z�������.��B�h�U���j�E��R���.3����|�Oj��R���`e6��O�PT~�i{	d�Zim1���[(-�$��������Xfm�jloo/J.~������������65L��riDL�3�K�%?������T�;`g���6,�������T�a�0�o�/5|a5��	fsm�^]�)�	'�M��O����U �f��H�R�q�Z�D��B2[���[�I��)��[��7[���������U�mU���?�����qmZ2�U�7�E���X?������f�?���(:\�M��L��r�k7b�DT����S�E����%����$i����w�	���Ca�pS`Qw�8.F'��!�DO3 ���0}m���hhe>JC@�A�ze��<�&�4��nm��������Glm�~�]<���M�6���n�;�v���%����0d�����������;8i����O�*�
���H|������`��v9��b��q�a�F%o�M��ZH�IU4�;����<���`s�E�G��O
��gEMx�[s�}�DM���p��p<������(�>�������������t%��i��C��-���{��rx3I���)b9������Z~�K#�P���TS���7!8K��z�y-��������609�
"0&0�P�EiJM��0e���t&zJ�[�"\	��1t�}��	nr4�*`����H$0;!1�~�j!�'���n��u�����0L��JEO��R�!��$��
�����z"H�wI��������t��a�����Jp��������i<��C���s��)����f6��u���A�������(����5
(��:�*�5���b�����x\���rp��H�0]O�pER:�6��j+�J�o�����Zx::Wc����$�|����!fo�G��'�B�wD,]�1�d��u�zx&���d���u��u�3ek4��td?�����,D0$|~40%�[�
�[_�_&���}RN��&�V�:��mOOK���[��~5�cl��~���y>���_�@��U�4���~�1�3��6��z�v��04'x�� �vmNl�j�m���3�j�N����c����&��9��(n��C�`��6�,�,5Cn�*J;�9�J}E��;��-W�@��?RP}�-���W���MT�d!�������(���P
���%
v���n��o�`x��D:�x	�x��/'q��'S�C�w��;wC= [���
5�����|�&���O��q�~?����<�b��{����]�Zj�%�p���&=4;���I���Uk�~�Vz�>��o�����$�B������4v�@n�?��g���!���#��Q-�UB�C��D�����S2��f��V�PBtimSY�D�L{��~�����n�~����9����&V�M$E�N��Z��8��8/�cd���]�/���NDX�CI	5�|�:&�$)���WRu�/X^1b����p�~�-:~���D�k�d�����-�&�B��H�����p��<�Jc��JL�z����|
���yBId����/��>���Y�~��X#s����w�X��v�3V�����y�]���|�zZTXG��N
�Qx�!SY9�xm�	��Wf���Xe��|}(+����h\Z�~�	�f?L?O�E�����*��P�����7��?���
���,��aq���t�k8�f�l��I9f���GB��=|��p>��B�Q��E��R������m�u��%��q���tU������7��U#������&���1p��,����w�#y��;�Is��*�T@�:m$8��F1��c�5��v�C{����7�x�L�P� Yt�����~
�k �n��+���3m�������������ow�zQ���g���JON�����=<?<MFh���G�@�� -�v��T��^��V��x��k�2K��"2��K��a���c�j�\/��7KW��
�K���8y�>���m�O�%���EW����\'����\�N/�M{�
(����\�w@����0�{9��v���u_�Shd������Wb��R���x!2g"��	��[FFV������ =H,M<����U�ffb�]����K.�
��RE��n���3+=ME�8�;b�w��3��?x7J���K0x�_�H�������&,�*w��4T�G!���Cw��3���3��H�!:����"P��rn�����h�a�<��N��g�H�=jh�zN��\$�	�P��F��=�
���s��
��
�[3����gx�t�V���UL����V=:�*Z�P�7�h��
&/Pq5:��A��*���N��tz�C��*(_*���O��d0�	O���s�.��6��Yi��e��j$�SY<�3�}�Z-Z����/���`�o�n4��2�?��|5F�������*�{b��������������*��z�e��Z���=��S�1#g��R����L����[�����2�����
��b����m��!���5��O#�a�>��_��g3����
��jE����S�e:)��=��`�o�l�����d��}/���.��W+����]��U�l�����}9o�&�#�����-j6���F���RU��Z������V����{���/#L��=F3^��u!5���z6���m�\>��v{��y�
��EcM
���C��Ksi��RL������@�����bXe�ce{a�E���XZ*�B����l_',. >�����[����
���_��x4���\�&�>/#x�9��Q��T���#�={P~�)������p���
���f�����e���!Y>|�����)�� �bX��H��x������y�r��q�M9rs�\}�3��9���0�%�8Io.��W��;8��"���q�����m>s�[/�C��>�
!��,����:
���-8	_�wk{��r@���Os���?��V
l����������+�Z����������O����h�����W+-����~�E����V��Q�Y����z~�]��
�G?o��v���Bg�u���%~q�fk���2�����+%�A�
�_J �N��wY��{r��=h�k{�N��R,����5H�6	�"|sT�f�RA�E&~��������]r����hmmUZ�;��OE�Zm`���?��������}N70?o���4�}�j�[V��r�h�����f��]�������H�D���j��_��?q*c0����H2�c�,�y��
�"�F���s,A��@���o��o~�A��L� �a^?����-�������X w%7�c8�
�q����XY�ml��h���l^�����k�!,@T���J�,vb�r�k�q}����^���9���4��������(
�A���aa�xeIt�yV���������r��r�q����� �K���
y��[���~����iR���t��AV>�bN�B��zN;���~��@��f��ma�W>�Y^��y�����(�����X!ly����50\Z�m~|]iy�=u0G�2a����g��t:u��k��g^t�#�K��;�{Yz��mU/�?����_t����Q
*Y�x7�|����7{�����	��~wt���������k�����
.�(E���w_i���+X
=�\��������}�qh�qhYr�K���J_~����	+Y+��5����5sx����
�
������������f�z�_����[��|������2JX�iA
k6J�]�o���;���2�[@[��_���������.o�^,)�����Ko���~��Z}hm�z�v�����i�n��nm���zUoC�7�A�����t>��D�-�=��rD��@�./�]�����MU�ys��1)d�Y�m�j�%}�*5���d������&5�?s��4,$�����ds-3s�xB�[�Nq��7�������7�*�y�����U
~�M\���y��4}�~�������0k�U�,������,�e%�����L�	o���`>��yU��]���0�)fd'�z���/2�,FA!Y�2d^0�W*�����E8L��v	��(���� ��_p�4�	K���,E
�Mz]q�Qd��5i�^��{��A|�F]�\\L9�JP�f\�)�m<�6�/dKp��O��.k�;~�������A��_xw������[��Y��A�����t7���K��{���g�F��U2B@�wH�������h�������z4�{���j��`}x�����z����[2qFK�ahmU��|�a����`��w*A�"O��O���k�c[�����;��*������]�����������(e�,�5�Tq�?�E���sp�\l|�����&{��s�/��u�\�;���(r�!l�X���V��|���|x���
�7�~�Xt#*�^//b��R	k��G�d8.�C��<>�=�?$��]=��=.��~�j�rL�T7(u��\���Jo��_�{�oI�
�^m�I��j�R�;���X:E�G:lT�������o�II��H��1p�L�S	�D��pR�����O��97�<x*����|�ju�G��>�`&g�tl:l��>��m�d�G�����S��Y�������� �Gz��NF���A��Qo����>��Z����z��@z��������Z$��JP�U��T��4���N���?���j�}��M�b�Q��(���e*)����5�����y���J���x�
�6'}	n���k`k�����]#��zm]����a��:��[��DLS�NH�=M6A�����7�0�|H=)������)o8���������}��9���y�gu�U�l����nD]�~
{PnP���[uy���V�i�w��9�-e��C��
�X:����zs���� �fB�k��J@�>�uoQ������u���+3�XHQ7}x�%��>������/�<�����W/�Y/��)�(*�
����yT���B4�j��2����A���@��?<����)�2;\x�08|��x�$�D)��UH�
z�
>x�kw����y��`������Z^?���d�X�9��B�a
�D���Er	&I��y���Q�=�)�8n�/���`"�-�5����HGc���S�K���<�wo���
�t��������wk���C�A�7�F�a��c�L���va�E-��v�������!X^g(�&p�_Nz����x+�*�������'�����of���6����3�?����@�6��.Y���lV�����p%X
?�D}����w8}x_J�G�lSl�^0%��������������s�'������5������n�?����^���U�<�(	�=��c�"������1��I@�W�f
�4���������X
�S������)����
,&[X��D��{�as�R���j�2�&���8���� �������O;G?��:�ai�zU�V��a|
G1�V/���"��Q|:G������h��������hCu</
��+(��9z���%u�R���nO�u���	�>�$6�>��6�U1f!��=���t�{����.u������`�<����$��H�Y�	�}�}C�=��FU���8S9�R1��;!���`/-���8�����)�;>FS����}��D�o�:H��*������R�3��7������?0>SZ�ZU�
��l�a�a��G���bu�;� ��u��a��r�Nj�"�k���E~u�U�pd����]�\���<@��u)s~*xN�T*��,8�e���^���&~�V����u�w������9��J]!�/*s��f����� �I�����O{�����
���
�`����Aw�;�'��;�?y�~�GS$�����WGQ�H�����(������3	�kn����]n�;''�����������+P�N��3s�S[|� �-X^��&�~8<i�bB����H{xA
VU�l�q��6�
�P[�q4�
��th�Ca�Q�����1�*�8�bX�s����q��G��Y/<3��NF ��"���Z���c����k���K���Y@l��Z��	��Es)^��HQ�@���|
�7�u�����N�F��%������P�Q�
o��()���*w!���8���S�����p�P��)3�,YF��Q�h��(�0��k��?��S�s��"���<(yx����Dm>�2R�hQ������2�y�������j^��Y~�J(�G{?�:Y�������
��Rm�Y� |����z�����G�z7��J]��,,�5]v.������k�v�JWq�<�l������������s����V�Ms]����-�N�[�4�N&d9��Kf9�O��$�+Pl2!|���!qPM����~��� �`�Q�&�O|�o;���w�!����#���\<��WG��CX�E����4��#��>p�S�=����sD��Q�BIO������+�a�� �B'�4b����Sx���t=K�d���q�Dx,���7}"x���f=�bH���a$U9������C�����|�v�\�?����q�fu���mp�?�l���f��Y�Z��~�f&��fo���;������V��~�?���y��	m�d>A����_3����W���Vw@����	j�:�t�Y���a����f����pMq�^�h����r�G�N5����������^*�ArV�<X	N{I9X	Rx
�DM���qY+|,������������63Vl@�[��lw���6K�������T77����i{�$k�p�C����x+�o���q����,�j�w�
;h��D����f�
�.�e���R���Y���g*��Zk7pH�mC<X��N$�`���j��.Z��V����9���	���EXC��2��7M0p�����e����6q3uo���m�qU�����f�����C���%{W��(���^k�,�az�yws,��[d�#+�m��F�5hMI]��AG�f=e#��$��K�����~L
��5y9q������?w%��RR��`�T��X�z�x��N�����H;��<��;�O���u���F]Ti���0����+.����:�����g�����k�>m���z��:���(<�i��O��\��j�?R7����t��f4�����X�����h��`���::���f�4�������_	X����)�&��������*���f�uJ��2]	J�����?�t0��F�����F�8�cIB5�~|�_�]*a��2;WV��
�	,?��n�����l2��s�VH�w�f$�z�p4�64������md[%�Z�������'��2�@�4%\C���.��F������6�K�Kh~��U�$����K%;A�T������������j�@�4�2X����� �*�g����)�Z��9N��?������*xw�����O,f�����:����*I��y���h#J�EhJA���9&UZ�y��(�s�uQrO{Q��������Mm#�~�����0�����e��PK�c�����r	{U���d{�����3#���-���NU�������i�iUF������f���O�O�c���?|h�.*�}S�O�O�V���&'I'F����������n����/[ �A�������k"o���T�6���}�=>�`"�
8
������ ��h��C�~#�L��(�6B�������!���}g�jn�5��4�S�����xD�]j����"���8eW�^�"�dDKK���iEb\�V$e#�kh�_�j�^��������q����!)�2j����Q�)�Y������ ����$������x9F{�_B�����d���u�uU��:��,��z�j����;���Z��!^��;>�����;�#�u��Q+��Z�;��E;��)�����@������D��+���C�?j�9?7Z����&;i��z��5Z��Wm/�^tjK��v�^'Z�!5c�V'=F�@���.��G�g1�jf
��EFxV�K���k~���&��?���_rS�E2#����;��������b�Cz�D����~�)���Mw�Cq=P�����P�WOS�!����N���>#l�a�v4��w3����=P���!445S9���
��	�)rpF��];c��x5Q*��������"����|8i�#�m�m����~��BN^NY\R�>zn����4m��0CtH�NR
r�6*�a�&���M7����>I��(e
o</���fh(����D��~���!������2�����9N\u'�D��%M6��g={�)	�at��(��t���&�1<Q� 
�0p��28i���v�fRm��Rf&VZ����Zo�����ge���.�z�0P��h�u��q�l���f��a>��"21OY����7Zb���H����������;������2�������#����^���yTX�CL�������Ga(Ic��!~�&��{����\/q�K���5���������#~2��*a#�PM���%o�4e2W\�}�U���?(�nh���Uwk�m����;��-�{��o]~9;�;���v���-n�4��djQ�iq���e����5'UoBG����vs���G������r/gB:G��N�C�s�0����8WNL��vG�	����Lz��fB���_5�O��\���|�����\�:I}��i;��;�"�,JC������~i�l�$�t]����)�S�����st�W������V�oP��8���[����\�,�����	���N����(5�9j�����`_�p�O����$�����<�n�d1���nD���1J�Suo���M���
�S�~P����{[��}���wj��[�t��4n�SF�}N�w�\��K���n���1����Vw��U��(Z+���\�6Pn��*:�_���D��$�4/@<��*���g����o����������W�8O����y������=J�����OyC]a��;��8.��x;����?��;��.?�B��m����#�m��w�'#�g���{o�uu��k��&]�V��"����Z��'���6)e�@KZ��{x~���,<��l�����2L.Ru�PIxCK�PG�c�bN]�v��<��=��+�4�fD=�-�(�s7E����@������V���aP���v�~��y@�]�~��a0�F�g��+/D�������c�:]Z�#OD���g�cK�t)?�eVWDTE(>��������%�[�.>��J������h�m�)f���~uo�l]4�d�����W�b9#��	��d0�H���7_/�N
j���^�p�+��pQB�50�O����,�,Fj�EDQu���i�Hr�}��@��(���~<��y����j���jm�nf3�����=���K-u�1V�J�7:��sw�c���/��!S/�r�N����	��EK������zL3���}Pp��C�~���+��U�w����:kt*
"�v)�����v�
���)g�qx�><;=P���~��V[�Tn���7Ju9mc����//*�:�Wc=p���������Lu�	���Ue���LFn�{�@�e�eX��
�e2w������Lm��!v�0J�ZH��4���EG�X\��]��M`��������t1f$�IeG�������S�[G�x���U�����#k5o]n�}���;t�3�S�r����{U�B����H�ev@�����h%�P���C����q+E���	9�,��4
��U��CJ�$k����T�8��I_(J
L<
��8������}���#~�t��R���lc�A�� ��I�8xi�v����zX�O\�hq'�����'�������t�9S�-��+[���2�!b$[�TZx��������z�,�I�);�sB���>Q!�Q���k#�L�5Y�c��\�7�"�����Q����Z�{i[k��g��2	��qi�T*��.ZD#O0E���x���:fJ&�8=7�J#�gQ5�H0\Se��==��8�T%ZeFTb=�9��Oo����h	Y�����T�l�e�d�(5-.B��&�U����1��+��K
.*�n�z��BL#�:9�@�Jw�p��=�<����&)�!(	�L��������d�A$\c��IO�c>�~�����n�{����!��Q�M��$��U1������l���+��z����p#�����QHDaS���������Z��E�w��P��E�Aq���_��R����<�,�B���������:��G�%	i��`������y�,�`���o��0�4�&�"����@�;��cc�����P.w�*����#
'�deA��`!w���)�����3C)��j��IL�g�X��8xr�v��J��3�����i��%ER��YZ)�,���G��0�@���A�j���F'p�^~:d7�f��|��L]J
P����\F�`l�+e0���%��@�.����,-����-!�#��KG�v\v�%w2vr����r,wh@Jkc�G�U���1Z��N9��K���by��	N�� �������C����1O�������3�Qz�B����B��>��J��E}&��Cx�{mI�xkM�'���.~�O�����Q�����tJHW�E@DdD�&F!��"8^�e�?/�s_���7����r$4v�W���x	jE��' L�����(\��\o��p�Ki�4U��u��������c�����P@��I�J|�O�����U%�OQ���j~h����j�,��=����Iz�������;^�Q��+�)ew)5��h$��d�~��}��W��ik�lF���K�7U+O?q��x��'�����d�L�9��g�*�h���"Dg�j6Fa:����4�#K~�j_����R��p}	�c3�jmKs�0��&.@-�-.Z�5�3z�?�.	��a�/�3W[B��4�a����lhl�����e���,�.n�/-�|���<��I^�S��a��,�YGVl�)>�77���&&K�[��&��P����Vm���z����������>��,m�3����2��J�"����������UF���_�`F�Y(Cl1�/�FB)r0��7N'_�����
�L����h�.j��e���N�
�d����ltg�Z�1_Wb���w��qm'�h�w��O%��#�����"����������3��	��[Nr}w
c`>@S<�a�4�(��ATz@T�t���I�E�A��Xm�<l�������40(�o��Idc��*4����
�	P�mS�}�}T.�4f*'���z��{U8����?�"B�S�	Ka��w��H������c�|4�}:����'���H{������A��u�D���������Jt�C�j��W�8��y2<Q�RF�~������������2#4���������:�y.�C���I����9C)[{Z�2oz��Q)lD��M�E��T� ��*�=P:���~k@i�������n�����<�^�&@��{�����Q�v��;�j]�6�9Zwp��ch0�H���&�$��D�aw}�D�cA-��}���_�vXP�����{���h|n���^������;u�}�y?���au����������o��,��y�|�u��u��FI����8��G�����K�I���y�D1������'�(N��"�K$���`d�1HS���K�LbFij#��$fvR�<�l�F�fFR�����Y�J#��X����$���B+����el[[[�,J��1'K��gk����k�O'�$U���3T����V�����W��9 ��x�*�����;���V�>Q>������vAPRFq-J����F���7Y�9�8������;�H���AEIC�e�-w�R�F0��S9&������`�%9����d��"��4GD\8�Dd��ys
^��N������ �tJ�X��n���O���d���e��Ej���|��c�n0�m&
���}	M^H+��~����LEu�L1�P^��)���/�������qNsR�__�
���� �>���!<1����R��M�w1�����jM
�b^m�������b��(�Y�����������O���4a�:7$����S�p,:��:�	�,�X���w���tv�$�&����wG���DB"_wI�"a���p���wRE.a�!�)k����N;��0s��7]��Wd�v���6�4�6�\����g����o�5Y���������+���#w�}�|����1nf%���f�60(<z'+ ���+�g�'�7/����k��SvFN�����K���#��{A�P��jM�N�S�����,��v���[���6��XA�V}�Vc����[fP���b��CN
j��:I����t#2�����>n�y�/� ���wD����f0��q�4���:bS.����`���y#���H�+�����HH+��m�1�B���H��H������X�r����j���1��3��1�kTD���L��#z8�r]F
!Z�XD��=��|D7�st!�����r�BtGO�����d[���ex�Mr[�`�j[�m&}!��&�����K�B�����������l�����=�/WV���_N�c�:��6��q���������o���]ko������_�]�G<��zNWc_>6[���H�����\������S�&�Z2�LN�P����4yM'}�$����������4l��m�-����6�S������jL��t(�C(@5�L��UY<������^��;�������]a:Hy�,M����aT:��k����"t0��r� U� �0��/+AoLtH��S���>d��k�I�q��\����C�><:����HDU#AMm�c�a-Q�r6��1����
J:������8�:�m���7������}�'���ucO���I����kHDj�0����'�%^�(�0:�`��Y�c��;O�1�����<b���^�;jd��7��(EO`���Ui4M�p�G�d���P�6�)��d<�����8*���S�)}�
���J��J�(Pw��
�}�z�Y����n��5���B���Hc	�N-���py��<Dz�C�)�ay������i>���"M��by�4tX"�s�'u���������m9���Y���sz�������l��`���~w���bm���M��t':���m��k�I��,�p��x��t�>��<�x\sc.	�(Gn�f�M��_�����gz�8O�ly�97b�b�[2�3�D
, ���11X/�0E6Jc?��S����S����6��Vd��$���������1�<
�
��LcS,'��%{���G!���4#��9N3:���Q���U�������ifI�������P��6���EtY�6$�����f�Vm
���PK�U�P&�5��zvj�u��I���{��`���7���4,�+e2�GIl��^���-��(�-'�-Tjk�Y�<�u_VU�9�������i}����s\���q�����N���SG���lrO���:�]���SN�xn������M�
~����J�Ef��2����I�;W�(s�~
I�*��L;k�����E��7��V4�i�����Fq�q)�[�y�t����6*J-;O�E�D�J ��o���r�)����ufcYG%E��N�%���e|�1+�Z�x��o�����E�!9���I�������uN)��`t�PM�#��H�M����b��^m2�eW�*5���s#����x�V��������kg��;k:
0�@0�\ZWg+W+��F�������q���@�\}��r���n���7�]��eW]FG��9:Z�S�.����pi�� �]��d�����X���'�39=��a���s[7�_��>�Q�_��&84��Ql1>b�04D2�-eM�F6����L��`����s)�d���;v�����_��4.��N�I�s�������e���	?6�4��.��w�M�4/�� ����k��������X@�%��~5�J���b�_%�Cp���L���J��f�Z���Wv���j�l��`;3���
���g�k���4�D��������Zn��$�vY\�q�B���r��+�q�����XN�K��S�h��w�O�0���4m���4p��aG�����:���wx>�������7"�x���m�Z
A�B_��{�zMe�E����Y=z���=�r7��[�Z<�n��"Q����e�m��z�Y���d�M�����o���9��:�55!������7��~�mk��z	=/P
�f�2���d��E^��1�O�(�J�������XxkJ������2)�1u��R& &^��I���=�ydW<n��qYZ��o���-5�6 ~��v���jvAQR>>����k|
���V�9��]Y���u{�Zn�;�<���������o!(U=�=��0�>��w����-w���>�3�c���
��T�u	���K��q)2��F	n��������wna����9,��dt��r���q�K��^�GI�j�*����%
�����_��X���p��[E�~*������F�+��*� ]�o���t�X">��dB��-Cn�]����?j����k-w��u��i��iXx�����������o�����@����
�dbZ��g�5��>vV���A@����=f������ndW_��z�	��	KI�Q	���X����~�A� ��R���_�a����Y�G��O��f���e���7����B�l�S�4�'��({����E��;�r����Ma[!P J,_�(V*F*�S����Ts�G��n�u\.(�����^�7�|�q��]Y`o,����d�����u��*	���k{��y��n96�Y��`�4s�fk�FQ�p�3����X��3�(�%��r]�9�X����zW}��]e'��r�,��O��Z^�k�]e�n�����S���]�.����R�]��:���b^q���o4
��/6�=�1��g1~�{�����3Q��r��:�Aa����l���W_�$�)����>9u����|p�C���1G��sC^�z�w�W���Ssf`,�+q�\j�rs%{�s
DG�h�,�%�����P,����d��X�k��
c1��������_�W�!���)HI�� ���B��t�����I�L��H��Iy�����T�g)���x���!��`\o?_��(�-��Uf�]���*�m5�U�(A��;�<������Vi8��in>�;~�uY���?���)�H���t+L���2���7<�2?��Rz
�[t�+��Uf_�������a���19/�w0�^#EM���D��)y�qx �ZQ���'��X}��Y���J����,C�V�R,�.�����	�1B��v�cweH4e8��b���U��S��H���X���X�����A��s���{���Rs�:y��?��3A
�����p����v��!&�e��t�R-����{a
G
#46Jim Nasby
jim@nasby.net
In reply to: Alvaro Herrera (#43)
Re: foreign key locks, 2nd attempt

On Jan 31, 2012, at 10:58 AM, Alvaro Herrera wrote:

I think it's butt-ugly, but it's only slightly uglier than
relfrozenxid which we're already stuck with. The slight amount of
additional ugliness is that you're going to use an XID column to store
a uint4 that is not an XID - but I don't have a great idea how to fix
that. You could mislabel it as an OID or a (signed) int4, but I'm not
sure that either of those is any better. We could also create an mxid
data type, but that seems like it might be overkill.

Well, we're already storing a multixact in Xmax, so it's not like we
don't assume that we can store multis in space normally reserved for
Xids. What I've been wondering is not how ugly it is, but rather of the
fact that we're bloating pg_class some more.

FWIW, users have been known to request uint datatypes; so if this really is a uint perhaps we should just create a uint datatype...
--
Jim C. Nasby, Database Architect jim@nasby.net
512.569.9461 (cell) http://jim.nasby.net

#47Alvaro Herrera
alvherre@commandprompt.com
In reply to: Jim Nasby (#46)
Re: foreign key locks, 2nd attempt

Excerpts from Jim Nasby's message of mié feb 01 21:33:47 -0300 2012:

On Jan 31, 2012, at 10:58 AM, Alvaro Herrera wrote:

I think it's butt-ugly, but it's only slightly uglier than
relfrozenxid which we're already stuck with. The slight amount of
additional ugliness is that you're going to use an XID column to store
a uint4 that is not an XID - but I don't have a great idea how to fix
that. You could mislabel it as an OID or a (signed) int4, but I'm not
sure that either of those is any better. We could also create an mxid
data type, but that seems like it might be overkill.

Well, we're already storing a multixact in Xmax, so it's not like we
don't assume that we can store multis in space normally reserved for
Xids. What I've been wondering is not how ugly it is, but rather of the
fact that we're bloating pg_class some more.

FWIW, users have been known to request uint datatypes; so if this really is a uint perhaps we should just create a uint datatype...

Yeah. This is just for internal consumption, though, not a full-blown
datatype.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#48Alvaro Herrera
alvherre@commandprompt.com
In reply to: Alvaro Herrera (#45)
1 attachment(s)
Re: foreign key locks, 2nd attempt

Excerpts from Alvaro Herrera's message of mar ene 31 20:55:19 -0300 2012:

Excerpts from Robert Haas's message of mar ene 31 14:12:10 -0300 2012:

On Tue, Jan 31, 2012 at 11:58 AM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Well, we're already storing a multixact in Xmax, so it's not like we
don't assume that we can store multis in space normally reserved for
Xids.  What I've been wondering is not how ugly it is, but rather of the
fact that we're bloating pg_class some more.

I don't think another 4 bytes in pg_class is that big a deal. We
don't do relcache rebuilds frequently enough for that to really matter
much. The bigger cost of this patch seems to me to be that we're
going to have to carry around multi-xact IDs for a long time, and
probably fsync and/or WAL-log them moreso than now. I'm not sure how
much you've worried about that, but a new column in pg_class seems
like line noise by comparison.

I'm not too worried by either fsyncing or WAL logging, because those
costs are only going to be paid when a multixact is used at all; if we
avoid having to wait for an arbitrary length of time at some point, then
it doesn't matter than we some things are bit slower. I worry about a
new pg_class column because it's going to be paid by everyone, whether
they use multixacts or not.

But, having never heard anybody stand against this proposal, I'll go do
that.

Okay, so this patch fixes the truncation and wraparound issues through a
mechanism much like pg_clog's: it keeps track of the oldest possibly
existing multis on each and every table, and then during tuple freezing
those are removed. I also took the liberty to make the code remove
multis altogether (i.e. resetting the IS_MULTI hint bit) when only the
update remains and lockers are all gone.

I also cleaned up the code in heapam so that there's a couple of tables
mapping MultiXactStatus to LockTupleMode and back, and to heavyweight
lock modes (the older patches used functions to do this, which was
pretty ugly). I had to add a little helper function to lock.c to make
this work. I made a rather large bunch of other minor changes to close
minor bugs here and there.

Docs have been added, as have new tests for the isolation harness, which
I've ensured pass in both read committed and serializable modes; WAL
logging for locking updated versions of a tuple, when an old one is
locked due to an old snapshot, was also added; there's plenty of room
for growth in the MultiXact flag bits; the bit that made tables with no
keys lock the entire row all the time was removed; multiple places in
code comments were cleaned up that referred to this feature as "FOR KEY
LOCK" and ensured that it also mentions FOR KEY UPDATE; the pg_rowlocks,
pageinspect, pg_controldata, pg_resetxlog utilities have been updated.

All in all, I think this is in pretty much final shape. Only pg_upgrade
bits are still missing. If sharp eyes could give this a critical look
and knuckle-cracking testers could give it a spin, that would be
helpful.

contrib/pageinspect/heapfuncs.c | 2 +-
contrib/pgrowlocks/Makefile | 2 +-
contrib/pgrowlocks/pgrowlocks--1.0--1.1.sql | 17 +
contrib/pgrowlocks/pgrowlocks--1.0.sql | 15 -
contrib/pgrowlocks/pgrowlocks--1.1.sql | 15 +
contrib/pgrowlocks/pgrowlocks.c | 159 ++-
contrib/pgrowlocks/pgrowlocks.control | 2 +-
doc/src/sgml/pgrowlocks.sgml | 14 +-
doc/src/sgml/ref/select.sgml | 137 +-
src/backend/access/common/heaptuple.c | 2 +-
src/backend/access/heap/heapam.c | 1841 ++++++++++++++++----
src/backend/access/heap/pruneheap.c | 10 +-
src/backend/access/heap/rewriteheap.c | 16 +-
src/backend/access/transam/README | 6 +-
src/backend/access/transam/multixact.c | 1093 ++++++++----
src/backend/access/transam/varsup.c | 2 +
src/backend/access/transam/xact.c | 3 -
src/backend/access/transam/xlog.c | 19 +-
src/backend/catalog/heap.c | 14 +-
src/backend/catalog/index.c | 8 +-
src/backend/commands/analyze.c | 10 +-
src/backend/commands/cluster.c | 36 +-
src/backend/commands/dbcommands.c | 15 +-
src/backend/commands/sequence.c | 8 +-
src/backend/commands/tablecmds.c | 11 +-
src/backend/commands/trigger.c | 2 +-
src/backend/commands/vacuum.c | 92 +-
src/backend/commands/vacuumlazy.c | 23 +-
src/backend/executor/execMain.c | 14 +-
src/backend/executor/nodeLockRows.c | 23 +-
src/backend/nodes/copyfuncs.c | 4 +-
src/backend/nodes/equalfuncs.c | 4 +-
src/backend/nodes/outfuncs.c | 4 +-
src/backend/nodes/readfuncs.c | 2 +-
src/backend/optimizer/plan/initsplan.c | 6 +-
src/backend/optimizer/plan/planner.c | 39 +-
src/backend/parser/analyze.c | 58 +-
src/backend/parser/gram.y | 20 +-
src/backend/postmaster/autovacuum.c | 45 +-
src/backend/rewrite/rewriteHandler.c | 32 +-
src/backend/storage/lmgr/lock.c | 13 +
src/backend/storage/lmgr/predicate.c | 4 +-
src/backend/tcop/utility.c | 46 +-
src/backend/utils/adt/ri_triggers.c | 41 +-
src/backend/utils/adt/ruleutils.c | 30 +-
src/backend/utils/cache/relcache.c | 29 +-
src/backend/utils/time/combocid.c | 5 +-
src/backend/utils/time/tqual.c | 411 ++++-
src/bin/pg_controldata/pg_controldata.c | 4 +
src/bin/pg_resetxlog/pg_resetxlog.c | 17 +
src/include/access/heapam.h | 21 +-
src/include/access/htup.h | 85 +-
src/include/access/multixact.h | 65 +-
src/include/access/rewriteheap.h | 2 +-
src/include/access/xlog.h | 2 +
src/include/catalog/pg_class.h | 24 +-
src/include/catalog/pg_control.h | 2 +
src/include/catalog/pg_database.h | 10 +-
src/include/commands/cluster.h | 3 +-
src/include/commands/vacuum.h | 6 +-
src/include/nodes/execnodes.h | 8 +-
src/include/nodes/parsenodes.h | 36 +-
src/include/nodes/plannodes.h | 12 +-
src/include/parser/analyze.h | 2 +-
src/include/postgres.h | 7 +
src/include/storage/lock.h | 1 +
src/include/utils/rel.h | 1 +
src/include/utils/relcache.h | 4 +-
src/test/isolation/expected/aborted-keyrevoke.out | 276 +++
.../isolation/expected/aborted-keyrevoke_2.out | 278 +++
.../isolation/expected/delete-abort-savept-2.out | 76 +
.../isolation/expected/delete-abort-savept.out | 111 ++
src/test/isolation/expected/fk-contention.out | 3 +-
src/test/isolation/expected/fk-deadlock.out | 34 +-
src/test/isolation/expected/fk-deadlock2.out | 68 +-
src/test/isolation/expected/fk-deadlock2_1.out | 75 +-
src/test/isolation/expected/fk-deadlock2_2.out | 105 ++
src/test/isolation/expected/fk-deadlock_1.out | 44 +-
src/test/isolation/expected/fk-deadlock_2.out | 67 +
src/test/isolation/expected/fk-delete-insert.out | 41 +
src/test/isolation/expected/lock-update-delete.out | 65 +
.../isolation/expected/lock-update-traversal.out | 18 +
src/test/isolation/isolation_schedule | 5 +
src/test/isolation/isolationtester.c | 1 +
src/test/isolation/specs/aborted-keyrevoke.spec | 31 +
.../isolation/specs/delete-abort-savept-2.spec | 34 +
src/test/isolation/specs/delete-abort-savept.spec | 52 +
src/test/isolation/specs/fk-deadlock2.spec | 16 +-
src/test/isolation/specs/lock-update-delete.spec | 38 +
.../isolation/specs/lock-update-traversal.spec | 32 +
90 files changed, 4855 insertions(+), 1331 deletions(-)

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

Attachments:

fklocks-9.patch.gzapplication/x-gzip; name=fklocks-9.patch.gzDownload
�W�9Ofklocks-9.patch�=iw�H���_Q����X�W:��86Nx���v�LG�
S!�U�6�����
��d9�I��n�}��R^�zE����a����yC��}j��5�������S�R!�sG�J��wR�����������/���/������A�mg�'BJ�R8�v@�H�L��|�����&��a���A��������3�����|�r��:>2��0
�_
�[�k-�	���	I�
h�w��ph�v����+�f@mr����5-�������/<���\�g	����P�%����R<��}oh��u����r���oK*���U0���zxw�g}��S�#�3�f
5gD��n�5i���G�'��5�)��x:�{��;���UOJ�y�m�u��Ij�rt�=H]�Tj�k��/'um��������K��o�>��4{o�:p�����q����2��F�,(P��T��&0��o<�d}���Uw�8��8
�\�J�Jc6j��;+��@F�^!��X��wL�Q�[�C�8��(��"�����$�.�e&9l7�������x��S����`�	=�x�
�`!��U���F,�(�t������}~AthI�(��>CZ)0Ba�7�uF����n�h�9��@�%���Q*������\����7x!1j8rBF�=����w����{fs���CfOB�l�����p��#��Y����~�a9B�/9��������f��}�!�f���by^>�����\t���6_�e���0��������&9$�n�u����r|����hr��������k�����*_y�����J!�L���c��.��%�kYhSt�RDw+Et���2V���RD+y��������sQ����k������U���"n��<}�l[2ADg�)
��
�pw�
��Bj�Z�3�3���tY�)
����\Z*��h��|�TZ��~9|�T����B��3P*�O��,�6&��b�&�.!"��:�W�~�6�m����f�V�"%��2�U���L�����d�|CC����r�wf`w���������9�<9|%,3��<�&&���`?��{�2�D�O��}F��o�!���	~#���+��Y� o.�������������vS�|X��/*O,	�����������p�
���o:�.���0Y&{{1o(x��-�>a���[���d�.�J�e�z+]��������p�8W��z#���j�r��k����|��.)���+$xk��`�(4rkH�6N�~j!`v������L�5.zW�WRJGBb���i�|0k{���:��]�8������g�t)S�31L��������� �_N�`9� K���2}&�>��������^�t[j���"��.7�lZ6y��E�����D��L�`�7�a���6�4�1����������_��f3�n�c��N��W�t[�)^�����J�� ?#�$�M��#��V�O�L����l��� ��u������� ���J�'��S7��IVHm�h����#Z��4���GD�������"�]�|\YQ��B������	0��)
j��{x7�e����f�trb�7���x� �Sd1��4��A��O��	��69�h<orqt��A{cB:�"]�9���0��]��)iZ5m�B�2�����i��aUb����w�rS�U���	].����O��?/���	�Cl9�\��X�qy&����o���(
�/$X+���n����|`i���/�v!do*��W�`�UJg^�AV_�@L"�
��,��0E6EQ!��
�����9~C�0`a(D����e���>�IX�!�U5����'SD������a���Y#/�fg�����0S�"����	S��D&��A�w\���q~^����rQ��9pj	,�u& �O>���d�g�Y������*��FWs�P��Ka��ES���T'�
�r�|nBz�@�3��LR���S��In���&5Kl4,>���X*������r�$v���ip�c�x��sR�8�M'ei"SIl�����#��QZ<+�U�C��z/t���x��d~�Bk@�	8rC1��LNI��1�W:����D���/%rl�B������E���0��c-@�&�f/����&������+ ���UJ+��V������1��gL$e�!����T�����Z��]��2/�(�����
��#e�$\$�*�R�����/�����TT.���������xo)
���G��,��T~
No^�����C�^8 �,�����\�Y����Q9����U�4����U ���9�G�8��/�L��@������kv��\��y�G:W��03U{��?�AFG��������A�%�I@���NeS]�������DQ�����m=LTsxU����������u@�E�����Go�������Z������1?R���:��#&��F���)���I6�94���C��.�c��"��GH�A�����*&���~?�4����I^N�cv��=y�;k^u���k���(+�j���QO��>�j�����6��	�����%Bq+�_r~�$|�a��`cB���r����;3�\]��N���W�-$�������G��
����<n@ ���#/��;���8P�;Q/:�`(B�K���}�Ae� pbmu�%�1rh�7��h��;���k��d���S��y-vK1S�+�Z-���c��R�=���{3t�2��Jx3���lc���<�=��FD)���<�����}a�uD�]�-�qj�`�V�=g�
�o�K%
nW�e�u����k�����������nU|�:�a!
Lg_�V�	z+�����Yz�����qX���U��[�S�������/g)�J)�BEYi)H�l	���xr���Sb��Qy��hDMe���il�-�O���mq�<���2�K*��<��"�\DW���������Hf(�}���(IF)��QN:i����/��+^��N/]_�R��*���j�9�"J��74�dlf��jSKh��k��-�69~7��*����~7;$��n�5i'��I��K�����79ju��3q�������wL��x`9&�{K�@�,��{�������#��I���%��A�����b�>���|&"`����fw,�C�,���}�;@���!���=|����.�D�|��?��2�	������yF���sG������D$�a��f���U�����~�����L�v�A`r�� ��N?������2x
|M��z�_v�
&� ������!�I�C?�����F����;FmG��v��4�oA6=�R�QI*v�1��+B�Wb ��O� ���nU_�D$5@�#��y�
��� f�qq�����t��U�]��P���K?��e��#���\b�e��g�i1��UB�(�v�����N���_Nr��U9��,/������57�s�h�f�^"��H��Uv�*}c��5��O���x#�0cB�_�3����e�1X�S��BF��R>7&x�O��K�	�1���
���4����'��*��g����uR���!S(Gg$���;A��c�i���d_U�O�J��j�L��co�%����3&�����%�g];������J`���V����4���{�!�
*�B�s2
�M/r&[�<��s&����=,�U�c��9�����i?�=*E�%>���p�/��y���V���� ��"Q�������*��i���amb}���a�$w?M����
E��AL���x\=�e��P.����	jz���>�*4A�h8���������l���^_8����k��C��G��U
��Q�?~�z��v��������n����4����Kg<E�f&�&�z!P���SG��CE@J�04%���V#@m�����D&&���v����LT�"��OI�w�T;��_�O�z��qg��s|�������X�&�O1a7;�N��4���(��YL�<B��n4j�o<�$Di�l7)E������m���ws��c��V�1��_0�4��?���=��:��4�������	�t�[�p��*�����E��5����q��W����������D�}���9��z$�]}�0{o��'`VE2�"������[��uT������[����e�V�	����=���u������5J^0�En��6���[[�~��9RH�s�����g�9�N<e���'31o�%Q�c�(�������y�4���qoiL�~XN��q�E��GvxJG�lq���C��`�x&-{gQ�W��9�P�$W�(���]�����g��^5��t��8C�wM�;h8��5i]x"2�qno�;���Cvru��9f��#��c&�g�\I�o��f=��VR�[IR�{�R��#:�a+�e��b���8q^$�=��U���E��f�LR@�K�������X{(M�����AA!*��h�W����u�9����n���zQu���@<����G�-��{L����dZ�P�
a��74�Ej�x@I��'&d�T`)5.�3��}!8��^X����98vr�r�d���hU�Y�~�ZID�����s���\�F �t��43UX�r�x�P�o���`���/&�4N��z���h�A(�H�f*���C�M�w,X���MQ���G�\W<<j�.M��^�P����������6�W�Jt���H����e7.>�E�@3hc�LG�!E�����t����0����^���cb
LP�� �?
G�=P6U�cL�6)�s�H�hbO�-3���FD�L�)$������%��C�K����<8�)h3�F�Y=P��o<�E`1������^g�k�5�E.@(��z�77�Pqe�k�i�If���
���&�0�b����K/��$=�4�C��L���9��H��Q��:�<N�������t(I)����Q�z�L���`�����z������(������t�$���}���s��+�"���}�Y� �w�*�<�6SmwSl���Yo<���2HY��zc��5�r�U��=n����(�K����e>r���5���\_�b�0�`R!l"D@�N�|?�&f~
���0����U�w�S�Z���T�|\�#9�G����Q(%68}�a����2z�S����;��f!Klr�F��w�/����9s7D-c{�G����[�wh��.&A�C�ZQ
�]wC3��L{#�Ep!�h]�h��F��lE�[	�#Mb�����D�bX�d����Y+g�Wz��1�����\'mw��;�m�R|���S!��
���
��L����Z�
�D��L�`�`g9:�@{e�LV�'�|(m�j��t?��A��U�^�&����VS����f��B��2��-ftw�S
K
F�B<����)�����Y��hH����B�VO�)2�@���(`���[f�Vl��������[l|�������1onz#��"l�%�>|����DC�'�Adl���hV�$��e���{��������rK\�x�<��Zm����6������X$\=a>
���o�A��S�Lc^�`��&��������,#�DxN�?~#w�oq}���'�o%��������&�"�������d�����
Z���8�dy��������=�V���
�O�[�Y%�4�3�p�H��5��H�+ubw����"V���P�,s��BRV�64)�d V�x�U`s�p"F����

/�Sy��O��'��}\������F�4��,`��OC����(�����@�5N��/��"�� �����P�h(�����
$o"_du�4d7�_	�9�N���<m�������]�=F` 
@�������6T��X7j�k����J�����PA�x��C��]�En��Z��W��OI9�u8��36�e����7�j�����V>E�~�#0	�N/�������O�,�B*���J]%�N�����LU�40�8���uP
����>{��,x��>��n��O��u�$C���$'*����LhI������k7�`*��^��OI���F>:��I�"���#F���8��B��B2I������z<:?��W}l_�������f�8�����G��zr�k�vggcx���<�`��K��Jt��<�y����<��Ux��E���0����\ngQ�Ux�F 4OP��lY����/Hn)�/)z��|w/h�3�"{]yQ�5�2����k�Mz�>S�-���-����w�{x��$ �u��QM#�H#�'����W��������0��~����OW�k�J�c!���`2T�1vch
"t9?b��J��'}Y�F(;:TbM�1*��i�U��9�����,��6�R0.	��HX��X�S�<���	��x0*��Kc(��9��fT�
 ��'��-�9d���yy����i4����8w������/��;���
��3�H��*����1�P�2y ��m��&;���]7��J6�:��^/e�����OW{S������;F������@�!�g_/������xD��a
�lX�M�,��"�y�A:���c.8��}.M����d��%=�+�H�V��Y t������Vy�?��Xa�x�Q����I��R���$|x�k1E�I��l2��9��ei2\�s��t�|��W�jC��r����7)� DX��CWQ�t����d����c ��
<V6t��m�C>8�q��B].�'T\T���J�43�X@>�?�N�
l�5�����OF�#�.I{��-� ��vq�z@�d�H�l����sQ���a�H�����(n'V��>%�bz0�n_�%��5�
��<��=���X��/G<f-�H
��3�����B������#�^0��c}F����$���#�-!����eV�Y� 
�v��G�S6>�i�5u��G�(o�1��1o�4������������|�S��&������� �ik�P�nV�r���G�����8����\{��1�# p]�z�g}�z���m6~AU����p<N
�<��c�1''�Z@��GY������
�Vz�w�e��R�x��*��a�%��,\�%����;K��	O�:/��>C���~<���
��{Y����on���_b�J��/����1��h�j�b�C6�^%�GQ�����mV��G�B&H��Z�����J
q'�S�
�!��|�I�����Bt��0��x���J��9\
]�����w�I���x<GT��"wpwg5�L�����q���
��Pm!�8Y
���`U:�Y����x��{�W&�<�[��~��*A<�5�Q1�����*�Ys�I�����8�%�����fZ�8}T��3�H����)sT��N����D�\�]���L�@ ��V����t�?�Z�x����0� ��o�g5�>�����qI��)<X`J����b�O��,�a����ay�������,``p>�p��?��x��g�r-�Cy}�|�rA�T�+��
)H���6P�����[����0H�Q�Oh���������S����z@p��.#����,���/n�}'��(�����(E;)+������s~��c��\�e
s���!�����[t.��K�\�\������~@�t��U*����>�K^^	�����M�e����,�th�|��Z�3NO�����(���05��I])bZ�Y��[���[���;�s}T��}@��O���;!G�fP�	nSN�7��q��>��E��3��
TW�z>=�\�Q������L�g�I-���I���s3������c;�y?EC���Qv�$�<A������������4���9\�HD�81P�MSd�����l������+�#?�s�7������_��<I�����t��LC�e���|T��r���-�|<	��u�y����w�=��)����:����E?��{�����j��^{�~X8}���7:�.?����*�[���
a�\�lY���N����
��*�#P���3��4�7���gc$����M��������p)���8Hq�`��O\g%��Pr:����&�p��a�o�s%gg����>~xv
�<��B��v%��8��VB�yR��+���R-�>��lW)su��ta0��T�d
QjFC��a�2�mm�k��z������`��!����x	���'���k�� W��X6�������u(�."8Pq/x}����{�&qa:,��h$\b�:J�$�E��D�`�yr�_�0����*�1Fk�`!}T�b3=b'�-����4�����2sW�|�q��`�	t��`-@��Yb�����^��@�j0o$���Z@�O/�HjFf�U�V�2W
�j����e�����w.�ypLU'��//���#�v���Cj8��g��������(S��6)�2�����04v"��d����+�u�h�KL���Z
$�������\>
=c��7R�9cf�7�����o�'����BM��b8��]s�n��+�V��
��{e��/PG�*�g�'O��	��_rlf(��C���UD��i+.Fq����2����4&���a�?����K�e�#�����j�;�\)4�E
C*x�a)��+� bk��ZA���;���fY���>~_�F�G hV��%r�%S���u����w�]�o!V>m���7�����V��c����� 
��- ��=A����_I���l��4��k��;���:9��1���F���hJ�	%F�����Y#�2���v��C�zR_����aD���V���ud�~4E�, �D�T�q.����8�0�c���(:��	:V���������$�s�$��839l����]���^&��;�?������''j��=�}�0��r8{��H��aV����I���(�)QA�
/��ek�z�X^�Ek���u^��z���V��l����&�{dl�pt�4���'���=��	?�I����|���Y��&��-[�Y�7��fS��W,A��]7x��=(V�_���_D�����g��J�s�)7(�b�Y����9�4�:�*L:b� 9��A?gp��&��������f}�����_g������.x��'T%ot%��(�����f@OEny�D����r^�]�_9���T|���w/^����y��#j]���C5����0���#�/rqs�]d�����6���i��f�=�j=D���E���������Q4���}��-hb�d*��;��Q������
MZB�����6�\���
������V��)H��t�������*����B0����v`/DOj:gP����W��~��B!M.Q���v������_����\��@�������E�-�Ea�}I��]y���\0x
�
��v;�Ph0���!�f�� �6p����Ap�U6���1�r\a��dM=������V/5����7��<NtH�x;I b�<��Oy�K6�K6���{7��92����j������j��$.:�w�7��7���"�������QP��F�Gd���"�iS#��J��&��d�-��)���H'��;K�!�<1��k_�? �����|�F��Y}^=SZ&sI�f+>�d�i����j;N��0k��G������E]��^��\o����t
�����	rT���3)o>��x�:�����Z����d\6�K����n�r�����!��4���h�����uk�n3���$�w���.���o{��[�	"��f+���nkJt�A���e�S1y\��H�%s
yi��L�d����o�6�#����}9�}���w���/p���s���6��!�`�
O��;��}
��{�G�h��3�:�����A/r�� ���$��i�8b� .���6�l����[�-;��O+��Wy��cC�V�YU���U���Rj���,n�����p�x�����{�������Z���1��5i�}���T�m��	������D0���1�v1m��"��>��,�L�c�QPI��#F)���D |7�������Q�O�0q�q2��\s����5��nD�������9��O
�I�Q���6(����;��A�������x#����.=v��z@��,WUt|�<�c>v�l3/��/7�7�KF���C���i>����������c��S�����L�r6c����w��9�U�Z�������m�	�SKq/Y��T�j����pL�?r��mip�Z95���zJj��J	�����9Z�;T�c;GT3�Fn�XZ�-w�����������
RLk}�����i$.kIe!O�-0Y<Nw�OtI
���VDn���~K�I%b��8��+�����m�Cg�'���<��F�����&�����/�����tO)���Zh�h��`�[���z��a�m;�T)}A�Q��@��j�s����I�����m��]�;�v��W��G�)�������&��o���R�������N����15���`��ZG�X����
�IY�����_���
X���[^��J�
Z�~t�$�(�D��~eE=�]���7�`�S�x>�E[;�eW\f��+vbe��
R3Px��<�����t���P���m��E�-�U��yI�R�����UY��?)!�&
d*���e:�=���!�3���PM�����e�"��)��c���GH���B��� �]��VSD��!~�5����c9�v���"'�!			�g�$:�Fo�#���ef�ZR�M����u[����R��5R�4G��]T{�pZ����	���m����5��&f������2�*#��>q����wP6{��@�?�p��,t���@\>O���F�2@���)`4�@P���"PxA�mN�pQ4��ssvHb��|H��$O�+R)�R�$�PL����Lu'�� ����LT��%�����Jf�W�g����u�A���TaS�#�1TFSH��U3I�X���[jn4��oT�����JuS����x4��_�.�Yc<O��&��^<�tV��0M1�G�<IMa�5���k	H�!Q��@WU�'�yB62�i1�lM�O�8�o6=?i]�6!�[����Q�6���fV�%���V}�UE�3m��r���i���%��[�a���|���b	?z�u��I��1�p�xk�EN���x�������[�'�Q�	a������hxo�1���~W����KKT<$�Vk%�Np��w�F�zg����l��� ��e�e��{�m7�p�mlXr����O��������B�#y����	S�g�eG��7����#�"���>&����8�������n����.�����t*V$�d����dm����	�m����N>��r7���S;�.{P�p`��~��a�����1>~���ou+S��������ep����Q���N)91?|�`*qX��R;�G��6D�Q���b�'���:BA�P���g��$��3����jIR�N#��|d`s�zO����3��a _���U[*���������Hp�(��_r��W2F�LT"���].��8Q&���h�[��Z�S��(��'�"o��(�����`�`�%T��Nt9s��=3���������x�2��~f�k�V/N#,���������b[��
��u4�;�������u�h	�������j��6�)no�n;�?O"�^��`�G�G���R�-1$��@���\s"#��jH`�2"(��;�0��c��K���Vom.�����_�v���U:���$W� <bT����c���&�1zD��5d%Q��g�#��k��zS
�R����/uu�2���e�S&Tm�RO��0^z���30���hq~�QCt>P7��S�f���B��!�����V��Y�-�U�O�pE
�Gk�tq'i��u��mt#�[�@{�T����6#��ovpz���%����.X�QH�f����1�4�^�d+5vt;Y��c�Q"���)&?'����R��T���`@�lr�72���L�*\����}�(P�a`"y�F�/XGS�,^��X�1��rGm:�@��}F8�����r-e��"=v��qb�Q���Z��:>Oy|DN�&�W{�)+��6���S8sb�����'�6�#�����h�m�4:����9��R���'1<����'�q�.U����p�H|,������L�b�1�Bp>U� y�%"'&@
���;zIN!�>�������^>����_g�/y��Ra�/���T]�X���O�@A���C�w�j�mQ��U#{�<iCZ ic)p�6�-��tO����^�d���a��v6�������2����a8�v�z��4`y��������z{��	vwM�~U����_�#M���2<�%�5sz6�?�v��W���[���gF��r�e*u���s�����n9J�vL��wfPb��qf��h��T��&T�j�-qU���m.��(
N���9���@5���0o	��2�a�C��S��//�4,�kV;�a���6�"#V��(����T���!P}�q%���OO��������4�L����3xP�����T�4	/?������'��m��8�����}��v{�<D���]����:�Bf���Oyfq�!,����8�C)�#C���;;���d ��Ib���������'�:�4i��!R����V��sr_�G���]l�yV�*g�u��Eg��������v�Td�Bt������m�w`��
����J�r�T���d�&����P�r[���d^��y���?�����q���.v��K~GM�7��%S��������A�2j��M�]�?�	�
f�f��k�)�
E�����������^R��%[�U Z���#T�[O���
?_��,LK>�
��T��u�o�����\C�1S3��H���z����������7��<�
����n���f���p�b���C.��hz���t)���yn��|:�:giUuX�?7Y+����F��~�<��������-�����Y���������K��V	\�u1��CF�hB	p��J���K�F,�>I�!��SE\G
{��zFGq��UJ�m����`$7�F��^��0��u��vFj*�CE���!E���B�+�*�Gb��x2��N/��\[b�+��7*��or��q����r�'�V�'��.�Dp��P��O����wv�3o%gX�]�1����7pn����yu������7�S����4�4�ye,����C�,��>�5����w�y�{�3�[�N���p�Z�����N)*�����X
2�m�@j�<���������/�����0���XV&��y�B��L��O8^
y�Qon����/o����/��m����j>o�D4��vH'������O�#K��m����A�vB�	%��Sap�~���`����=��4���d�f;�I_���/x2��)iE�������`<���\eY�Bn��4��
{.�2������6m�������	�q�m8��Ep/������@�=����%�Y�P�\cZ���L��zmV@�XN������ks����o���V�9������x	��4�1��c��K�z>�����mq����z	���7�3�P!�i�����/p$M?7�#��H3� o����an�����?�}��n9�nS<�:�m������~0U+b�l�C	��,��.�|�~<�
���y���)��0��l��9 �f�^ya�����_$CJ�����
�������X�OS��X
e�J$g���_���|pd�����C�'�e=�J����`[p�Fc�Q�h47��uo��m����C_�6|eV�S��72�9��}w������:�1�S�^��D���u$�z�Y��Ka��^u�U���x�7���Iuc{���\o8,����]�������3�v��F�G]����~`��.l�$6��|��(,m���
��������Y�o08�b6�`17,{ �A�	s�����\%|%~�G��W/��h��ho������b	nkxkk�����3�����Nt�`�U���I�kC�:g��KE�\Spk2����$%b��`G�\-��I��je�����f����mq^R��W���k���
>����~�B���������j}Y�yu�����<��@$��4�T�����j��N%5B�#|�������D'���V��o��%z�t�~���Sds7E��0x����lnS#�	ws!)�I�>�M%��^�#r�,������������o����9<�?~be�FBj��h��WB�����
S�(������LX���m���H+_���=���Ig~V�	2^sk�B�0�tf�O���#m/��
'�CS��d��
��A2�]�"|Keb
�W��n�����~��l���p������2�"���9��Br����r�������W�UIK���t�}i:;������xZ�����h��,�
���b�!��b���������Y{���Yo���������D�����=��;O���/N�n�e�]P�F�_�8.'���J�C�6�f���U��8��KE��d��MNmk�s����IF.��X��"\��Y�����A��J�T-I�LJ{�K��	�.:�U�8�R�������j�b�jRe!sF0R~x��������B-�����}>.���45DN���}k�p���C��3R��s��� �Z/~��g ��7��=��zSi�o���I��_�x>BM�1�o)"��������;�X9���Bui���b+0��n�����<d�s�ac���N�R����%������^�i������K�+����0�`��������/��+�Je�H1�b1�C&�����	t���V0���{j*Y�,�6���F��n�������
��-�!W�\���Z"��~U*f�G1�����'��mq/~#�
��Cy����X�jL1A�5�����\��+g���{K��]C���R�_��O3C�[���X����W+�O���l�Qw�7����k)�<��WFd�����O����d����������^o��y����x*������Ps�siO��}L��Q��8�01R�k�*�Q�"���M������_v^�>{�o���Av^}�y?�xJ���%QL.QP�
������>x`����A����m,��{�:�����T%�'�:�h��R�!l�����J��N%	���fl�z@��
������
�g"D��QK��.��chk@)�*�SwEYP3���T���K��
]L-b�V"`T����u3���\a�\�#ur��%�,9�(H��rn4Ra�����D*;k�w7oi�o8�I�G�������A�0�����K7��K����EB��� G�$:�u+��9��u+�B��K�7����~,���AJ'��Z���E�Cp��d���K\inP%�$(WZ&F\\��U���V���,MY^+�������M�����i�����O�By:�-��?��=�R����)�5��(BN������"^m�@ul�}&�����
N^�^��D1�}#�Y`����	$��2��-��������E�-I<���P�5@�hmo��x���{P��smIsa���c��A��'��\��}J
]�LP����0U-�l>Eg+I�����m�*����ak���eH.e(�iO�b�5����+slym�v��oZ�����O3n�i�����5z�l�i>��e���&s�J��������Z����-���:�.X����H���)�AJ)�C�R�����j� ���sv��:fukh�A����M����0��a�u����[D=�1�KU-f �t�$�hO�B�
��.��R�a���X���c]6S+T��Q���^�}�+��Y����m�bFP�*�kZ�,T<�8�Z��ok� �T�	� �����(M�\�S9�,� ���C��*���x��)o�S�'8H0����������3S|�����,�-5��X	Pd~�-�cW��F���s��c<NEKT,�7���k�����T�`r�.�~�j�^����^���2��V�?4���(��C�W�(�0
tE�+�1�N��*!�`�����H9�����Ru�D������7��P3A�q$�6!Y�`�T���S��VD}PO��R��7�a�V�%#c�w�Y�%�
��5t�d���Av��+ZB����1j:%��2�c���V�����E�TrP8�J����nu��������E���y���p�Bs�Z��Q��*��������x�B���B:��k��.�s�64c��y���������G��5))���5_H9�����~��q��p�a8Iv$H/��+���
�3�lX'JqV����� ��p%x��+S�i������E�z�U,Wjqb��P��l�l�����6�\�IJ��
Z�5��+�" EV�8����\�B���'�������*��Ds
�"@��� �!�:���bu�!����%��=8���QO�<%�����(������{EieD�e���W"#�Z9
���C*#����������%��Y�r��u��qu"��t����H���C�����[t"�����`���z�D����;�5(m=#pg�B��K�^wT[��r�J�/sf�NK�*J�A�(\�U,�M\�5~J���E��\��7���A�7��b�p�� C��w�co�t����.U�R-U���%�l��b90�w�E����I���z�����<1,s�\/�E�E���>
�r��,��B�<A9����S.��j����UE���k�\�2R	�+���k	=��N$�H��,i�������)���(���F��e���#g`�a�Z>��j��������Y$��o�
\�BmD��U����������\G������04��D��@��4���0�'�-�O'T+$�,���]��
x�!�]v��z�K������]�����-���W���5����,z���L����rv?���L0����H�����9������X���������4���"r���d����8ff�<s&�F�X>W�@.O���YZs6
��l�M�����l2���@���5��+�����	�)M|�C�J.��An���Er��l�p�Z�/�f8\Y��bJ��n�����3Qz��W�X��5�]�*�[�)v�����Mcs��,��k��y�Z���@�����T������R����5��C�����L�!����x���+�v�&��O�C��+.���0��k���d���V*����y���-�2��Dw��1���\�Ij��]J����bU4�k�X�w��� <�5[�����4�:�&7[I~V�C�~�%�l7"�
��"�Vvc�s�<���z�M�I���M=����KU�1mxO�9<��}��B)�o���;R�y�8�@m���i�t|�aW�D�_qy����*��r�L�>P��\]>�5�7�����������W��:��u�F���E9�����%������L�j^u�J���5�Gw�!5�DJ���0:n�%e����@�%�����G��<�Fi����{T�*�tE8�����B5%r�L(��@��0T����(KF�wl�Y���>������%&Z-�$�z���|�������9�-Q^���wY��=��L�X�nO�z�(�b�����7��1���B�|f�����Gb	C�s�^{K�X(��9_4�r����pV!�U�N��fF���]?�����_t�Ii������w%�p�������Y�(�l��^���B,��0 w_~�"������+b)a�3��d:����-Q��HcH�lse�����q�C��6H?
�$�(�����5�������n�&�Lo���t��ja*(�|bk��������2-zTt'oM�4'�`��	�P�����^�,V�;tSt�6'W�8EhB��`��b
�p����5���1��(�=����9y6:|�5�Q�@5-kuv��PX+�f�3��V1���)Y��#�M'���K�#�%�E�!VM�.-�Q&@5�UU��!��5���vIhY��� �a�^���2L{���+���q4�g���K��?�'�k�+���#U|�y�=�6$��hh���������l�I`P�Y�����@J��/n"�1������a�{������8H'C�@��X��*����a����YN��V��c��G�L}��
�>���V����d�zS"��M�q��d����SDP~�T�qsrx^���R�APa+3I�%e�t%��b�y�;�pp��aD��j�6��0LS��qA=���08�1��NFzs�d5��@X&m��f����"^�J�.�O�z*F5�%��������T�(��<.8'�����w+��%.����"�^NT4;�IW �����}V|*���o�Du�2$����g)`W����Y"��%{���{�H�w���B�����G����T�����6�]����f�����af��
���4������'D�����yi��
g:��'[�,�=/9���V����o&�X��aL	q�x�����e�������u%4!OD�����u>i�|��������=>�=:���t���N�T���=?&��I���
	L�j;��r�A�I�'��"S���ZL�����&����(F�����LZ����h�����Z����s��M��iP������@y�����O��6�c;`[�%1���.q,�p'���KF"��G*��0��y����~h�o���f��h����]��4MF��4�,��q��k�������x0����Z���1!�K'��!R%s��3IX���d,PD�i������f�[f�#��6����M����e3�*���m�=W��b��
3@Ca����mx�tV��)V�o�oKSz#u[�a}��������z����r����n]_�Ob$+/b�2��>���"O�|��Y���T�������lhs�_�
�4bT���'
��MPUc"��F�t���[���!yb�X�=*�b�S]�K�L1�'�s�
����:J�N����C%�S��n�$��$D]�M�w������nS���G������e����l�):��J��-�����)�m�r�aV�@a������3M������H�C:<�������(~PZ�?{��ii*�?#R�M�7X�k^�ik�^��.,�g��k�0=��kC�]V���i���v�����\��<%���U4En��N�q�V+q��$�i	3��F�#�zJ]"��L�c��5����a������X��>���8�-:<�%�6s��jRe���K�6��r[��k�2N(��vp�<���:�\�Q	>���U�?���>�I�<\��~d�F��Q��~d�F����fm�)���<*>F���d�e �F�����u�E��p�z�����������5�6�m+��u��V�Y��b|,��^R������x��0���C^�����p�qDn�8���'�~Y��X��#��'g�_�d��)4����[���q��NrF_U���^B1�������T�������Dy+�������6�Y�D��+��K4/u�l2����Y���39�%�������!)c���\��
�O���T}sG��@J{�sCz��js:!��������B�T`E~����br��k����V���F-g#������J���O�M>�`���GuqJ?��4�[�W�i����)�����/�F|���o�Bv�w���t��D�P�����R=���yW�\|A9?GWn���C�/*QYg����b;_���M��U2��m�V-��$�Q�??���tN���f��\f�������T��I�c�5$�%�k�[�����d�'��w���0
F���w�MtN��x���|"������w�2"���G�����b;,��^t�j��+����v����p:
��o#O���`r?�>�`�`���j��wU1��u�P'BE��U��T�X����s������j��r�������<��)J_�6�9��o��B��X	D
��F�x9S��<��K����1��$�p\h�5n��y�Rz�X�	��a�s�^�Y���(
���Y���J�~�R�-\W��:��`Ou
N(�P%���`��T��SM�v"�74�Y�!F�h���rb�+{)m��bH�{��;�	
���L�
������6Sh�bZ��,��F3u�C��n�3�#e2�
K�_8���
]���������n��M�o���D��������������'�@%���9N��4��a���(F����/�S���N���D��g��pu�<Qn�p�B|�����>3G���B�T	��f�����1b���������u����JI�����LHx�Ijl��gY�j����=c�����WD���4��a���/FE��<
{��::��O`�&��	�|�!vh��a���
�#�+k��fO:3E��
��������_|x��>�H�!�/8����xB�M?�d�y��4~V��N5#��Gk�����������7d�Q���yg�����S�����^'�Ua�\��9����_-���:��	a1����TK���#|�D.�?K���W2%�d������A�<���VQ3#)��US&DVR�%rCB�� 1�,y�-coE1y��/��`�	|n�,$(�M>��1bE �RX��p�� 4�3F���M��$k���f�u���f;i��*�4����2�s9�E����3m������SN����
��u����99[�����4@��F5G8��&�e*6������5HU����i���;�	�T�&c������!TNI+x��UZ��a8�3e�;�B�W|�S�B��7�^� gG��$05�cb��OJ����1\���)=��(&@�0�8���(A�!�����7���'��i}��V�]���]�bH��6�I��4F%YG
F��@f���w3S*�kN�;���,�i�����1�n����m�i���!�������"���g���oHEU�X��*DY�q��PB;�b�-�L�2g0����|��T����31OHSd��IY��{�����=x_^-����5V����]����;���9��RU����CN��a�<���������P"!z�H�e�zI������j	��!�i/��~�C�n��[n��q�	���G�46�aYc��]'C!��seJ�u`�6����d[�jS�a��9�oj%�����3�^q,a�������� nk��S����b������e��g������������C���qp���y�&�Z�g����0����y�)�<?#�r��
��S?S��)�G�)�������%�����g54Pf�~�J6s�����@bj���W�9��o�)P^w4N��$�QL
_~:{�B�����
\	�:�-�
��
Oq@�-1��os�T���P�K�����"g��c:�����uP
���I���$��~�t�U���ZV�^w�����5Zo��,���9��:��
l�W�o��4���^�����:����W�0:x�O)����y�~e�?h�,{`l�pvh��Bp�5�J���'�����������O�`����&'%V�����f����G4Y�S5�>.��ep����P������KS�Dg�(1xAz@�e�'��T^p|�L�9q?t#��E~[�����%���{��P����uH�|N�S\�/�,�Z�>��
a���3�uR�+
�uH��lr�!�,�j�h���#� �T����4��c�.8V1���EE3Il
�
:�����DYS�?�<��������2���V�P��������d8����'��Q�%7��><|$S��a�
�56�����clP������[����D�.)F�����u�����O���[�P�����Q�%�ii�45�����d�Dd��$��x9���	�fuRG�����E�n�j��G���d�!8{N�oso�c�[���f�D�[	'N(��F�>�z���$�tk|(��H�Yrka�K��"A�b��;vqsk/������S5+�k����cU���m���u���LDR�������:o���fcn��N2�&a��elB%�������
~��{���Df�{S������hn����-O��c��L
��3����}!"�d�����s��v@����%�w�\>��{��.��Q�u��u��y	�mm�mm������_����F�oV����B��:�G�_�4k* UQ�q�
����#i,�V6F{�l����M�69�n<��mo������v7G��JX��YbW�&,�Iv'�@��
��r;��T�fJ�Pv��F��K$1P���D�.p0t�7i[�B1/``t6Q$
��r����K��?Wxaz��d��*��Lf�x=�7������H��8���nK��t���q+c�@G�r��������\[Tf��)�g=�=n��E�t�n!3�ri���C]w��M�+f0-9����X�.������q�44%5�U5���B��0�!���:������T1����������6��|���LK���N��S���Ui���a<
bhU�O������E8�H��a�c�����4������NG�5qt��k��x�]D;f> �G�h-����=�L3 ����u���Z�����\M�6�T���?���d@h�cPhFc5�<4x��$z ���K�c'"
�����(����!���I!W'���X��`��O���+@��S,���Y�J�<1ig���S�")zR�P%J��<A�����Z�<g5���3����z�� s\��0/�"�R��AC��a���`jR
���(Q4�H��q
"���\��$?���j�����1ld8�.����
����LR2�"aqe�����%�����p��(��Q�S
]��%Br��6�f���Zkg)�x�����z��l7�v����l'�#��O�#'3G�f&�����X�`��q�Xp0����5c�9K��t���~�MIF�� s�J�g�;q������h	FZ��d&�l���L�`�$W}Yg��E���%"�N���*�`t�������m��\�������������ragYG�7�t;'�M��[sl)'`8d�i�Sf�L��������ony���(����v(�$(�m�����6(���:ky�|@�x�KA�X,���z&
![\�	�Z|�(J<7��Y�{�e����
����l�G�H�>F*d'R�%�h�"�����������\�Lu�1�Se7B��`TArn�`<���LU2��z=��um��4q����FP��[5kx�~����^�m�[uu��E'��
d��#��d�qV� ������VI������W���
IEg�B��*��������������������A������TQ����d�Cf���JK^	J�u�\r6��\^72Ql�A���0�
�������<�o���+:$������>$��+b-�U����azf�y�Ud�_�Qn]�Vt�l����B�xy���bY���z����4j��I�cb��T�fY�,������B�o��|Z6�|�_��e�h��_��Q��)!%a��Ii����XI+:<Wo��u~1_��}���w
7r��q�d��������7�%F�FaFj	����,��1�g��/Tj�2L��T
Yg���������T���h�L.�C������D�|Y`[�?��sp0)�ES���0]��,N���@�����X�kV1.���_�R�,?��Fv
���y�S�B	����F����4Q��g����7�vn#��}�������r�9m���2��`]����~.�*c���#[�`#�PdCX����� E��D�z�[lB����bF�|o���i���c������<�LW����K1�����f�D��J��Q.&�g�*�t�K��`��Rb�L}���)��
H�%�����
(���~W�5��Y	��8���Q1�����Kt��8�aG����9�����,��0Q�*�g*��F����O%[�88&����(�0/�"@M�$�b+�9_��[���k�������9l�"1�ha!b*�Wahp
�=Vy��N^R�F#(�*���-�������N�FQN����O
e:[��gT(��1�$�'t�y^3+�t7������%>������~g��x��������K5�z��Ab�1b5�,-�K�����>��^^$h�NS�1��h&�MX��v2N�������_�a/>��p����	��F�9o,v��t�R9VID�����+�Ov�a�O��V���8�����Yb��b$��a�D���<r���P �gD-�`��%"�
r�p�a��������h�2N2S"U�����5_��	�9�w��x����pB�&J/������)�>����`���RC�$y�04>�e��)#;��U�D	q�EF�-z�p�J=
O��?�
F����R%���@G�6�`b��5c�
�O���MR��T�S>��I��z��#a���I[������pp"l�C�C���3��z|]�T2���=�H�|�MU��pU��r��,-�
�,�LE�Vn_��Z9v���Q6z�x3�=-�����#����e��K�(�>�r����p-I����c�++r�J���������q�^&��\YQ��x�X��U��	����d�WQ|}�|������g�_U�>'��<.���7]�q���`�I|��'��+e����j�D����L��Q�������s1��eQ4N%!��}[��\nH�	��v���"p���#x����=��b))��_�H%|�Cuv�L��?��?|	�P������G�/���,����6c������{����.���6��B!�~���K_�o���F�����n*���v,�������������j�����2����������\�%�Q����f���Z �k�5Xl�i(+�"&���y�/>L��!V��	������f���:�������Wh,1g+�%�.VGm���k���H����xh�b�}�6.~
�kp�zm�=�h�k�O����6)Ew������6�6k���*P��3�E;	YC���z��5@�C� ���&<��p(U*�ggY4~�����~��~t�c�f
��?��(�$`���fs��������h������^��lo��s��4m6`�6MM]��0H��K|�TJJi��gx��V��l��f����f�	��������}J����	)���L�Vk���>�T�,�	����h(I���W�P��Q�t��b>��b��7�U�0���R��=���Ojp����Z�X�M����c
�w*��p�������z���n0{�A0M:[�����a��&h#j�H��|@�hO%�������H���T��+,{�E��w�&.=���&1�i��	4�]�N�oc��
����D��%f{}#O$����H�����C��
�UO��*WV�U��u��wU"H�6�����->�i�fD�c����}|��z`�V��*Wz^�3/�@�
�!	�=��%[����[p|n���yws����$���Uo6������C��gS��H�:���K�T��/����"]nm�7k[��W�����KH�H���<!�0s��H,vMT��B������XeQY�����O�G��%�Xe#����|k�L`�4�!����������u���.N5�����7Y���s�������Ua!�Y��y�������!f|���������I�~F��Bu�~mFL�j���M���eU�An�
Uj���s����LZ����rUs����j �t,�'��(6
�������^XKDCr"�;�<��m������^���<�P4E�^������r&���	��zS�������q1x����M�8}�E�S���D|x��j������UP����k��Jo���8vo*�����?���%��cg��e����������u�����R�W��dj��7����%���s�����`�<�o�'�.,72������<���������i\b�dV ����tTV �&�$#Tb3��b^��5������U�ML�)�T^������?����������Z�_l�����c!��tB/��>aC��
2�t�r(�c���D����y%����p���v��#}�@�<!O�������Z|?yP�L���r;��zms��x{Q������7�`�i;�^�T]�%v�>�<$���1�j�g�_�D���*<�|����|P�[�!�b�62�1������%�|?	�KC�d2�0���s�{Y�p`�i����SC�@�o_�������hm6k[���k���%�G��<���V<��B�~~�
��|�LY5W�}�@��4��0	T�|�RAf~�T�]�-d�7���s~�L)�����u���[
`���}������c���O�o��1��Np��M��b���k��f(*�e����`_���5��!�
rZ��������{����a=h�hm�KX��>�������U���s�]c��D���3�������7�k[����1�Tyg��,�01��;+S3�2}&l�kV���f+�R
�{����=(b�	������w��������	����� h�����������$� |�����9��a������0�������s=�������[2%���:>�`7�!p�i��Y/Td����0d�d��)�*t�"���	h���~���e���o����Q�h�z��-�,8��]J%0�;�&�I����W�������$;x�y{t��h���	C���Y��^
�<�t�^�A���I�;��~�"�i�s	��{j5x7d!I�S����C�#������8)���c�s>�*�(�����XLc]�y�����-1GC� ����Zk�F�R������1`s��G��M�����)U�+Y?��r-�k�z�uga������,F��[��{�y�G�]�v���n��Rz��^�?H����qY�����|9��C0L��4�'�����h��R�d|5�4!���88��1��j����f!O�^4��*���`*w�,S�^Ca�X8/�I�<�g+�0h^Q���7G	�F>�Ge��9��[kd�g�;
,���[�d2��� I�����cT���b���0�`���8�!�v<8m!�!a�Ei����g��A��aeI������� ���j���tk�VWV�����rpm;k�U�M�S,QB�

6��5���-Q��� ���2���wTw�	��P}�fP
��c!`M$Mb7$�(|n�P�$�W���sJ����b�09������U�p�2�W*�����q��B�@��x�����~��k����1�2$��_����5���
�[@5D��$X�/{Vk�v�_��������<rc�y�P�h#�4g-�AY�=c�S"�d�������������������^�
��Y�M~��7���F����o<R��F}��h��j�Gd\�y8�����<c��=g&QT������
�w��.��T��D���+F!@�����>NCm`�Z��,w�Y�m4[
�f�����=[
���\�����	#�x	��!w�rQ�J:�t� @7&���a�C�q�u���M�/T]4���P�#7�[����TX"K
�K����}"���Vm����zR�&o�����y)p�Q���Z�M�����~�XWk�r�O��]'�x�
`�Wo{�M�ygfs�v�i�����D�.S�,�QK���JR�_�i[��;�;�e*�-�a�w���._�e�Zm@��hJ��ctU����(9���e�����LH�`
�mA����K;o�%�}TsC`?��f��ay���fR@19[��
D[��?a��KPG�2��W��G���_��+���|\�����^Fa�v+�)'��2�q��$��f?�@�������e"�R"��PV	��|X���B�[��]q�S
/D4\��R|������9e`a��"N�P���`�|��Q
���`D�5�
��Z�z$�r
��MR���y�va����S����s6��n���sW�GTV�i:�����@��Kx�b�@ca0YT�i���c(K���I@R4�j���&�`����S���<.������fT��T��$N<��a���z�:�+b����G{�))�`�����IF�<�.m�b��W���K����2N�Mq
�4���km�,0>U�
u���i��
�l4�*,}�f@Fi�&l�����E$��!lU
�?��g�fV�X.}{e�Z�
�l9�D�m������^��Uk~��k1����/K�T[I�)��<B��w���j?��;\�ekAVB���`����x�"����T9���2���I���Sp,��q��j�\r!��>=L������v(����M���������1hP��Y�OK���DM��MT�2�6K�d������0G~���(��9�T��=�j��e�:z�� �j������*Tqq<�Q%v?�Xh������Y���B����!R9B�vX3���5
�$:�V}�{E�Xz�]y���!V��9!����|���
Fi�V��L��kSb:��DxR0.|@�1�q��W����fK��x��Vs��r��	�P�h����@5�����n4s{@)A�%���>�l�\G}@+����]���*���5F�E��Un����x�'Y*���r��%c�b�S4a�	��1��Bh�B�Ueb��X�Io�Mx��Ok�H�32k�`�(��d����=��r�f��m�92s�Uj�NN�pE<Cm&���b����(I��A�,z\�d
.{����k��=���&;�9x�Y3�0ZgFT2�����������w��*J���^��p4���Y��JX�7�4�~CfNA&SK�����e���&�Q�	��k�\�����M��ZC5r4MI�?����v>Q�2�5�
��,x��R���:��������ON�$b��!�fRK�7V����j�+~f���
]��[:�'��
Uc(	c�O�I���U)a�����r3!������S���Jm �� �H^#��5imHn���:�L���K@�4�j1�{���h��KAY'�>�u��n�R�8�
Mp���J".4�D�W��A�����CP�PjC�f�:b����H��KFWl��v������jc������{��n2���/���YM5k�QtN$/����V�������x�0�[{�Y��P��_���d�J�v�`��N:8OK�!�"wkg���������=�s��=�Q�@���?0�J��"��U����@������#,-��U3U���@9��e(���9BJ�C�������/U������U��+.���CE	�lXR��O��y
�;�}v��Du����X���Br��a�~!��<y���&Hy���bf������)�����I�Q���Q����}�@�{��{�1������������k����lh���X�k�`+���wA���Rf�&XNB��!����%=5��5�3�|����P�>�������r�^��$�
�,���/��GN��[�8gZ����������K��20x�����5_AU�������gy��UO;��_?�?���[JE��tCF�8I�C��L����V�*��8(th�h��b
E�o|��7`}�g>�+'	/�=�%{�|L���)�;#X�G�@���$��Z}S�xw��W�[]�T��N���~��N�A��"u�'�J�n6Gz����Q<9�cb�	���oxL�7���|#������qG���stZ�����k��x����c�����-��kc�E�,+������NT�H�	������X��o;\�D��RE>���X95@8?
�YjL�|�Zp�)eZ��Y���0�M�D�K�LY���\�D�D�����-����#a�����fDE.3�z�"��:&P�����|j|&\�z�M������
�+���������
��9�M=!Z���q��p�^Q�(���k@]F�q�'!*DtvG���c��O��
r��}���&X�+�R,j�I����d�����3.O��I���"R�S�da<
�y���8���������qDE8x�R)l��gd��g�-z��F -�x�R���|�j�%?���'��d��I��}�{�3l�z���N��r��Q��^$K1�
���#b����C#R�[npzwc������G���V*M���4���,MMmyr�4-���sb��pB���A����L������|���'�F�L���Y�������i�0���1>CG,
��*(V>�H����2����:��U"����!���a�����-���������������=pbR#��ws����o��b�:�Q=����Yn\�5��T����,|6��1a�l�����p_�]��\�1Mg
�g�����zc��8����RgZ�v��S~������Q��DW(�#�-��21s9CX�^�}d+4[���C�p�4'�h����Y��IS��Z��.�a�\���gVur���m<���9x�XA5�}�F�K�}�qo�o�������d4�}���������Q�������H
P�����x�,���P����
��O�A���E�Yc�3�DZ�i�w
}�^�l����-'��&�G��T�`RV?z����^	{uF��$����:E?Q�F��.�3�JWP���V�H��g�����G+���mu)�A��V,�(�i|�QK�����oS�dq5�FT%k��(|E$y��dj�� ������K�����zU��d%����f��
��.�2,78�G�*Eqa9/6����R$�0��W���)����f�r"�w��1�:���	F	��������+A���jF���J��U�����<�b����Y\�=���{AQ��{�#�������G���pg�
>p��_9y������g���W�o^�������1�_��P�Z�C��O
��u3��I�a��n��6�N����?{��������y!���v����}ho����Z��`�o�z���������v-r�Z�_$����S{:�w3�C����"9�}����
�,������F��;������5v85������R�l��|��#�����0�Ub��jcj�V1=���d������/���e���oJ��Ed'6�q'��E��S�
�z����p���.{�+�N�!�y����w��XI$M����bc�Ev�I'#p�k}m)O��t��xx���>��M�8y�7J�����*D���O
������B��>_tg��\7S���?�
���j`���U1��"u7K��M������a�&���@���?�a�sO{;�=���n	�����NSg��������F�����c�i4Z��f
�������)��^�F#�/1`UP��@
���^|�����|�3
T����g��y.A�����X�V��#��jh5��C��[���p��@��@���x�G��[4>#�e�xaB�����p����������F�_���`A��y�>(���W�g���e]GB0���G%�Gb=og�#t2��ckH��G��K�j�J�E��C�H�����:�����n"l�Z�K�������W�@����)F ��/+k~+����jgyQ�v�U���b���)�V�� wh�~wb�<__D�,�wS	�%Do����$��r�S�(L��
��4�:_&�.4sK���|�- ��^�R�	'/�sJ������������I��D���yL�.�D���6u��e/�����=
����Pg�A/����GU �q�9G@������i,������x'e�c��2�a	����`��We-�C���_C���^��N�e
j�i�.KW�I��cj`���g4�����r�;�s7)���@YO�nK._YD._	�Kd��+S��(�[�.���y��bO�����+q��[M��7�-}/�23����kR\X���u���?���Kp�(z;��wl����F-x�/"�:B�!M~?!�9�������Dz�n��	����5_]m�0� �����'�e��n��������g�Ksh�e
�}QM� UU� Xz�vw*�������5Z��u�L2�!eC�bL��~s�W�m������=���cF���	a�W�,�}�.�j����M�9+��nRc�������j�
lE��nl�k����u[s���f�<U��v���7�;�7�����_���������f��6��������������=x������Et_C�	Q<V��1�r�m�L\*�m�E�C{W&�M��	9���=����I�����f�����!���i�&:��G�h�0;��2��R1�c��i���J�����p7`v��K���@��}���r����rD�[�B&���$�O4�*=�w_Sw��eQ_�����h���Zs�����4�����c�O��d�����+�.-�����{g�0�.vT2��/&��k���B�J��v*�����B'��8�o���X�.��j�����x��W�_�u�C}(���9OJZmX����b��8N�������9{EpJ��������>��P6NFA:��u�/#N[Q��@d_���VmC��^��V��&�X��(z?�QO�o(��"�X��_xV��8I�=��!�m,�E!c`�2�����8��5i�0%g��Um4z����<A1q.��Z�Z�bb�%�YS=p�f��
�,���>�m���LsM��H|�9��>������I�*w�d���}�R'-��#�->Rv���@xvUq����6��h���RT}���P�_���{��Kg�M����|t���;����[�E[��Ld����q�@�=x���3���Hj'��"x�D������f#��� �M*=!O��~6���V!yQ��U2�+�ISdx�,���h���paP���5�`|��~������j-������v��"sK�������l(��r���	���d�,��M�����4��gL�%g������;�
��9�����0\X�F��M�Z�����.oA��-p��/��k�c�h{[����w�Ta��'��(��_s�yi�����'/�im�9��l���t��l���:3�0S'���]�5��1v�.�+N�;��O��zQ��������J��B�sJ��"5�����+?J�B��\�������P���5�?�}	p���Uk���~�pbaLPc�I28��:���1�F���V�q�#�������e���$8��������z�j/�����Vm�����*���tB�z1��#����8�*/�����Q�E�o�C���Wk�6E��u���f�Qk6�F�Y����Q&��?�����}�a�L��|��)D��C�lf��E���W���mn�-9�NF>m�oi�k���3����f�xr��a!`�����Aw*UpL�g��7�G![���c�����|��f���Fi2J1�V�S�*p��6��Cm.�>Z�C�����p��+�#=6�M�V���
=AS}�>���4B�����l�=��S��m�%�a�a~���8S���q��%�9�V����������^� �(3�_�Z>�I�Y����[ag5��[H��U��@G�0��T[-�@�����y���2���E��������v
�]�����^��fV���^W�X�*���ejx��5�>���0���y��J��!�(�{�qtX3�������&��K�1�����k1q��n���Yg���a�[��V�]�a��k�{�q���Ia
i>t�A+zi�(���L/��5%c��<|+� \/~�a�!E=a���@j_)���y[���X����;V�1[�6F���R��[��m��G�|�Cc�\��� B�[������M_c�!��$G�Wf��K�����9sT�-{���v�yN��4���$�)d���c7����'��bEVc?:U����%H��g�[����d�d�_F���dP�0Gt��wS����1�����RzL���1�Tg*o���K����K@����3G�>��4]�������8��v��S��KZ�R,���0-����y��K���b��zB�KiD"��D1U����������R�\U��?T��lz)�{r_1�=e�~��+F26����k,���r�A�;�_0�N��)�b����|H�?�4
�O�ipw�(c0�b�f��7�ME�J��_hCb91�����(���l�������;���@x}N�����
�=2�����G_h��x�Y�����Z�p-s$fu����?|�m��7q���� %�kb�~��b��C�m�2��Z��}�����H��dD�?�,P��i����B�:f����k��q���]�X�����j���������X3e4(�!���W�/kR�c�oa�7�*	 %n��������k����v��R����<{bQQ���c��u0��p�VG}D[�N�����/
Q��'g��8�U'q�-�[����	���f[��;�pbPf����{7�,�2�\�*���TG�">�aU������Rr�Yk�
}���g��q�
D�o�:M���<�$�S�F����KY"����WU�CAP��}�&\�,����e��Zs*(I���������fr�WT!:�V�����G��f)i�7Q8�p,����P�N�z
���U�%�]�n��,|�/�t��A��P	Z��k����M���l`B?35���w�O���r�m�������������I��xa���;.���t=����g��Y`��*U��n�����_ ^X`Z��������������<+��tGs���v����hN�(��k0���q����E��S-'y��d����wtp�9��;98|S]^��]:@����0IY$��&���T��EY7�O6&� p�0�ak�]k��6�|��i����������6�o�������fm��&Zx��X>���\��ujnz�$
!*&E������A�!��[C?���l��xx�����X�gO'��	�����~��?�a���e�I�*�J�'�G<��_���9�U���`]*�Y��'�T��z��cP^���*�K{G��'�t'�������$�O�n-��O�x�9�6��L���-"
��c������)��v�E6��e_�������c�}<��Q^�P���$��(�Y��f���B|==��]1�k<X�!<��f�����*_k����#�����f��(����\+�_`�,*��*�w-�f�e0cH����C�aTm�(J/���$p{��5b����� �5��!���r�P���B���8�m��3���
�]^�@��D���!�(��"��{ ol�Z��������/�6��l3gh+������J�re�n�Fw���
1���v���$���.������W��rZ�B��.��t��^W�h��l�	o!-{�|���}�A�U�a�����5 ���^������'7��m�����;D�h �<�S*�BW���U���+d�W�)��}��$��@N�4��3�O����w��|�U���<-N������vA�i~�Y]:da"���6p�������s�h(M��P����?J!���>�����5L~���]y�6�~�G�W��k|�������J}M;I)v��O�Yk7MD�Bc��������_1�����w�M:�HV���N������y�4��9c�3L���V~�x�p��G�x
��%�m�v}H�q;�����Q4�������4��x�e����~�k������p�I����hSSN�+OTXv��(���X�V]�)2!F4q=mf�T����g7&5R���J�^�����p���{�����}��?Q��]]����WW��:V�����u��F�X�
v����]P�@b��-,�c���$�b�c%��NRXITS\�<7~`C�[�m�a7���������(�~�=�����6�:P�Y4�bC<�����G��pH���T&��� �c��6�?rP,�k��//�G���GG�G07�pH�&�|�~gAoBqSi����q�S���^l3�������h�������,������)K��<��1l�LZ�H-���q��.`�����d�\��v�Z�L
��Y����I?���O��\>���R�+�z>��X��Y1���QN����;���U����%m�d�1,?��]��6�]]�C,�����9s���4&@<QF��Px��`��v��\S$/l)fnm]n-��z�-Y��L>��83���T�(��~�������$D�]��*�������
.L�Use~kA4������B��P�$& #H�j���'\�n2� ����Z�K�0�EAa��nh4>��XT�=�:�M�{	����}�����,��H[����&�+mm��=�J'��~��T�Iu���V��A�QR�&I�w��7����������^i��{�#�y8]�7������8y����Z��2��[Vt<@���M���".+MO	{PM	�(�$X�5���?���	�r�F��45a���wa�O�3�����x${n�/�('�(`�T��� {7�E�[3������������2bO���Y9��e�>��V��>>x�f�U�}���N�����}�f���#3�e����2���gvQAS=�~=���(n&�dL���#PLp�>1�E�<F(3�8C,�k2�4��2Kt���Hr��(Mc��>���=:|y��������	
�����/�7xc��W�Q(�v��h��h0�BM��!'����,����0l���}�}���^�;���va���?G�	!���V/�^)�{�:P�#����X�=�H�N��u8<`p�	����������l%��=Xf�d\,3�gXb,� ����� )K����	������V�����7o^�$�o.�H�R#"�1��~�n[�VPA<�)[���;QWE}�H��k��Tk������I&�#�G�j��+g���UTr�/���+�(�J�g}
,M�n����z8�%v����Y��(?���#������7��P{]S(�,i���Y��%O��>[\Y��0���,���V�`]�����H�%�����_��<W��rHL������0���!�����,
hc���N�mn���m�u��s#���5=EC�����[����G�6
���<�Q|]�<#�`�w�RR,�4�s�8�k�T0�z����E
�h��b��������eF�U���u��
x�Oacr���idq�����>!.0�~���j���	Kc�g]�������WBB8V<U�e��h��
[Sn�[�J�=#�\�E`��������^���7�P��N��<�L���E�X|j�4:���p�$�-@G�$ME1Y21>}nr*����LNv����eT��Z{����w���7/X���$2��\�WiG�a2ra �3�e�f���_�`���t�Dl�����N��F�v���
3��J��#2��:I~4n��V��2L"���33w��E��s�[�96����k�Q�l���L�����e8}��s;���
:D���9�0[p���C�`+����`�Q^��������vk�n����v��7���G��������P����$�x����F�hs�
�+s��H���(�shl�j�Mwl�sN��I����b������S�z*^�����t%��C<���{�����
���[5	82�s���a���M`
V�����VW�!?i��v���l
�h_�r��R������[����[�-W��T@�
u���W�#.�'N�O�PD.3����r2�W��~�4�n�r�>���1��>�8���^�����B��Dc���XK�w%0��A��ZXc{�>���c%>*�5~���Px
i\?>�Dt����S<��cD��g�����k�,[a��^��f�4���F�I�t���[��Y��Y�/��0�&�/���E8��V3���}�����
`�0��:���Aqm��q%�~������>�6���D\o�%���T����6�]k ��E]��G�R�@K:�0�U�
�B�^�scm<�7�����!�)i���d��j]CIx���m�5�a�<�,�<
O�4	�����M��G����O�3,��1-�)%i���v ��p��m��>j���T��)Q��F��������8g�z�ig��EOE�����e�m���"�r��������z�^_7��_�k��#�M2����**�h�d	�x5V2$B������7�����((���V�qm���*�����uu^�y�>U1E|�2��$� ���)T�3�Q9����y#3	Lm��qF_C�F����_`�(Dh��	���^k�����@xo0��	������Q�bn�1>������e����-e����Y��a� �[��������Wg�+3�ZkH*��pP78xs��q3���"� ��|?!0K�!|��
Gh�����;St�������CY�:��4� bAy���g�0��9||�����n��8\z�d:1��I��[+��	���f%�|2����P%�W=��h<��[L����b�[����\oo����������9�=�f�d����S�L�z����B�����f9�Q�>ex���j���q����&q�'gS8�t��M��IR$�
Z��z1923����Z��6���1F�L�Vi�!�F��Po{�g���������m\��d-B!���)��0N��?����j�{T�@	O,0�W�:��]���w��[��Y`��%u.�fN�2s�{F�����T���C7���~vp�z��gn�^�����n�������ul����03`�+�a�l}f@���nU*�B]U���X��tAI���B�p��#�!R���e���\�}WJ�DY(�6���y�X@T���v�D���@�~&i�B�		c�^��1I�C����U�k��c���0=��������v"!0�#�H3��)�1���
%`\�$����	*�"od����k�������7Ty���Q9�������u�!�s�W[����>H���L���U Y'C��+���H�	Z8�5��(Fx��^�����6�9n<��~���I*�5����5���q�?3�02������t�qR����l'J�������)8'X�y�����S�W�+Ot�y�n��9��\d����fWb�&�W�v�9��C_1\�����<��$��weI���z�����\	(u�t�%��Z.��t�1�]��P[A�"��^y��,��;�`�s��Ku>�8���YQ���'R>c���[JA�tc}�J��{K������P��l�i��o5��i�D����;�zj��L���m@��o�4R��i�����|@�ct�w��yS(?�S_d+�O��&���rz^���.(��$���
��H�n{("�DB���q��-�/���"�g����eg�~���a���	{=L�G��q����r�$3�B�4�����bB<� _���ZG�G
,��x�2��+�;��7����� ���)����Byz���jv(^�z�w�Vvfk��3�a*O���0��C��?Jn �f��,Lc2�/,�\R
���<�;7��J����t����
�r���)��4�]�8%{��U7]f�c��b��!(E5
��"�R�6����ket(��8�&xgy����rd�;\��3�l��8v{����]�[��T���+H!��|3����+p*����z:u��#|�	t(!p�ryV�}z"�T�� ��-���x'm�a'mY�M_{B�ZYXpb�u���%��:�~�Y����)����jnB��*�Kw����6��m�����������
���5��������Gafy�y4�
oilE�[4����)=o�(sE#���K's�8�i~*�mG��W���g��,0��D�fW1|6�+�r���`�:�?��K��&L:��V�q%h.�s��l8�����W����'�����m�S�c�W���N�z���Yv�Z��~4�0�u�R�U8A����p�=z�����+��k����\�~w�����*,�,����=�w������
��[�� �(�n�<;�K'���y{����U��dC�o[2lq��RI6������j.���up���I�stE�oOq����*�x�3�M�k��a���K�g'���5:s�]�����/N��s�-y�jo�L�d�0��C ��q'>��{jj�\)���|i�k��9�bl����������Y��#*��3�F_\R�9���(�V]�u�M�������.�^������������F|F�j��c=*�_~n(�e������{'_���L�M\�M�S{�:��~�����9���dT=IF�e�=[��K���u�s��:��R�&��l��>��4���� �r�M���B��M�r(E��n�-=|��� >-$7�F��a����S��dd[���Um��F]:�=���Ede�=�bSO���q
[��mE�>�}���O���X������b"�h^�
�<O�Q?�����}��Jp8<������)b#�1�8�������v��|��q��m���6���d�D�1� �����������xW�w�[�-��'\>���pU��
bY����;]�H)k�3}�z�EH���L~~��~�����I�B�LF
����P���0�Y��e��:�n%(�(j�r��CLb���hMB-�������
3��5uf8Z����N�A#(�B�P9Jx���U07�:�W&����`�Ou�\�dbh����6w$�����%7=�g�����W��XY~�z�y��t�u���(F�����Qc*rU)n�k��^&���/��/�����������o�j����`��3;5�*����h
 �u-�*�a��}`�'��]|��
�=f'7�n3����4��4D9^#z������e�u�k��c�O~����o�J&�7�Z�7���o_t�,��;F�p�-S>��pL���w�E<c��OK +;P��O(���@����������@�(���y� ��	����X
���U�!��JsT-I}�5����p{/y���u�[[
�C8���{���xH�f�N���
�r�cr
9T
�b\�P���&���5&��`U����I����[���dHz�Q���y��-��H��*-�ijf\��3���S��l�E��.��O�yGW������ly���,���;�����b��C)�O,�~��{���������������)�bwq�|��K����~�R$,�z-B���Zm����sRx�]�����Q7��n����m�v<��>�_�'�EA����?�E�����vD@����Z)H����Z���M�*�������,���f�	���=���i�+�g|& �d��WN#���e����*|�u]���:���lLy\�&����z!��:�����1�B��2��M�)=�z�.ib0s4~,��5K��b�L�@��8�(��@�|R����r%k-�<,�(�[z�T*��U�CS�\:9�����=<>yy��?��2�?��������I������&+�n�3on23��w��-�5�8k��l��CL������h����*�� �A��J<���``���|��-�����P��%��`� (AJ(2�O<��L�@9�5�J�'N-U��9�hC��>�o���n�g��1�I���e1���������L�����gL��������
q�C-y�%L��:����T��f���&	�0�9���J_i�ln$gD����MY�\��*N�rR����5	Lt�����};4f�e5:�]��B�A���U��<����;�V
��J*���:)�mX���V���rX����5p��W����
_���|�.>����*>�$��.�@Mq���1
�^5DB�j��������i|��|�����/��Uy�Q������������p�0�E��p���t�����%'M�A0��]�f%���(��xc�:��!\�ir�4��HS�Z�:��p��w����f���3e��U�r���5��:�l���?�e��6Q��{l�����{*/d����|)����������=�S__�����u�T����<�u�r�%�bw����C6���=dF�P#p��d�BS��k����3���P���-��1�
pa
����������	�$c�o�������#-���kP�8��A c_=����;cK��M�C�!iLY/���0�c���[�Q�������d�O�I�{7�i�d�I6v��;$����H��cBJ%dn�Q�G�2�����T�VIC��<C���K+(N���eG�xr>��Qqjb�5Q��h��k��\nh,�� =��*���H�����v�~e�n��-J���2��"�iP3��C�5@���w�O�e�|��6�:Kx]c�;#������������DXJ�Y|xJ�6@]2����x ����	��c�7Iav$�[���"�x+�i��W���
�>�;�	�l�03�������@��&�+���>"+A]0��>L#�f���F�A��3*9�����H����*BZ����$�[:_jA<D��1E�J�V�@������^�a���4�w�����D�k�sH�����O��f��O�<�3���]��"���,�/���!gB�Y<�����G�+�����%UPT�p&�v��b��P�
TQd*��)
�hrYU��V�[:*=����J�!��	���P�����o0��32�P�=���*WIQ.O%EF����x���3,��gF�H�UGX#���%+��dC��������K����F��e�Z������m���3�`���|�z�@D�T�I� %�P��r�2�d���
�{�������S��q4i2�u�Np�S2�q�K�!��9�wU$��4
6���:�p.@������n�ZU��\��9�����

pd�U%�[f�� �mY�V+-�V�E���J`�r�)h�,�
���z�0�F�O�5����q����u��(��|5���5Rz
�-�[�Z����avn8��%�{6���Zc�D�������������:�F���������Ge��������W�6���}������Q{���Xv��T6'��L�FP�yWU����|�����K]��r��W����P�M�
�����}�q,P"��9	' z������RBUM�=���B���@q��Q��*������d�u�������1�)���H]VB&����#)��?�B�
�s��
��G�9�:���#5�PM�d��i<���b���`G���,����4/~�������id�����:���[�G���p�d�Ri�������;'���x��hY�c�k�U������;o��:ow_��)-D���hD����IR.����
�/0<�r������������*��#N6*��<����G�9�O[�e`+{j����F'�=�b�b����B��]�SIba���Y�(���H��\vs����hc��l?Y��jN�F��U�o���1 '��.�����;����	�7�uB�EX������zI��5ubHc�v��|	h�C�"�!��H��m�Zl�9��K1��.�=E�i���.�:s���Z����`��lc��Lo�����vr����y~��z����b��ef�+����j;\9D�>RNU�l9�!c��h��:�2X�V�C
r+VkP��+.u`*����9p�a��{M��P���g;��Bl4� F&h]�A
���B����'Pu��0"�0%t)��)�FL����1��(�V�Oc����@d`��or�h~e���w�KYV�R�z������x���z~���+@�M 6E�(?*�\m��b��W�J��l��R_yu�Z��@�����!���{oi(���R�R�W���:�hX�(k	f�����oiIl�f���<��������Kf�p��G���Mp�i�
O��r��/�����`�y>N����|��s�P=�����ok\�,�t'���H��_�l�����d�bx��8�=���Xs����1k������j�~��V�W.u��zs��h4�-���f~��d�+�����(B�
�>�:��J��S��0�
7�}��QR��������~:9��<��@I���(���JE���EC4� �t��+�FMX�(�O�/��{�X�c_Z����q?�b|)'�c�(�_=��0����X��T��{a.c�������������C�Z���N�JS'������Li�.�E����<��+�9���]������F3/��z�)�&u�� �F�V�����&�������2�J���z�1[�v ��c��6�� U���b;rB�v��	�������*�h9"�
�f��KU��H�inv+���c��(�(B(�wJ�T���4!ox�,Y"#���a����2LY����,�uLXgYH�Y?h��]KSLw%:c]0]�����4�O9���B�(Wb54R��
��4K�3���fY����j�9}�P'�M���j�0J*������V�;�`^��z#�Kyc-xH_a����4���G�zv�����IN=�q$��Fyp����O.���-���B$�V.�������%���k]Z��jl���3#Pt�Vy��SuAU��5��x�"��_Ec��}����l-�b�Q���|��_�������2�)�c��$�>q}���������u9/l/MM��h��n�!�6*��<����Z�O��������fd�G���J/TK�r�4O}N��#M[:���/��J�5!k���!���p��J^��UHA��������{���wQM��@�|`e�RE$�\q��'�
����T��qx+@Y�;�(�(gK�(�%b��%�Vi���g��0A���d�&�3��6d��l�����������KPz�@�iZ������������!��7�H<�E
���{R�T0��Z��t�f*nO�{����[Vofn�y�$�70�����0Mj�BAI�Y��?"e�2������e�Fr��l�����\�?0>?�����r�qC���Eo��g������N��zd�G2���E]G�~\����������%F���#��z}�#B�L�����T1��(��������@~c��#tYV���u���g�U�(E;9�h�d�s��r��w��,j��s���x�.�N����W�~0s���BK�"���PA����aS[���;V�>%��Q�#�\J<Ao�K#~J�7��N������M<������*A�s�L�����Py���b�:}�v��n���wb21H�����y0�$�Z#� ��5�)x"�kc�m�;!'f�����<f��������m0��"�Y�IPBm�^���Y��:������@��|�{�I�/#��c����42u���[b���M��b("'Wm�����64��]�3�����,���y�cj���M�
s��m��~��@�������}�8~�ima@X+O5��n����7��E�0�r3e���._���=9�.�����EPV�d�7�m�����<��W%Lhyn*��J�!X��W�����9)y�������@	|�
X�f���|��_p��|��B^o����=���GQtO���M/n��q�v����sOs�����0�&��.-��\O��^k����%�b\:@aON�9
�W6�yw�;2F��s�jV�������a7�H��![Q�-��d�X����~D�@-1.�0�m�������l[V8.��{26iMl�M�^t
3*"Hy[�f Q�=Cas������I�������U��`_$�~r���X+7��=���%Lcr!\v��*Y��L�@r��4�9/S�7�'&L�-�"X�7�������}!�2�����^��z��s4/1*�u��e��"�nx��v<{���[��0�z���"�)#�v1�N}8��U
Zd��dH�YZ-��c���[��Q��xZ�S�[-=�+�O��Zy~�7?����6R`�Y� �w��r"W=f�W�G����wr���I
�Yy��������w(Q�$BH�q���P��R�v�����^�����y�����rZ%�y�w�*��;Ga�%����Yn6f-�.�C�.��H��\&C���k#"������`~EcgH���[<�-kI9���:�_)O;&�D�(�-]2�	�����F���2^A�y���1J$�����z��������m�q1�s����u`��)Hg�I<�dx�&�EBp��	�����s�(<`�c���$��i��������`�:�tDb�d�������	H����9�Ss�{.H���N"'��W�����9}E�[��v��[-��D�zq*'�\��~3���Y��'�5_����9�;������w�Z����#��=z������f��mE��`�E�J���F��?`-�3
?Q�jt�r�]��*�P����I�����a����^��vi�/6���������H����G+��v���������P8���V���n6�"��������6���h�\��Y������uJ��	F�s2��fo59�*����Z�0�����h��
Xx��I��$G�"�=�M=�y�����m��i����p_�L��h���J���
V����!
�+�1WL�SQ���(��i�j����n�7k�v}�Z���w�l#��� �^|����v�z���>���g����lM��1^J����+��mh��y�U������m���G�$������k8z�:7�����}
��p,y�Z����e��`��$z<	P��NT�E6�v�|��YV�[w<o[k��5u�5���5��
8��M�:=,����<|���3��<O�x\�����h�Y=��+[ ��?�;y~���2��:�h��!BF<Pf�GJ4��������|�������6�s�DA ��K<��'�(x��S���v��
���#�6Y�#���U��i�]1���U����H�"0����^?�����U���*K�e"Vf3�v�5����N�x�/B����S�fP�������������)�����zS��&��s�6����d�Y������,�5�R�YK\�5�Pt����n��T��e�P�=|Z���v�+�/�5P"c����\-�t"�z
��|��U���Xc���	�h���o�w���'�o0�&����#'���'���z�o�F8�v�N�Z�O��������Ly�����x[x��g2�`��n���at�4��?��\�9"��0������B���:;	��������)8{F��2�*��D�S��M%���+��u��z���
���z�l$!x?N�����Fi%�'!������q)i�y\gZ���<��������MR�D�����T�g����9�i��Q4������+��W��}]�����s�
�j
��!D�Mg������-�%��y�1�&[��6HQ�����yV���e�>O��^`�&��Qkl["�>��:����v��5O
�(�T6�����V�z[�0�g����	D��1�������8 v�rZ�_]"�W?���	z8�����y���S��������%��"� �>���w�v>����98�(�eU��R���X��T#?��6��A�����b*�9z�y���O(���Q-`�kG��,.=����i����������d��M�x
t+�h�v��*���_�VN���x�a{c�F����[�"�3�X0���b<MR�8:}���]�"����Uc�Q��>��s���8�%�Y��N9BP!�i��:���ct�laL�Glon��5� 0_t�n c�����Q����fmc�^w)L��@�B���Xc2�LO�!sF����C&U<O#U`��Bb������+�)��^�������v�3��� �����������Ev`�����9��{��g��u�s�+�$����"�i^|����-���@��
��>��?���� ���<���qB��e���F���{/������q��l���e���x��n����UW*�xr�\f���B���x|����)Unw�z
�_"(k�O���YtL@�X�_��`�H�?�Q(�H~��b�	N��H��h�f�ip�c*�������&6�&�n�&x�/�Y���?hO�l�t��X���_�[���V��s�����/��+/��o���		��>w�X�c-i�ht�������"nV2����OP-?fT8v�&�(U�e(f?�.��j�4=4&�^�2YG�����y����B�b�`W"q�pH�_���~�<�C����%u�,�1��G�'�77`��������%-�����~��V������V��M?j�C�������s������4�j����Dom�Z"+Z@�JB5r����_383��C��A2eE����C�A�5���Ub��j���L��������"b ����9H������
�>���z�q��W�������%�-�-���%!DZ.�p���� �o��X5�>�a��W�u4HRn��XU�Cmf$E��}�&���/fO�9�rQ���Q�EG%��?k�UP��U����6l�����{�m;W=@�����Y�:	��
�$�47�c+���!o�1�2X����+��!���K����O�
��+V ��0���YI� DJ����G���jB�-�-8$�����#���m��O�����"�B/�b�l������6�{��}�7��S�8���wzW�<�+����.Q�Nf�<�k�N��*�~�����2��w~��� �OQ
��&#}9��C\���T5�d�^�R
:C�4����!�)�`S��������o�
�K
jj�3�H�+�m�j�u�o��;�#�^V}g$��a��Ga7Zv���-���B1������6Gs`���V��<�0��1�L�(�U��_h7�)Z
��ZF�*��f�1{�7��N����7��H�A!A�%RDd	�,�dI*ry��=<��%��0e:��KX�v2�1���*f���J�X��
��3�o����f��sZtF����
����< j;Y��[&��� j!�y���z��n4�t��4v�R����/m5������V9F�5t����06��A?�����%��Y��(WBbkF�U����0���s-�ER9@���{���g2����#�����)w9/�([�X�gc�ph�4:������fU,����e�/R����%�./"����AX���]D ���b��;�W3����a�w��6�����~6����{*-����<Db{�C�$�cH�������cH;���������X�V�r���2��/b��9�_�Um6���6J4�r#��H��Z�v���OV������H	z0B��L*�h�S1�[[��V���������of�}����n����3:���Q��^���%1�C�ta����I�C������x8<����GJ�g:��V),-U��]y4����3���B�]�\��7T���]�}�U��[I�� ��gi��r*av|��C&C>�����Z�c����Lx4���
H��?�������0Q������<YA�G�TT�hR�\�3	�
���������b0���MK���y�&��{5�+�U�������H�����o���o}������+�2n���"Z�^k;�P��s��Y���h��|�1b���EX��������f���nk66pv7���1u.f��?\c%w��D����(�q?qn2Z+%���6e[�m��Y"��lF/S4���N?B��V��J'}���3����	�"���'��_%���^��?mG�Zc��K�������u8h�*��:/�����7�R�XPB����{�[6�M�������_N����kMH��Q%��J����$���7L)u�����)����������G��2�S�����9}����+ ��B��;%=�J{K��������_>��b�����Z��&I�wz�{�t���g�'�G3��h��xo�0�2��e]�E���d8ToK��`���2���`����P�Vx�c�����r��F����F�I�������C�
VE�#��XaS@&a
Gi�	��<�X�b8�����O�)���++�������K�N{��#-1�rY��
T�r �<����s��X��6	��[|��/���+�Y��^tfF���	�G�
i������k��W�Q�
J ���ZC���cvD^�i�CH���P�L�C�iBNV��([�V�{<�L��y��f��?��3���3A��t��/"P�j��������bj\���y����7��;�wOv���/�$�c�f�U}[S&���&�G���f����"4�-x���8W�lt�����ymT���"��x8�,��h���j�E$+��]��Z�3G�YU�g4���/o�I�������9(�0[0�����ZKiM�4i:���w��.�.�8C��N���rt�;E��)�t�Q4��~���tj�Qr��C��O��������]�Su^�MA������f��=k�Z����l���IrQk�YkY��������R�A��V�%Uw(&��x@��]�Ej�v�On��[l�n��EgT��y�-�\�
3���,�k��Z����fm�n��[���0�~Yv}�u	oEi��������l���|�8B���8��N�w�����4i���n`)��|��Y��f����p�ki���&��Y�����U���4��A��Sqi��o5������M&|km����=~�T��i7����8v���Y��Sg����S�d��h��;b��|E��d
�E}K���2e���~�V��; ��8��p�*@��������3W�o�
��r�����f�����;�������������J�'��q����L/BE�U)�
)�J��������D0XvL��A
�B#�u��sA�b�=��)��*�cmjnE=�C����yF�D<'u^������a��.w����0�U��d�(`mg13P���o}M-��-+��^N����������Z	��8�q}�R������~�y���V�/n�1J��2{��p��l���6�4�g��<:��-K�N��x�kA��5o1?�c����Z��������]�'��$����g9�\	n���llO9��i�L*���^�7�b��cO3S�O�R�G�4�&�h���������*?�wu�}�U����Z0+��N���^�)g'���,����_j��D�p�h�(9�Py���K�������(��w��C�N�}�+���?
^r��~�8�.	,�k��/�gQ��%�
�u;MRc�smv��3����7�c����q9�}""V<.|wt}k���q�
[�b! ��"���RT.��H�g�:4 B4j�S�	W���&|DK�QW��a0�d�Q���/?*����W�3	4�''�Pc=����#��E����1Iu;�-K����P:l��j:�������/x����{z�]�d\C'p��
���7p�4����������vV�T���YRy"sSU.������U�vl��ad���ZW4�b�TQ����Wy������CF��a�i��1��b���Vy�Y��m4��vO$��5�*
<���Y�p�����<N	�g&�-��dk9�����,+7g���U.,��=���~�E@��uD(ol�l�~�~���7
�^�.e#�&b1��K?�#���v���Z�o�����q���#�js���-<��5N}��!K�8
��-�>�D�>Q.���Q�I&��d������N �Vo;D���q�"�b����>�EK���aC��8�'�{��k
�c~'j�M<��8�r:�g�dT�X�p����	D�.�����;��������=�,���H����H*����������
D�.V��"<~AP�>�K�!Ws��8A#��p�1@��$�e6J'�����y����s,I,9�]�����x��W�a��ad�}���-���%����2�A��\���i2Z%A�6j[6��}��Zi����3�(�j�6T�)���@�%�'C��b��9X��X��s����I*�E��*\�o�3����Ib@����{yc=�������G�[�&�������O�.�����I���U
H�[�;t\�~����_RB�G�s"P��e����p�r$��+J���5����+��Q�/L*Z����O��-e�&�����jBd�`���c��{<o I>��Y]�Q]FI?�^�
����~F�D���Z���Z�	�Qo��P�W�k�Y_I`z�Hv��50�+j�o�}����m��2~Qd&��a�����"����P>{3�|n
�����;A;<^���������G����8-�����v
Y����ta��N������m�76	�q��i���3������fC3�y������������t�5��4��������:O�/C*���w7nUO���
����!��0:��o�v��7-&�y7����%{���
������o"|H@
&� c/���I���AjQ<�A�I��Dt�������������
�O�z��G`�#+v���EF�����Qc�b�]��(����E}���:�`����K��J_l2�����b-�B�!�z�M9N���kj�������#tR�l�_�Jzd���S���S:�@y���,C|z$@����f��i����H3�_)9~��V!@���0K�0�+�im�i������^���R2����������Q����\��EA���%��
�V�tD.����������G�v�(ph��;���������x�6�T�R�#zC�t���x��}�8�@��;�G�� [�//���<�V����@����-���jE�&���h�|����$r���j���
R2�J��Z�H���q}�M(�2M���t�"�r�����qf�T�I��b��*[P���r�Wc�	�1C�nP��i�
EtO��3T�~F|%�-l�"��@�'�^�@�3
�mQ�N�4���4�|qc�������0G@�U����
�1R��D�s��[�}Ga_��}l���]k�[��M���Zks���`\W<�hz���A{�W8l������2��r���O��M��9")yWk�9Q���Z�w	dY���x��c�p�`r�TS�����B7�s/�&��-���z%�"�m�T�Wc*	��N�^�����-�$��Y�����D��b	,�0���dxgvf�9�����V{{�����1�~�bM��4|+�LU���4M���������b����	��+�x�8Il�;��{�9$HF.���\��h�w�3�������v����_a�g!F�Z�@{E�;�(�SEr�����[�Z���/IY����+��G�.o�b"nM��),�-Ol��'���}��c �����3Q������6�j�e.<�2��(!�����+�kz!�X[CQj4����`��YkoXuen{���]�eQ����5w�T��X����JV]���+o�UX|����;!3h�#2��as��J�;]rj�����������������u8�}9Hs	|Dy5o��J^��w���v��#'�0���*2����&w�T0VM�]�p\{s��������L������E��_��K��K�q��Bz���q�FI���������<e�^k���<�!����W��{�_*+�0cc�`�J=��VM�e��;%����"��h�xI�N7������"P�%�����������H3JUK��pX���� �����{��1����,��h\�	��)������no��-u�IE��ke���WN�>c�W�=M�L����zIp4(i'8w����W2k��,�����R����Q����M(��!h
��W��Ov����m�e6��y�f
�~����v���C���oW����s>��0�����_%Qgz��S��,�O�J�u�1�����a�N��>Q���.��5S��i����4�>��� ���gl���~��D�h�6k��
�Xam�y;���dn��cY����%L�8�� �aGI����.`L�}#���&`��(�r�0�Q�&�x���L1,K�p�[9���m���F�?�v�Q��$��o�������Ur�1mQz����`�_;��n����M&�����V�WN����P��^���T(xX��E}W9�����t�_`�UO�`�S���}��N����J��L�������L7@3il�oF���8V�EV�Q
��)
���$�K�.K���;f�a)�;=�I�T�r.��R�[����b�u������cF������u�����Qx��ov�=Y�:�J�~���f��2_h?O�i!���gE�c����t��$��z<_�R|��,��m�zbCT����_������)��ckE�'y��|��
"��	8�r����6<�.�&'������V��'Sq������������p2����_n!��z�%��`��\hm7k�u�����S[�'�kT@C�����������q
�qw4NM:�\��r��	S�g�|�*b��2:��-�
4�W�o�#��C��'��>�<���*Fb���cC���;��w9&����0��p}S���ET����gjT��"+� ��a�4���`�������+�?�y��q4u���A�uw��5���q�N�:{��b,;�+
��	E�`���F���� ]�d1A�3��f��O�$������h����[s���T�'�����RU0|y#|��Re��k�g���5^3�E����	�B�@�b�{�T����o���j��
������x
�x�O�����j��\��a/�f_�>����L���\���������Y?<��h�^?�����;y�Y*�/�|�[����������(x���������v�������|�w���������s�L�g7�V���G}qY�D���>�I�S���Be���`����8���d�4��d���y�����������s��:�u8h%R�+�B#��	�R�&>d8��`�(��d�����H��)d�qp�%iTC�!���O��������q����5����UXd��"�@)
%��U�t�5G<Wf�i7�!<;�bna�E��O���Hc����Z�Dh���s��l�H����V$������������;0_<[O������^�;>�����II�rSI���u1��.EgC�iCd�2�7�N��aZ�PA|����%�5��X��B���h��.��i�@j��\bP�N�F�5�s���Me�(�?H�a��
����x�������*d���=3�s�-r+����d�o�Y�����L�~��h����������d\��@"�#������))A��@�v���%k�$02u�C�eeI��{�cA��K& ��b�3��:Fi���� E[�RY�|Z���a���
R�
�����q7��"^�Q�,�	}�
�1�4��Z���|D�l���"(���3��b��N�Yz]\�t} i=��@��@����S�1�{0��(��U6�,����F�.J1��<�>2Q��/�5|��M���%�}c����AE���LM�$h�8~���'�|!	���}y���G�**�oB�Z�c��
�P��o+�	��|�j*�S3^�i��a����`\�������X��"��x��*}��g�)�@�$1��8Hb��������V%g9S�DO�
Z�<�&�����^���h�i������1���U���������5kW�S-n�n<���
�iX��9����.*�Yc<��i��I�5���?������1�s��;�g����:��Wt�yQIX��2�5��I$���cy��A�1z� ��z����{�xo���Q��������|IK�[ W��0�.>0L���]��p�Q�yr94�x3n�
��H;sx>����x��6t�]ZP$r8Y�u�Y�����	w�`�+�BA�ncU�\�G�dF��������u�lWG#1k[;y����n4�
eM�/z}��Y������;|������G�/������_�Q��+O������?�����c�!���\��n�����Y��Y��5��Vu�a���d2����G�g�V����/�����~`���d/P�����'�_}�x�o^���}w��@������_���p��S�b�8�-�o�����egkO��C
3����M,(���p}\���O^@)]��Q��������<��da�7s�.v=��e�f�om��I���0�Y��~�G����*����]��y�G���;@����KDRz�7ct�|��������qm5-R���-�M:_\W��1����h������.���}+����m*��b)r�(�g/bU���@Li�?J��S%�
�Bm�����������N��.o�(����Y$�J���\t ���F��
�b�Gt�(L�:��N�}�m�b��wh�o-����,Dhd���5�D)��dD�kv\]�WO�(��_^D� ��B2��k�LYQ�	�I���\Y�uW�&��� 8Nj������N��r������\���d���1z/�a/�Y��������,� ��:�`2BbR�DZ�)��Z�&��\�����c��a29�(���X��q����3��x��K<�)��}&!/�jo�6�����R1D�T���L<|��N�uf�o?:#0�lYExy+�K����=yG��I������G'���
��%*��>%�~�A"�I���PP����8!�?fW���sO���*�����ff�1���~���yPs���:E��^�on�67���MN)��#�(��ON�~���[(#xD�M�k���->(�D�ZNg��uf���$J"O��Uy�2ma�)7��?����-��Z�O����l�t,�F�V`����F�%{	�]�f	��L�Ix�!{�{8p�l7Z�mUf7���1�d�I�pI�A�#�w�yi"0R�0
�����)��u�L��4S6c�P��(B���/L4_x�>r��o�k��M�����Cj��E�ks(�Y�1�:��7�(2�AI{
���x�#?�
�B�Y)�v�S��lq8C��X���g]^��8,����	���Gz�u�C��
�E��������m��K��?���=Q����@M�Y��4��]e,vB�YFVe���&��(?fRS^-�h��Jy��O�^~��k/�w�b�T��~+;����[<����^�����p��+a��?3x��Dn�i~��K��{��8�S�ks��b�zF��J��i$��C�T���<�xq<��@�k*�MMli�bqv���?�y�q���89B�&(���l��Q����cG������8N����b����A���5�
����{�����3�>�	�i<������R(�f���y���9�d-H7���5�)0������~69;��1�z���b����4��	Gj���@y����w��L���|�Y��/����O�8���	Y-,N�l���14�v-x'�.y�|��Q�iN���I+�������A���:=\EJ�6�{�]����M����i�T�B�;p�� �9�}�F#�$���x
���H�	�X�W��g7�Q���p�'��f���
�T�]��S�r���s��
�J���~�4Ji��j�Q�G���!+P�7�f.$��gEBj������������	w�s�*+�O���Ry0�(��d{�&�4�xLh�W�1J�]�=@fP�H���1-�[8����\]We�B�	0r�,:D�}��P��$�4Y�q�m�L')i����Vq�|*�{���	��+mo/�+4uv���a�a�G�$�0��c��������++Ui�v��c?�T������������9i���{����S������]�2������!R������X_7���?�v���#*���;������O�'�ZZt��g�����a�S�S���������VHx�}�A-pV��v��<l�y��y�[��P�q�F��Q�$5��pu�3y$���RI�*q��av��V|G��LS��
I	gQ8� ��LTl-�/q[��X��l2>�Y�9\M^Cl$T.��
I(��'�FR���5|���J��o�Yoi~��NF�_��
h�  �1�dJl^U\L�J���F��Q� b����4�������G���c+����W�������7�4���&��>�������������'�L�4�io�����O���y9�����������<���yjH_oW���\�������K�IIw�o����f3��4�
��\t�.��@�V��U�X%����F��{�����nqu���Z�4�����)��Xt���!�
h�ES�9��~���h�#I1n�2�593a
X>���g���^����s��c�o���#��G����F���z��L�#�����h����k�n��O4�D-����
��2��P�2f0��@'T�Sb���&�"u�,�d�e.���,L����&1G���0R/�z�������Sa��m�r[��|�Q�����@Xd������e�����7�?Mna�^k�h�G��1����h)U������s4)��|�9�}�j�DG��H~��8�M��sp�.��Y��zC:E$����������+����u�	K�qz�(	'���������?��.��UH&��sr��_����d��c���%g���D�����Z��.[��j���4��;�$C�*+f	AZ}ff����3�&�O������/Z	��0��G�s��J2\4#<M�1�K,�\���e��d:}+�6W�K��sp�����wno���;��H�8TvA
����5B���{
B���X������d�C�3���\�zu���`2�T���z����V���w��Q���PU��	.y{���
�u����l������#���y�v���F�.���uyp���,,���n�<�I�4
E���\�^p��������g�?��Q����,��;7�!=�zx���J��r���e�H��#J
��I�����P�Z�u�H��U���o��g�}�T��b�Q���Xn��������w����\Jk��������JC�}����O�e4���������7xej��	�e�����{6|��]}O�t��q������X�����>d�q���������Y`N��\e!�r��/�l5�����w`�I�:}1���7��_��.�����
Rp'��P�O�,�����V~\����?9��z"pbz�]�����B���'�Z��w`fde���?��n���;�c��$4�������oeY�����;��$���E�V��6�X"��u�����F�[�]�����R<&���V$���e'f$��v �q4���
�>"�=M�H`��*� `�X5�bick�A�(
�dR'9^%d�?��1:Z�a���UK�S�� ��X�3w�s?��A�NFp^��_�cB�5��� In� $q0�����'.�a*gL�U�L@c���US��|
C`��^t�u�ig��p��&I�^Z����"���&�5�y,�`����b@5�Qy�f��5��vh�i���M��y��Xe�/�uWY�b^�&V�����5�g����.�Pr��1��5|IeEq�1����1M��nl%<��:Bh�r�.*V���'(��|��S�/�E���FO�>��rHV�������S\<�[b��o�����\������2�.����Y��F1:+&1J��w��K���X�����\P��FXx�1~��:�1]gYS�L��sw�(�	���}T�Mt������T"���������#N;X������*e��J���C��W��
s�J�gCen�-��N������	�?M}���,C��
��:kf4�����n���w�1�W�Y�1)����B/v__x�_q%������Od�h�[[_k��4�k��j�������h�&��,��J-.6���6�[�����7���W�c1��
Rm�k�r*�z�p��a6����8�v'��?���I���j�Z�%H
�O��������_'C�To��G���� �b��c,H����ps���?��1����F�L�K �7���Z������\w�Y|J��BP���p��n��j;�/z��N����w�G�E�9�?z�$�. Q�%8Lc�Z��K�*��`���z9���w����X#���@��t����u����C�*����}Q���5Z6q���&���)�������ah���-��2WvT��!voG��6a%�Y��Nlvg3�������}������+6[���2�]�`xg(iN0b��e����U���'��{��u��K,}(���8���^FC"]ugo���o��3�R�����;PsTy{����������"���$LWK�Y��V���J/�dX+�s���T
0~bkl�P�+�Q�N�!��k{h�p�����"I���x���;���g����dW���x�s��#z�\���<�G�3��w���X�t0:�-�2�I�L���x[�������z]E���K�@�H��/�4�I�A�w��
 ���OKWQ�bh�"�����q�t����;�� �y����,���ci�7b��v�*o/,>
�
�L�be��"�	B�|���0���ha_%[=��2��Y=:`��w��#,��&����'#=a��`G(�)��"��;+��"��x���7��]U�� �@�	�o�!��h�I���M��b�4�`e4AA��A2%�}>�C���@@����s�:���VHe�E��b��ef.
>���O��j���� 6�����7������,L�69������Zu�������� Sa}j����sj���?�v����o��7q?L�!o�^j���S��}"��8�>_�"���(��V�O	g�<��d0/��Wl7�-��3�o�c�?�6��c4g���M8���&L���Y�O�NE��5��J�;sma�b�p�e4t1}�V�U�T���Z�"�����&�n�>��%��0��������h�GG�����$�����)	~�]������rU�5����.r��U��8���GbD���-���Pi|>Lqg5�{x�L2��l	�)Y2�HU[\��o>�g;���?{�t�LI�b ����m<e_`�*s7��^��e[���e�i�����2��b�M"�a������i*<!_t'qEM��RxW�i\	����F��r���A�^��������.��������^������0=��TA�I�?��Wa�[[���6,�-��z��0U/��`��!�Pd�jDZ'�����]DX�O���)��j�������U��CGg�:V��gX�6��v�r'��9�~��#*G����d$�2�R��(�����~����\}y�G��"}
�jF@K�����~�/�%���Z:���i��$���2M*�S��wn[��Zc�!s�b�:$iv`}��
�����l�{V���X_@V�y#�5w
M*�os��b�~^>W�-J�M���~�e/�AEK����_A�J#Y.�o0,�������	�������QG���#� c��t����1��������� j�h����{5��t����y��J~*}�;�i��mP�������%g�s\��Q�wX��g����!l�}{����g�~��	�m-�`�������OTd�_��Q-on�k�/Q-��aeG'��#�cNN�bL1zO�.�JGm��m>6��h�w��vQ-�u���/�6���V��{�;�b�}���X�=��1Vb���B���q@��Ag7~+��
Q��n���
�4�}O�D�hKm=&n�1
t-��{gqa3�g�#�>�c3^��#��Y��Gc~K�������u[G�y�,��elm��X+HY?f|�	������L�E0�Ts�����&�1Q)���S���R��`���]oM��A��/�*!��*����{����@c;����I�����Q����Je=J.�1
�;�@N��1_�3����&�$E�,3���~����dr���� l���J=�R>��?�����o� o�?T��^�83@�%\�V��\U�qTA�b_���|�!zI/c�z�`��5E��ZR��_f?}�� ����
�
���'���f_��b������N�i	N1:OF�X�m�H�X�4�)�����w{p�������"�����a�|���3 9�*�p��A�G�9b83��,���ni/���8��a��3�zT.���a������l�MY$�~3q��hykc�c�1�l�z��������x�����8�������X�,�z{	c�)����J@����hH��0F�O��>��A1>[��T3)�k E/MM�z1xg�WP.)
�tb=6������(������O���lc|�&�����t/��
������]�D]pN��uf����:�~:�70����+��Bq�t}j
J�6��Lt�u�(�����S�Z(jh��L�+�R��w,�K9������F���
�7\*7�X��D�Ax�e���>�j�"�E���Y2 ��U^q����V4D.np�|�QJ"T���K�\�#j�rn��_�<~���^���.�c���b�\����<����|'��H>���.l��a99�����'��������)Qd����A��r��2�T��Z��a%�J��Rt Zk;sq&v�����8�\�r�R�o��%����Vs8�z��|�a�8�}���*��k�����P`.��b�jFF��0<;f�L�J���!�J��Q��e�M�����t�(�����n���L#@�
�zq�g��������AtK��-����?C�T��<S����s��
�������Q�leq�k����P_�u�%�~J��������V��Z�)�����F��|�+NG��*�&�D��f$w�Z���\�9����Z`�.����2'ix=����h��z'}O����F���!�o����)�.�0���q���p�����'�/A�g��q����h`����p�5iE$���/��Z�_A9����9�L��J|������b����9C��@��z%��*��?���k|O��b�_>��f�����@�7���E�R�����\I�i`nf�x��b����������_) y���[/c�g���u���!u���I��M���,�;4dI��tI�r�4����a���}4���0�Y?�F���5	*�Ea#8��w1���G}���no�6��m�@l��(�A��
�K���d\%��S,�e���v��O��oO��}��9���s����������gI�Q~%����#��(}�?��U����#�:)�����u�{w�t`��<�?>~r�9xs�t�~$���xX���Y}
����������tZ��������w����8���+�V�=���l�W�?�0��p��A���eep��`������R������"L��������47��T����t�����x��\�����O�'���@=���-�u��i��_`r|d�X���5�Up�EBn'�y�����&�>b�E}}�{����D��\�h�L�/�LE�[���I_���}yy�.��+�liW ��/�(>�1���%���=0p�8�c���@ Yx��mx��L�*�]���_�N�[�[za���8�����.�����$�����]��F�X����� D�h<W��j.x����m��,L�^vQ�����],B�Z��_�����Y�l��7��7�W���yQ������4�����<J3��[��W�\o�6��m�:/~��:IT}�R�qwv�^/����`�^�u�x�k�"�^�L�}o���X}��[4�J`]<t���� �����!.1�/���:	�,��>��c@�:����3�*�V���*���%F2R� \M;WM�xx���L��;�?�D����d���Dy�o&N��M}�C����W�-�th>D��t��S�VUd�w|�������>�����Y-�,��&�8Rf����>
,rM�J���p=�U�?�D�-J����y�������E��/���9����a=Fsc4B�RT�t�;�!��\�)sw1��wCh��{:�n'o8��{���6�,���_�a���D�o�����L8C �$s�����1�W�C�����_Uu���Z�������g����ZG����#��)(M��$�|���=�Y�����L����#����������i���O���?�+p�����E����|��K)�(���Z���pKP�%A(*��,�9��2�w���S�aT�*�u52�����`��Z��=�����
���M��'���� @�X/���\�z�J���G&���'����m�~r\����n&t����.����&�����K�fBB�p�:
��kF�"�p:m���[�#�*�n}./??W��1�T��/n_.��/�r@��)����4p��V�i�k�yH+7'�T���M/q����>���i�+�8�����a�����+��)����_�9��u���������S��^�iG=#e���f�h�K��lS�%
:-�6KF�Y��P����yNBYZ�W��i��A�!�	�If���S�����Fx��8
.?�B!c�n|@�h�Y�M��V�5�x�!����1��X�Q�,52��?}�[�T�+���@�JFs/K��Lm�L^s��S�3�Y{����i�������a�J��.P�j���?�8��*�mt�|Ny��+ ��	�qfV�{������,�p�^(����S��f
��f�\�V(>�	�i"l��)n�K@$u3�H6�8��� �n�qA2�H����d����7�D�7+�5����5�Q�L+����j����3�_��~��MM��S������W�����t`�m 7ZM��2�L��y!'��c��������X���1����u��b�m�����R�k��)�����7��X�/*�"����"�L+f�*�r�V4hy�o�����}�%�D��F
v&������Q����G����K�Mezs�;��|���n1�����`��y���������e����`�OJ����(�gF�H<����0b���-�P5������`!-�W+�}���7��X�Zk=$��,i1���M��l������c��@4J��AuZ<&���s�g�����,�����t�{^�-aa�T��� ��xFX����Y�\����W��YZ��K����1j9�P'�+%�R��W�?�L��}�^�������.�]��>@�v���Z�T/��a�T�����9���ODe������^�����G������X�����y��QK�^��-U�z�~aO�;����m�^O��O<�����_X"�����h�!�=����8%��|�=��z	�a0�C���1�,��<@O��=��M|�����0��].��j���
&���qz�q�h��o����6��z�����1/q���l�#Z��?���zF��S� ���T��B��T�O���<�����qK��������
���w���
��#����@�i*F=M���bX��(�{*E%b���'l��v���w!�KX��n�jD�nQ>�a���e��}Dr�r�?���<>��v���-���I���$I��h�O�JH�v���k�����g
0~���J�(�t\��
G��
DR�v����;���)	v��^�����`�}3��r����s�1H5OD^�WvTT��j��k|4\$9e�gy���}9 sG�W����z��V���I'���[,;v9�,�h-|�;�{���-8�o�e���]��m#�PD_9�[k��=�������2 �����H���m@������>.�Du�[��{�o>F:����y��
9�����G"?��$
@�D��c��9�#����=��6��^X��n��*R��F�p��������>��{��-(E�+B�X1}W���J�pyQ
�k�����
�3��2�������b����XA��8������V|�'���y�k���= �FM����T���:���_�������,�>��o�C�^b4�l�(��c<s�k6a�rM
��2:��j
fu��$Ve���]
�D��U��^F��S}[*�(�/nY��g��y��6�,�;�r����t��	<JZ���W��6����_<w8���p(��~��&�#��A,3��NC�a�T�z���w$|������)���xK<r�^��I�f�����������/=�C�T��0���,��M�-�M�i��:���:Z,�mK��Q1����������^��NIi���<' a�Q��C�s�_���|
^����J���]zW-��W��\�c� r������x�M�{�4
��������T�cr��F�xp����$�M0Sb��:��2�`�����I�a���f"X���u��0�\� ���=~���P��l,a}	[Lz\�K�RKpU������<��^�{����&C����H�����������-�2b����k����H)9�p�;/�>�[��"^QDt�k���!	�Fa^�����=�FV���������L|��p(�k������:Nw�?1�N�c&����q1]`�+$&>�hh�Jx�B'�F��u6
����/�X��X��Q0�c�����]I�y�������|caE,#��x��u���j
��/�Ph�h{�{��N��z�5��b���oz%4������C8��L����>�����(��y�:F��X���Be,��9����^6��|�
�q�{��#�����������"����yP������%8
�h�B2t�sW��GN�`��Q��|��X\g�o��a�]���������W���E�?�����@���y:��A����8��f� ����D����_�?�vry�a���������Q���"��<�#������n�1p������&=8����C^�Ub
��xp�o���pC,=���K����u����������~o��>��c 8����O��CNr5i<
*u��+���x|y�:R�GQr1��C��S|���"k��F��������,�?����w�^5DsB]��*O��+r��f!�YA>���|t�[�����!���
�j��e�i�@��eY8/Z,pV�s������8����
j-�K�����A>RG��Y>��	��R�)��FJ/�6�����6a���)Yz;��$3L�U�Y����	K����FW�h$N����h)�[.�po�W(��H�X� 8�XF�v7=��������xzG_���hP�S5F�������l'�������^)`���sj�p�x
BTM2^>�-�����j������=��h,K**���5�p�!�s
��a�O��3��=_���K�UF.�
����!�e�����	�I��l�h��]�����������:��O�w���\�B#��,y�t���o����5���|��D:�t3��f���j��,1�:yX6�)!Q����������V>��Ry�M3��V�s�t�H>{��H�Z[	��#X��xn���2��V�7MD�25:��b���D�N$�rj���yG�e[�����Tb�w���A�����`����K8�J�?�WA��u������&���3*�(���$85{1:�W��R)��������U �j���#l�?B��4������Z�6���x4�M��T�o��.�A��K��`�Z�^�	����dO�{�4j�����7�������T��������Lk�k�q�������<m�?6���A��V?�J�$��C��=��d�����J��U�b��V�����=k�c��x�j��Q�����<P��iT����J��s@�j`�����W�F��	unke>Tqx������.�y?Z�6_Lw�N�	�<[���7���Q;�������:�^,�q5a;���XS���,����b&z�49�)�\jz�a*��krDP`��wl�1����v��5����^i��'��/�:bz�I?!�r�Ck(/	����{:�z1�L�a$���rE�!���v��t�,�H�7*F�RZ����]���p��q�^&��f��	���WTg��d�H��|�OV��]�9n�)02����B��4+rs��)����I�� �Y�S���3�-���������Ic�l]������ �����k���G�|oV�F�j���+�8G�a����	���9�19�������O��(W��Q������SB}�����Cx��]�YMPQM����#�����,����]��~T�^��������_b��
���-qt���~�fac��p�~�����H"]��S�:I`;K�$]���[���^�f����>�����ucU(��H@������<�T�z�5��w^�[�G��0�������z���?����{�������?,�Sg��0XA4>��#�Bu�������\���7�'��0�|�Y+m\�G���r�#�%�K����'u0���7W�	�b�>�\J��CH���`&��q;&�� ��1{`��'���L�8�,J��;��3�qZ����k�M�0��(|�2����(���%����RO�������';��V��9�!�h=vQ�Ka^�U��7���m
�����B�e��p��;��u�oZ��z�W�eT������&e���IN������t�K|��+�r�)��^��E�W��)�s�s,
�z����/�2�uS�t7NrxM]F]'r8|�p��������=������9�������_K�$-��.&&��B�cz�D����t��:0F5VUe�@������,�Z2��@f9Sc|�:��m�$;�w�(�q��R\�{\����=.Z����������������r���5�d��"���F�������
����G���e+>6�C�v��=�P��+^����Sx��~����9(<?~T&��pr��5��B������#!��8�@2����OH�l��i��2N@������{A�U�+�6�������YO-���R�sk5�P=�G.��nx��OA��	65�����w������`�O!^;�A�J!$2� ��
6��Z���(�\|��	{qw��
~���B�>��;P��(��&���h)��=�y^�_�<!p���!��x�z4)b/r�bQG�������#�8{�j��9:D��l��
�Z��bc�-����L�����y�N<�8�6�&��	���c�B���+��/��[fF��!2� ��7x-��$�W�)�<��#�i����M���:2eF�'
������f�<"��9���(3c�����&����d�����H�����Vp��?b�%���v�:l�^�����o������1�
�A���P����'������I���k}N��dKx��+;�����Hc�(DzW�|�c��,�
��������w>��#+���l�Gh������d|��IP��ad�#�����O�Jw�(�Y�`=�����D�K"*4��k����(s��v���
��Gxo`�r$��n��
�l;�-����h)��s��Og�S��IG�9�}�}��������V��rK04�.���yLu���X].�1�Y�}�c�b����F����5��lZc(!��d�|�@f��f#������t�/`/������������A5Z�?�/M�h�c�r�n�+"��r�"0*X9��J�g/�����6 �Z+%c�|��@���O��������=:��:���^�]�:�"r�u�"��> ������
c����m��J��o��.����^����N9�ut�f���`�tn���~rR�M���A��d�o��������:��[�<8I���{]������Y������_��)���<����c��5���P+��Q�Ts*�Vk���k���[�����t��'���/��=]��<�������������E��@s�,�������C����XT#�7rY��;������o��d	��h�s�[~b�e�����x�F�ED[a J�w1 �r��W����;���]��w��N������O��������z��s;7��z=���@(�Qru�a�JeN����r|F����Pm�R���O����	]B��LO=����4c�1t��MZZ������E�� pH��,��vr��o����&N�L��Z��'����QU�_y^�]#=���R�������u��G���h� ���_�(�c���#�����V�����$�����{�C������)�����s�F���Q�s�g�d��Q�~mu:@E�o���nwo���Bk���D�\�`$����G�!;��S��b88B��L|�6��vG��'�e����_�����}�������,�����C�z;��){Y(�F.w�
|o���*L��e|����x]n���ZK�n�#�
��PA0����e�X�4��^g@�u��K��������]|�?o��K�_�{����m����OG��-��"�����.a�N��}���t���Ar`�)���K���FN����~�F�����	@��6��}���"�G�f0�� �W�V��txy�[��@��0m��6�B����e
&7���\�� �<��U������Y��d��m>���V�n��G���aht�����l��\����^�����<����j���
���+���B���LB�X��x�1x��n���}K����W�1+��|��� o��R�+���U�MhI�f�� �z�]���OCT�5����-76��x�Fo�tp:���K�@Y#,�����p�a�8�����Uv�}l���/Ls�4�S��e{�W�
t���������N��|�x�e��
����~�m��~��J������$��l�p�q����V�j�
�Z�K�n�%>�=Kk�B�/>��9>��P(�~�J�2ZA7V��[��n��;�;�a�p�;W#��67����^��O�O�@U9�����1�?q���#'
���X.�����w@P��/�����$��Y�����r��!�������!
��%
��;��=����������0O���	ee���0����
�_�������{���Q��Q:Q���\��qb7�2���<F� B{W&+�&�|N���<�BQ�Q���o��s|��>fI��������,�x�k9�U�d��� M6>{5��!qQ(�_�������1���@��"��d ��>;=������H���xfl��x4�����d�g�O�'������C�s�U�,�5�V��3��}���������x��u��������%*����Be�8�/��/~%�|��E[$U������x?�Fw|(#�n�\Wh���F	�7�.�������h�R��{��4�4x�v����".T����4���5N�}8�lsga���H&=�������������@/-�)�������!����T����y�L���fi0���.;�Xa�������W�e����;t7����28@{.��5�l�/�>���;����R��������T��{����F��Pp���J�M�K�xK
��/��j����n��
oGe��W K
%kA�����`�P���x��G��{�m�9K���<:X��4FL�����=�D.*�E�m��/�������xH��e
�{mg)!T���1�!7���t���j���M�V4%���*��y��������
k�]�B
�K���Q���bO8NB��1����Dm�ley7uj���C���������;�����;��"���K����-��H�?n|�B>.�8�%�\�n^L�.)|3S���
���Ftt��;.��_z�+P,����W��$���<s�8������Oe�o^�;��R� �; &-����P�6�q�Yo��,������=P�00^\;�k����$�W
%� �
%4�>3���"@�9O�qes����+�
D|
=I�'���0/
���b{��I����lRy�!�1i�Hl���$�L7��M��.+���NL&���tR�^�3�f5C*$����5��Z������B������'@��3�gK�f��/�P����7!������VR�dJ3�����R\���Z���#;dCbb����Y��i��F����"����=��}7��m>�\���/����;�_����������s�m���(�����l	�Mn���H:�ic��Z�h�f�gC�[Z>�=Q
A���8����^h�}�Ty�K�+&X������&)����oQ�P_����X�}��R��# ���kl��(������������e��
6pI���qw(�%ok���}��H�{F��>D�r�?��S���buH��(�.b�'q����&q�LY�&��j�$|U�d#��i�x|�)0�������4R;�����a{y�aur��pe��9*'��d���R�n�f��A,M��#F���L��O�u�
#X�Y��D���h���qH�g�2&�0Xt
��W�|q�Uf���h�Wf<(����\��RH�����<
���0:�����/Z���������������d�Z�uz�$X��L�}O_�4x?CY���5������>K9��[k@��q8!D��{�q!P~�
Yq��@�DA��6�Zh�b���R�fA$����w���'�0wF�GU�^�K���pL�
�F����������`�~V������_v0��|Ei//��b=2eLB�A>�wI������<0��*r!�����-SzF���Q�|��n
��uv��d���G���^�����q�r��t�4X����C�N���`(�idg~���dk&���<?@?�
���p�SM������W���S\���+�D)�+i�3�\�B�y�.���(/yv4��v���(�Zt�PG�K�5D�D��Rzk���������!�?E���PO&b8�%��KO7[�J��
`oF���3t���������\��_�'.�f��F?
���=�:7h2�����?
<�T��YA�\����%&���-��1K
'�C(k-cJ�PE���>����xr��450����v�+��k�Z���x�Y9
�w�,���)�
������C������p(��F[hVD����t�Jbyq�6[������y#_�@B���������s#J7b���d���E�����'NA.Ldran#���_���W���WpQ
���{���x��G�d�[)M��&\Z�6��6a�8oV�fXaE�dR�MiBCocl}����������U9(���+r���c$�V�&�D��\x���7Hv����&`������S���4���� \����J�������z-�^��c�;���c�J����n�,�2���B����s����w�V~
���������-��E�.QQ�rc��-�d�V�S�R.\�g ����=�z|$/_$�A�����s~�??�?�3�I����l��|�w�|��t����q�9%��!5c�Y%9�AK�
\���;�����L����T1d�x���bk<�;5\*�\[�)5f�fw5��w#�Xi��r&���=�����~��,k�{�Kp��L��~�Ue�����%!?�PO��[g���!�M�������8�3����C3����DMH=9������b��M�����o���b��8��J�s�������q���q�A�.Gll����i��d�tY���9�T������b:���O�s��/�V�UI�f���g�%I�UP/�!o-�.n�a�]'c��\��`��.P��&��
���;�Y�5�c��3G�7��@���1 �K(����/uR�8oK�].!�;5��k�����z&-'l/|���n7>�f&�F�(1W$U��3���-��h�#��
�d����^BG�e�f�2���Pv{M�4�	��*��U�YL<�8�	��_�G�����( �%U�o���uMq,����7�2] 'G����_B��Vq�_r/����<9�{5v"@
!��XD{*�V���~F�����M�Fc<��fZ_�i$S�����UJ�����~���k���1t����Cox]/y�Q�����G���f�F�;���-��&�,M,;�G4mG7�5��+�@#��
{���q�R8���g���;�����Ih��6�G�[<�9��z�����j����h���v_b��'K8�yj�����L����J���M
�xCT�n�����7o��o0�~����Y1\����OZ�['��}q�zt�������Q���_���ON9=�4~p�?����b����Z,Q|����@lM#�A�0������>,�({f����~=n��]^\�������\����>;>*6+�������������3~�����@x��o�AZ}��O�L��+b��ycu��m
vy~��/�s�.���hq��]�w0�BM�@����|�_6z�h�<����wr�S�i�A��M�%���1R���Xoh���*�SL� ���3�d�� ��������)w���2Au]ox��p����yJ���e�
F[6�ua���Q.�P,\p��������	��m�&DJx�=�F�hVMJ2zL!E�K�����/9�x^���t��Kn!�L(1.?<����.���%S�C��sB
����N��9��	������b�@��dV=�s���{F����u������EG�{���0�Rkf/c��N��E�/����#1r���o�E�"����
��Im������q�p?0VE�0{a>��o�|�:��&>�T�o��)��P�Lh)uK�\�2�����TC��G������T!h,{8�3t�>��� ������{����	!%&�E���v��`�%�2��)/\�������U����HDL�7
>�S�J|�p���"�z�@��w��j���s��_N/>���J��/�A�R�?�<������O�O��1��-���G������9�������[�r��+(����VP��|�#�����r}�����*��L��1��<$D�_����9���(B|+n��	�o��{�m�4f���a#7�#�TR6���g\���y��g&����$=�����N<���t��j����y�i|l�U*��������e�V�d�Zw
k�Rg��6.V����*��Fl����w��R7�����ngv���w��%HN�#����KP�ce^Y���?���X����sT8z��?�Mr�I����@�IVv�^GU^��u�y|v�:��3��CmHTQ�1��Z(��@�n������q][����;�;�c�p;F00(f/{��n��,����U`�r�4�����*U���$9:��H"�g�Hl��xH����N����#0�����X�U,�c6�@�%��a��(%;��['9.�Z���9���������n��i�O����BE�
��Xu�9��~x�3uG5��'?��Om���
�c_��v;>G!���=�^����oTw�Y�FuQ�3�Y)b��B�Q�������(�Mh�����$�6�x�`����Q���@{�`�)���������/l���}xv"����@y������d��������q�A����B0�]��n���_�vGW\���n��f)���N�	����������p�H��*S�Z�@z+"�H����Q���`9Np��q��5(b� v��R�����1�0���v[�2��q���"j_����#��l�6�X��#�q���KV�
cQ�\,�'�]\���d9I��ZU���~��P���t�q�C�u��4����j�a��i��^�5z�0 h�O����7B�yR���7�%Z�L-y
��u���|�8n��.8���C���w�w��2n��[@�:�r� r�T�A���}*!�x	
���E�����������(��PF�K\
��mM�B���L,�.`��M'���(8�����z���'R{��GO�J�XD�6�t�D"H�������M+N�m���R�����E��)�-��w��}��ol�|n���cD�]"�����{d���$��5
�����U�F��3����kRO��7���"��uc}��f��B���
s=w�W�%�<3�s����{��k8�9CL2��aR1���e+�E;���G�kW����xX-�S�r�%t������%~!����EL<8b:�Y]~���Z�e��������
&9��`�K����?���h�cZ�+7�j����,i���~��+�b�gfH>"/���&�Gz���"U�`�l{���N�������������aI��K�,����GI#�r;h�|��q/��'�� 3�	�>Z��i6
����2�K")�3C��vy���	��
��
���`X�����*�EOU��F��N����$����~����h��,��v���5�QE����E����N���nNk�P�,v�<s�����a
���'p?=
P(����4��^�Sy��w9mc���_>\^�2�g�������`���N�d���K�6ZhwwWOm"�3�D��l������bZ?��*�S��gM�!��D+�(�D��P��G��	g�#��@��3������$�1�U*;:��<>������<a�s�3���!��r|����;�8��������P��dO.��y/[o�#���k4�P_X������;"A��"*���q;E���3��w+�iH���n
i������|�BI��%rf�H5D���(��(��rpGv����-�?��a�"nA�p� ��C��sXi"KI�Bx���g���{�N�O��
Z��2i9S������B�=�!|$G�T����g��:���z���6-�p�b���.�>.^SA��D��4XMV�>��e�S�\6r�9���>>���c@���?�f�!.�c��K��P��T�0��x�(�x5��J���()����@�Z8�g��Y�(g�������==��|)�,�5�������������%!�i�BaCvi�����F�����P�,���^|��|X�#��.���������O/P�RW<6+��>��&0�JP|m�2�GkV��&�\�X�\|����z�h6�Z9��q��]�_�)��m�#$�������l�j2sv�a��H���������t�]"��7�.��U>b���{O�D�Y��c�*�x�FW������];�$��I�%�>:��~��@���j��x��{:D�1o���{����$>a�.�9�
v����m" �+1�a�����q������������+�kV��~�����Q��v���3m��4�����)�����n���M���bP��8��}����YU�>���sV�qO����l�ZJ4�p���3����GID�
��y�n^���W��x���\���s�f*�z�-<F0�\��09����"�Ve�����d�Y���c��������u���zs�b���93�#����)���U5��V[S�-���XU������+�`�w�l���9#�c��:Q�p�:����f9���/y#t�B��9�=6Cp�J�q�X���'��b�~�O��w������#���*����2�1��o!YS����a�/���1)�<>2�J��E��_,K,	�L�"���/�������1=w�h���j�s����6
N��;?�����+=B��~.��+T��;#Q�Q!���W�X?�RIv�����y��P���V�������$�8�u�ft���p/j�v�B�+�k��s�%�?��(��"�2m�aA�����c��&�S#����<D$��Sl��*�f����n�<<�!���{����:�1��H���_^u|����!��T���iw�����P6p����I���x0>$+y�?��:��r���J!���g|<8�����
��VK=�&��=/�gW�W������:sq�����������O��N�VL�b%=��d��2��\k�x�"�`�����m[W��.�]�}�3��"������T��cQim0:� ���?Y�)t�B��!���������������J>�C�h�)j��i����~�H�RG2�Z���~J����4��W���K�c)���C�T�����m�5�����/[E�"*����+���;��R��
0`
�IH8n����"o��zW������J� �=��Z���~��(���UuAod���>�@|<���_�[n:$&h�,�f"�M5�	y�m�*��DCj
R����GN%T8���?)VY��v���q���Uds`�K������Q�\���{
�k��q��GS����s{}�3,b��gG-*�o,�w9�����s;�K�'K�����1,>���6����
GC��e���z�ou�3KU�,�e����oX�0;66���e�w�a9j�V�c�������	�(��V�������a!i�I���R}D��f��>p4�.���I��	��n8"7���Y���+���4�sE��o�~�W���m��[��[�00MA��[<�~�^/?6��[�^B0�E��T��������7����S@�Z$v��x	����d�	����Q�
v�>����@�l�x�M�r�.��6"+0��'���O�W4T>B�*��O�8��~2�k����2��'8%8X�T�9��Sj�} ,����I"*�[�d�
pu%b�)%9�d�K�Y�� �
V?�����gqa�c�`�����F3~�)�M�r0r�]�p�/?��<,�J5���~u��ho���x�r	/�a��O��<�p��^��{w��]��j�'/������f������7 �@�\�����J�9<~���=,;��]��Wq��T���
�DEx�b�����k��B)��v�������C�����|�������o�.�CK���_FY��^�Ox��2�����EY4�t��(�(���oX`��������dvXP�������
�����tv|z�zo�/FodJS����F�g�XYi���c����>���g���7�����??�H������8������?�9���%��f��a�g?_F�(�|��*���,��Q�z4h�a��;8�5��Q�s����k��-�H���k�j�5���E�P�I�QC�/^���?������vww����[AX6	��������"D���E��
W($%-$)�8%�k�D�?�.���`�n��G��y�(~�I^Q�L@2��z �V�7H���N���aY����#1%���5�[:�r�!�h��8��IB1=w�h�sI�bz� yF�I"q�bZ#$>;�DhI)�u3
�LIE����v9o���	���D��
�Q�	����{ _[/�&@��\�	��5�l�kc�o�z�=J��	�?07�M�| q�M�| ��W��}����6
��oX�����
�������}��/[>���u';�+���W{��������Z�fv���7a�M�}��r@w�
�n�4����Rf�b�;���wr��u��x�;����8K�!���}�E ���p�Y�Jf�C��d��2V�w�K�zC����x��K���*����j�2O�)���
�'V2��]���M��/�L�4�d��r��N��{r)������C�s��� � fc>>o����]������AaY���s�iu�q-PK[M��!uy���u����e��x��5B���Q-1Q���sH����E�=�N ��.�>���)
���F�n�7}����>�����x~���(��'%|�FA&�60(|�F<�
?W��>�����`�j��O1{�e�$0_�e�$��Y;�������9B%r
���O�P��d)��E�]�����6�CG��i�
���D�6�����#����-��q$tn�~7D�i0s�m�8��,
z"������g0��q�4���c2bG.M������WD	��ZYn�e3-$B�M�ph/�A�#	��X���b��<��f4�-�VY>�'�+���@.@s��$����
h��l������\)Z:�����H�dG�W����Z�d(��F��c[�hu&���V0�U��,�������������1���t���G�qI�OH�/���N]oJ[S�8^�����:���?�g'��
�,��#s�q�n���v����9�}��:���xRd����#�����4QZ������rrS�|�&7U��S.����,��@4c�;�@Y�����<$�����r<���A,?r�r�A��R~x1-3�g�����:��������]n<~��;3���x�Z��g�,���A/���!����Y�������I�v�x���2��2�0�~X��X~H����������*(0���<'f�91��@3��:�BI�[�^-r�����������.+f�j_�A� :�B�Z���!�%ku�����G>��%�Y��m�'��	�.�*X�u�X=+G��|��jf�5=`����9�%��fl�<��!dF`z15��8��b�s�P��!
$:i�@{�i��q���	k��r2C�r�Ce}����!d�[����v=����;t��v����z]��7A�����9�6A���$��6A�	"-� ���� �z��gDZb�bDZ<l�H�\�E����G��|gh�p����c�q���+�� ;6��R�Z�c�&�5A�um���������Yu�Q�%��bu�t����<���<{�q�u�g�p�����9o-H���5��Tv���
�d�Ky1�jj�/�B~,���4~x�������8|G.y���q�a��$��c��r�"]��b���Q����Dz?��$0=����-RWgL[�p��f����s��q#J5��Q���V���c�1�����g+���cV�����Q:����ztbK�uY�J^��{������7�'7l�+�d����>.��4��������r~�\��V��1�kZ7�iU�]�Z�O�����g|Z��5�E�i�9�$k*8ux��`��{��XW���|�:/99�	�=�+��,[���
����r��8e}M���kl\=G�XS�kB*�4���s��H.��[$��yS����j�L������9.yh�U~��:wDo�.Sm;K�O�B������	��Ag
�O�Y'a`5�uVT$4��Q1N�Y�G:V�Y�����}Y��ypE���|Tl�"���t�3�2 :��
���JD������|�e�\�e�U�JE�$�b���@�0X����������0������<�
��������w�~��m���w�)�� e��m9�v>��^	w�rHL��id��O�!�?��2i�������.+
�LT:��z?I����W>AM.o�c���������WR���xT����~c�?���H���Y���iU�Fv��N��&Y�
�+�.���2b��CWo�A���Sv|qv�y|v�NZ�['������p��_����.
��7`M��/�K������q��3X�?����%���J��x>�;	��-.;�$V%��,��?;g��~g��[S1v?7c�W|!"��
)�O
�v~vrr���$bX f�=�w>;9����N�Qf��+B
yV�a���Bcs����X�����K�h�+w���!��RwO+����9�kND����o��scwG};�v5m���U����{�
�}��:�
}9
W��)���F2���~h�w}������������������o��2�v�v0��N9�V$���|�����f���w;9�6��V��z����m��������J��	�%�h�$q="�����$R
���������H��:�����@g���#�21y�j��Q��������bW�8��$�L�
ol?�_�r��t�������b0����|�p���z�)��m�ss����-�������r;v�g3�a /��-�����Rr�z{�:E$���d!_	}�b_nl�Yl8v��XCvcM���u ��[��q+"��F	n��`S;q�����-|2���(�\��a��+i�{;L
�F���o,�L�B��t��o�c��,`t�8P���Bi��������Q���Z��:o����#8J�u�o9�-������G�g��������sK��P��Pe��$,|l����[��`��v����hX����pR1���g����>v����A{��i{�i1�,'u��!�Fz��	�/O=a91�T��y��q�>���o��������9��!R�	�Ul����VO���i�%�>K��1Cr&����}������<3�s�&5�*���m��]��r��P|�|�|�|�\=E��r��,��u����9��VV�����~����b��J��@�q4���.h'��W�P��7
��4��
��t�Pr(�>�9�fw#fIw�#����X�CVLr����Z����9&U�����*:���+�Ky�������vM�7GW���g��I��=iv��[�u���x�����*��w��*��L���Sv��`�u�Gj�IP��}��
EZ�[��g������������Q���m�LZ��L����cU|�f)��u"���[DY�����E��o���3�����%��KN2�\�^�\c�_[8e��������D(���
�x��P�k��rC1�F�1�i�����Wj�&��+%�����3>
��maL�����+e�<���y8oal�R._%����S�4Y]9={��5p�cw�/[��
f�]<��2��e���`��:7v@�8�o3gh�p��|x�����gC��Q~�-L��;�4~�L��1o�!���pm���P��eI=��-��
����}��D�|g8��o1=/u�o����N�j"yK��<nO�p\m����X��[��>���������nu�������dn�hcq��.����u�nM�� g)������"����\m��r��-����9Hw~��;����[H�Y'�u���Q.HN9X��	7��qI�L�[��rs���BLs�VoeV�1
#49Noah Misch
noah@leadboat.com
In reply to: Alvaro Herrera (#48)
Re: foreign key locks, 2nd attempt

On Mon, Feb 13, 2012 at 07:16:58PM -0300, Alvaro Herrera wrote:

Okay, so this patch fixes the truncation and wraparound issues through a
mechanism much like pg_clog's: it keeps track of the oldest possibly
existing multis on each and every table, and then during tuple freezing
those are removed. I also took the liberty to make the code remove
multis altogether (i.e. resetting the IS_MULTI hint bit) when only the
update remains and lockers are all gone.

I also cleaned up the code in heapam so that there's a couple of tables
mapping MultiXactStatus to LockTupleMode and back, and to heavyweight
lock modes (the older patches used functions to do this, which was
pretty ugly). I had to add a little helper function to lock.c to make
this work. I made a rather large bunch of other minor changes to close
minor bugs here and there.

Docs have been added, as have new tests for the isolation harness, which
I've ensured pass in both read committed and serializable modes; WAL
logging for locking updated versions of a tuple, when an old one is
locked due to an old snapshot, was also added; there's plenty of room
for growth in the MultiXact flag bits; the bit that made tables with no
keys lock the entire row all the time was removed; multiple places in
code comments were cleaned up that referred to this feature as "FOR KEY
LOCK" and ensured that it also mentions FOR KEY UPDATE; the pg_rowlocks,
pageinspect, pg_controldata, pg_resetxlog utilities have been updated.

All of the above sounds great. I especially like the growing test coverage.

All in all, I think this is in pretty much final shape. Only pg_upgrade
bits are still missing. If sharp eyes could give this a critical look
and knuckle-cracking testers could give it a spin, that would be
helpful.

Lack of pg_upgrade support leaves this version incomplete, because that
omission would constitute a blocker for beta 2. This version changes as much
code compared to the version I reviewed at the beginning of the CommitFest as
that version changed overall. In that light, it's time to close the books on
this patch for the purpose of this CommitFest; I'm marking it Returned with
Feedback. Thanks for your efforts thus far.

On Mon, Jan 30, 2012 at 06:48:47PM -0500, Noah Misch wrote:

On Tue, Jan 24, 2012 at 03:47:16PM -0300, Alvaro Herrera wrote:

* Columns that are part of the key
Noah thinks the set of columns should only consider those actually referenced
by keys, not those that *could* be referenced.

Well, do you disagree? To me it's low-hanging fruit, because it isolates the
UPDATE-time overhead of this patch to FK-referenced tables rather than all
tables having a PK or PK-like index (often just "all tables").

You have not answered my question above.

nm

#50Simon Riggs
simon@2ndQuadrant.com
In reply to: Noah Misch (#49)
Re: foreign key locks, 2nd attempt

On Wed, Feb 22, 2012 at 5:00 PM, Noah Misch <noah@leadboat.com> wrote:

All in all, I think this is in pretty much final shape.  Only pg_upgrade
bits are still missing.  If sharp eyes could give this a critical look
and knuckle-cracking testers could give it a spin, that would be
helpful.

Lack of pg_upgrade support leaves this version incomplete, because that
omission would constitute a blocker for beta 2.  This version changes as much
code compared to the version I reviewed at the beginning of the CommitFest as
that version changed overall.  In that light, it's time to close the books on
this patch for the purpose of this CommitFest; I'm marking it Returned with
Feedback.  Thanks for your efforts thus far.

My view would be that with 90 files touched this is a very large
patch, so that alone makes me wonder whether we should commit this
patch, so I agree with Noah and compliment him on an excellent
detailed review.

However, review of such a large patch should not be simply pass or
fail. We should be looking back at the original problem and ask
ourselves whether some subset of the patch could solve a useful subset
of the problem. For me, that seems quite likely and this is very
definitely an important patch.

Even if we can't solve some part of the problem we can at least commit
some useful parts of infrastructure to allow later work to happen more
smoothly and quickly.

So please let's not focus on the 100%, lets focus on 80/20.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#51Jeroen Vermeulen
jtv@xs4all.nl
In reply to: Simon Riggs (#50)
Re: foreign key locks, 2nd attempt

On 2012-02-23 10:18, Simon Riggs wrote:

However, review of such a large patch should not be simply pass or
fail. We should be looking back at the original problem and ask
ourselves whether some subset of the patch could solve a useful subset
of the problem. For me, that seems quite likely and this is very
definitely an important patch.

Even if we can't solve some part of the problem we can at least commit
some useful parts of infrastructure to allow later work to happen more
smoothly and quickly.

So please let's not focus on the 100%, lets focus on 80/20.

The suggested immutable-column constraint was meant as a potential
"80/20 workaround." Definitely not a full solution, helpful to some,
probably easier to do. I don't know if an immutable key would actually
be enough to elide foreign-key locks though.

Simon, I think you had a reason why it couldn't work, but I didn't quite
get your meaning and didn't want to distract things further at that
stage. You wrote that it "doesn't do what KEY LOCKS are designed to
do"... any chance you might recall what the problem was?

I don't mean to be pushy about my pet idea, and heaven knows I don't
have time to implement it, but it'd be good to know whether I should put
the whole thought to rest.

Jeroen

#52Simon Riggs
simon@2ndQuadrant.com
In reply to: Noah Misch (#18)
Re: foreign key locks, 2nd attempt

On Sun, Dec 4, 2011 at 12:20 PM, Noah Misch <noah@leadboat.com> wrote:

Making pg_multixact persistent across clean shutdowns is no bridge to cross
lightly, since it means committing to an on-disk format for an indefinite
period.  We should do it; the benefits of this patch justify it, and I haven't
identified a way to avoid it without incurring worse problems.

I can't actually see anything in the patch that explains why this is
required. (That is something we should reject more patches on, since
it creates a higher maintenance burden).

Can someone explain? We might think of a way to avoid that.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#53Simon Riggs
simon@2ndQuadrant.com
In reply to: Jeroen Vermeulen (#51)
Re: foreign key locks, 2nd attempt

On Thu, Feb 23, 2012 at 1:08 PM, Jeroen Vermeulen <jtv@xs4all.nl> wrote:

Simon, I think you had a reason why it couldn't work, but I didn't quite get
your meaning and didn't want to distract things further at that stage.  You
wrote that it "doesn't do what KEY LOCKS are designed to do"...  any chance
you might recall what the problem was?

The IMMUTABLE idea would work, but it requires all users to recode
their apps. By the time they've done that we'll have probably fixed
the problem in full anyway, so then we have to ask them to stop again,
which is hard so we'll be stuck with a performance tweak that applies
to just one release. So its the fully automatic solution we're looking
for. I don't object to someone implementing IMMUTABLE, I'm just saying
its not a way to get this patch simpler and therefore acceptable.

If people are willing to recode apps to avoid this then hire me and
I'll tell you how ;-)

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#54Alvaro Herrera
alvherre@commandprompt.com
In reply to: Simon Riggs (#52)
Re: foreign key locks, 2nd attempt

Excerpts from Simon Riggs's message of jue feb 23 11:15:45 -0300 2012:

On Sun, Dec 4, 2011 at 12:20 PM, Noah Misch <noah@leadboat.com> wrote:

Making pg_multixact persistent across clean shutdowns is no bridge to cross
lightly, since it means committing to an on-disk format for an indefinite
period.  We should do it; the benefits of this patch justify it, and I haven't
identified a way to avoid it without incurring worse problems.

I can't actually see anything in the patch that explains why this is
required. (That is something we should reject more patches on, since
it creates a higher maintenance burden).

Can someone explain? We might think of a way to avoid that.

Sure. The problem is that we are allowing updated rows to be locked (and
locked rows to be updated). This means that we need to store extended
Xmax information in tuples that goes beyond mere locks, which is what we
were doing previously -- they may now have locks and updates simultaneously.

(In the previous code, a multixact never meant an update, it always
signified only shared locks. After a crash, all backends that could
have been holding locks must necessarily be gone, so the multixact info
is not interesting and can be treated like the tuple is simply live.)

This means that this extended Xmax info needs to be able to survive, so
that it's possible to retrieve it after a crash; because even if the
lockers are all gone, the updater might have committed and this means
the tuple is dead. If we failed to keep this, the tuple would be
considered live which would be wrong because the other version of the
tuple, which was created by the update, is also live.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#55Simon Riggs
simon@2ndQuadrant.com
In reply to: Alvaro Herrera (#54)
Re: foreign key locks, 2nd attempt

On Thu, Feb 23, 2012 at 3:04 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Excerpts from Simon Riggs's message of jue feb 23 11:15:45 -0300 2012:

On Sun, Dec 4, 2011 at 12:20 PM, Noah Misch <noah@leadboat.com> wrote:

Making pg_multixact persistent across clean shutdowns is no bridge to cross
lightly, since it means committing to an on-disk format for an indefinite
period.  We should do it; the benefits of this patch justify it, and I haven't
identified a way to avoid it without incurring worse problems.

I can't actually see anything in the patch that explains why this is
required. (That is something we should reject more patches on, since
it creates a higher maintenance burden).

Can someone explain? We might think of a way to avoid that.

Sure.  The problem is that we are allowing updated rows to be locked (and
locked rows to be updated).  This means that we need to store extended
Xmax information in tuples that goes beyond mere locks, which is what we
were doing previously -- they may now have locks and updates simultaneously.

(In the previous code, a multixact never meant an update, it always
signified only shared locks.  After a crash, all backends that could
have been holding locks must necessarily be gone, so the multixact info
is not interesting and can be treated like the tuple is simply live.)

This means that this extended Xmax info needs to be able to survive, so
that it's possible to retrieve it after a crash; because even if the
lockers are all gone, the updater might have committed and this means
the tuple is dead.  If we failed to keep this, the tuple would be
considered live which would be wrong because the other version of the
tuple, which was created by the update, is also live.

OK, thanks.

So why do we need pg_upgrade support?

If pg_multixact is not persistent now, surely there is no requirement
to have pg_upgrade do any form of upgrade? The only time we'll need to
do this is from 9.2 to 9.3, which can of course occur any time in next
year. That doesn't sound like a reason to block a patch now, because
of something that will be needed a year from now.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#56Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#54)
Re: foreign key locks, 2nd attempt

Alvaro Herrera <alvherre@commandprompt.com> writes:

Sure. The problem is that we are allowing updated rows to be locked (and
locked rows to be updated). This means that we need to store extended
Xmax information in tuples that goes beyond mere locks, which is what we
were doing previously -- they may now have locks and updates simultaneously.

(In the previous code, a multixact never meant an update, it always
signified only shared locks. After a crash, all backends that could
have been holding locks must necessarily be gone, so the multixact info
is not interesting and can be treated like the tuple is simply live.)

Ugh. I had not been paying attention to what you were doing in this
patch, and now that I read this I wish I had objected earlier. This
seems like a horrid mess that's going to be unsustainable both from a
complexity and a performance standpoint. The only reason multixacts
were tolerable at all was that they had only one semantics. Changing
it so that maybe a multixact represents an actual updater and maybe
it doesn't is not sane.

regards, tom lane

#57Alvaro Herrera
alvherre@commandprompt.com
In reply to: Simon Riggs (#50)
Re: foreign key locks, 2nd attempt

Excerpts from Simon Riggs's message of jue feb 23 06:18:57 -0300 2012:

On Wed, Feb 22, 2012 at 5:00 PM, Noah Misch <noah@leadboat.com> wrote:

All in all, I think this is in pretty much final shape.  Only pg_upgrade
bits are still missing.  If sharp eyes could give this a critical look
and knuckle-cracking testers could give it a spin, that would be
helpful.

Lack of pg_upgrade support leaves this version incomplete, because that
omission would constitute a blocker for beta 2.  This version changes as much
code compared to the version I reviewed at the beginning of the CommitFest as
that version changed overall.  In that light, it's time to close the books on
this patch for the purpose of this CommitFest; I'm marking it Returned with
Feedback.  Thanks for your efforts thus far.

Now this is an interesting turn of events. I must thank you for your
extensive review effort in the current version of the patch, and also
thank you and credit you for the idea that initially kicked this patch
from the older, smaller, simpler version I wrote during the 9.1 timeline
(which you also reviewed exhaustively). Without your and Simon's
brilliant ideas, this patch wouldn't exist at all.

I completely understand that you don't want to review this latest
version of the patch; it's a lot of effort and I wouldn't inflict it on
anybody who hasn't not volunteered. However, it doesn't seem to me that
this is reason to boot the patch from the commitfest. I think the thing
to do would be to remove yourself from the reviewers column and set it
back to "needs review", so that other reviewers can pick it up.

As for the late code churn, it mostly happened as a result of your
own feedback; I would have left most of it in the original state, but as
I went ahead it seemed much better to refactor things. This is mostly
in heapam.c. As for multixact.c, it also had a lot of churn, but that
was mostly to restore it to the state it has in the master branch,
dropping much of the code I had written to handle multixact truncation.
The new code there and in the vacuum code path (relminmxid and so on) is
a lot smaller than that other code was, and it's closely based on
relfrozenxid which is a known piece of technology.

My view would be that with 90 files touched this is a very large
patch, so that alone makes me wonder whether we should commit this
patch, so I agree with Noah and compliment him on an excellent
detailed review.

I note, however, that the bulk of the patch is in three files --
multixact.c, tqual.c, heapam.c, as is clearly illustrated in the diff
stats I posted. The rest of them are touched mostly to follow their new
APIs (and of course to add tests and docs).

To summarize, of 94 files touched in total:
* 22 files are in src/test/isolation/
(new and updated tests and expected files)
* 19 files are in src/include/
* 10 files are in contrib/
* 39 files are in src/backend;
* in that subdir, there are 3097 insertions and 1006 deletions
* 3047 (83%) of which are in heapam.c multixact.c tqual.c
* one is a README

However, review of such a large patch should not be simply pass or
fail. We should be looking back at the original problem and ask
ourselves whether some subset of the patch could solve a useful subset
of the problem. For me, that seems quite likely and this is very
definitely an important patch.

Even if we can't solve some part of the problem we can at least commit
some useful parts of infrastructure to allow later work to happen more
smoothly and quickly.

So please let's not focus on the 100%, lets focus on 80/20.

Well, we have the patch I originally posted in the 9.1 timeframe.
That's a lot smaller and simpler. However, that solves only part of the
blocking problem, and in particular it doesn't fix the initial deadlock
reports from Joel Jacobson at Glue Finance (now renamed Trustly, in case
you wonder about his change of email address) that started this effort
in the first place. I don't think we can cut down to that and still
satisfy the users that requested this; and Glue was just the first one,
because after I started blogging about this, some more people started
asking for it.

I don't think there's any useful middlepoint between that one and the
current one, but maybe I'm wrong.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#58Alvaro Herrera
alvherre@commandprompt.com
In reply to: Simon Riggs (#55)
Re: foreign key locks, 2nd attempt

Excerpts from Simon Riggs's message of jue feb 23 12:12:13 -0300 2012:

On Thu, Feb 23, 2012 at 3:04 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Sure.  The problem is that we are allowing updated rows to be locked (and
locked rows to be updated).  This means that we need to store extended
Xmax information in tuples that goes beyond mere locks, which is what we
were doing previously -- they may now have locks and updates simultaneously.

OK, thanks.

So why do we need pg_upgrade support?

Two reasons. One is that in upgrades from a version that contains this
patch to another version that also contains this patch (i.e. future
upgrades), we need to copy the multixact files from the old cluster to
the new.

The other is that in upgrades from a version that doesn't contain this
patch to a version that does, we need to set the multixact limit values
so that values that were used in the old cluster are returned as empty
values (keeping the old semantics); otherwise they would cause errors
trying to read the member Xids from disk.

If pg_multixact is not persistent now, surely there is no requirement
to have pg_upgrade do any form of upgrade? The only time we'll need to
do this is from 9.2 to 9.3, which can of course occur any time in next
year. That doesn't sound like a reason to block a patch now, because
of something that will be needed a year from now.

I think there's a policy that we must allow upgrades from one beta to
the next, which is why Noah says this is a blocker starting from beta2.

The pg_upgrade code for this is rather simple however. There's no
rocket science there.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#59Alvaro Herrera
alvherre@commandprompt.com
In reply to: Tom Lane (#56)
Re: foreign key locks, 2nd attempt

Excerpts from Tom Lane's message of jue feb 23 12:28:20 -0300 2012:

Alvaro Herrera <alvherre@commandprompt.com> writes:

Sure. The problem is that we are allowing updated rows to be locked (and
locked rows to be updated). This means that we need to store extended
Xmax information in tuples that goes beyond mere locks, which is what we
were doing previously -- they may now have locks and updates simultaneously.

(In the previous code, a multixact never meant an update, it always
signified only shared locks. After a crash, all backends that could
have been holding locks must necessarily be gone, so the multixact info
is not interesting and can be treated like the tuple is simply live.)

Ugh. I had not been paying attention to what you were doing in this
patch, and now that I read this I wish I had objected earlier.

Uhm, yeah, a lot earlier -- I initially blogged about this in August
last year:
http://www.commandprompt.com/blogs/alvaro_herrera/2011/08/fixing_foreign_key_deadlocks_part_three/

and in several posts in pgsql-hackers.

This
seems like a horrid mess that's going to be unsustainable both from a
complexity and a performance standpoint. The only reason multixacts
were tolerable at all was that they had only one semantics. Changing
it so that maybe a multixact represents an actual updater and maybe
it doesn't is not sane.

As far as complexity, yeah, it's a lot more complex now -- no question
about that.

Regarding performance, the good thing about this patch is that if you
have an operation that used to block, it might now not block. So maybe
multixact-related operation is a bit slower than before, but if it
allows you to continue operating rather than sit waiting until some
other transaction releases you, it's much better.

As for sanity -- I regard multixacts as a way to store extended Xmax
information. The original idea was obviously much more limited in that
the extended info was just locking info. We've extended it but I don't
think it's such a stretch.

I have been posting about (most? all of?) the ideas that I've been
following to make this work at all, so that people had plenty of chances
to disagree with them -- and Noah and others did disagree with many of
them, so I changed the patch accordingly. I'm not closed to further
rework, but I'm not going to entirely abandon the idea too lightly.

I'm sure there are bugs too, but hopefully there are as shallow as
interested reviewer eyeballs there are.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#60Kevin Grittner
Kevin.Grittner@wicourts.gov
In reply to: Alvaro Herrera (#59)
Re: foreign key locks, 2nd attempt

Alvaro Herrera <alvherre@commandprompt.com> wrote:

As for sanity -- I regard multixacts as a way to store extended
Xmax information. The original idea was obviously much more
limited in that the extended info was just locking info. We've
extended it but I don't think it's such a stretch.

Since the limitation on what can be stored in xmax was the killer
for Florian's attempt to support SELECT FOR UPDATE in a form which
was arguably more useful (and certainly more convenient for those
converting from other database products), I'm wondering whether
anyone has determined whether this new scheme would allow Florian's
work to be successfully completed. The issues seem very similar.
If this approach also provides a basis for the other work, I think
it helps bolster the argument that this is a good design; if not, I
think it suggests that maybe it should be made more general or
extensible in some way. Once this has to be supported by pg_upgrade
it will be harder to change the format, if that is needed for some
other feature.

-Kevin

#61Alvaro Herrera
alvherre@commandprompt.com
In reply to: Kevin Grittner (#60)
Re: foreign key locks, 2nd attempt

Excerpts from Kevin Grittner's message of jue feb 23 13:31:36 -0300 2012:

Alvaro Herrera <alvherre@commandprompt.com> wrote:

As for sanity -- I regard multixacts as a way to store extended
Xmax information. The original idea was obviously much more
limited in that the extended info was just locking info. We've
extended it but I don't think it's such a stretch.

Since the limitation on what can be stored in xmax was the killer
for Florian's attempt to support SELECT FOR UPDATE in a form which
was arguably more useful (and certainly more convenient for those
converting from other database products), I'm wondering whether
anyone has determined whether this new scheme would allow Florian's
work to be successfully completed. The issues seem very similar.
If this approach also provides a basis for the other work, I think
it helps bolster the argument that this is a good design; if not, I
think it suggests that maybe it should be made more general or
extensible in some way. Once this has to be supported by pg_upgrade
it will be harder to change the format, if that is needed for some
other feature.

I have no idea what improvements Florian was seeking, but multixacts now
have plenty of bit flag space to indicate whatever we want for each
member transaction, so most likely the answer is yes. However we need
to make clear that a single SELECT FOR UPDATE in a tuple does not
currently use a multixact; if we wish to always store flags then we are
forced to use one which incurs a performance hit.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#62Greg Smith
greg@2ndQuadrant.com
In reply to: Alvaro Herrera (#57)
Re: foreign key locks, 2nd attempt

On 02/23/2012 10:43 AM, Alvaro Herrera wrote:

I completely understand that you don't want to review this latest
version of the patch; it's a lot of effort and I wouldn't inflict it on
anybody who hasn't not volunteered. However, it doesn't seem to me that
this is reason to boot the patch from the commitfest. I think the thing
to do would be to remove yourself from the reviewers column and set it
back to "needs review", so that other reviewers can pick it up.

This feature made Robert's list of serious CF concerns, too, and the
idea that majorly revised patches might be punted isn't a new one. Noah
is certainly justified in saying you're off his community support list,
after all the review work he's been doing for this CF.

We here think it would be a shame for all of these other performance
bits to be sorted but still have this one loose though, if it's possible
to keep going on it. It's well known as something on Simon's peeve list
for some time now. I was just reading someone else ranting about how
this foreign key locking issue proves Postgres isn't "enterprise scale"
yesterday, it was part of an article proving why DB2 is worth paying for
I think. This change crosses over into the advocacy area due to that,
albeit only for the people who have been burned by this already.

If the main problem is pg_upgrade complexity, eventually progress on
that front needs to be made. I'm surprised the project has survived
this long without needing anything beyond catalog conversion for
in-place upgrade. That luck won't hold forever.

--
Greg Smith 2ndQuadrant US greg@2ndQuadrant.com Baltimore, MD
PostgreSQL Training, Services, and 24x7 Support www.2ndQuadrant.com

#63Alvaro Herrera
alvherre@commandprompt.com
In reply to: Greg Smith (#62)
Re: foreign key locks, 2nd attempt

Excerpts from Greg Smith's message of jue feb 23 14:48:13 -0300 2012:

On 02/23/2012 10:43 AM, Alvaro Herrera wrote:

I completely understand that you don't want to review this latest
version of the patch; it's a lot of effort and I wouldn't inflict it on
anybody who hasn't not volunteered. However, it doesn't seem to me that
this is reason to boot the patch from the commitfest. I think the thing
to do would be to remove yourself from the reviewers column and set it
back to "needs review", so that other reviewers can pick it up.

This feature made Robert's list of serious CF concerns, too, and the
idea that majorly revised patches might be punted isn't a new one.

Well, this patch (or rather, a previous incarnation of it) got punted
from 9.1's fourth commitfest; I intended to have the new version in
9.2's first CF, but business reasons (which I will not discuss in
public) forced me otherwise. So here we are again -- as I said to Tom,
I don't intend to let go of this one easily, though of course I will
concede to whatever the community decides.

Noah
is certainly justified in saying you're off his community support list,
after all the review work he's been doing for this CF.

Yeah, I can't blame him. I've been trying to focus most of my review
availability on his own patches precisely due to that, but it's very
clear to me that his effort is larger than mine.

We here think it would be a shame for all of these other performance
bits to be sorted but still have this one loose though, if it's possible
to keep going on it. It's well known as something on Simon's peeve list
for some time now. I was just reading someone else ranting about how
this foreign key locking issue proves Postgres isn't "enterprise scale"
yesterday, it was part of an article proving why DB2 is worth paying for
I think. This change crosses over into the advocacy area due to that,
albeit only for the people who have been burned by this already.

Yeah, Simon's been on this particular issue for quite some time -- which
is probably why the initial idea that kickstarted this patch was his.
Personally I've been in the "not enterprise strength" camp for a long
time, mostly unintentionally; you can see that by tracing how my major
patches close holes in that kind of area ("cluster loses indexes", "we
don't have subtransactions", "foreign key concurrency sucks" (--> SELECT
FOR SHARE), "manual vacuum is teh sux0r", and now this one about FKs
again).

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#64Greg Smith
greg@2ndQuadrant.com
In reply to: Alvaro Herrera (#63)
Re: foreign key locks, 2nd attempt

On 02/23/2012 01:04 PM, Alvaro Herrera wrote:

"manual vacuum is teh sux0r"

I think you've just named my next conference talk submission.

--
Greg Smith 2ndQuadrant US greg@2ndQuadrant.com Baltimore, MD

#65Simon Riggs
simon@2ndQuadrant.com
In reply to: Alvaro Herrera (#59)
Re: foreign key locks, 2nd attempt

On Thu, Feb 23, 2012 at 4:01 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

As far as complexity, yeah, it's a lot more complex now -- no question
about that.

As far as complexity goes, would it be easier if we treated the UPDATE
of a primary key column as a DELETE plus an INSERT?

There's not really a logical reason why updating a primary key has
meaning, so allowing an ExecPlanQual to follow the chain across
primary key values doesn't seem valid to me.

That would make all primary keys IMMUTABLE to updates.

No primary key, no problem.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#66Kevin Grittner
Kevin.Grittner@wicourts.gov
In reply to: Alvaro Herrera (#61)
Re: foreign key locks, 2nd attempt

Alvaro Herrera <alvherre@commandprompt.com> wrote:

Excerpts from Kevin Grittner's message:

Since the limitation on what can be stored in xmax was the killer
for Florian's attempt to support SELECT FOR UPDATE in a form
which was arguably more useful (and certainly more convenient for
those converting from other database products), I'm wondering
whether anyone has determined whether this new scheme would allow
Florian's work to be successfully completed. The issues seem
very similar. If this approach also provides a basis for the
other work, I think it helps bolster the argument that this is a
good design; if not, I think it suggests that maybe it should be
made more general or extensible in some way. Once this has to be
supported by pg_upgrade it will be harder to change the format,
if that is needed for some other feature.

I have no idea what improvements Florian was seeking, but
multixacts now have plenty of bit flag space to indicate whatever
we want for each member transaction, so most likely the answer is
yes. However we need to make clear that a single SELECT FOR
UPDATE in a tuple does not currently use a multixact; if we wish
to always store flags then we are forced to use one which incurs a
performance hit.

Well, his effort really started to go into a tailspin on the related
issues here:

http://archives.postgresql.org/pgsql-hackers/2010-12/msg01743.php

... with a summary of the problem and possible directions for a
solution here:

http://archives.postgresql.org/pgsql-hackers/2010-12/msg01833.php

One of the problems that Florian was trying to address is that
people often have a need to enforce something with a lot of
similarity to a foreign key, but with more subtle logic than
declarative foreign keys support. One example would be the case
Robert has used in some presentations, where the manager column in
each row in a project table must contain the id of a row in a person
table *which has the project_manager boolean column set to TRUE*.
Short of using the new serializable transaction isolation level in
all related transactions, hand-coding enforcement of this useful
invariant through trigger code (or application code enforced through
some framework) is very tricky. The change to SELECT FOR UPDATE
that Florian was working on would make it pretty straightforward.

-Kevin

#67Noah Misch
noah@leadboat.com
In reply to: Jeroen Vermeulen (#51)
Re: foreign key locks, 2nd attempt

On Thu, Feb 23, 2012 at 02:08:28PM +0100, Jeroen Vermeulen wrote:

On 2012-02-23 10:18, Simon Riggs wrote:

However, review of such a large patch should not be simply pass or
fail. We should be looking back at the original problem and ask
ourselves whether some subset of the patch could solve a useful subset
of the problem. For me, that seems quite likely and this is very
definitely an important patch.

Even if we can't solve some part of the problem we can at least commit
some useful parts of infrastructure to allow later work to happen more
smoothly and quickly.

So please let's not focus on the 100%, lets focus on 80/20.

The suggested immutable-column constraint was meant as a potential
"80/20 workaround." Definitely not a full solution, helpful to some,
probably easier to do. I don't know if an immutable key would actually
be enough to elide foreign-key locks though.

That alone would not simplify the patch much. INSERT/UPDATE/DELETE on the
foreign side would still need to take some kind of tuple lock on the primary
side to prevent primary-side DELETE. You then still face the complicated case
of a tuple that's both locked and updated (non-key/immutable columns only).
Updates that change keys are relatively straightforward, following what we
already do today. It's the non-key updates that complicate things.

If you had both an immutable column constraint and a never-deleted table
constraint, that combination would be sufficient to simplify the picture.
(Directly or indirectly, it would not actually be a never-deleted constraint
so much as a "you must take AccessExclusiveLock to DELETE" constraint.)
Foreign-side DML would then take an AccessShareLock on the parent table with
no tuple lock at all.

By then, though, that change would share little or no code with the current
patch. It may have its own value, but it's not a means for carving a subset
from the current patch.

Thanks,
nm

#68Alvaro Herrera
alvherre@commandprompt.com
In reply to: Noah Misch (#49)
Re: foreign key locks, 2nd attempt

Excerpts from Noah Misch's message of mié feb 22 14:00:07 -0300 2012:

On Mon, Feb 13, 2012 at 07:16:58PM -0300, Alvaro Herrera wrote:

On Mon, Jan 30, 2012 at 06:48:47PM -0500, Noah Misch wrote:

On Tue, Jan 24, 2012 at 03:47:16PM -0300, Alvaro Herrera wrote:

* Columns that are part of the key
Noah thinks the set of columns should only consider those actually referenced
by keys, not those that *could* be referenced.

Well, do you disagree? To me it's low-hanging fruit, because it isolates the
UPDATE-time overhead of this patch to FK-referenced tables rather than all
tables having a PK or PK-like index (often just "all tables").

You have not answered my question above.

Sorry. The reason I didn't research this is that at the very start of
the discussion it was said that having heapam.c figure out whether
columns are being used as FK destinations or not would be more of a
modularity violation than "indexed columns" already are for HOT support
(this was a contentious issue for HOT, so I don't take it lightly. I
don't think I need any more reasons for Tom to object to this patch, or
more bulk into it. Both are already serious issues.)

In any case, with the way we've defined FOR KEY SHARE locks (in the docs
it's explicitely said that the set of columns considered could vary in
the future), it's a relatively easy patch to add on top of what I've
submitted. Just as the ALTER TABLE bits to add columns to the set of
columns considered, it could be left for a second pass on the issue.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#69Noah Misch
noah@leadboat.com
In reply to: Alvaro Herrera (#68)
Re: foreign key locks, 2nd attempt

On Thu, Feb 23, 2012 at 06:36:42PM -0300, Alvaro Herrera wrote:

Excerpts from Noah Misch's message of mi?? feb 22 14:00:07 -0300 2012:

On Mon, Feb 13, 2012 at 07:16:58PM -0300, Alvaro Herrera wrote:

On Mon, Jan 30, 2012 at 06:48:47PM -0500, Noah Misch wrote:

On Tue, Jan 24, 2012 at 03:47:16PM -0300, Alvaro Herrera wrote:

* Columns that are part of the key
Noah thinks the set of columns should only consider those actually referenced
by keys, not those that *could* be referenced.

Well, do you disagree? To me it's low-hanging fruit, because it isolates the
UPDATE-time overhead of this patch to FK-referenced tables rather than all
tables having a PK or PK-like index (often just "all tables").

You have not answered my question above.

Sorry. The reason I didn't research this is that at the very start of
the discussion it was said that having heapam.c figure out whether
columns are being used as FK destinations or not would be more of a
modularity violation than "indexed columns" already are for HOT support
(this was a contentious issue for HOT, so I don't take it lightly. I
don't think I need any more reasons for Tom to object to this patch, or
more bulk into it. Both are already serious issues.)

That's fair.

In any case, with the way we've defined FOR KEY SHARE locks (in the docs
it's explicitely said that the set of columns considered could vary in
the future), it's a relatively easy patch to add on top of what I've
submitted. Just as the ALTER TABLE bits to add columns to the set of
columns considered, it could be left for a second pass on the issue.

Agreed. Let's have that debate another day, as a follow-on patch.

Thanks for shedding this light.

#70Noah Misch
noah@leadboat.com
In reply to: Alvaro Herrera (#57)
Re: foreign key locks, 2nd attempt

On Thu, Feb 23, 2012 at 12:43:11PM -0300, Alvaro Herrera wrote:

Excerpts from Simon Riggs's message of jue feb 23 06:18:57 -0300 2012:

On Wed, Feb 22, 2012 at 5:00 PM, Noah Misch <noah@leadboat.com> wrote:

All in all, I think this is in pretty much final shape. ??Only pg_upgrade
bits are still missing. ??If sharp eyes could give this a critical look
and knuckle-cracking testers could give it a spin, that would be
helpful.

Lack of pg_upgrade support leaves this version incomplete, because that
omission would constitute a blocker for beta 2. ??This version changes as much
code compared to the version I reviewed at the beginning of the CommitFest as
that version changed overall. ??In that light, it's time to close the books on
this patch for the purpose of this CommitFest; I'm marking it Returned with
Feedback. ??Thanks for your efforts thus far.

Now this is an interesting turn of events. I must thank you for your
extensive review effort in the current version of the patch, and also
thank you and credit you for the idea that initially kicked this patch
from the older, smaller, simpler version I wrote during the 9.1 timeline
(which you also reviewed exhaustively). Without your and Simon's
brilliant ideas, this patch wouldn't exist at all.

I completely understand that you don't want to review this latest
version of the patch; it's a lot of effort and I wouldn't inflict it on
anybody who hasn't not volunteered. However, it doesn't seem to me that
this is reason to boot the patch from the commitfest. I think the thing
to do would be to remove yourself from the reviewers column and set it
back to "needs review", so that other reviewers can pick it up.

It would indeed be wrong to change any patch from Needs Review to Returned
with Feedback on account of a personal distaste for reviewing the patch. I
hope I did not harbor such a motive here. Rather, this CommitFest has given
your patch its fair shake, and I and other reviewers would better serve the
CF's needs by reviewing, say, "ECPG FETCH readahead" instead of your latest
submission. Likewise, you would better serve the CF by evaluating one of the
four non-committer patches that have been Ready for Committer since January.
That's not to imply that the goals of the CF align with my goals, your goals,
or broader PGDG goals. The patch status on commitfest.postgresql.org does
exist solely for the advancement of the CF, and I have set it in accordingly.

As for the late code churn, it mostly happened as a result of your
own feedback; I would have left most of it in the original state, but as
I went ahead it seemed much better to refactor things. This is mostly
in heapam.c. As for multixact.c, it also had a lot of churn, but that
was mostly to restore it to the state it has in the master branch,
dropping much of the code I had written to handle multixact truncation.
The new code there and in the vacuum code path (relminmxid and so on) is
a lot smaller than that other code was, and it's closely based on
relfrozenxid which is a known piece of technology.

I appreciate that.

However, review of such a large patch should not be simply pass or
fail. We should be looking back at the original problem and ask
ourselves whether some subset of the patch could solve a useful subset
of the problem. For me, that seems quite likely and this is very
definitely an important patch.

Incidentally, I find it harmful to think of "Returned with Feedback" as
"fail". For large patches, it's healthier to think of a CF as a bimonthly
project status meeting with stakeholders. When the project is done,
wonderful! When there's work left, that's no great surprise.

Even if we can't solve some part of the problem we can at least commit
some useful parts of infrastructure to allow later work to happen more
smoothly and quickly.

So please let's not focus on the 100%, lets focus on 80/20.

Well, we have the patch I originally posted in the 9.1 timeframe.
That's a lot smaller and simpler. However, that solves only part of the
blocking problem, and in particular it doesn't fix the initial deadlock
reports from Joel Jacobson at Glue Finance (now renamed Trustly, in case
you wonder about his change of email address) that started this effort
in the first place. I don't think we can cut down to that and still
satisfy the users that requested this; and Glue was just the first one,
because after I started blogging about this, some more people started
asking for it.

I don't think there's any useful middlepoint between that one and the
current one, but maybe I'm wrong.

Nothing additional comes to my mind, either. This patch is monolithic.

Thanks,
nm

#71Jeroen Vermeulen
jtv@xs4all.nl
In reply to: Noah Misch (#67)
Re: foreign key locks, 2nd attempt

On 2012-02-23 22:12, Noah Misch wrote:

That alone would not simplify the patch much. INSERT/UPDATE/DELETE on the
foreign side would still need to take some kind of tuple lock on the primary
side to prevent primary-side DELETE. You then still face the complicated case
of a tuple that's both locked and updated (non-key/immutable columns only).
Updates that change keys are relatively straightforward, following what we
already do today. It's the non-key updates that complicate things.

Ah, so there's the technical hitch. From previous discussion I was
under the impression that:

1. Foreign-key updates only inherently conflict with _key_ updates on
the foreign side, and that non-key updates on the foreign side were just
caught in the locking cross-fire, so to speak.

And

2. The DELETE case was somehow trivially accounted for. But, for
instance, there does not seem to be a lighter lock type that DELETE
conflicts with but UPDATE does not. Bummer.

By then, though, that change would share little or no code with the current
patch. It may have its own value, but it's not a means for carving a subset
from the current patch.

No, to be clear, it was never meant to be. Only a possible way to give
users a way out of foreign-key locks more quickly. Not a way to get
some of the branch out to users more quickly.

At any rate, that seems to be moot then. And to be honest, mechanisms
designed for more than one purpose rarely pan out.

Thanks for explaining!

Jeroen

#72Vik Reykja
vikreykja@gmail.com
In reply to: Kevin Grittner (#66)
Re: foreign key locks, 2nd attempt

On Thu, Feb 23, 2012 at 19:44, Kevin Grittner
<Kevin.Grittner@wicourts.gov>wrote:

One of the problems that Florian was trying to address is that
people often have a need to enforce something with a lot of
similarity to a foreign key, but with more subtle logic than
declarative foreign keys support. One example would be the case
Robert has used in some presentations, where the manager column in
each row in a project table must contain the id of a row in a person
table *which has the project_manager boolean column set to TRUE*.
Short of using the new serializable transaction isolation level in
all related transactions, hand-coding enforcement of this useful
invariant through trigger code (or application code enforced through
some framework) is very tricky. The change to SELECT FOR UPDATE
that Florian was working on would make it pretty straightforward.

I'm not sure what Florian's patch does, but I've been trying to advocate
syntax like the following for this exact scenario:

foreign key (manager_id, true) references person (id, is_manager)

Basically, allow us to use constants instead of field names as part of
foreign keys. I have no idea what the implementation aspect of this is,
but I need the user aspect of it and don't know the best way to get it.

#73Kevin Grittner
Kevin.Grittner@wicourts.gov
In reply to: Vik Reykja (#72)
Re: foreign key locks, 2nd attempt

Vik Reykja <vikreykja@gmail.com> wrote:

Kevin Grittner <Kevin.Grittner@wicourts.gov>wrote:

One of the problems that Florian was trying to address is that
people often have a need to enforce something with a lot of
similarity to a foreign key, but with more subtle logic than
declarative foreign keys support. One example would be the case
Robert has used in some presentations, where the manager column
in each row in a project table must contain the id of a row in a
person table *which has the project_manager boolean column set to
TRUE*. Short of using the new serializable transaction isolation
level in all related transactions, hand-coding enforcement of
this useful invariant through trigger code (or application code
enforced through some framework) is very tricky. The change to
SELECT FOR UPDATE that Florian was working on would make it
pretty straightforward.

I'm not sure what Florian's patch does, but I've been trying to
advocate syntax like the following for this exact scenario:

foreign key (manager_id, true) references person (id, is_manager)

Basically, allow us to use constants instead of field names as
part of foreign keys.

Interesting. IMV, a declarative approach like that is almost always
better than the alternatives, so something like this (possibly with
different syntax) would be another step in the right direction. I
suspect that there will always be a few corner cases where the
business logic required is too esoteric to be handled by a
generalized declarative construct, so I think Florian's idea still
has merit -- especially if we want to ease the transition to
PostgreSQL for large shops using other products.

I have no idea what the implementation aspect of this is,
but I need the user aspect of it and don't know the best way to
get it.

There are those in the community who make their livings by helping
people get the features they want. If you have some money to fund
development, I would bet you could get this addressed -- it sure
sounds reasonable to me.

-Kevin

#74Robert Haas
robertmhaas@gmail.com
In reply to: Alvaro Herrera (#59)
Re: foreign key locks, 2nd attempt

On Thu, Feb 23, 2012 at 11:01 AM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

This
seems like a horrid mess that's going to be unsustainable both from a
complexity and a performance standpoint.  The only reason multixacts
were tolerable at all was that they had only one semantics.  Changing
it so that maybe a multixact represents an actual updater and maybe
it doesn't is not sane.

As far as complexity, yeah, it's a lot more complex now -- no question
about that.

Regarding performance, the good thing about this patch is that if you
have an operation that used to block, it might now not block.  So maybe
multixact-related operation is a bit slower than before, but if it
allows you to continue operating rather than sit waiting until some
other transaction releases you, it's much better.

That's probably true, although there is some deferred cost that is
hard to account for. You might not block immediately, but then later
somebody might block either because the mxact SLRU now needs fsyncs or
because they've got to decode an mxid long after the relevant segment
has been evicted from the SLRU buffers. In general, it's hard to
bound that latter cost, because you only avoid blocking once (when the
initial update happens) but you might pay the extra cost of decoding
the mxid as many times as the row is read, which could be arbitrarily
many. How much of a problem that is in practice, I'm not completely
sure, but it has worried me before and it still does. In the worst
case scenario, a handful of frequently-accessed rows with MXIDs all of
whose members are dead except for the UPDATE they contain could result
in continual SLRU cache-thrashing.

From a performance standpoint, we really need to think not only about
the cases where the patch wins, but also, and maybe more importantly,
the cases where it loses. There are some cases where the current
mechanism, use SHARE locks for foreign keys, is adequate. In
particular, it's adequate whenever the parent table is not updated at
all, or only very lightly. I believe that those people will pay
somewhat more with this patch, and especially in any case where
backends end up waiting for fsyncs in order to create new mxids, but
also just because I think this patch will have the effect of
increasing the space consumed by each individual mxid, which imposes a
distributed cost of its own.

I think we should avoid having a theoretical argument about how
serious these problems are; instead, you should try to construct
somewhat-realistic worst case scenarios and benchmark them. Tom's
complaint about code complexity is basically a question of opinion, so
I don't know how to evaluate that objectively, but performance is
something we can measure. We might still disagree on the
interpretation of the results, but I still think having some real
numbers to talk about based on carefully-thought-out test cases would
advance the debate.

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

#75Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Alvaro Herrera (#59)
Re: foreign key locks, 2nd attempt

On 23.02.2012 18:01, Alvaro Herrera wrote:

Excerpts from Tom Lane's message of jue feb 23 12:28:20 -0300 2012:

Alvaro Herrera<alvherre@commandprompt.com> writes:

Sure. The problem is that we are allowing updated rows to be locked (and
locked rows to be updated). This means that we need to store extended
Xmax information in tuples that goes beyond mere locks, which is what we
were doing previously -- they may now have locks and updates simultaneously.

(In the previous code, a multixact never meant an update, it always
signified only shared locks. After a crash, all backends that could
have been holding locks must necessarily be gone, so the multixact info
is not interesting and can be treated like the tuple is simply live.)

Ugh. I had not been paying attention to what you were doing in this
patch, and now that I read this I wish I had objected earlier.

Uhm, yeah, a lot earlier -- I initially blogged about this in August
last year:
http://www.commandprompt.com/blogs/alvaro_herrera/2011/08/fixing_foreign_key_deadlocks_part_three/

and in several posts in pgsql-hackers.

This
seems like a horrid mess that's going to be unsustainable both from a
complexity and a performance standpoint. The only reason multixacts
were tolerable at all was that they had only one semantics. Changing
it so that maybe a multixact represents an actual updater and maybe
it doesn't is not sane.

As far as complexity, yeah, it's a lot more complex now -- no question
about that.

How about assigning a new, real, transaction id, to represent the group
of transaction ids. The new transaction id would be treated as a
subtransaction of the updater, and the xids of the lockers would be
stored in the multixact-members slru. That way the multixact structures
wouldn't need to survive a crash; you don't care about the shared
lockers after a crash, and the xid of the updater would be safely stored
as is in the xmax field.

That way you wouldn't need to handle multixact wraparound, because we
already handle xid wraparound, and you wouldn't need to make multixact
slrus crash-safe.

Not sure what the performance implications would be. You would use up
xids more quickly, which would require more frequent anti-wraparound
vacuuming. And if we just start using real xids as the key to
multixact-offsets slru, we would need to extend that a lot more often.
But I feel it would probably be acceptable.

--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com

#76Noah Misch
noah@leadboat.com
In reply to: Heikki Linnakangas (#75)
Re: foreign key locks, 2nd attempt

On Mon, Feb 27, 2012 at 02:13:32PM +0200, Heikki Linnakangas wrote:

On 23.02.2012 18:01, Alvaro Herrera wrote:

As far as complexity, yeah, it's a lot more complex now -- no question
about that.

How about assigning a new, real, transaction id, to represent the group
of transaction ids. The new transaction id would be treated as a
subtransaction of the updater, and the xids of the lockers would be
stored in the multixact-members slru. That way the multixact structures
wouldn't need to survive a crash; you don't care about the shared
lockers after a crash, and the xid of the updater would be safely stored
as is in the xmax field.

That way you wouldn't need to handle multixact wraparound, because we
already handle xid wraparound, and you wouldn't need to make multixact
slrus crash-safe.

Not sure what the performance implications would be. You would use up
xids more quickly, which would require more frequent anti-wraparound
vacuuming. And if we just start using real xids as the key to
multixact-offsets slru, we would need to extend that a lot more often.
But I feel it would probably be acceptable.

When a key locker arrives after the updater and creates this implicit
subtransaction of the updater, how might you arrange for the xid's clog status
to eventually get updated in accordance with the updater's outcome?

Thanks,
nm

#77Simon Riggs
simon@2ndQuadrant.com
In reply to: Noah Misch (#76)
Re: foreign key locks, 2nd attempt

On Tue, Feb 28, 2012 at 12:28 AM, Noah Misch <noah@leadboat.com> wrote:

On Mon, Feb 27, 2012 at 02:13:32PM +0200, Heikki Linnakangas wrote:

On 23.02.2012 18:01, Alvaro Herrera wrote:

As far as complexity, yeah, it's a lot more complex now -- no question
about that.

How about assigning a new, real, transaction id, to represent the group
of transaction ids. The new transaction id would be treated as a
subtransaction of the updater, and the xids of the lockers would be
stored in the multixact-members slru. That way the multixact structures
wouldn't need to survive a crash; you don't care about the shared
lockers after a crash, and the xid of the updater would be safely stored
as is in the xmax field.

That way you wouldn't need to handle multixact wraparound, because we
already handle xid wraparound, and you wouldn't need to make multixact
slrus crash-safe.

Not sure what the performance implications would be. You would use up
xids more quickly, which would require more frequent anti-wraparound
vacuuming. And if we just start using real xids as the key to
multixact-offsets slru, we would need to extend that a lot more often.
But I feel it would probably be acceptable.

When a key locker arrives after the updater and creates this implicit
subtransaction of the updater, how might you arrange for the xid's clog status
to eventually get updated in accordance with the updater's outcome?

Somewhat off-topic, but just seen another bad case of FK lock contention.

Thanks for working on this everybody.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#78Simon Riggs
simon@2ndQuadrant.com
In reply to: Robert Haas (#74)
Re: foreign key locks, 2nd attempt

On Mon, Feb 27, 2012 at 2:47 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, Feb 23, 2012 at 11:01 AM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

This
seems like a horrid mess that's going to be unsustainable both from a
complexity and a performance standpoint.  The only reason multixacts
were tolerable at all was that they had only one semantics.  Changing
it so that maybe a multixact represents an actual updater and maybe
it doesn't is not sane.

As far as complexity, yeah, it's a lot more complex now -- no question
about that.

Regarding performance, the good thing about this patch is that if you
have an operation that used to block, it might now not block.  So maybe
multixact-related operation is a bit slower than before, but if it
allows you to continue operating rather than sit waiting until some
other transaction releases you, it's much better.

That's probably true, although there is some deferred cost that is
hard to account for.  You might not block immediately, but then later
somebody might block either because the mxact SLRU now needs fsyncs or
because they've got to decode an mxid long after the relevant segment
has been evicted from the SLRU buffers.  In general, it's hard to
bound that latter cost, because you only avoid blocking once (when the
initial update happens) but you might pay the extra cost of decoding
the mxid as many times as the row is read, which could be arbitrarily
many.  How much of a problem that is in practice, I'm not completely
sure, but it has worried me before and it still does.  In the worst
case scenario, a handful of frequently-accessed rows with MXIDs all of
whose members are dead except for the UPDATE they contain could result
in continual SLRU cache-thrashing.

Cases I regularly see involve wait times of many seconds.

When this patch helps, it will help performance by algorithmic gains,
so perhaps x10-100.

That can and should be demonstrated though, I agree.

From a performance standpoint, we really need to think not only about
the cases where the patch wins, but also, and maybe more importantly,
the cases where it loses.  There are some cases where the current
mechanism, use SHARE locks for foreign keys, is adequate.  In
particular, it's adequate whenever the parent table is not updated at
all, or only very lightly.  I believe that those people will pay
somewhat more with this patch, and especially in any case where
backends end up waiting for fsyncs in order to create new mxids, but
also just because I think this patch will have the effect of
increasing the space consumed by each individual mxid, which imposes a
distributed cost of its own.

That is a concern also.

It's taken me a while reviewing the patch to realise that space usage
is actually 4 times worse than before.

I think we should avoid having a theoretical argument about how
serious these problems are; instead, you should try to construct
somewhat-realistic worst case scenarios and benchmark them.  Tom's
complaint about code complexity is basically a question of opinion, so
I don't know how to evaluate that objectively, but performance is
something we can measure.  We might still disagree on the
interpretation of the results, but I still think having some real
numbers to talk about based on carefully-thought-out test cases would
advance the debate.

It's a shame that the isolation tester can't be used directly by
pgbench - I think we need something similar for performance regression
testing.

So yes, performance testing is required.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#79Alvaro Herrera
alvherre@commandprompt.com
In reply to: Simon Riggs (#78)
Re: foreign key locks, 2nd attempt

Excerpts from Simon Riggs's message of lun mar 05 15:28:59 -0300 2012:

On Mon, Feb 27, 2012 at 2:47 AM, Robert Haas <robertmhaas@gmail.com> wrote:

From a performance standpoint, we really need to think not only about
the cases where the patch wins, but also, and maybe more importantly,
the cases where it loses.  There are some cases where the current
mechanism, use SHARE locks for foreign keys, is adequate.  In
particular, it's adequate whenever the parent table is not updated at
all, or only very lightly.  I believe that those people will pay
somewhat more with this patch, and especially in any case where
backends end up waiting for fsyncs in order to create new mxids, but
also just because I think this patch will have the effect of
increasing the space consumed by each individual mxid, which imposes a
distributed cost of its own.

That is a concern also.

It's taken me a while reviewing the patch to realise that space usage
is actually 4 times worse than before.

Eh. You're probably misreading something. Previously each member of a
multixact used 4 bytes (the size of an Xid). With the current patch a
member uses 5 bytes (same plus a flags byte). An earlier version used
4.25 bytes per multi, which I increased to leave space for future
expansion.

So it's 1.25x worse, not 4x worse.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#80Simon Riggs
simon@2ndQuadrant.com
In reply to: Alvaro Herrera (#79)
Re: foreign key locks, 2nd attempt

On Mon, Mar 5, 2012 at 6:37 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Excerpts from Simon Riggs's message of lun mar 05 15:28:59 -0300 2012:

On Mon, Feb 27, 2012 at 2:47 AM, Robert Haas <robertmhaas@gmail.com> wrote:

From a performance standpoint, we really need to think not only about
the cases where the patch wins, but also, and maybe more importantly,
the cases where it loses.  There are some cases where the current
mechanism, use SHARE locks for foreign keys, is adequate.  In
particular, it's adequate whenever the parent table is not updated at
all, or only very lightly.  I believe that those people will pay
somewhat more with this patch, and especially in any case where
backends end up waiting for fsyncs in order to create new mxids, but
also just because I think this patch will have the effect of
increasing the space consumed by each individual mxid, which imposes a
distributed cost of its own.

That is a concern also.

It's taken me a while reviewing the patch to realise that space usage
is actually 4 times worse than before.

Eh.  You're probably misreading something.  Previously each member of a
multixact used 4 bytes (the size of an Xid).  With the current patch a
member uses 5 bytes (same plus a flags byte).  An earlier version used
4.25 bytes per multi, which I increased to leave space for future
expansion.

So it's 1.25x worse, not 4x worse.

Thanks for correcting me. That sounds better.

It does however, illustrate my next review comment which is that the
comments and README items are sorely lacking here. It's quite hard to
see how it works, let along comment on major design decisions. It
would help myself and others immensely if we could improve that.

Is there a working copy on a git repo? Easier than waiting for next
versions of a patch.

My other comments so far are

* some permutations commented out - no comments as to why
Something of a fault with the isolation tester that it just shows
output, there's no way to record expected output in the spec

Comments required for these points

* Why do we need multixact to be persistent? Do we need every page of
multixact to be persistent, or just particular pages in certain
circumstances?

* Why do we need to expand multixact with flags? Can we avoid that in
some cases?

* Why do we need to store just single xids in multixact members?
Didn't understand comments, no explanation

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#81Alvaro Herrera
alvherre@commandprompt.com
In reply to: Simon Riggs (#80)
Re: foreign key locks, 2nd attempt

Excerpts from Simon Riggs's message of lun mar 05 16:34:10 -0300 2012:

On Mon, Mar 5, 2012 at 6:37 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

It does however, illustrate my next review comment which is that the
comments and README items are sorely lacking here. It's quite hard to
see how it works, let along comment on major design decisions. It
would help myself and others immensely if we could improve that.

Hm. Okay.

Is there a working copy on a git repo? Easier than waiting for next
versions of a patch.

No, I don't have an external mirror of my local repo.

My other comments so far are

* some permutations commented out - no comments as to why
Something of a fault with the isolation tester that it just shows
output, there's no way to record expected output in the spec

The reason they are commented out is that they are "invalid", that is,
it requires running a command on a session that's blocked in the
previous command. Obviously, that cannot happen in real life.

isolationtester now has support for detecting such conditions; if the
spec specifies running a command in a locked session, the permutation is
killed with an error message "invalid permutation" and just continues
with the next permutation. It used to simply die, aborting the test.
Maybe we could just modify the specs so that all permutations are there
(this can be done by simply removing the permutation lines), and the
"invalid permutation" messages are part of the expected file. Would
that be better?

Comments required for these points

* Why do we need multixact to be persistent? Do we need every page of
multixact to be persistent, or just particular pages in certain
circumstances?

Any page that contains at least one multi with an update as a member
must persist. It's possible that some pages contain no update (and this
is even likely in some workloads, if updates are rare), but I'm not sure
it's worth complicating the code to cater for early removal of some
pages.

* Why do we need to expand multixact with flags? Can we avoid that in
some cases?

Did you read my blog post?
http://www.commandprompt.com/blogs/alvaro_herrera/2011/08/fixing_foreign_key_deadlocks_part_three/
This explains the reason -- the point is that we need to distinguish the
lock strength acquired by each locker.

* Why do we need to store just single xids in multixact members?
Didn't understand comments, no explanation

This is just for SELECT FOR SHARE. We don't have a hint bit to indicate
"this tuple has a for-share lock", so we need to create a multi for it.
Since FOR SHARE is probably going to be very uncommon, this isn't likely
to be a problem. We're mainly catering for users of SELECT FOR SHARE so
that it continues to work, i.e. maintain backwards compatibility.

(Maybe I misunderstood your question -- what I think you're asking is,
"why are there some multixacts that have a single member?")

I'll try to come up with a good place to add some paragraphs about all
this. Please let me know if answers here are unclear and/or you have
further questions.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#82Simon Riggs
simon@2ndQuadrant.com
In reply to: Alvaro Herrera (#81)
Re: foreign key locks, 2nd attempt

On Mon, Mar 5, 2012 at 7:53 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

My other comments so far are

* some permutations commented out - no comments as to why
Something of a fault with the isolation tester that it just shows
output, there's no way to record expected output in the spec

The reason they are commented out is that they are "invalid", that is,
it requires running a command on a session that's blocked in the
previous command.  Obviously, that cannot happen in real life.

isolationtester now has support for detecting such conditions; if the
spec specifies running a command in a locked session, the permutation is
killed with an error message "invalid permutation" and just continues
with the next permutation.  It used to simply die, aborting the test.
Maybe we could just modify the specs so that all permutations are there
(this can be done by simply removing the permutation lines), and the
"invalid permutation" messages are part of the expected file.  Would
that be better?

It would be better to have an isolation tester mode that checks to see
it was invalid and if not, report that.

At the moment we can't say why you commented something out. There's no
comment or explanation, and we need something, otherwise 3 years from
now we'll be completely in the dark.

Comments required for these points

* Why do we need multixact to be persistent? Do we need every page of
multixact to be persistent, or just particular pages in certain
circumstances?

Any page that contains at least one multi with an update as a member
must persist.  It's possible that some pages contain no update (and this
is even likely in some workloads, if updates are rare), but I'm not sure
it's worth complicating the code to cater for early removal of some
pages.

If the multixact contains an xid and that is being persisted then you
need to set an LSN to ensure that a page writes causes an XLogFlush()
before the multixact write. And you need to set do_fsync, no? Or
explain why not in comments...

I was really thinking we could skip the fsync of a page if we've not
persisted anything important on that page, since that was one of
Robert's performance points.

* Why do we need to expand multixact with flags? Can we avoid that in
some cases?

Did you read my blog post?
http://www.commandprompt.com/blogs/alvaro_herrera/2011/08/fixing_foreign_key_deadlocks_part_three/
This explains the reason -- the point is that we need to distinguish the
lock strength acquired by each locker.

Thanks, I will, but it all belongs in a README please.

* Why do we need to store just single xids in multixact members?
Didn't understand comments, no explanation

This is just for SELECT FOR SHARE.  We don't have a hint bit to indicate
"this tuple has a for-share lock", so we need to create a multi for it.
Since FOR SHARE is probably going to be very uncommon, this isn't likely
to be a problem.  We're mainly catering for users of SELECT FOR SHARE so
that it continues to work, i.e. maintain backwards compatibility.

Good, thanks.

Are we actively recommending people use FOR KEY SHARE rather than FOR
SHARE, in explicit use?

(Maybe I misunderstood your question -- what I think you're asking is,
"why are there some multixacts that have a single member?")

I'll try to come up with a good place to add some paragraphs about all
this.  Please let me know if answers here are unclear and/or you have
further questions.

Thanks

I think we need to define some test workloads to measure the
performance impact of this patch. We need to be certain that it has a
good impact in target cases, plus a known impact in other cases.

Suggest

* basic pgbench - no RI

* inserts into large table, RI checks to small table, no activity on small table

* large table parent, large table: child
20 child rows per parent, fk from child to parent
updates of multiple children at same time
low/medium/heavy locking

* large table parent, large table: child
20 child rows per parent,fk from child to parent
updates of parent and child at same time
low/medium/heavy locking

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#83Alvaro Herrera
alvherre@commandprompt.com
In reply to: Simon Riggs (#80)
Re: foreign key locks, 2nd attempt

Excerpts from Simon Riggs's message of lun mar 05 16:34:10 -0300 2012:

It does however, illustrate my next review comment which is that the
comments and README items are sorely lacking here. It's quite hard to
see how it works, let along comment on major design decisions. It
would help myself and others immensely if we could improve that.

Here's a first attempt at a README illustrating this. I intend this to
be placed in src/backend/access/heap/README.tuplock; the first three
paragraphs are stolen from the comment in heap_lock_tuple, so I'd remove
those from there, directing people to this new file instead. Is there
something that you think should be covered more extensively (or at all)
here?

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

Locking tuples
--------------

Because the shared-memory lock table is of finite size, but users could
reasonably want to lock large numbers of tuples, we do not rely on the
standard lock manager to store tuple-level locks over the long term. Instead,
a tuple is marked as locked by setting the current transaction's XID as its
XMAX, and setting additional infomask bits to distinguish this usage from the
more normal case of having deleted the tuple. When multiple transactions
concurrently lock a tuple, a MultiXact is used; see below.

When it is necessary to wait for a tuple-level lock to be released, the basic
delay is provided by XactLockTableWait or MultiXactIdWait on the contents of
the tuple's XMAX. However, that mechanism will release all waiters
concurrently, so there would be a race condition as to which waiter gets the
tuple, potentially leading to indefinite starvation of some waiters. The
possibility of share-locking makes the problem much worse --- a steady stream
of share-lockers can easily block an exclusive locker forever. To provide
more reliable semantics about who gets a tuple-level lock first, we use the
standard lock manager. The protocol for waiting for a tuple-level lock is
really

LockTuple()
XactLockTableWait()
mark tuple as locked by me
UnlockTuple()

When there are multiple waiters, arbitration of who is to get the lock next
is provided by LockTuple(). However, at most one tuple-level lock will
be held or awaited per backend at any time, so we don't risk overflow
of the lock table. Note that incoming share-lockers are required to
do LockTuple as well, if there is any conflict, to ensure that they don't
starve out waiting exclusive-lockers. However, if there is not any active
conflict for a tuple, we don't incur any extra overhead.

We provide four levels of tuple locking strength: SELECT FOR KEY UPDATE is
super-exclusive locking (used to delete tuples and more generally to update
tuples modifying the values of the columns that make up the key of the tuple);
SELECT FOR UPDATE is a standards-compliant exclusive lock; SELECT FOR SHARE
implements shared locks; and finally SELECT FOR KEY SHARE is a super-weak mode
that does not conflict with exclusive mode, but conflicts with SELECT FOR KEY
UPDATE. This last mode implements a mode just strong enough to implement RI
checks, i.e. it ensures that tuples do not go away from under a check, without
blocking when some other transaction that want to update the tuple without
changing its key.

The conflict table is:

KEY UPDATE UPDATE SHARE KEY SHARE
KEY UPDATE conflict conflict conflict conflict
UPDATE conflict conflict conflict
SHARE conflict conflict
KEY SHARE conflict

When there is a single locker in a tuple, we can just store the locking info
in the tuple itself. We do this by storing the locker's Xid in XMAX, and
setting hint bits specifying the locking strength. There is one exception
here: since hint bit space is limited, we do not provide a separate hint bit
for SELECT FOR SHARE, so we have to use the extended info in a MultiXact in
that case. (The other cases, SELECT FOR UPDATE and SELECT FOR KEY SHARE, are
presumably more commonly used due to being the standards-mandated locking
mechanism, or heavily used by the RI code, so we want to provide fast paths
for those.)

MultiXacts
----------

A tuple header provides very limited space for storing information about tuple
locking and updates: there is room only for a single Xid and a small number of
hint bits. Whenever we need to store more than one lock, we replace the first
locker's Xid with a new MultiXactId. Each MultiXact provides extended locking
data; it comprises an array of Xids plus some flags bits for each one. The
flags are currently used to store the locking strength of each member
transaction. (The flags also distinguish a pure locker from an actual
updater.)

In earlier PostgreSQL releases, a MultiXact always meant that the tuple was
locked in shared mode by multiple transactions. This is no longer the case; a
MultiXact may contain an update or delete Xid. (Keep in mind that tuple locks
in a transaction do not conflict with other tuple locks in the same
transaction, so it's possible to have otherwise conflicting locks in a
MultiXact if they belong to the same transaction).

Note that each lock is attributed to the subtransaction that acquires it.
This means that a subtransaction that aborts is seen as though it releases the
locks it acquired; concurrent transactions can then proceed without having to
wait for the main transaction to finish. It also means that a subtransaction
can upgrade to a stronger lock level than an earlier transaction had, and if
the subxact aborts, the earlier, weaker lock is kept.

The possibility of having an update within a MultiXact means that they must
persist across crashes and restarts: a future reader of the tuple needs to
figure out whether the update committed or aborted. So we have a requirement
that pg_multixact needs to retain pages of its data until we're certain that
the MultiXacts in them are no longer of interest.

VACUUM is in charge of removing old MultiXacts at the time of tuple freezing.
This works in the same way that pg_clog segments are removed: we have a
pg_class column that stores the earliest multixact that could possibly be
stored in the table; the minimum of all such values is stored in a pg_database
column. VACUUM computes the minimum across all pg_database values, and
removes pg_multixact segments older than the minimum.

Hint Bits
---------

The following hint bits are applicable:

- HEAP_XMAX_INVALID
Any tuple with this hint bit set does not have a valid value stored in XMAX.

- HEAP_XMAX_IS_MULTI
This bit is set if the tuple's Xmax is a MultiXactId (as opposed to a
regular TransactionId).

- HEAP_XMAX_LOCK_ONLY
This bit is set when the XMAX is a locker only; that is, if it's a
multixact, it does not contain an update among its members. It's set when
the XMAX is a plain Xid that locked the tuple, as well.

- HEAP_XMAX_KEYSHR_LOCK
- HEAP_XMAX_EXCL_LOCK
These bits indicate the strength of the lock acquired; they are useful when
the XMAX is not a MultiXactId. If it's a multi, the info is to be found in
the member flags. If HEAP_XMAX_IS_MULTI is not set and HEAP_XMAX_LOCK_ONLY
is set, then one of these *must* be set as well.
Note there is no hint bit for a SELECT FOR SHARE lock. Also there is no
separate bit for a SELECT FOR KEY UPDATE lock; this is implemented by the
HEAP_UPDATE_KEY_REVOKED bit.

- HEAP_UPDATE_KEY_REVOKED
This bit lives in t_infomask2. If set, indicates that a transaction updated
this tuple and changed the key values, or a transaction deleted the tuple.
It's set regardless of whether the XMAX is a TransactionId or a MultiXactId.

We currently never set the HEAP_XMAX_COMMITTED when the HEAP_XMAX_IS_MULTI bit
is set.

#84Simon Riggs
simon@2ndQuadrant.com
In reply to: Simon Riggs (#82)
Re: foreign key locks, 2nd attempt

On Mon, Mar 5, 2012 at 8:35 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

* Why do we need multixact to be persistent? Do we need every page of
multixact to be persistent, or just particular pages in certain
circumstances?

Any page that contains at least one multi with an update as a member
must persist.  It's possible that some pages contain no update (and this
is even likely in some workloads, if updates are rare), but I'm not sure
it's worth complicating the code to cater for early removal of some
pages.

If the multixact contains an xid and that is being persisted then you
need to set an LSN to ensure that a page writes causes an XLogFlush()
before the multixact write. And you need to set do_fsync, no? Or
explain why not in comments...

I was really thinking we could skip the fsync of a page if we've not
persisted anything important on that page, since that was one of
Robert's performance points.

We need to increase these values to 32 as well

#define NUM_MXACTOFFSET_BUFFERS 8
#define NUM_MXACTMEMBER_BUFFERS 16

using same logic as for clog.

We're using 25% more space and we already know clog benefits from
increasing them, so there's little doubt we need it here also, since
we are increasing the access rate and potentially the longevity.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#85Simon Riggs
simon@2ndQuadrant.com
In reply to: Alvaro Herrera (#83)
Re: foreign key locks, 2nd attempt

On Tue, Mar 6, 2012 at 7:39 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

We provide four levels of tuple locking strength: SELECT FOR KEY UPDATE is
super-exclusive locking (used to delete tuples and more generally to update
tuples modifying the values of the columns that make up the key of the tuple);
SELECT FOR UPDATE is a standards-compliant exclusive lock; SELECT FOR SHARE
implements shared locks; and finally SELECT FOR KEY SHARE is a super-weak mode
that does not conflict with exclusive mode, but conflicts with SELECT FOR KEY
UPDATE.  This last mode implements a mode just strong enough to implement RI
checks, i.e. it ensures that tuples do not go away from under a check, without
blocking when some other transaction that want to update the tuple without
changing its key.

So there are 4 lock types, but we only have room for 3 on the tuple
header, so we store the least common/deprecated of the 4 types as a
multixactid. Some rewording would help there.

Neat scheme!

My understanding is that all of theses workloads will change

* Users of explicit SHARE lockers will be slightly worse in the case
of the 1st locker, but then after that they'll be the same as before.

* Updates against an RI locked table will be dramatically faster
because of reduced lock waits

...and that these previous workloads are effectively unchanged:

* Stream of RI checks causes mxacts

* Multi row deadlocks still possible

* Queues of writers still wait in the same way

* Deletes don't cause mxacts unless by same transaction

In earlier PostgreSQL releases, a MultiXact always meant that the tuple was
locked in shared mode by multiple transactions.  This is no longer the case; a
MultiXact may contain an update or delete Xid.  (Keep in mind that tuple locks
in a transaction do not conflict with other tuple locks in the same
transaction, so it's possible to have otherwise conflicting locks in a
MultiXact if they belong to the same transaction).

Somewhat confusing, but am getting there.

Note that each lock is attributed to the subtransaction that acquires it.
This means that a subtransaction that aborts is seen as though it releases the
locks it acquired; concurrent transactions can then proceed without having to
wait for the main transaction to finish.  It also means that a subtransaction
can upgrade to a stronger lock level than an earlier transaction had, and if
the subxact aborts, the earlier, weaker lock is kept.

OK

The possibility of having an update within a MultiXact means that they must
persist across crashes and restarts: a future reader of the tuple needs to
figure out whether the update committed or aborted.  So we have a requirement
that pg_multixact needs to retain pages of its data until we're certain that
the MultiXacts in them are no longer of interest.

I think the "no longer of interest" aspect needs to be tracked more
closely because it will necessarily lead to more I/O.

If we store the LSN on each mxact page, as I think we need to, we can
get rid of pages more quickly if we know they don't have an LSN set.
So its possible we can optimise that more.

VACUUM is in charge of removing old MultiXacts at the time of tuple freezing.

You mean mxact segments?

Surely we set hint bits on tuples same as now? Hope so.

This works in the same way that pg_clog segments are removed: we have a
pg_class column that stores the earliest multixact that could possibly be
stored in the table; the minimum of all such values is stored in a pg_database
column.  VACUUM computes the minimum across all pg_database values, and
removes pg_multixact segments older than the minimum.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#86Robert Haas
robertmhaas@gmail.com
In reply to: Alvaro Herrera (#83)
Re: foreign key locks, 2nd attempt

Preliminary comment:

This README is very helpful.

On Tue, Mar 6, 2012 at 2:39 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

We provide four levels of tuple locking strength: SELECT FOR KEY UPDATE is
super-exclusive locking (used to delete tuples and more generally to update
tuples modifying the values of the columns that make up the key of the tuple);
SELECT FOR UPDATE is a standards-compliant exclusive lock; SELECT FOR SHARE
implements shared locks; and finally SELECT FOR KEY SHARE is a super-weak mode
that does not conflict with exclusive mode, but conflicts with SELECT FOR KEY
UPDATE.  This last mode implements a mode just strong enough to implement RI
checks, i.e. it ensures that tuples do not go away from under a check, without
blocking when some other transaction that want to update the tuple without
changing its key.

I feel like there is a naming problem here. The semantics that have
always been associated with SELECT FOR UPDATE are now attached to
SELECT FOR KEY UPDATE; and SELECT FOR UPDATE itself has been weakened.
I think users will be surprised to find that SELECT FOR UPDATE
doesn't block all concurrent updates.

It seems to me that SELECT FOR KEY UPDATE should be called SELECT FOR
UPDATE, and what you're calling SELECT FOR UPDATE should be called
something else - essentially NONKEY UPDATE, though I don't much like
that name.

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

#87Alvaro Herrera
alvherre@commandprompt.com
In reply to: Robert Haas (#86)
Re: foreign key locks, 2nd attempt

Excerpts from Robert Haas's message of mar mar 06 18:10:16 -0300 2012:

Preliminary comment:

This README is very helpful.

Thanks. I feel silly that I didn't write it earlier.

On Tue, Mar 6, 2012 at 2:39 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

We provide four levels of tuple locking strength: SELECT FOR KEY UPDATE is
super-exclusive locking (used to delete tuples and more generally to update
tuples modifying the values of the columns that make up the key of the tuple);
SELECT FOR UPDATE is a standards-compliant exclusive lock; SELECT FOR SHARE
implements shared locks; and finally SELECT FOR KEY SHARE is a super-weak mode
that does not conflict with exclusive mode, but conflicts with SELECT FOR KEY
UPDATE.  This last mode implements a mode just strong enough to implement RI
checks, i.e. it ensures that tuples do not go away from under a check, without
blocking when some other transaction that want to update the tuple without
changing its key.

I feel like there is a naming problem here. The semantics that have
always been associated with SELECT FOR UPDATE are now attached to
SELECT FOR KEY UPDATE; and SELECT FOR UPDATE itself has been weakened.
I think users will be surprised to find that SELECT FOR UPDATE
doesn't block all concurrent updates.

I'm not sure why you say that. Certainly SELECT FOR UPDATE continues to
block all updates. It continues to block SELECT FOR SHARE as well.
The things that it doesn't block are the new SELECT FOR KEY SHARE locks;
since those didn't exist before, it doesn't seem correct to consider
that SELECT FOR UPDATE changed in any way.

The main difference in the UPDATE behavior is that an UPDATE is regarded
as though it might acquire two different lock modes -- it either
acquires SELECT FOR KEY UPDATE if the key is modified, or SELECT FOR
UPDATE if not. Since SELECT FOR KEY UPDATE didn't exist before, we can
consider that previous to this patch, what UPDATE did was always acquire
a lock of strength SELECT FOR UPDATE. So UPDATE also hasn't been
weakened.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#88Simon Riggs
simon@2ndQuadrant.com
In reply to: Robert Haas (#86)
Re: foreign key locks, 2nd attempt

On Tue, Mar 6, 2012 at 9:10 PM, Robert Haas <robertmhaas@gmail.com> wrote:

Preliminary comment:

This README is very helpful.

On Tue, Mar 6, 2012 at 2:39 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

We provide four levels of tuple locking strength: SELECT FOR KEY UPDATE is
super-exclusive locking (used to delete tuples and more generally to update
tuples modifying the values of the columns that make up the key of the tuple);
SELECT FOR UPDATE is a standards-compliant exclusive lock; SELECT FOR SHARE
implements shared locks; and finally SELECT FOR KEY SHARE is a super-weak mode
that does not conflict with exclusive mode, but conflicts with SELECT FOR KEY
UPDATE.  This last mode implements a mode just strong enough to implement RI
checks, i.e. it ensures that tuples do not go away from under a check, without
blocking when some other transaction that want to update the tuple without
changing its key.

I feel like there is a naming problem here.  The semantics that have
always been associated with SELECT FOR UPDATE are now attached to
SELECT FOR KEY UPDATE; and SELECT FOR UPDATE itself has been weakened.
 I think users will be surprised to find that SELECT FOR UPDATE
doesn't block all concurrent updates.

It seems to me that SELECT FOR KEY UPDATE should be called SELECT FOR
UPDATE, and what you're calling SELECT FOR UPDATE should be called
something else - essentially NONKEY UPDATE, though I don't much like
that name.

No, because that would stop it from doing what it is designed to do.

The lock modes are correct, appropriate and IMHO have meaningful
names. No redesign required here.

Not sure about the naming of some of the flag bits however.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#89Robert Haas
robertmhaas@gmail.com
In reply to: Alvaro Herrera (#87)
Re: foreign key locks, 2nd attempt

On Tue, Mar 6, 2012 at 4:27 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Excerpts from Robert Haas's message of mar mar 06 18:10:16 -0300 2012:

Preliminary comment:

This README is very helpful.

Thanks.  I feel silly that I didn't write it earlier.

On Tue, Mar 6, 2012 at 2:39 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

We provide four levels of tuple locking strength: SELECT FOR KEY UPDATE is
super-exclusive locking (used to delete tuples and more generally to update
tuples modifying the values of the columns that make up the key of the tuple);
SELECT FOR UPDATE is a standards-compliant exclusive lock; SELECT FOR SHARE
implements shared locks; and finally SELECT FOR KEY SHARE is a super-weak mode
that does not conflict with exclusive mode, but conflicts with SELECT FOR KEY
UPDATE.  This last mode implements a mode just strong enough to implement RI
checks, i.e. it ensures that tuples do not go away from under a check, without
blocking when some other transaction that want to update the tuple without
changing its key.

I feel like there is a naming problem here.  The semantics that have
always been associated with SELECT FOR UPDATE are now attached to
SELECT FOR KEY UPDATE; and SELECT FOR UPDATE itself has been weakened.
 I think users will be surprised to find that SELECT FOR UPDATE
doesn't block all concurrent updates.

I'm not sure why you say that.  Certainly SELECT FOR UPDATE continues to
block all updates.  It continues to block SELECT FOR SHARE as well.
The things that it doesn't block are the new SELECT FOR KEY SHARE locks;
since those didn't exist before, it doesn't seem correct to consider
that SELECT FOR UPDATE changed in any way.

The main difference in the UPDATE behavior is that an UPDATE is regarded
as though it might acquire two different lock modes -- it either
acquires SELECT FOR KEY UPDATE if the key is modified, or SELECT FOR
UPDATE if not.  Since SELECT FOR KEY UPDATE didn't exist before, we can
consider that previous to this patch, what UPDATE did was always acquire
a lock of strength SELECT FOR UPDATE.  So UPDATE also hasn't been
weakened.

Ah, I see. My mistake.

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

In reply to: Robert Haas (#89)
Re: foreign key locks, 2nd attempt

I feel sad, that i followed this topic very late. But i still want to put
forward my views.
Have we thought on the lines of how Robert has implemented relation level
locks. In short it should go like this

a) The locks for enforcing Referential integrity should be taken only when
the rarest of the events( that would cause the integrity failure) occur.
That would be the update of the referenced column. Other cases of update,
delete and insert should not be required to take locks. In this way, we can
reduce a lot of lock traffic.

So if we have a table like employee( empid, empname, ... depid references
dept(deptid)) and table dept(depid depname).

Currently we are taking shared locks on referenced rows in dept table,
whenever we are updating something in the employee table. This should not
happen. Instead any insert / update of referenced column / delete should
check for some lock in its PGPROC structure, which will only get created
when the depid gets updated / deleted( rare event )

b) But the operation of update of the referenced column will be made more
costly. May be it can create something like a predicate lock(used for
enforcing serializable) and keep it in all the PG_PROC structures.

I know this is a abstract idea, but just wanted to know, whether we have
thought on those lines.

Thanks,
Gokul.

#91Simon Riggs
simon@2ndQuadrant.com
In reply to: Gokulakannan Somasundaram (#90)
Re: foreign key locks, 2nd attempt

On Wed, Mar 7, 2012 at 9:24 AM, Gokulakannan Somasundaram
<gokul007@gmail.com> wrote:

I feel sad, that i followed this topic very late. But i still want to put
forward my views.
Have we thought on the lines of how Robert has implemented relation level
locks. In short it should go like this

a) The locks for enforcing Referential integrity should be taken only when
the rarest of the events( that would cause the integrity failure) occur.
That would be the update of the referenced column. Other cases of update,
delete and insert should not be required to take locks. In this way, we can
reduce a lot of lock traffic.

Insert, Update and Delete don't take locks they simply mark the tuples
they change with an xid. Anybody else wanting to "wait on the lock"
just waits on the xid. We do insert a lock row for each xid, but not
one per row changed.

So if we have a table like employee( empid, empname, ... depid references
dept(deptid)) and table dept(depid depname).

Currently we are taking shared locks on referenced rows in dept table,
whenever we are updating something in the employee table. This should not
happen. Instead any insert / update of referenced column / delete should
check for some lock in its PGPROC structure, which will only get created
when the depid gets updated / deleted( rare event )

It's worked that way for 5 years, so its too late to modify it now and
this patch won't change that.

The way we do RI locking is designed to prevent holding that in memory
and then having the lock table overflow, which then either requires us
to revert to the current design or upgrade to table level locks to
save space in the lock table - which is a total disaster, if you've
ever worked with DB2.

What you're suggesting is that we store the locks in memory only as a
way of avoiding updating the row.

My understanding is we have two optimisation choices. A single set of
xids can be used in many places, since the same set of transactions
may do roughly the same thing.

1. We could assign a new mxactid every time we touch a new row. That
way there is no correspondence between sets of xids, and we may hold
the same set many times. OTOH since each set is unique we can expand
it easily and we don't need to touch each row once for each lock. That
saves on row touches but it also greatly increases the mxactid
creation rate, which causes cache scrolling.

2. We assign a new mxactid each time we create a new unique set of
rows. We have a separate cache for local sets. This way reduces the
mxactid creation rate but causes row updates each time we lock the
row, which then needs WAL.

(2) is how we currently handle the very difficult decision of how to
optimise this for the general case. I'm not sure that is right in all
cases, but it is at least scalable and it is the devil we know.

b) But the operation of update of the referenced column will be made more
costly. May be it can create something like a predicate lock(used for
enforcing serializable) and keep it in all the PG_PROC structures.

No, updates of referenced columns are exactly the same as now when no
RI checks happening.

If the update occurs when an RI check takes place there is more work
to do, but previously it would have just blocked and done nothing. So
that path is relatively heavyweight but much better than nothing.

I know this is a abstract idea, but just wanted to know, whether we have
thought on those lines.

Thanks for your thoughts.

The most useful way to help with this patch right now is to run
performance investigations and see if there are non-optimal cases. We
can then see how the patch handles those. Theory is good, but it needs
to drive experimentation, as I myself re-discover continually.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

In reply to: Simon Riggs (#91)
Re: foreign key locks, 2nd attempt

Insert, Update and Delete don't take locks they simply mark the tuples
they change with an xid. Anybody else wanting to "wait on the lock"
just waits on the xid. We do insert a lock row for each xid, but not
one per row changed.

I mean the foreign key checks here. They take a Select for Share Lock
right. That's what we are trying to optimize here. Or am i missing
something? So by following the suggested methodology, the foreign key
checks won't take any locks.

It's worked that way for 5 years, so its too late to modify it now and
this patch won't change that.

The way we do RI locking is designed to prevent holding that in memory
and then having the lock table overflow, which then either requires us
to revert to the current design or upgrade to table level locks to
save space in the lock table - which is a total disaster, if you've
ever worked with DB2.

What you're suggesting is that we store the locks in memory only as a
way of avoiding updating the row.

But that memory would be consumed, only when someone updates the

referenced column( which will usually be the primary key of the referenced
table). Any normal database programmer knows that updating primary key is
not good for performance. So we go by the same logic.

No, updates of referenced columns are exactly the same as now when no
RI checks happening.

If the update occurs when an RI check takes place there is more work
to do, but previously it would have just blocked and done nothing. So
that path is relatively heavyweight but much better than nothing.

As i have already said, that path is definitely heavy weight( like how

Robert has made the DDL path heavy weight). If we assume that DDLs are
going to be a rare phenomenon, then we can also assume that update of
primary keys is a rare phenomenon in a normal database.

The most useful way to help with this patch right now is to run
performance investigations and see if there are non-optimal cases. We
can then see how the patch handles those. Theory is good, but it needs
to drive experimentation, as I myself re-discover continually.

I understand. I just wanted to know, whether the developer considered that

line of thought.

Thanks,
Gokul.

#93Simon Riggs
simon@2ndQuadrant.com
In reply to: Gokulakannan Somasundaram (#92)
Re: foreign key locks, 2nd attempt

On Wed, Mar 7, 2012 at 10:18 AM, Gokulakannan Somasundaram
<gokul007@gmail.com> wrote:

Insert, Update and Delete don't take locks they simply mark the tuples
they change with an xid. Anybody else wanting to "wait on the lock"
just waits on the xid. We do insert a lock row for each xid, but not
one per row changed.

I mean the foreign key checks here. They take a Select for Share Lock right.
That's what we are trying to optimize here. Or am i missing something? So by
following the suggested methodology, the foreign key checks won't take any
locks.

Please explain in detail your idea of how it will work.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

In reply to: Simon Riggs (#93)
Re: foreign key locks, 2nd attempt

Please explain in detail your idea of how it will work.

OK. I will try to explain the abstract idea, i have.
a) Referential integrity gets violated, when there are referencing key
values, not present in the referenced key values. We are maintaining the
integrity by taking a Select for Share Lock during the foreign key checks,
so that referred value is not updated/deleted during the operation.

b) We can do the same in the reverse way. When there is a update/delete of
the referred value, we don't want any new inserts with the referred value
in referring table, any update that will update its value to the referred
value being updated/deleted. So we will take some kind of lock, which will
stop such a happening. This can be achieved through
i) predicate locking infrastructure already present (or)
ii) a temporary B-Tree index ( no WAL protection ), that gets created only
for the referred value updations and holds those values that are being
updated/deleted (if we are scared of predicate locking).

So whenever we de foreign key checks, we just need to make sure there is no
such referential integrity lock in our own PGPROC structure(if implemented
with predicate locking) / check the temporary B-Tree index for any entry
matching the entry that we are going to insert/update to.( the empty tree
can be tracked with a flag to optimize )

May be someone can come up with better ideas than this.

Gokul.

#95Simon Riggs
simon@2ndQuadrant.com
In reply to: Gokulakannan Somasundaram (#94)
Re: foreign key locks, 2nd attempt

n Wed, Mar 7, 2012 at 11:37 AM, Gokulakannan Somasundaram
<gokul007@gmail.com> wrote:

Please explain in detail your idea of how it will work.

So we will take some kind of lock, which will stop such a happening.

...

May be someone can come up with better ideas than this.

With respect, I don't call this a detailed explanation of an idea. For
consideration here, come up with a very detailed design of how your
suggestion will work. Think about it carefully, spend hours and days
thinking it through and when you are personally sure it is better than
what we have now, please raise it on list at an appropriate time. Bear
in mind that most people throw away 90% of their ideas before even
mentioning them here. I hope that helps you to contribute.

At the moment we're trying to review patches for specific code to
include or exclude, not discuss huge redesign of internal mechanisms
using broad brush descriptions. It is possible you may find an
improvement and if you do, people will be interested but that seems an
unlikely thing to happen here and now.

If you have specific comments or tests on this patch those are very welcome.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#96Robert Haas
robertmhaas@gmail.com
In reply to: Robert Haas (#74)
Re: foreign key locks, 2nd attempt

On Sun, Feb 26, 2012 at 9:47 PM, Robert Haas <robertmhaas@gmail.com> wrote:

Regarding performance, the good thing about this patch is that if you
have an operation that used to block, it might now not block.  So maybe
multixact-related operation is a bit slower than before, but if it
allows you to continue operating rather than sit waiting until some
other transaction releases you, it's much better.

That's probably true, although there is some deferred cost that is
hard to account for.  You might not block immediately, but then later
somebody might block either because the mxact SLRU now needs fsyncs or
because they've got to decode an mxid long after the relevant segment
has been evicted from the SLRU buffers.  In general, it's hard to
bound that latter cost, because you only avoid blocking once (when the
initial update happens) but you might pay the extra cost of decoding
the mxid as many times as the row is read, which could be arbitrarily
many.  How much of a problem that is in practice, I'm not completely
sure, but it has worried me before and it still does.  In the worst
case scenario, a handful of frequently-accessed rows with MXIDs all of
whose members are dead except for the UPDATE they contain could result
in continual SLRU cache-thrashing.

From a performance standpoint, we really need to think not only about
the cases where the patch wins, but also, and maybe more importantly,
the cases where it loses.  There are some cases where the current
mechanism, use SHARE locks for foreign keys, is adequate.  In
particular, it's adequate whenever the parent table is not updated at
all, or only very lightly.  I believe that those people will pay
somewhat more with this patch, and especially in any case where
backends end up waiting for fsyncs in order to create new mxids, but
also just because I think this patch will have the effect of
increasing the space consumed by each individual mxid, which imposes a
distributed cost of its own.

I spent some time thinking about this over the weekend, and I have an
observation, and an idea. Here's the observation: I believe that
locking a tuple whose xmin is uncommitted is always a noop, because if
it's ever possible for a transaction to wait for an XID that is part
of its own transaction (exact same XID, or sub-XIDs of the same top
XID), then a transaction could deadlock against itself. I believe
that this is not possible: if a transaction were to wait for an XID
assigned to that same backend, then the lock manager would observe
that an ExclusiveLock on the xid is already held, so the request for a
ShareLock would be granted immediately. I also don't believe there's
any situation in which the existence of an uncommitted tuple fails to
block another backend, but a lock on that same uncommitted tuple would
have caused another backend to block. If any of that sounds wrong,
you can stop reading here (but please tell me why it sounds wrong).

If it's right, then here's the idea: what if we stored mxids using
xmin rather than xmax? This would mean that, instead of making mxids
contain the tuple's original xmax, they'd need to instead contain the
tuple's original xmin. This might seem like rearranging the deck
chairs on the titanic, but I think it actually works out substantially
better, because if we can assume that the xmin is committed, then we
only need to know its exact value until it becomes older than
RecentGlobalXmin. This means that a tuple can be both updated and
locked at the same time without the MultiXact SLRU needing to be
crash-safe, because if we crash and restart, any mxids that are still
around from before the crash are known to contain only xmins that are
now all-visible. We therefore don't need their exact values, so it
doesn't matter if that data actually made it to disk. Furthermore, in
the case where a previously-locked tuple is read repeatedly, we only
need to keep doing SLRU lookups until the xmin becomes all-visible;
after that, we can set a hint bit indicating that the tuple's xmin is
all-visible, and any future readers (or writers) can use that to skip
the SLRU lookup. In the degenerate (and probably common) case where a
tuple is already all-visible at the time it's locked, we don't really
need to record the original xmin at all; we can still do so if
convenient, but we can set the xmin-all-visible hint right away, so
nobody needs to probe the SLRU just to get xmin.

In other words, we'd entirely avoid needing to make mxacts crash-safe,
and we'd avoid most of the extra SLRU lookups that the current
implementation requires; they'd only be needed when (and for as long
as) the locked tuple was not yet all-visible.

This also seems like it would make the anti-wraparound issues simpler
to handle - once an mxid is old enough that any xmin it contains must
be all-visible, we can simply overwrite the tuple's xmin with
FrozenXID, which is pretty much what we're already doing anyway. It's
already the case that a table has to have an anti-wraparound vacuum at
least once after any given XID falls behind RecentGlobalXmin and
before it can be reused, so we wouldn't need to do anti-wraparound
vacuums any more frequently than currently. There's still the problem
that we might exhaust the mxid space more quickly than the XID space,
but that exists in either implementation.

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

#97Simon Riggs
simon@2ndQuadrant.com
In reply to: Robert Haas (#96)
Re: foreign key locks, 2nd attempt

On Mon, Mar 12, 2012 at 5:28 PM, Robert Haas <robertmhaas@gmail.com> wrote:

In other words, we'd entirely avoid needing to make mxacts crash-safe,
and we'd avoid most of the extra SLRU lookups that the current
implementation requires; they'd only be needed when (and for as long
as) the locked tuple was not yet all-visible.

The current implementation only requires additional lookups in the
update/check case, which is the case that doesn't do anything other
than block right now. Since we're replacing lock contention with
physical access contention even the worst case situation is still
better than what we have now. Please feel free to point out worst case
situations and show that isn't true.

I've also pointed out how to avoid overhead of making mxacts crash
safe when the new facilities are not in use, so I don't see problems
with the proposed mechanism. Given that I am still myself reviewing
the actual code.

So those things are not something we need to avoid.

My feeling is that overwriting xmin is a clever idea, but arrives too
late to require sensible analysis in this stage of the CF. It's not
solving a problem, its just an alternate mechanism and at best an
optimisation of the mechanism. Were we to explore it now, it seems
certain that another person would observe that design were taking
place and so the patch should be rejected, which would be unnecessary
and wasteful. I also think it would alter our ability to diagnose
problems, not least the normal test that xmax matches xmin across an
update.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#98Robert Haas
robertmhaas@gmail.com
In reply to: Simon Riggs (#97)
Re: foreign key locks, 2nd attempt

On Mon, Mar 12, 2012 at 1:50 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

On Mon, Mar 12, 2012 at 5:28 PM, Robert Haas <robertmhaas@gmail.com> wrote:

In other words, we'd entirely avoid needing to make mxacts crash-safe,
and we'd avoid most of the extra SLRU lookups that the current
implementation requires; they'd only be needed when (and for as long
as) the locked tuple was not yet all-visible.

The current implementation only requires additional lookups in the
update/check case, which is the case that doesn't do anything other
than block right now. Since we're replacing lock contention with
physical access contention even the worst case situation is still
better than what we have now. Please feel free to point out worst case
situations and show that isn't true.

I think I already have:

http://archives.postgresql.org/pgsql-hackers/2012-02/msg01258.php

The case I'm worried about is where we are allocating mxids quickly,
and we end up having to wait for fsyncs on mxact segments. That might
be very slow, but you could argue that it could *possibly* be still
worthwhile if it avoids blocking. That doesn't strike me as a
slam-dunk, though, because we've already seen and fixed cases where
too many fsyncs causes the performance of the entire system to go down
the tubes (cf. commit 7f242d880b5b5d9642675517466d31373961cf98). But
it's really bad if there are no updates on the parent table - then,
whatever extra overhead there is will be all for naught, since the
more fine-grained locking doesn't help anyway.

I've also pointed out how to avoid overhead of making mxacts crash
safe when the new facilities are not in use, so I don't see problems
with the proposed mechanism. Given that I am still myself reviewing
the actual code.

The closest thing I can find to a proposal from you in that regard is
this comment:

# I was really thinking we could skip the fsync of a page if we've not
# persisted anything important on that page, since that was one of
# Robert's performance points.

It might be possible to do something with that idea, but at the moment
I'm not seeing how to make it work.

So those things are not something we need to avoid.

My feeling is that overwriting xmin is a clever idea, but arrives too
late to require sensible analysis in this stage of the CF. It's not
solving a problem, its just an alternate mechanism and at best an
optimisation of the mechanism. Were we to explore it now, it seems
certain that another person would observe that design were taking
place and so the patch should be rejected, which would be unnecessary
and wasteful.

Considering that nobody's done any work to resolve the uncertainty
about whether the worst-case performance characteristics of this patch
are acceptable, and considering further that it was undergoing massive
code churn for more than a month after the final CommitFest, I think
it's not that unreasonable to think it might not be ready for prime
time at this point. In any event, your argument is exactly backwards:
we need to first decide whether the patch needs a redesign and then,
if it does, postpone it. Deciding that we don't want to postpone it
first, and therefore we're not going to redesign it even if that is
what's really needed makes no sense.

I also think it would alter our ability to diagnose
problems, not least the normal test that xmax matches xmin across an
update.

There's nothing stopping the new tuple from being frozen before the
old one, even today.

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

#99Simon Riggs
simon@2ndquadrant.com
In reply to: Robert Haas (#98)
Re: foreign key locks, 2nd attempt

On Mon, Mar 12, 2012 at 6:14 PM, Robert Haas <robertmhaas@gmail.com> wrote:

Considering that nobody's done any work to resolve the uncertainty
about whether the worst-case performance characteristics of this patch
are acceptable, and considering further that it was undergoing massive
code churn for more than a month after the final CommitFest, I think
it's not that unreasonable to think it might not be ready for prime
time at this point.

Thank you for cutting to the chase.

The "uncertainty" of which you speak is a theoretical point you
raised. It has been explained, but nobody has yet shown the
performance numbers to illustrate the point but only because they
seemed so clear. I would point out that you haven't demonstrated the
existence of a problem either, so redesigning something without any
proof of a problem seems a strange.

Let me explain again what this patch does and why it has such major
performance benefit.

This feature give us a step change in lock reductions from FKs. A real
world "best case" might be to examine the benefit this patch has on a
large batch load that inserts many new orders for existing customers.
In my example case the orders table has a FK to the customer table. At
the same time as the data load, we attempt to update a customer's
additional details, address or current balance etc. The large load
takes locks on the customer table and keeps them for the whole
transaction. So the customer updates are locked out for multiple
seconds, minutes or maybe hours, depending upon how far you want to
stretch the example. With this patch the customer updates don't cause
lock conflicts but they require mxact lookups in *some* cases, so they
might take 1-10ms extra, rather than 1-10 minutes more. >1000x faster.
The only case that causes the additional lookups is the case that
otherwise would have been locked. So producing "best case" results is
trivial and can be as enormous as you like.

I agree with you that some worst case performance tests should be
done. Could you please say what you think the worst cases would be, so
those can be tested? That would avoid wasting time or getting anything
backwards.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#100Noah Misch
noah@leadboat.com
In reply to: Robert Haas (#96)
Re: foreign key locks, 2nd attempt

On Mon, Mar 12, 2012 at 01:28:11PM -0400, Robert Haas wrote:

I spent some time thinking about this over the weekend, and I have an
observation, and an idea. Here's the observation: I believe that
locking a tuple whose xmin is uncommitted is always a noop, because if
it's ever possible for a transaction to wait for an XID that is part
of its own transaction (exact same XID, or sub-XIDs of the same top
XID), then a transaction could deadlock against itself. I believe
that this is not possible: if a transaction were to wait for an XID
assigned to that same backend, then the lock manager would observe
that an ExclusiveLock on the xid is already held, so the request for a
ShareLock would be granted immediately. I also don't believe there's
any situation in which the existence of an uncommitted tuple fails to
block another backend, but a lock on that same uncommitted tuple would
have caused another backend to block. If any of that sounds wrong,
you can stop reading here (but please tell me why it sounds wrong).

When we lock an update-in-progress row, we walk the t_ctid chain and lock all
descendant tuples. They may all have uncommitted xmins. This is essential to
ensure that the final outcome of the updating transaction does not affect
whether the locking transaction has its KEY SHARE lock. Similarly, when we
update a previously-locked tuple, we copy any locks (always KEY SHARE locks)
to the new version. That new tuple is both uncommitted and has locks, and we
cannot easily sacrifice either property. Do you see a way to extend your
scheme to cover these needs?

#101Bruce Momjian
bruce@momjian.us
In reply to: Alvaro Herrera (#83)
Re: foreign key locks, 2nd attempt

On Tue, Mar 06, 2012 at 04:39:32PM -0300, Alvaro Herrera wrote:

Here's a first attempt at a README illustrating this. I intend this to
be placed in src/backend/access/heap/README.tuplock; the first three
paragraphs are stolen from the comment in heap_lock_tuple, so I'd remove
those from there, directing people to this new file instead. Is there
something that you think should be covered more extensively (or at all)
here?

...

When there is a single locker in a tuple, we can just store the locking info
in the tuple itself. We do this by storing the locker's Xid in XMAX, and
setting hint bits specifying the locking strength. There is one exception
here: since hint bit space is limited, we do not provide a separate hint bit
for SELECT FOR SHARE, so we have to use the extended info in a MultiXact in
that case. (The other cases, SELECT FOR UPDATE and SELECT FOR KEY SHARE, are
presumably more commonly used due to being the standards-mandated locking
mechanism, or heavily used by the RI code, so we want to provide fast paths
for those.)

Are those tuple bits actually "hint" bits? They seem quite a bit more
powerful than a "hint".

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ It's impossible for everything to be true. +

#102Robert Haas
robertmhaas@gmail.com
In reply to: Noah Misch (#100)
Re: foreign key locks, 2nd attempt

On Mon, Mar 12, 2012 at 9:24 PM, Noah Misch <noah@leadboat.com> wrote:

When we lock an update-in-progress row, we walk the t_ctid chain and lock all
descendant tuples.  They may all have uncommitted xmins.  This is essential to
ensure that the final outcome of the updating transaction does not affect
whether the locking transaction has its KEY SHARE lock.  Similarly, when we
update a previously-locked tuple, we copy any locks (always KEY SHARE locks)
to the new version.  That new tuple is both uncommitted and has locks, and we
cannot easily sacrifice either property.  Do you see a way to extend your
scheme to cover these needs?

No, I think that sinks it. Good analysis.

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

#103Alvaro Herrera
alvherre@commandprompt.com
In reply to: Bruce Momjian (#101)
Re: foreign key locks, 2nd attempt

Excerpts from Bruce Momjian's message of mar mar 13 14:00:52 -0300 2012:

On Tue, Mar 06, 2012 at 04:39:32PM -0300, Alvaro Herrera wrote:

When there is a single locker in a tuple, we can just store the locking info
in the tuple itself. We do this by storing the locker's Xid in XMAX, and
setting hint bits specifying the locking strength. There is one exception
here: since hint bit space is limited, we do not provide a separate hint bit
for SELECT FOR SHARE, so we have to use the extended info in a MultiXact in
that case. (The other cases, SELECT FOR UPDATE and SELECT FOR KEY SHARE, are
presumably more commonly used due to being the standards-mandated locking
mechanism, or heavily used by the RI code, so we want to provide fast paths
for those.)

Are those tuple bits actually "hint" bits? They seem quite a bit more
powerful than a "hint".

I'm not sure what's your point. We've had a "hint" bit for SELECT FOR
UPDATE for ages. Even 8.2 had HEAP_XMAX_EXCL_LOCK and
HEAP_XMAX_SHARED_LOCK. Maybe they are misnamed and aren't really
"hints", but it's not the job of this patch to fix that problem.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#104Robert Haas
robertmhaas@gmail.com
In reply to: Simon Riggs (#99)
Re: foreign key locks, 2nd attempt

On Mon, Mar 12, 2012 at 3:28 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

I agree with you that some worst case performance tests should be
done. Could you please say what you think the worst cases would be, so
those can be tested? That would avoid wasting time or getting anything
backwards.

I've thought about this some and here's what I've come up with so far:

1. SELECT FOR SHARE on a large table on a system with no write cache.

2. A small parent table (say 30 rows or so) and a larger child table
with a many-to-one FK relationship to the parent (say 100 child rows
per parent row), with heavy update activity on the child table, on a
system where fsyncs are very slow. This should generate lots of mxid
consumption, and every 1600 or so mxids (I think) we've got to fsync;
does that generate a noticeable performance hit?

3. It would be nice to test the impact of increased mxid lookups in
the parent, but I've realized that the visibility map will probably
mask a good chunk of that effect, which is a good thing. Still, maybe
something like this: a fairly large parent table, say a million rows,
but narrow rows, so that many of them fit on a page, with frequent
reads and occasional updates (if there are only reads, autovacuum
might end with all the visibility map bits set); plus a child table
with one or a few rows per parent which is heavily updated. In theory
this ought to be good for the patch, since the the more fine-grained
locking will avoid blocking, but in this case the parent table is
large enough that you shouldn't get much blocking anyway, yet you'll
still pay the cost of mxid lookups because the occasional updates on
the parent will clear VM bits. This might not be the exactly right
workload to measure this effect, but if it's not maybe someone can
devote a little time to thinking about what would be.

4. A plain old pgbench run or two, to see whether there's any
regression when none of this matters at all...

This isn't exactly a test case, but from Noah's previous comments I
gather that there is a theoretical risk of mxid consumption running
ahead of xid consumption. We should try to think about whether there
are any realistic workloads where that might actually happen. I'm
willing to believe that there aren't, but not just because somebody
asserts it. The reason I'm concerned about this is because, if it
should happen, the result will be more frequent anti-wraparound
vacuums on every table in the cluster. Those are already quite
painful for some users.

It would be nice if Noah or someone else who has reviewed this patch
in detail could comment further. I am shooting from the hip here, a
bit.

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

#105Noah Misch
noah@leadboat.com
In reply to: Robert Haas (#104)
Re: foreign key locks, 2nd attempt

On Tue, Mar 13, 2012 at 01:46:24PM -0400, Robert Haas wrote:

On Mon, Mar 12, 2012 at 3:28 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

I agree with you that some worst case performance tests should be
done. Could you please say what you think the worst cases would be, so
those can be tested? That would avoid wasting time or getting anything
backwards.

I've thought about this some and here's what I've come up with so far:

1. SELECT FOR SHARE on a large table on a system with no write cache.

Easy enough that we may as well check it. Share-locking an entire large table
is impractical in a real application, so I would not worry if this shows a
substantial regression.

2. A small parent table (say 30 rows or so) and a larger child table
with a many-to-one FK relationship to the parent (say 100 child rows
per parent row), with heavy update activity on the child table, on a
system where fsyncs are very slow. This should generate lots of mxid
consumption, and every 1600 or so mxids (I think) we've got to fsync;
does that generate a noticeable performance hit?

More often than that; each 2-member mxid takes 4 bytes in an offsets file and
10 bytes in a members file. So, more like one fsync per ~580 mxids. Note
that we already fsync the multixact SLRUs today, so any increase will arise
from the widening of member entries from 4 bytes to 5. The realism of this
test is attractive. Nearly-static parent tables are plenty common, and this
test will illustrate the impact on those designs.

3. It would be nice to test the impact of increased mxid lookups in
the parent, but I've realized that the visibility map will probably
mask a good chunk of that effect, which is a good thing. Still, maybe
something like this: a fairly large parent table, say a million rows,
but narrow rows, so that many of them fit on a page, with frequent
reads and occasional updates (if there are only reads, autovacuum
might end with all the visibility map bits set); plus a child table
with one or a few rows per parent which is heavily updated. In theory
this ought to be good for the patch, since the the more fine-grained
locking will avoid blocking, but in this case the parent table is
large enough that you shouldn't get much blocking anyway, yet you'll
still pay the cost of mxid lookups because the occasional updates on
the parent will clear VM bits. This might not be the exactly right
workload to measure this effect, but if it's not maybe someone can
devote a little time to thinking about what would be.

You still have HEAP_XMAX_{INVALID,COMMITTED} to reduce the pressure on mxid
lookups, so I think something more sophisticated is needed to exercise that
cost. Not sure what.

4. A plain old pgbench run or two, to see whether there's any
regression when none of this matters at all...

Might as well.

This isn't exactly a test case, but from Noah's previous comments I
gather that there is a theoretical risk of mxid consumption running
ahead of xid consumption. We should try to think about whether there
are any realistic workloads where that might actually happen. I'm
willing to believe that there aren't, but not just because somebody
asserts it. The reason I'm concerned about this is because, if it
should happen, the result will be more frequent anti-wraparound
vacuums on every table in the cluster. Those are already quite
painful for some users.

Yes. Pre-release, what can we really do here other than have more people
thinking about ways it might happen in practice? Post-release, we could
suggest monitoring methods or perhaps have VACUUM emit a WARNING when a table
is using more mxid space than xid space.

Also consider a benchmark that does plenty of non-key updates on a parent
table with no activity on the child table. We'll pay the overhead of
determining that the key column(s) have not changed, but it will never pay off
by preventing a lock wait. Granted, this is barely representative of
application behavior. Perhaps, too, we already have a good sense of this cost
from the HOT benchmarking efforts and have no cause to revisit it.

Thanks,
nm

#106Robert Haas
robertmhaas@gmail.com
In reply to: Noah Misch (#105)
Re: foreign key locks, 2nd attempt

On Tue, Mar 13, 2012 at 11:42 PM, Noah Misch <noah@leadboat.com> wrote:

More often than that; each 2-member mxid takes 4 bytes in an offsets file and
10 bytes in a members file.  So, more like one fsync per ~580 mxids.  Note
that we already fsync the multixact SLRUs today, so any increase will arise
from the widening of member entries from 4 bytes to 5.  The realism of this
test is attractive.  Nearly-static parent tables are plenty common, and this
test will illustrate the impact on those designs.

Agreed. But speaking of that, why exactly do we fsync the multixact SLRU today?

You still have HEAP_XMAX_{INVALID,COMMITTED} to reduce the pressure on mxid
lookups, so I think something more sophisticated is needed to exercise that
cost.  Not sure what.

I don't think HEAP_XMAX_COMMITTED is much help, because committed !=
all-visible. HEAP_XMAX_INVALID will obviously help, when it happens.

This isn't exactly a test case, but from Noah's previous comments I
gather that there is a theoretical risk of mxid consumption running
ahead of xid consumption.  We should try to think about whether there
are any realistic workloads where that might actually happen.  I'm
willing to believe that there aren't, but not just because somebody
asserts it.  The reason I'm concerned about this is because, if it
should happen, the result will be more frequent anti-wraparound
vacuums on every table in the cluster.  Those are already quite
painful for some users.

Yes.  Pre-release, what can we really do here other than have more people
thinking about ways it might happen in practice?  Post-release, we could
suggest monitoring methods or perhaps have VACUUM emit a WARNING when a table
is using more mxid space than xid space.

Well, post-release, the cat is out of the bag: we'll be stuck with
this whether the performance characteristics are acceptable or not.
That's why we'd better be as sure as possible before committing to
this implementation that there's nothing we can't live with. It's not
like there's any reasonable way to turn this off if you don't like it.

Also consider a benchmark that does plenty of non-key updates on a parent
table with no activity on the child table.  We'll pay the overhead of
determining that the key column(s) have not changed, but it will never pay off
by preventing a lock wait.

Good idea.

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

#107Noah Misch
noah@leadboat.com
In reply to: Robert Haas (#106)
Re: foreign key locks, 2nd attempt

On Wed, Mar 14, 2012 at 01:23:14PM -0400, Robert Haas wrote:

On Tue, Mar 13, 2012 at 11:42 PM, Noah Misch <noah@leadboat.com> wrote:

More often than that; each 2-member mxid takes 4 bytes in an offsets file and
10 bytes in a members file. ?So, more like one fsync per ~580 mxids. ?Note
that we already fsync the multixact SLRUs today, so any increase will arise
from the widening of member entries from 4 bytes to 5. ?The realism of this
test is attractive. ?Nearly-static parent tables are plenty common, and this
test will illustrate the impact on those designs.

Agreed. But speaking of that, why exactly do we fsync the multixact SLRU today?

Good question. So far, I can't think of a reason. "nextMulti" is critical,
but we already fsync it with pg_control. We could delete the other multixact
state data at every startup and set OldestVisibleMXactId accordingly.

You still have HEAP_XMAX_{INVALID,COMMITTED} to reduce the pressure on mxid
lookups, so I think something more sophisticated is needed to exercise that
cost. ?Not sure what.

I don't think HEAP_XMAX_COMMITTED is much help, because committed !=
all-visible. HEAP_XMAX_INVALID will obviously help, when it happens.

True. The patch (see ResetMultiHintBit()) also replaces a multixact xmax with
the updater xid when all transactions of the multixact have ended. You would
need a test workload with long-running multixacts that delay such replacement.
However, the workloads that come to mind are the very workloads for which this
patch eliminates lock waits; they wouldn't illustrate a worst-case.

This isn't exactly a test case, but from Noah's previous comments I
gather that there is a theoretical risk of mxid consumption running
ahead of xid consumption. ?We should try to think about whether there
are any realistic workloads where that might actually happen. ?I'm
willing to believe that there aren't, but not just because somebody
asserts it. ?The reason I'm concerned about this is because, if it
should happen, the result will be more frequent anti-wraparound
vacuums on every table in the cluster. ?Those are already quite
painful for some users.

Yes. ?Pre-release, what can we really do here other than have more people
thinking about ways it might happen in practice? ?Post-release, we could
suggest monitoring methods or perhaps have VACUUM emit a WARNING when a table
is using more mxid space than xid space.

Well, post-release, the cat is out of the bag: we'll be stuck with
this whether the performance characteristics are acceptable or not.
That's why we'd better be as sure as possible before committing to
this implementation that there's nothing we can't live with. It's not
like there's any reasonable way to turn this off if you don't like it.

I disagree; we're only carving in stone the FOR KEY SHARE and FOR KEY UPDATE
syntax additions. We could even avoid doing that by not documenting them. A
later major release could implement them using a completely different
mechanism or even reduce them to aliases, KEY SHARE = SHARE and KEY UPDATE =
UPDATE. To be sure, let's still do a good job the first time.

#108Alvaro Herrera
alvherre@commandprompt.com
In reply to: Noah Misch (#107)
Re: foreign key locks, 2nd attempt

Excerpts from Noah Misch's message of mié mar 14 19:10:00 -0300 2012:

On Wed, Mar 14, 2012 at 01:23:14PM -0400, Robert Haas wrote:

On Tue, Mar 13, 2012 at 11:42 PM, Noah Misch <noah@leadboat.com> wrote:

More often than that; each 2-member mxid takes 4 bytes in an offsets file and
10 bytes in a members file. ?So, more like one fsync per ~580 mxids. ?Note
that we already fsync the multixact SLRUs today, so any increase will arise
from the widening of member entries from 4 bytes to 5. ?The realism of this
test is attractive. ?Nearly-static parent tables are plenty common, and this
test will illustrate the impact on those designs.

Agreed. But speaking of that, why exactly do we fsync the multixact SLRU today?

Good question. So far, I can't think of a reason. "nextMulti" is critical,
but we already fsync it with pg_control. We could delete the other multixact
state data at every startup and set OldestVisibleMXactId accordingly.

Hmm, yeah.

You still have HEAP_XMAX_{INVALID,COMMITTED} to reduce the pressure on mxid
lookups, so I think something more sophisticated is needed to exercise that
cost. ?Not sure what.

I don't think HEAP_XMAX_COMMITTED is much help, because committed !=
all-visible. HEAP_XMAX_INVALID will obviously help, when it happens.

True. The patch (see ResetMultiHintBit()) also replaces a multixact xmax with
the updater xid when all transactions of the multixact have ended.

I have noticed that this code is not correct, because we don't know that
we're holding an appropriate lock on the page, so we can't simply change
the Xmax and reset those hint bits. As things stand today, mxids
persist longer. (We could do some cleanup at HOT-style page prune, for
example, though the lock we need is even weaker than that.) Overall
this means that coming up with a test case demonstrating this pressure
probably isn't that hard.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#109Robert Haas
robertmhaas@gmail.com
In reply to: Noah Misch (#107)
Re: foreign key locks, 2nd attempt

On Wed, Mar 14, 2012 at 6:10 PM, Noah Misch <noah@leadboat.com> wrote:

Well, post-release, the cat is out of the bag: we'll be stuck with
this whether the performance characteristics are acceptable or not.
That's why we'd better be as sure as possible before committing to
this implementation that there's nothing we can't live with.  It's not
like there's any reasonable way to turn this off if you don't like it.

I disagree; we're only carving in stone the FOR KEY SHARE and FOR KEY UPDATE
syntax additions.  We could even avoid doing that by not documenting them.  A
later major release could implement them using a completely different
mechanism or even reduce them to aliases, KEY SHARE = SHARE and KEY UPDATE =
UPDATE.  To be sure, let's still do a good job the first time.

What I mean is really that, once the release is out, we don't get to
take it back. Sure, the next release can fix things, but any
regressions will become obstacles to upgrading and pain points for new
users.

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

#110Robert Haas
robertmhaas@gmail.com
In reply to: Alvaro Herrera (#108)
Re: foreign key locks, 2nd attempt

On Wed, Mar 14, 2012 at 9:17 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Agreed.  But speaking of that, why exactly do we fsync the multixact SLRU today?

Good question.  So far, I can't think of a reason.  "nextMulti" is critical,
but we already fsync it with pg_control.  We could delete the other multixact
state data at every startup and set OldestVisibleMXactId accordingly.

Hmm, yeah.

In a way, the fact that we don't do that is kind of fortuitous in this
situation. I had just assumed that we were not fsyncing it because
there seems to be no reason to do so. But since we are, we already
know that the fsyncs resulting from frequent mxid allocation aren't a
huge pain point. If they were, somebody would have presumably
complained about it and fixed it before now. So that means that what
we're really worrying about here is the overhead of fsyncing a little
more often, which is a lot less scary than starting to do it when we
weren't previously.

Now, we could look at this as an opportunity to optimize the existing
implementation by removing the fsyncs, rather than adding the new
infrastructure Alvaro is proposing. But that would only make sense if
we thought that getting rid of the fsyncs would be more valuable than
avoiding the blocking here, and I don't.

I still think that someone needs to do some benchmarking here, because
this is a big complicated performance patch, and we can't predict the
impact of it on real-world scenarios without testing. There is
clearly some additional overhead, and it makes sense to measure it and
hopefully discover that it isn't excessive. Still, I'm a bit
relieved.

I have noticed that this code is not correct, because we don't know that
we're holding an appropriate lock on the page, so we can't simply change
the Xmax and reset those hint bits.  As things stand today, mxids
persist longer.  (We could do some cleanup at HOT-style page prune, for
example, though the lock we need is even weaker than that.)  Overall
this means that coming up with a test case demonstrating this pressure
probably isn't that hard.

What would such a test case look like?

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

#111Simon Riggs
simon@2ndQuadrant.com
In reply to: Robert Haas (#106)
Re: foreign key locks, 2nd attempt

On Wed, Mar 14, 2012 at 5:23 PM, Robert Haas <robertmhaas@gmail.com> wrote:

You still have HEAP_XMAX_{INVALID,COMMITTED} to reduce the pressure on mxid
lookups, so I think something more sophisticated is needed to exercise that
cost.  Not sure what.

I don't think HEAP_XMAX_COMMITTED is much help, because committed !=
all-visible.

So because committed does not equal all visible there will be
additional lookups on mxids? That's complete rubbish.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#112Simon Riggs
simon@2ndQuadrant.com
In reply to: Robert Haas (#109)
Re: foreign key locks, 2nd attempt

On Thu, Mar 15, 2012 at 2:15 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Mar 14, 2012 at 6:10 PM, Noah Misch <noah@leadboat.com> wrote:

Well, post-release, the cat is out of the bag: we'll be stuck with
this whether the performance characteristics are acceptable or not.
That's why we'd better be as sure as possible before committing to
this implementation that there's nothing we can't live with.  It's not
like there's any reasonable way to turn this off if you don't like it.

I disagree; we're only carving in stone the FOR KEY SHARE and FOR KEY UPDATE
syntax additions.  We could even avoid doing that by not documenting them.  A
later major release could implement them using a completely different
mechanism or even reduce them to aliases, KEY SHARE = SHARE and KEY UPDATE =
UPDATE.  To be sure, let's still do a good job the first time.

What I mean is really that, once the release is out, we don't get to
take it back.  Sure, the next release can fix things, but any
regressions will become obstacles to upgrading and pain points for new
users.

This comment is completely superfluous. It's a complete waste of time
to turn up on a thread and remind people that if they commit something
and it doesn't actually work that it would be a bad thing. Why, we
might ask do you think that thought needs to be expressed here?
Please, don't answer, lets spend the time on actually reviewing the
patch.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#113Simon Riggs
simon@2ndQuadrant.com
In reply to: Robert Haas (#110)
Re: foreign key locks, 2nd attempt

On Thu, Mar 15, 2012 at 2:26 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Mar 14, 2012 at 9:17 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Agreed.  But speaking of that, why exactly do we fsync the multixact SLRU today?

Good question.  So far, I can't think of a reason.  "nextMulti" is critical,
but we already fsync it with pg_control.  We could delete the other multixact
state data at every startup and set OldestVisibleMXactId accordingly.

Hmm, yeah.

In a way, the fact that we don't do that is kind of fortuitous in this
situation.  I had just assumed that we were not fsyncing it because
there seems to be no reason to do so.  But since we are, we already
know that the fsyncs resulting from frequent mxid allocation aren't a
huge pain point.  If they were, somebody would have presumably
complained about it and fixed it before now.  So that means that what
we're really worrying about here is the overhead of fsyncing a little
more often, which is a lot less scary than starting to do it when we
weren't previously.

Good

Now, we could look at this as an opportunity to optimize the existing
implementation by removing the fsyncs, rather than adding the new
infrastructure Alvaro is proposing.

This is not an exercise in tuning mxact code. There is a serious
algorithmic problem that is causing real world problems.

Removing the fsync will *not* provide a solution to the problem, so
there is no "opportunity" here.

But that would only make sense if
we thought that getting rid of the fsyncs would be more valuable than
avoiding the blocking here, and I don't.

You're right that the existing code could use some optimisation.

I'm a little tired, but I can't see a reason to fsync this except at checkpoint.

Also seeing that we issue 2 WAL records for each RI check. We issue
one during MultiXactIdCreate/MultiXactIdExpand and then immediately
afterwards issue a XLOG_HEAP_LOCK record. The comments on both show
that each thinks it is doing it for the same reason and is the only
place its being done. Alvaro, any ideas why that is.

I still think that someone needs to do some benchmarking here, because
this is a big complicated performance patch, and we can't predict the
impact of it on real-world scenarios without testing.  There is
clearly some additional overhead, and it makes sense to measure it and
hopefully discover that it isn't excessive.  Still, I'm a bit
relieved.

Very much agreed.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#114Simon Riggs
simon@2ndQuadrant.com
In reply to: Alvaro Herrera (#108)
Re: foreign key locks, 2nd attempt

On Thu, Mar 15, 2012 at 1:17 AM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

As things stand today

Can I confirm where we are now? Is there another version of the patch
coming out soon?

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#115Alvaro Herrera
alvherre@commandprompt.com
In reply to: Simon Riggs (#113)
Re: foreign key locks, 2nd attempt

Excerpts from Simon Riggs's message of jue mar 15 18:38:53 -0300 2012:

On Thu, Mar 15, 2012 at 2:26 AM, Robert Haas <robertmhaas@gmail.com> wrote:

But that would only make sense if
we thought that getting rid of the fsyncs would be more valuable than
avoiding the blocking here, and I don't.

You're right that the existing code could use some optimisation.

I'm a little tired, but I can't see a reason to fsync this except at checkpoint.

Hang on. What fsyncs are we talking about? I don't see that the
multixact code calls any fsync except that checkpoint and shutdown.

Also seeing that we issue 2 WAL records for each RI check. We issue
one during MultiXactIdCreate/MultiXactIdExpand and then immediately
afterwards issue a XLOG_HEAP_LOCK record. The comments on both show
that each thinks it is doing it for the same reason and is the only
place its being done. Alvaro, any ideas why that is.

AFAIR the XLOG_HEAP_LOCK log entry only records the fact that the row is
being locked by a multixact -- it doesn't record the contents (member
xids) of said multixact, which is what the other log entry records.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#116Alvaro Herrera
alvherre@commandprompt.com
In reply to: Simon Riggs (#114)
Re: foreign key locks, 2nd attempt

Excerpts from Simon Riggs's message of jue mar 15 18:46:44 -0300 2012:

On Thu, Mar 15, 2012 at 1:17 AM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

As things stand today

Can I confirm where we are now? Is there another version of the patch
coming out soon?

Yes, another version is coming soon.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#117Simon Riggs
simon@2ndQuadrant.com
In reply to: Alvaro Herrera (#115)
Re: foreign key locks, 2nd attempt

On Thu, Mar 15, 2012 at 9:54 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Excerpts from Simon Riggs's message of jue mar 15 18:38:53 -0300 2012:

On Thu, Mar 15, 2012 at 2:26 AM, Robert Haas <robertmhaas@gmail.com> wrote:

But that would only make sense if
we thought that getting rid of the fsyncs would be more valuable than
avoiding the blocking here, and I don't.

You're right that the existing code could use some optimisation.

I'm a little tired, but I can't see a reason to fsync this except at checkpoint.

Hang on.  What fsyncs are we talking about?  I don't see that the
multixact code calls any fsync except that checkpoint and shutdown.

If a dirty page is evicted it will fsync.

Also seeing that we issue 2 WAL records for each RI check. We issue
one during MultiXactIdCreate/MultiXactIdExpand and then immediately
afterwards issue a XLOG_HEAP_LOCK record. The comments on both show
that each thinks it is doing it for the same reason and is the only
place its being done. Alvaro, any ideas why that is.

AFAIR the XLOG_HEAP_LOCK log entry only records the fact that the row is
being locked by a multixact -- it doesn't record the contents (member
xids) of said multixact, which is what the other log entry records.

Agreed. But issuing two records when we could issue just one seems a
little strange, especially when the two record types follow one
another so closely - so we end up queuing for the lock twice while
holding the lock on the data block.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#118Alvaro Herrera
alvherre@commandprompt.com
In reply to: Simon Riggs (#117)
Re: foreign key locks, 2nd attempt

Excerpts from Simon Riggs's message of jue mar 15 19:04:41 -0300 2012:

On Thu, Mar 15, 2012 at 9:54 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Excerpts from Simon Riggs's message of jue mar 15 18:38:53 -0300 2012:

On Thu, Mar 15, 2012 at 2:26 AM, Robert Haas <robertmhaas@gmail.com> wrote:

But that would only make sense if
we thought that getting rid of the fsyncs would be more valuable than
avoiding the blocking here, and I don't.

You're right that the existing code could use some optimisation.

I'm a little tired, but I can't see a reason to fsync this except at checkpoint.

Hang on.  What fsyncs are we talking about?  I don't see that the
multixact code calls any fsync except that checkpoint and shutdown.

If a dirty page is evicted it will fsync.

Ah, right.

Also seeing that we issue 2 WAL records for each RI check. We issue
one during MultiXactIdCreate/MultiXactIdExpand and then immediately
afterwards issue a XLOG_HEAP_LOCK record. The comments on both show
that each thinks it is doing it for the same reason and is the only
place its being done. Alvaro, any ideas why that is.

AFAIR the XLOG_HEAP_LOCK log entry only records the fact that the row is
being locked by a multixact -- it doesn't record the contents (member
xids) of said multixact, which is what the other log entry records.

Agreed. But issuing two records when we could issue just one seems a
little strange, especially when the two record types follow one
another so closely - so we end up queuing for the lock twice while
holding the lock on the data block.

Hmm, that seems optimization that could be done separately.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#119Simon Riggs
simon@2ndQuadrant.com
In reply to: Alvaro Herrera (#118)
Re: foreign key locks, 2nd attempt

On Thu, Mar 15, 2012 at 10:13 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Excerpts from Simon Riggs's message of jue mar 15 19:04:41 -0300 2012:

On Thu, Mar 15, 2012 at 9:54 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Excerpts from Simon Riggs's message of jue mar 15 18:38:53 -0300 2012:

On Thu, Mar 15, 2012 at 2:26 AM, Robert Haas <robertmhaas@gmail.com> wrote:

But that would only make sense if
we thought that getting rid of the fsyncs would be more valuable than
avoiding the blocking here, and I don't.

You're right that the existing code could use some optimisation.

I'm a little tired, but I can't see a reason to fsync this except at checkpoint.

Hang on.  What fsyncs are we talking about?  I don't see that the
multixact code calls any fsync except that checkpoint and shutdown.

If a dirty page is evicted it will fsync.

Ah, right.

Also seeing that we issue 2 WAL records for each RI check. We issue
one during MultiXactIdCreate/MultiXactIdExpand and then immediately
afterwards issue a XLOG_HEAP_LOCK record. The comments on both show
that each thinks it is doing it for the same reason and is the only
place its being done. Alvaro, any ideas why that is.

AFAIR the XLOG_HEAP_LOCK log entry only records the fact that the row is
being locked by a multixact -- it doesn't record the contents (member
xids) of said multixact, which is what the other log entry records.

Agreed. But issuing two records when we could issue just one seems a
little strange, especially when the two record types follow one
another so closely - so we end up queuing for the lock twice while
holding the lock on the data block.

Hmm, that seems optimization that could be done separately.

Oh yes, definitely not something for you to add to the main patch.

Just some additional tuning to alleviate Robert's concerns.

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#120Robert Haas
robertmhaas@gmail.com
In reply to: Simon Riggs (#111)
Re: foreign key locks, 2nd attempt

On Thu, Mar 15, 2012 at 5:07 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

On Wed, Mar 14, 2012 at 5:23 PM, Robert Haas <robertmhaas@gmail.com> wrote:

You still have HEAP_XMAX_{INVALID,COMMITTED} to reduce the pressure on mxid
lookups, so I think something more sophisticated is needed to exercise that
cost.  Not sure what.

I don't think HEAP_XMAX_COMMITTED is much help, because committed !=
all-visible.

So because committed does not equal all visible there will be
additional lookups on mxids? That's complete rubbish.

Noah seemed to be implying that once the updating transaction
committed, HEAP_XMAX_COMMITTED would get set and save the mxid lookup.
But I think that's not true, because anyone who looks at the tuple
afterward will still need to know the exact xmax, to test it against
their snapshot.

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

#121Alvaro Herrera
alvherre@commandprompt.com
In reply to: Robert Haas (#120)
Re: foreign key locks, 2nd attempt

Excerpts from Robert Haas's message of jue mar 15 21:37:36 -0300 2012:

On Thu, Mar 15, 2012 at 5:07 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

On Wed, Mar 14, 2012 at 5:23 PM, Robert Haas <robertmhaas@gmail.com> wrote:

You still have HEAP_XMAX_{INVALID,COMMITTED} to reduce the pressure on mxid
lookups, so I think something more sophisticated is needed to exercise that
cost.  Not sure what.

I don't think HEAP_XMAX_COMMITTED is much help, because committed !=
all-visible.

So because committed does not equal all visible there will be
additional lookups on mxids? That's complete rubbish.

Noah seemed to be implying that once the updating transaction
committed, HEAP_XMAX_COMMITTED would get set and save the mxid lookup.
But I think that's not true, because anyone who looks at the tuple
afterward will still need to know the exact xmax, to test it against
their snapshot.

Yeah, we don't set HEAP_XMAX_COMMITTED on multis, even when there's an
update in it and it committed. I think we could handle it, at least
some of the cases, but that'd require careful re-examination of all the
tqual.c code, which is not something I want to do right now.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#122Noah Misch
noah@leadboat.com
In reply to: Robert Haas (#120)
Re: foreign key locks, 2nd attempt

On Thu, Mar 15, 2012 at 08:37:36PM -0400, Robert Haas wrote:

On Thu, Mar 15, 2012 at 5:07 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

On Wed, Mar 14, 2012 at 5:23 PM, Robert Haas <robertmhaas@gmail.com> wrote:

You still have HEAP_XMAX_{INVALID,COMMITTED} to reduce the pressure on mxid
lookups, so I think something more sophisticated is needed to exercise that
cost. ?Not sure what.

I don't think HEAP_XMAX_COMMITTED is much help, because committed !=
all-visible.

So because committed does not equal all visible there will be
additional lookups on mxids? That's complete rubbish.

Noah seemed to be implying that once the updating transaction
committed, HEAP_XMAX_COMMITTED would get set and save the mxid lookup.
But I think that's not true, because anyone who looks at the tuple
afterward will still need to know the exact xmax, to test it against
their snapshot.

Yeah, my comment above was wrong. I agree that we'll need to retrieve the
mxid members during every MVCC scan until we either mark the page all-visible
or have occasion to simplify the mxid xmax to the updater xid.

#123Bruce Momjian
bruce@momjian.us
In reply to: Alvaro Herrera (#103)
Re: foreign key locks, 2nd attempt

On Tue, Mar 13, 2012 at 02:35:02PM -0300, Alvaro Herrera wrote:

Excerpts from Bruce Momjian's message of mar mar 13 14:00:52 -0300 2012:

On Tue, Mar 06, 2012 at 04:39:32PM -0300, Alvaro Herrera wrote:

When there is a single locker in a tuple, we can just store the locking info
in the tuple itself. We do this by storing the locker's Xid in XMAX, and
setting hint bits specifying the locking strength. There is one exception
here: since hint bit space is limited, we do not provide a separate hint bit
for SELECT FOR SHARE, so we have to use the extended info in a MultiXact in
that case. (The other cases, SELECT FOR UPDATE and SELECT FOR KEY SHARE, are
presumably more commonly used due to being the standards-mandated locking
mechanism, or heavily used by the RI code, so we want to provide fast paths
for those.)

Are those tuple bits actually "hint" bits? They seem quite a bit more
powerful than a "hint".

I'm not sure what's your point. We've had a "hint" bit for SELECT FOR
UPDATE for ages. Even 8.2 had HEAP_XMAX_EXCL_LOCK and
HEAP_XMAX_SHARED_LOCK. Maybe they are misnamed and aren't really
"hints", but it's not the job of this patch to fix that problem.

Now I am confused. Where do you see the word "hint" used by
HEAP_XMAX_EXCL_LOCK and HEAP_XMAX_SHARED_LOCK. These are tuple infomask
bits, not hints, meaning they are not optional or there just for
performance.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ It's impossible for everything to be true. +

#124Bruce Momjian
bruce@momjian.us
In reply to: Bruce Momjian (#123)
Re: foreign key locks, 2nd attempt

On Thu, Mar 15, 2012 at 11:04:06PM -0400, Bruce Momjian wrote:

On Tue, Mar 13, 2012 at 02:35:02PM -0300, Alvaro Herrera wrote:

Excerpts from Bruce Momjian's message of mar mar 13 14:00:52 -0300 2012:

On Tue, Mar 06, 2012 at 04:39:32PM -0300, Alvaro Herrera wrote:

When there is a single locker in a tuple, we can just store the locking info
in the tuple itself. We do this by storing the locker's Xid in XMAX, and
setting hint bits specifying the locking strength. There is one exception
here: since hint bit space is limited, we do not provide a separate hint bit
for SELECT FOR SHARE, so we have to use the extended info in a MultiXact in
that case. (The other cases, SELECT FOR UPDATE and SELECT FOR KEY SHARE, are
presumably more commonly used due to being the standards-mandated locking
mechanism, or heavily used by the RI code, so we want to provide fast paths
for those.)

Are those tuple bits actually "hint" bits? They seem quite a bit more
powerful than a "hint".

I'm not sure what's your point. We've had a "hint" bit for SELECT FOR
UPDATE for ages. Even 8.2 had HEAP_XMAX_EXCL_LOCK and
HEAP_XMAX_SHARED_LOCK. Maybe they are misnamed and aren't really
"hints", but it's not the job of this patch to fix that problem.

Now I am confused. Where do you see the word "hint" used by
HEAP_XMAX_EXCL_LOCK and HEAP_XMAX_SHARED_LOCK. These are tuple infomask
bits, not hints, meaning they are not optional or there just for
performance.

Are you saying that the bit is only a guide and is there only for
performance? If so, I understand why it is called "hint".

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ It's impossible for everything to be true. +

#125Bruce Momjian
bruce@momjian.us
In reply to: Robert Haas (#104)
Re: foreign key locks, 2nd attempt

On Tue, Mar 13, 2012 at 01:46:24PM -0400, Robert Haas wrote:

On Mon, Mar 12, 2012 at 3:28 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

I agree with you that some worst case performance tests should be
done. Could you please say what you think the worst cases would be, so
those can be tested? That would avoid wasting time or getting anything
backwards.

I've thought about this some and here's what I've come up with so far:

I question whether we are in a position to do the testing necessary to
commit this for 9.2.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ It's impossible for everything to be true. +

#126Alvaro Herrera
alvherre@commandprompt.com
In reply to: Bruce Momjian (#123)
Re: foreign key locks, 2nd attempt

Excerpts from Bruce Momjian's message of vie mar 16 00:04:06 -0300 2012:

On Tue, Mar 13, 2012 at 02:35:02PM -0300, Alvaro Herrera wrote:

Excerpts from Bruce Momjian's message of mar mar 13 14:00:52 -0300 2012:

On Tue, Mar 06, 2012 at 04:39:32PM -0300, Alvaro Herrera wrote:

When there is a single locker in a tuple, we can just store the locking info
in the tuple itself. We do this by storing the locker's Xid in XMAX, and
setting hint bits specifying the locking strength. There is one exception
here: since hint bit space is limited, we do not provide a separate hint bit
for SELECT FOR SHARE, so we have to use the extended info in a MultiXact in
that case. (The other cases, SELECT FOR UPDATE and SELECT FOR KEY SHARE, are
presumably more commonly used due to being the standards-mandated locking
mechanism, or heavily used by the RI code, so we want to provide fast paths
for those.)

Are those tuple bits actually "hint" bits? They seem quite a bit more
powerful than a "hint".

I'm not sure what's your point. We've had a "hint" bit for SELECT FOR
UPDATE for ages. Even 8.2 had HEAP_XMAX_EXCL_LOCK and
HEAP_XMAX_SHARED_LOCK. Maybe they are misnamed and aren't really
"hints", but it's not the job of this patch to fix that problem.

Now I am confused. Where do you see the word "hint" used by
HEAP_XMAX_EXCL_LOCK and HEAP_XMAX_SHARED_LOCK. These are tuple infomask
bits, not hints, meaning they are not optional or there just for
performance.

Okay, I think this is just a case of confusing terminology. I have
always assumed (because I have not seen any evidence to the contrary)
that anything in t_infomask and t_infomask2 is a "hint bit" --
regardless of it being actually a hint or something with a stronger
significance. HEAP_XMAX_EXCL_LOCK and HEAP_XMAX_SHARED_LOCK are
certainly not "optional" in the sense that if they are missing, the
meaning of the Xmax field is completely different. So in all
correctness they are not "hints", though we call them that.

Now, if we want to differentiate infomask bits that are just hints from
those that are something else, we can do that, but I'm not sure it's
useful -- AFAICS only XMAX_COMMITTED and XMIN_COMMITTED are proper
hints.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#127Alvaro Herrera
alvherre@commandprompt.com
In reply to: Alvaro Herrera (#126)
Re: foreign key locks, 2nd attempt

Excerpts from Alvaro Herrera's message of vie mar 16 10:36:11 -0300 2012:

Now I am confused. Where do you see the word "hint" used by
HEAP_XMAX_EXCL_LOCK and HEAP_XMAX_SHARED_LOCK. These are tuple infomask
bits, not hints, meaning they are not optional or there just for
performance.

Okay, I think this is just a case of confusing terminology. I have
always assumed (because I have not seen any evidence to the contrary)
that anything in t_infomask and t_infomask2 is a "hint bit" --
regardless of it being actually a hint or something with a stronger
significance.

Maybe this is just my mistake. I see in
http://wiki.postgresql.org/wiki/Hint_Bits that we only call the
COMMITTED/INVALID infomask bits "hints".

I think it's easy enough to correct the README to call them "infomask
bits" rather than hints .. I'll go do that.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#128Robert Haas
robertmhaas@gmail.com
In reply to: Bruce Momjian (#125)
Re: foreign key locks, 2nd attempt

On Thu, Mar 15, 2012 at 11:09 PM, Bruce Momjian <bruce@momjian.us> wrote:

On Tue, Mar 13, 2012 at 01:46:24PM -0400, Robert Haas wrote:

On Mon, Mar 12, 2012 at 3:28 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

I agree with you that some worst case performance tests should be
done. Could you please say what you think the worst cases would be, so
those can be tested? That would avoid wasting time or getting anything
backwards.

I've thought about this some and here's what I've come up with so far:

I question whether we are in a position to do the testing necessary to
commit this for 9.2.

Is anyone even working on testing it?

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

#129Bruce Momjian
bruce@momjian.us
In reply to: Alvaro Herrera (#127)
Re: foreign key locks, 2nd attempt

On Fri, Mar 16, 2012 at 10:40:01AM -0300, Alvaro Herrera wrote:

Excerpts from Alvaro Herrera's message of vie mar 16 10:36:11 -0300 2012:

Now I am confused. Where do you see the word "hint" used by
HEAP_XMAX_EXCL_LOCK and HEAP_XMAX_SHARED_LOCK. These are tuple infomask
bits, not hints, meaning they are not optional or there just for
performance.

Okay, I think this is just a case of confusing terminology. I have
always assumed (because I have not seen any evidence to the contrary)
that anything in t_infomask and t_infomask2 is a "hint bit" --
regardless of it being actually a hint or something with a stronger
significance.

Maybe this is just my mistake. I see in
http://wiki.postgresql.org/wiki/Hint_Bits that we only call the
COMMITTED/INVALID infomask bits "hints".

I think it's easy enough to correct the README to call them "infomask
bits" rather than hints .. I'll go do that.

OK, thanks. I only brought it up so people would not be confused by
thinking these were optional pieces of information, and that the real
information is stored somewhere else.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ It's impossible for everything to be true. +

#130Bruce Momjian
bruce@momjian.us
In reply to: Robert Haas (#128)
Re: foreign key locks, 2nd attempt

On Fri, Mar 16, 2012 at 10:08:07AM -0400, Robert Haas wrote:

On Thu, Mar 15, 2012 at 11:09 PM, Bruce Momjian <bruce@momjian.us> wrote:

On Tue, Mar 13, 2012 at 01:46:24PM -0400, Robert Haas wrote:

On Mon, Mar 12, 2012 at 3:28 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

I agree with you that some worst case performance tests should be
done. Could you please say what you think the worst cases would be, so
those can be tested? That would avoid wasting time or getting anything
backwards.

I've thought about this some and here's what I've come up with so far:

I question whether we are in a position to do the testing necessary to
commit this for 9.2.

Is anyone even working on testing it?

No one I know of. I am just trying to set expectations that this still
has a long way to go.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ It's impossible for everything to be true. +

#131Alvaro Herrera
alvherre@commandprompt.com
In reply to: Bruce Momjian (#130)
Re: foreign key locks, 2nd attempt

Excerpts from Bruce Momjian's message of vie mar 16 15:22:05 -0300 2012:

On Fri, Mar 16, 2012 at 10:08:07AM -0400, Robert Haas wrote:

On Thu, Mar 15, 2012 at 11:09 PM, Bruce Momjian <bruce@momjian.us> wrote:

On Tue, Mar 13, 2012 at 01:46:24PM -0400, Robert Haas wrote:

On Mon, Mar 12, 2012 at 3:28 PM, Simon Riggs <simon@2ndquadrant.com> wrote:

I agree with you that some worst case performance tests should be
done. Could you please say what you think the worst cases would be, so
those can be tested? That would avoid wasting time or getting anything
backwards.

I've thought about this some and here's what I've come up with so far:

I question whether we are in a position to do the testing necessary to
commit this for 9.2.

Is anyone even working on testing it?

No one I know of. I am just trying to set expectations that this still
has a long way to go.

A Command Prompt colleague is on it.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#132Alvaro Herrera
alvherre@commandprompt.com
In reply to: Simon Riggs (#114)
1 attachment(s)
Re: foreign key locks, 2nd attempt

Excerpts from Simon Riggs's message of jue mar 15 18:46:44 -0300 2012:

On Thu, Mar 15, 2012 at 1:17 AM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

As things stand today

Can I confirm where we are now? Is there another version of the patch
coming out soon?

Here is v11. This version is mainly updated to add pg_upgrade support,
as discussed. It also contains the README file that was posted earlier
(plus wording fixes per Bruce), a couple of bug fixes, and some comment
updates.

There's also a SRF for inspecting multixact members, pg_get_multixact_members,
but I'm not sure how useful it is for the general user so maybe I'll rip
it out of the patch before committing.

I mentioned elsewhere in the thread that ResetMultiHintBit was bogus: we
don't know, while running the various HeapTupleSatisfies routines, what
kind of lock we hold; so we can't do anything to the tuple beyond
setting HEAP_XMAX_INVALID. There's probably a good place in page
pruning that could be used to transform multis containing committed
updates into plain no-multi Xmax.

The whole thing can be seen in github here:
https://github.com/alvherre/postgres/tree/fklocks

(While creating this patch I noticed that I had created v10 of the patch
on March 6th but apparently never sent it.)

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

Attachments:

fklocks-11.patch.gzapplication/x-gzip; name=fklocks-11.patch.gzDownload
��eOfklocks-11.patch�<kW�������0;������u�$���&��$�G�Z�&����7��oUu�i	2�����3�������]B7
��jS3`jCs��3'
W�r��]��W]cak~]c�U���;��kj�a���a{�P��V�����Q��VS�Pe
J��'�����}��_���8al|�������/OF����3x?�l�R�,��������G���A�^�]���<���2��2S���`4��j-��i�L����v@O�`1�"��7s�.J�P����9_�K�W�:��fP$���.�g>Wm���xX��g��uvk3v�M���F�>��rL; <�K��v��@p5�q�����
g��_�0I����x�N=X�x�X��i�-�����AS��w'�7
4�IFe��Pg��{L����S�0c*�X���-�W��Re���e~�cm��������{�"'�T�:�������j�<=�$���|af�����Eg9�q)��40���#��6�������)����8?��������k/@=��T��?�������8,��+�]1
�+O�iW���p?`D�Em���%�.��7������
{��]^��W�w�d�L�ye���^o�����Q1����`�a���-k�?E�{��:���_��,�c�q�D��\��X|��������w��#r���A�`��;�L{��a�\h��j������RJ������\�����\����U9��*[�����/����Z���s;�H��d��k��Nk_����)���8/[�x�}"�/�,���L��35`����a�|���8�=�����Fr~:A�����6=2*>m���a&���"^?{�RXY�0�y�?���i5���l�T[���|��`G���<�^�#P���y1���{���$%��8"��)�P`U�X��?�Ux�s��b	s�0�J<�Og�S��a0��[�r��2�$�L8u���z}��@;0�W&)��F
�l�yPm�!���{�c����c�������V��oBg�7���
��b���'��km�/&����?�
�g���	�HC�N�E+��
���3�����p�G����5m��XC9��S��UhD,iL�����G����(I*1��U�������������72#BI�(��K���7#.�v!�6I�|Fv����
��z���~y������*��
}R���q��>D'���v=`�QN���q'����G��G�I������N�M�3��g��N1�����#2h1�U*XP-�k�Y�m�P�X_?�����+Bn����������|HpYI�c�Yd�H!�2"���x ���8����,�$�\'���[	��:�sv�V&'��OnV���6��jZ��UEuPe���yo�������w����i��{��mr��
����u�u��r������S��ek3	].r{�1e{{��������&�yA�b���B�����5�#�7nC����2���,gdv(��3��3bW0F']�R�z�S<�o?���7C''~se$�hx��C�7�A��� #R*7�����
�K��s��=��w�;�k?�^s��5*d��@�3 }f����>��2��=c(����c�`O�dp5����6����3��Vae�������H�%.��R
\M���������[������y;YPVns���)SJf��#��4��}���������������M�Ey��F�Du���n1��SE�����[vq}>��tNF�����3:���|��tF�����h4I�>:b�@��<�e���UF���s>�p/�.��XW0}E�"2y
���"GA�.
C���$����|��`s����n"�M�	"���l�=z�1�(�H���(rPi���Fep����)�Y&�hh��Wk}�������
BG�i�#��)�K�}�t'���^4T�K���)�(�*k�R�2T���������43W{!�����|Dg~]B�
BkL4X0`M�����cs�:^��A�$'��En�!���Y;[��b&��S��5����D.�����A=�t�{R}�S�Zp�����t����l���vn�kF����me���H:���%�w��������=�n���fg�n�Z��]����������R7����g��1=�l51+w�Q���t�������1�HKM����a����E�j|2�?;gq�i7[��~s��(�S1�w�k=a]X���kQ*+!�q���>d@����6
�����d����������<	\h``~���r���7���1!`�x\��F�$e,���SPq���Pr�CC������3�`b9�W�_�G��M9>*�T���I��+���c����������^7�As�]x��<?���thHg����z}�O�i�-�'l�����R��J�{3�^{�K����B��zT��������z��]U�
~[��7�������?���j�V�������p�A�{��<��'Z�o�t��
W���"�
���sG��D����^vh�A���������b��B!7�M�1Q���`O���
VU�k��? ��5�������4�W\�2��0��f��d��@�teC������!/��&D��a�rT]�+�W�}�ka����|Q�<v:�_���>1P��b`[	 W���DWf�s��"*����q����z��R	X&�d�<v�w��7I qs���H|��0��|���K�O���i�KPL�f��vc�r�}�|�R���=H� @k��p���;�\��;��7 S���!���,/;��*��Y����C �����6
z'���2���c����J/�5��y8������;�<�h��aR0��v���fV[��B�����j?ma���Am#H�1��,+6��\�R[�Vjk�J���_�����_�i�_;��e����Cc�yQf9���`��c����C���
�����v��w�}c�`e���������]���gk;�v���m[s�����Vx`��Pl��,�J�N`/����K������zW*����>	��^��,�����:�J;K.M��b�z�6�
����];��8�o������O�3yM��F�"���M��I~���S��`�T������>�/�t���&t4�a4���)�A������3�(=����W*�
����Ig�3:Kg��a�����@�lY,�n�&|���������������k��?�O���>;���������AW���tV�l]�����{�,<�����n�*)��**���z���::�"����-f�DTJ�-&�^IE����wsB�a�Rr4��)[E�GU�bh��
��<��Y��� �9�q��B��%P�J�B�7�2u�J�&����-���!�[0�@���TZ�J�w%����%���\�o.:7�����y���n8�C��,D�6�a)+���\����&�%E��W�W4*/h����_Z��0l����{!��a��U�����R�&�f�
�T�/1�
>�la�zO�@��Y��^b�?>��Lt���E�Tua}���]�R�rJ^�:N��T�����B�nysH����e��������3B8�R�<���l����Y!����j����y��K|�s$��e��&��~%g�	�m�C9W�bQ�������Fk��0]Xz�g���DsC���7�T�^�lF�x���L?��!k�Gd�����qFV����w���W�]��D$!F�yX�B� �c#�5{%�'CCN���o�cu�����df��I	b.�)J6"�d�L�8�Q�@l
�
������A���?
7����^L�.'u�+S����,��H��P���?���R	�#^�������J%�M����RE��
L�[�k	�f�D�����|�|�Ay��&b
��'�N)���q� �:EHE�6�%�6�<�`'58d
k��Fg�>��66�o���-���?+b������I��uh�Or>�Ov$�E���D7#$y��/!�4�M3#L�n5��D'N���b��}�9Sj�2����_"[X�g���VI���� �����x�Q7��[��6�)E�F��<�R5fK&�����WB�I�r#�B��J�O&�J�G�Z��(���N�|�F����2�X�E�%��"d)~�Y��7��b�H.�q���f���#�[�,�#6c�C�d���i�����@��M��4������D���<�P�?a�����B�&B)�j�<~�IY����cJZ��Y����<n-n�ZX�Q��n+B&����b_5����6�=Ra8�u��"'cK�}H��B��]BG/`����$���,��m��e��������*��R�h�y9|Q��S�m���=�e[���)�����3�&���3�yzx������?��������P��k&��3��vt��5�\��J���_p���O$�P���p�����9����z*���g�%ms�n���{zM������������3�.�{b�PT�<vD�9u}u�uQ2�A�c�����$�(��E�E��NE�|����sc�~��HZ�-��n)����X�W2�~�'��]/\_�Q����Ml=T��a���q
�~o��Ss|�N�W1��o4xt��<�O���G��X\�A���i��G���+{�O����|#
�*�|*�.�����v��
���_�����,U��/��[�:f��w�����n��iC�b88���e�fT��'�wU&S�/�?��S����
R���,{���S����{��?��SbZ��S��v�]���;�K��[ml-��WM���e[��[��Y����otV��
�[5�*0n,{Sq^����PrZ[x���8��D��0���/B4���x��bW't��'zi�����=�:��[��������IK3vy����������(��GA�[�^�V��^���u-:W�����<��@��1)6��Gx3`�*P5��r��g��\K�:W���-�8��9j$G83S�W}���Ep�pO���U�Q�������A�U�x��p����B#��n���)�[K��=�_�l��lR��^3�I�c����_���O�k���"��({�ak���%�?^���s��
������Z�������^����pC�h�z�n�!��vE�*hW��55�(N5�y�x4�?z^�lI2T	��RW�<���xe9��2C��W�1(� �Z��c��)����F����\w���ZZ����h�������;�"�q�Yr���=�+��������}b��0��7�.�A���AB��T�S�,���n��!��:�w���O�, �?���Eo���B@/��s	�V�ky`_B\���aw�
T^�'6�����}�1�uG'`ug��p���O�Y��������R5I�?Q������*j���d�Ue�z�h.��w���Q<����)��Y���_$�G��S}~|��G������z]��aM8��c�>jH������Cx��^�u��wI�Q��$�����W�DE���p��~���F�#b
�x��.6�'b�A����E!��H��gbG��5��N�EG�T
S���5��t�)��5Vc����Q�S�����8�&�u������+��9��,�F�����yw��&�
}�,�T(��U�Q����5IY��}x�@�L�D#Qt������%�\�R��3�c�$��w}����;F"�p�	T�y��7�`��������������%�.6B��Ce!�~e�S:4W���f�%R^'QM
5���?D��Y��J�E�p���%fd����S��}��C���e�Iqd+P�}(�O�[�87��p5!E=���f��@x�#39�V������:m�S�w�^�����.��<���+��5H��fNM:J�]#���{�^aQ�"��Atw��^J���5�z����I��?|�S9���Y�G >#��T�������Sf�/�2=,V���`-$;3;C���W|���1��h��h_JK��N�������f����s/����o2j�}2��\b0��$'���w�����k!Od�������w�b�������N����]����������%G�;�|U�����j���r�����2�}��j������T��?�9��#���X"x�����D�����DPzf=XM���.q����C���Y���/��%�Y^��&��.r�ww��?��xTh��b&��^�`������[���'9�xt��_l�_l$d#�$���l���|=6�g��<�>Uf ��V��<���v�����)�U�ir��#��}����������U�,M�W�C���7�w�t�s���C_?�a!�?]x������zb{�C^��T���i@<�������WM[%W��tb��H)����������h%wQ|uev8���d�����,���=�����
�K���J����q�v9
�5��"�;���=j���5�Z��_��k���{]��*'����po3�RwwW�����Y��h���t��)���jw
w�J��5������]yCXi'��-�xN�XA1
�e��yZ�l~}���>�+�]1Ez�y	���;F|v.�L
W'�*��P0��;���%�ml�{��g`f�0�Q��(��O&���F�R�l�������:�>cb��E������I��#��y���&y-��qB{��lz����AJ�s<d�n{��-5�P^;��%,�~5��
�V�y����.�y_�����d����5����U�BHL���kz�'S0l�����)��d&���~6��A�aB�!�P~-����<�r����Q�@�q��sD�����0dZ������5Iz=Fg�46D8K���:�Rg����
��y�^kK��Y�Q|'�z���j� w�`'E�d�!H�z({�SE��()a��u�X��}z0{w���0��]����^ �'���pa'{;�Nz;|��f'�<����pt�����1
G��=�D�h��Q�x�Y�8�h�5/�l��{��t��U�A8��������mf!]�`�'��H�3������M�V�]��n����������g:(�~5������q����U,;?,[��<	�"��N����+� d�x9x����p.pq$�k��~_9s����.��F{s�����&~r��k�Eua��G�������r�,{Q����a�g��df/i�Y��{�������t�-�6	����~������c����xH��+�5��d�<P���
K����,��G�	Y�\���E���M���3�(7�Sq�E�������P�
�N
�no�I����-����x���5�d���W������gpF��*pe$�q��PO��(��E2�H��.��so���t�L�Y�?���r�A�����!��07�{�]�[���W������������D���Y�Y��=��U��\��ijd}�;N��M2 +!J1�jH�����T�r�
S���@zL����4Q4�D���S�3�q$C)_K�be3s.ux����8���M��~���~D���KZd��7T
N��6���;���*Q96e�d��+s���RO�D��B�����;��&�5�����u��}~S\��hdV�/-F��B��x2���%��EF�1TJ3����A�)��HVH�}<�Q#�����N"���4�&!l�}|�+��)�({
�y��h�lq�
<�UA]�i���TS�j�l6�����;O����z�Rrc�9T,������o����-�;s�Y�X@�!ad���oi��I��n�5����)��=%c2i��H��L����%�9^�a��@M�Lo������l� ��}d��y�(�"3�u2��[�����u��e93�g�0�y�[S6�	��h��B��dFqFnf	�o�2m!���	����N��Y� - hU�-��fH{��e����:J�ofH��������E���[��Cn{1�&n�[�z���q���(��]k����X�G�D?��}5�?Gw����Ey}�����~6�F���9��X��)��wf��(O�
w�9��q�y[}�rSv����%��l�������(�b(�I.�i��$�����w��N����������]S!S���ybFnt���	"��d4�Fk��g���u�*�������l����T�C������|��o��qz�����Rgv��;�R*#����*(Q����,����N��M��z�k�U�G]���v��=���^������?�{{��<Z�8��;�m�vk���}����������!��8�lE0{�I�,R�"1����c�o�a�;�s�O@�U�8�!��=j�f�m�������O����EUr����r���EA���������5�>#n���sQzp��L�)E������:���D���p��c
b&�Xz�Lf�����M���g�Sy���rG�D�	�m]�|���$�}��2Q,�e�-c���F[�����K<LYg�:����_�A��3�
?�3'��{�*��y�����=�LG���tp�K���O��c;`��[r�Y��J?��d��z�4��u,��M�}�����y�s*����t>*l0����������F�:?~�]�!Ue���*z5�pM�R�Z�����]P1=*�Eg�&���J���,�M��Q�����a�pk������g\3'���f�5Kt�]��2�S;
���U�L�)+�"`��?Y5�'���1�QEl�HA��=$������vw���|�M��'���5��;�v����?R�:fA$TE��[jbJ*��J�-�<��]7'TbGO�Ry�T�=�Kg,\�dfg3=�)�B9z�������S-���"v�����D��D���Q-��1e��"*�3�^��S<M���ms��*�+�|I���$o��%��er&U�a�@M{Z�}���#qW����}Xz�J���]��I���J�4�xskp�l���&����B1��z�����R��������@���;����(->����|�����I�g������4G����%���8.Ux9��.>�:��H����A��31O���A�<���SD�wG�����c�h_��7��F�(������u���Wl�R�����e<������k�:/�\I���'��9
�����"q���F�eO���_�c�K���yn
�����ZfG�ll���wd&�dTu/�WW7������L��p�����#h�q|
y�	�(M(P�&g�k��i2A��3CG��y%F�T��l�O�u/kT��s����`f��f���9
$�pA?#�73��w�4���b����N>j�43���Z��0�j0�EwZk�H%���5�x(['�I��V�����z���A�1?p���-�rq�Eq���w�9�iP@��NL��RQ��~��9H�:6�z8���(�R��DZ�������O	��4��ZEL��]�>T�xF�uVb�8���x����P�+����7NVv�a�)��;"��h�a���O�
4BuHB���jIj�\rXp��������eQIe3���]j���*I<z�
���	��l�"K���#f�z�K0m��'��r�pb�,�q�
��M�+RA���Rs�%���q����R�>*����L�,��V!�B���|R�5�
x�p����K�'{��N�S�����>��55~j7���\'.v��w?j��7r��)�O��K�2${����H>R\2$I�S6��7�r���A�e^��O��{�Eo�|oY�1B�b�2�2s�37���������|��u5L��.`��s-TO~(�	�D`B�Zu�R�/!���(&��yz�OJm�)8=`�f���{u�1��2O�f�����&��\�n�E�1hD���lM'<tx�I�\���f�����u2���l@��{�a?�S�������
!�6
��*E~�vYy>�P�7�	�q��y��*��y���
v33��01y)-��$t�f`����$'�
��D�-rn��Q�'���!H$���'���������C^���p�usj���$-�.�$�u�Sz�W��\�Gz���@�H+]����]Q
x��U�b�p���"�#�
rl;�4�ed
U�{f����G��_����X����}]����>y�Y�C���=�C
������i�rhoK�N9n�V���&�	�HhN���"�!��
���$B5��i��@�����:
+�3�5%����M|��\p�X��>~����g>�(��.d;'�+SD�#t�a�>j�����<�<���A��A�uKx5=	u,�1�q@�|��_�X�.U\w3D,��#RM���"�	��`��,���X�E*���D-+j�����R�G��'�����Q��	��0s^3�����a� P������
���W}��J6Iv����<�N����G-�m�Y�A��9r�f�I����)��Q���F�Y�#	=�FQT���Y�x��Q,"����@>8�A��4�&�k��������+����J�F
�����4�;�#* M�����j_�|�(b��x%�#1��\z����C�������_WB�����k�q4�O���[��8�G-��)S�	���05��E��`Cm0�K	AnD�j/WI�����E�s�!.Wi���`��:.�9��<�������7�ub?�&H�	^o���w��XA�5�G�R�� ��{�-c����tj	s�z�����Nn��]��5�
r,�����*��,�p�Rv�6���?c�P���|~Ylj	��Z_a6S����$���\s�kTEQ\�ter�5��,����c��82p1g@K���f������)�mu2�#�`.F�!�a4vq�rj���Hc"V�#���&�$��`�@���Bl����sb��s�Z������Y��
�����T�fn*`'#�?5]F�i���F`v���Q{n]�|���mN�}�^�����,$:$N��q�M8��;sv�:%C��<��p0��*�cP�f\��H��th:��R2�8O#q�����dr)�@W(�����<<z��5V1�7dX$p�QF>s���(qz���f�q���!?� ��d������ZtZs��d��-��54��_�xGO�=�{4e������$���E�)KA2��s6F����7�H�>����b�	��Gd6V���a*y��P����F$x������������u��D6x���h=?W\te�.�K�`d�/WO��rc�U�D���V���4;o���Xz���+��O�����eR���A��&��'�,W4������x�A�J��uK�^~+


�_+��1��m�6��7��k�t0�-�p<�Dw�:A���w�Fo���:<�5z�Hv��j*���F~a�� �����}�{}��Z��}5S�#�]���c'�QA�<�e�5�����\,�W�|L�.��Kp�C���uXF0����M�������	��'x=ua��Z��),8J"��HC�� N��?_�+?�)�lRW�5����VTW�}��Pn�>�?0�vTx�i��.���[��g�O�>6S��������)��8]���A� B~:\�����u�"�%K������ZS.vcG6���
�"E�ig}5�&��Gun���8,�������������v?^�a;Y�����o��w�{�P��}|�]�&�w3�b��l�
n�j�z#�I�+��jB��CF�us�]|JI���
J�7F����Ngy=5���V]�������2��y�xB��n����\���"W��������Zy5�	��}�����=a����������A���+eS�m�] �x0��e�*�Ik�
uP��X�4����)s�E0@��~��=oaP������8���:������w���d���xd<(���t`�����Wae��M����x ��N�Z\�(���W|[��Z��B�B�a��%�����(�Y^���^G;}���x��o�9��z�5�M�Arq���>�sB�V���]������
��@�Ia
Rm���o
��/�x

���'l�r��� t56����|��2���w�	>��W��5�n3�-|�0.s�5�%���&�o'��
���s�L��&����%������:��;WZ���/���_k9L)�vH��>A�����u��m?VJ�0>��i����.���iv�l�����>���
7������r�����Z��^�}������x��!�i:Z��QO/�S^�6���0J�W�C�V"�6�+��W ��vC[U5��k!�������lqW
!n���>;1|�S2Fh?ldP��U��X��Mv��q<��C�nSo)�AyF)q�
)=�\�.�.
'�M���z��I��0����������t��,Cwb��24��Z�*"���X
��:3�5�/*c����������V�\U���<����`�����vm���t�["\Pq��g>�M�eMcn4�Ul��h�c���v���GQ�Dnm��i%�fs�c�\��{y������k!K�F4��\\r�E_r#��Jg���)<�Bg5�s�����#�avu5������o���p/���Q��/�	9R:��_7F�� K�+�L���'��R�aLm�{	�]�(3������H��*�Y+�UI\0$��g�>���+<_����3�
]�>���G~ ������au���:����(��'\m�9����A�y;��o����97���U�EF��d�����D����dVt�����0���l��n	&��kj�
^���"���&���2m1�����`/�����u�HUd;#�Vm�]�,�^���!���,X�/���������R?�T���'�������F�(T�d���F�hG����������dW��K�R
���yT�yKY�G������'�I������
[MY�0��l��������=C�\�p��0�y��l�W��
{"�.��������}�(�>�]��N�����q�f���y>D���$E���������-fn�P_�r4�|2$���1��7[TEDd����>[���������G"b��)���j)/��
�1���y2�5�����F��[�~]x���|�J4Wu�S��j����f����\���q���].���%j�����l$4�0Y!��5�
���a$�q������������I���dH����7����@���<���6e����r�E,�uA}���y��"A���+tv�8+DQxw�d�gCvp�9Y��<N6���FWw�������Tl��Z���Ei���'^��n���~(��6S�����-���=��e���S2M�����,�*I�pt�e]0��L�����2T�kQ�����������o/Q�}�l832�
��?�4�� �I�x!mZ��e����]���0u�M�	��F^[���a���� ��p��*L��)Z���n�������.%���t������j�M���.$N�U�aQ�3���������������0v�N����
�VoP����Y�^�[O,���#"Su|�x�Q�wM+��K#0O�S�fW>\6��,]��?�$1+&�
R��)��L��� ���O��~R�o6;~�abM����KD�tb��c!�l�q����n��#��f�v-��y�Z�@�&,�0HE,��-g�c�n����-"r��]�e�E�(���xpm�)5�R8������X����-*�����6Ba;(���u�3L���.��D��\��%�����7�rY�.3�?���������6Y#�������QZ����w����s��P������OY��P�5X@X�8Qx$Z?��S8S��������ji�4B64��(���<��n�{y�Z��{��i��2oz��P�� 0y���f����Xli���H��<^�	0�r����>YhAx�2��aA�X��C�D%��cM-�����
0&+�~SU�V�	ic~\Qrh�������lv�=���A�>Z�pZ�D�&��(�A�?;���Pj��A�I��B^iS���	�M};��5���g�$�sO���CsK�J���$#| 7"�P7�~�'�Z�P�b�54f[��K��'@��2,SK(�S�,	�����`����$-�49�D����H`�N2�cCg��7rNf�F� 5M�RL�D�}H�k�+���Y2�����	�����.�s�jy�]�q�*G�M����;d�u/��eb�������\y�EF�&��NM����n���<�?��{	EQ�7�~��p���V����4w8�J�|qr~���%����������y�o���-Ik.���3��!O�����a�[*I�U��4?*4��VEw�G��F��$��HVCf8����7��@�n$��t=����A��N�u�]�[1�6�].V�F�~�P�rED���]u�����3C�\�2qA��.|����fE�d�Z��T�JW���T�\`������������d"�
���#�A���3M�����'
����4�����LZ�[A�����Z���n�s������[_����^X���Q���1���R�+��I\��{1�`��$w�EDN��A�1���%���7�����F�\��V!�u���"�2A ��8��%Y�j�\.:������${��/���>=��<�
D�gh����n�+Z�4�LY~H�/�}�\��,/�I:�u=���#�����o�W�}k��E���������/0Q��]���L#q���&�����%�����-�@����YD����[�6�-������q�����
wv�D���N*�"�V��N�g��:l�N�h�:>������3mq1�Q�$V}J��r(���2�s�g��H~�T�>�#u7����jo��2Y_k'����d�$#�ID2���9�;ion�+��2=�LN�q=�/
1�9��EM��e�JAj�o�0���{��Q
.l.�0�qB�
\��z�k!2���������<)��p��,�s�M�7@L>'��U�$/CpKE��$H�;�y{�H����F�z�X|`���U�}�! �g�%�93��8@�w����Q�7�;E]	$E$�?p���q:�S�*�#�A��?I#�,4H'�����S�dD� ��e{�[
2}Mj��@{��\�I;��[�R�R4�W&Y��M���oj�.�����i�k����=�+���E������M���$4����|�T���UW ��m@aV�������p����H�! ��N?�:���p�x���-�:�p�j
A,s!�x&'�;G�	����7��$�;P"�.������$V��+����t�D�
���P����;_I� �����PN��:��|Y����|I����<{�3=�:�O2��x�������%/���������u�R��0�;K����q��`$R��7w�=e�����?�yY0������{;����������ye�G���v<�@�|:���>����Y���F�"
�fq�����&�N���
�ek`���#�t.HZ|�#A�:�`H��sq�+����i���:��PM�����:2�A��,��*8C���
#���X���@�j��$r�m3z��������T���H.��PC��8��e����M�Qn1&��*U��\e�����!�� �&�5�����,
����E������/
2���r������?�}q�Q �?$��������0���0z�4���3|�k��H>3����G��~$�7���N��_.�@�[�+Q=�e�.!0����q��H!��F��#W�!�1�cd�];���������5�gN����������Df���o]h`\�G2�r���xZ�:�B���Cb*BZ��~��9��x 
������@;��7�en����2��e4��1a�E��d��!c���1_�5gX����k���pk?jmnll��v=���P�Zf��Bv�� �b��z��uxa���m�8�%�R��5M���������<{�Q%�oZ���sE���TgUD��7�.w�����&8��a�n�����u�����\�>���0�$�)"i;�aw���f�
���Q�K����i�z^�NuS�a�WC�e�e�k��zc\
8�"�b��3����7v���)J�}���aH��k��!v�������K�&����Xft��������i5�����y#�@[�������0|��
����];|V1�������	��Wa1�s
p
�:oo���rV�-�
�����wVi+��h�ULS(�p��j9������!�n=5
�=�@�
E,��J]y|�x �hp�k�C�T'����V�u]% ��g�CJZ�%.�
�;o#��U�P�R��J�}&��[�

���5�p��btXFbVC�����"��l"��-��t����g�AX������,,���^b�!,) ���'���J5U���y �[�RfG�����dcEC��e:8DwAS�PA��T��q��yd��:���/�(�_��D��u*TREgEy�S�zY�K9"Ct�+��,$X�"�g
<�����b,���Q���,�l�������}�b�ri�{�.E+�3cP��i���jXz��61
���Z�[�|��:�;{���`�k�P�	�2=;�2���|��Y��x�p?�Y��[���2)���{D�"C�`��������h��	����LV�����4�'��d��B����
���um�T���xVQ�P"o5������_��UL�}�+�+�K�s8j^���KY��v)��~���i?��s��������z������$�4!����_�^���u_����ovN'��Y4�%:):A����������Y��6X��Ww=��$)fJ@�L6.�lO�s��������`M�1{=8�Z��

�����f����������������#;��d��3tV��/	^^hW�����Q~D��
��1��m�Fe:�&��>��nv�;m���vfZ)��s��;[��]9JX���
�]%xu^���6�~n�/�W���(��r}NH��96�#���?o�2�CQ(l�vG'O��31f\k�w(�i���������0k+�@��s*xh�!@���Q�\������lj�'\Bk.e�o�	-�M)��PG����x��j��5��v|����|k���3�\XBs��������\/{	�����<��rhX��*��I�$�iO�*s����)��)�C�@���6R/��!9Ju�BT�\�Q9sIXCE�R�j�JmD*���K	�H0�j8��������%��
l�P�{�"�5�����&��Y�
cttT>/=����%S�����0�r�/^Z��������
p���g�U-UNj �������||�q���N�K��	��S�#kZ;z�X"z���"��G>�r�d����d���7����	�:��A�)�T��
�8r@��(S������;<:�x}x�������3���������p:�4���K#��k�z�Nc�h�����t��k�C`4�x)x��'���L�Lz������������ �L��}K��an~5l$HC��ou���7{���,F������z��A����6����\�(��3�!F�We��j����v-�0�sR�f�.�K]��Z������T��_��y��dR	�f��l����E������h;�
�J
	l��Z�^\Z���V�?|t��{*��Y���)�����xT1�R�,�W����NeS6a�@�Q��C*5W_�u����M��7�5F���o�!���.��������[�����9S��f��%���n�`0�oH'_�,�p�Le.����?����^o9]�[T����FnX����\CA����������{����^��[�fX(���@1lR�->�)�(pYK�k�.U;��QE-Jq:z����va$E"�f�}[Uyg�k&K�`)��t=���r
x)B��5Q���1�����������]c}*�n���=mG�S�s��&����q���1OY[���$@����-�]t��B�$�
��A%y� ����jo�������$,3�*��g<J�S������z��X�y"��~�;����
�i�?U��;G��y{~|����H��;�d�M�P%�d@�]#�A�b#�yn�Sg�5R]���h��0�N�Tv�����j�����D��:c�J.�.0���"&�B����z9h��������t�.DJ�L�����6.C�����V�~��D����0��O+��-�Q������������������S�#�Eq�,���\1���,���5�����������-��"*�3�:�ZO��N�$e��=���\��J{���#u9����_	��^�5�~{�����D:y)e�4����ss���������'b)(�J(D�G��v'(�-�EY�������3e`Hm��0#]��^R-������`����8��m�����7�`�R��f����+���V20�=T�o�6��!���/����\G�Ms��Gun��T�5���
P���������L�I�0��8�t����M����a1F��E'���xab�HE��s��D�z��k1�fY?��b��5I�����5��#_p�0��������
���a��m���L�����M�����r��Ry�F<N>����A����pO��3�.OD����C���]n3����
����lp����]
�[����-��Br�(dJ�.���E�)�y�������t��c�D��&�&���4H>58GRm]������{(���L��e2��Q����]�3	�mZ!#����%Itz|������u��MF$�)�~�����TY�z-������Q��X<��P|6�������w�t�e\a�Q��Mf���\q	�r��H>�Q���T�!��wl��_<��<������N���H6��1Z���(��5P����|�$����BA��Z����R����C~�f�r[^L�����LU{���
P��V{{�����Y�^��]���e���<����}5�U
P��Oro��B���0��w�.j�+����s��R{.����D+�Wn���l	���/�3-������NSK�t�]t�y�{�U������oZ�KHz�F6	aTf���Yk���ezN5�K:�B����y���
#�M����n���_���2�����or�jpY�5�-�'p�DTM:O�Z���Qr�5���S�����.-��8�I��4D���9D�0��H���������`�m��
�>V��� ]�m�!.�)��]���a�R��$�W�y�I.������}��a�,����g?����#-����@��|?��qS��0]
T��1/��2����>�h#����,69�����}������e��1td����5?z�����6gs[�M39\0�b?��2<<n�r���������5/LB�+-��Y��E�9������D�L)�%J��Ev���
�O*JAV�eJN��N���i�$��lp[>I�%����|����u�[��K}_5Mj��B�Q�g���&� �ai��*��y��W���_dA2�v����!�������qR��P$j�rG�?��+��.9�kn�+�_�T���0���B�6i��L�@i��G(�K4������O^�xulS�M�Q~�|��P��$<3bG�KPG��'�<�6���������B���G�������_	oR��ZA������������:�]M����C���Z��k�@8��)�"�6���.f������t��t5��e�������T��*E����X8d���|��Z�f�@f;�0r�B��j�kh�[~�S�K�S<�\��\W:��t�r�����quue��Z���la'�:%Wr�s����e�EQ����A{d��vlx�M:Qy�G-@����A&���`O
��z�HLS�6���U3Z��V�X�-�U!�D�6�]��dYN,5�v,`��9��p
�(-��askdq��z��[oNez�J��E�)��b
4����� `0}�.A(���@������|�Y�e�$�N��j���������������7�����j�����B'6/P,Q����*��5����Q��B�)�"�]�
�2��s�+��K6�qq��>U�\�u0�Y��(76��L�LL�K����z�:���y�z
����I��g�ut~�R�B_�&�n/h�mbSR������"^N�����l�@K����L6�AJ�m�[���>pQ,`fa�T��ryf�,-1sN
��n���7���Q7m��O
�/]}��T�vrO�K9Lk������B_��K��4��5F��R�>�R��x��U
qY���r�*`N���b����&�i�:��H�����ieq >����Ib���/I6 ��K/���B������S�h<6u�O�=���R��0�<I���f�/w�<6��{_Joh�E9P�J�]�p(T���9�"P��=��K�����r�����J��B�8������C�9���,��6<�Z��V�m���ho�`��' :v�FF8G?�H86�%g�Z�Q���mi��f���_��XU�������|����
�~<N��}�ec�7�w:Bp�M>>E������mS���m�W�����+���H��j+C�9�Q�6�����d)���yr*��S.A�G���U�K�1��
i��'Z;Q$y���PUTS�l���S���S��3s�X�!��	�<�J�
�oN���/Ru�E�:t���bK�!�����+�REX�`UV�Qj���8�(�V�+�����_.���,
tQ���;)w��n/��\���+����$��K�����[0"�h7�c	k$j�p�
���|�����/A����\}
]�RoY��W�
F���7������w�//H�Gx#a�g�r ]�y)\,�g������r��.���>��L����':��YF\�a����t��O�V�;i�A��_��+���|pe�$���t��j��HBd?��kK�{���_�p�[A�����\���27�"?�Wqd����
��p6�{m4q�,c����|r���6�e�~�!���Ko����M��g������pnvt�?[lO��=[��+8m��������lj�l�����?������?
���ry�40��3�������DiPp*�:�i���XK������x	���"\���5t6h5�_g�g0ZH��]������2���|�v��G�VSC5����=x����U����-��;�+KaQ�ZQn�\�2�>�h
��PhKO�O8^���hz�%� "b�F%��d��CL,�,����3��U/�`|��CmM���NN�t%��]���RN��.U��*�(�Q�5��^�V���(m?N�B���2=������d(��F�D��3�eA�����������{o��pJ�.���d��H������ �R�������iY��R����lqBE���K�-!�/���E�s��S��|��D{�/���V�
�si���b�|�Kv�Wo�tg���3���z�Q"��GCz�����N�fJ�5�}P���}}h��3��! ���S���������f�}>�EtWkS$�0��@j��f��O^2�v^�6�
2�������"��W��^�.��V�_w�-�
��}%-�u_�������l�+�v�_e_�?�
[�����}Mp"k�|��b� 8|�u`>h�!�����VR����/	)���&q5P��DX���q8H����L�����*l�������n_��������j6GY���!��S���������^��R�{N���<�x'�S�K��%��_��!	f��R�q��/���t#����bXe�2Q;� �[:�����o���mr)�i-:������7��F���l!�".b�U���=��R����e�	��J���6�
GXQ�:�%#8�w��
����9Z������v �#P�'{�:I���#P��zR��k
,HeY9)������n]*t+H�_�
����2N�a�\�rFi��R]k+j��8b��"M�/M��"���`N����iw{�Qk������2����L���hG�!.�@@`lB~�nf����>k3��6W�qva��@�JbL�8G����QA�����)Z�a�r�
��$��3�3L�#��2`�G��}�.������a#��0z�iu���.�.���u q�AB���DP�I�q�[�A��O�t��w�b������Ty�:�8F��m��\�6����k+�j�smF��I`(�p�F����SL/��^V	��:"�^9�R[3�"�EMJ,6q�^z���_�����j��_~���`O��PS6��F��d����-G�EEZQ�`�Fw9�(�����$���D��������}1���k�B�4��i,F0`x�\QI�d�s��DR���
<�B"9k�25�p.��
�������*��kKH�S���_�fS�T��� ����Lz�h�fFq�\v��t�8-f�M�,������>�;��6������a�i,>4������c�`�;����Kfm�1�Ka��\<R�����3�0+n)m����{��d�Z�NH}Y��T�!�Z���P��CE��2��B�.(Q�gO
D��1�����#~/���`}/�.�t����_���_����}��	,�������\%�!�x����J�����/�/�Z��/b0�-��q0��X�r��y�	��+�����K��qL��R��Y�52�y��\�i�\�Yt$��51M)8���w�����8�4�}M��9wf���dL7y�W�	�<::yi���x�	�a:h,}�IJ�_���2eP�:p�I�r�!r���	���o���%\�jP�V}?���w��lf'������]-��c�b�,j������<
������p��kV�������T�zGY��/X*�8NH�����:c"����7I���/�D|0���os��RMS>tvC��U�H`��`)�t� &8�Rv�a`�c�C�a&��g�t>�"��)u������e�5)n�����!O�H*"Q����	>.�����Lj��.��
g��������U���4�"����V�
�u�#��3�%�_�t���r26J��\���]�>��t*�<��(��`�%.��D/�fb76�c]���Ki�\�pxg�hs
��'�UZWRW���5E��-�h��,Ib�$�pv�v!q:�9u�����������]�g8@$�
c�H��,�����N���U`=9Quv���8h-��~q�^���C,@dZ��0jX�<p9�������5ksdN���I��g�y|�n7�\��B����i��o�Zt�������6)JqO{�?/�J�"3k�k���'j�����g���=�,E�&����g�-��������"���X�^]af�>�w�{�5��jU��T�D��=����}�Pj�����"T���2���2_�d�5�r�,�����&HH���X.Q=��<!�������3@y&}Gd�|��U������/4�4C�����^�y�4���P~w����i��SP}U��N��p�x�`#|O�^�2����h�P�w������������<�gs3��g(�1[3��,;?L������
�l����>�Z�3]�9G��9�oMh�7HT��`�"���7s����w:y����_���F�����
��g���K;t�\u=��v���u]�4���T�:�]a���(����^�&s������>��{s.�����;���������Y����N�#la��j�RMp����vqhv������A9��K���Vw1�R�,��~�`1�V���d��
�=�6h���+��P���.���x���d�]��Z�:Y��b�i�����:k��H(��@H^������AN ?8������h9����bE��u�%$���{�:}2��V%���qe4a��u����u��>u�������^���O���4���r�M"��J��%�r�_�}��a��O�����i��U^��%� �>j��U�U!��*�4�9R�m�]�q{G�{�^��5���a�O8>�"�~X?�b��:o���5*�*RKR����p��@T�<h�.6�HH�#��|X��C�u��So��v��?H���.�����K`,��4���j�E��p1��9���4���v(�)<�v�n)�+�������x�D��.��Y��+��Q]���d89#-u���UH��*��9�\��8]}u���DGeG���`���m�FE���X2�o���Qa���*6;T1R5��r�S#���6��C/c{I��c�-���6�L���J�X1h�"j��r�n`������o��> x�����6����:��t��5�M� #D^��e6HQx�X�-��8��o/l_��|��d]U����O:�m�R�I�%��q�9���LX�>�B�8G���\���n�)
���H����.�Tb���[qv[�g���Q�?���k��4]�`�H��N{�n�`:v����oG���M�t����������]�����e��B�yU6�@I�b��$��T�}>	W��.;E�O�t��>���d��O���T��\_����R!=U�7d�X���ZO�-&o�xbZX�l�[{�=����SNYP�D�%��n�2B���wI}Z����X��.(g�%�\���n���}4_����p����yJ
Co�X���}�U����V@7p���{�|�/p1������{��U7x���:����2tvI!��3Q-h�����)�ig}�������j�p�-�!��r�f�%�����z !�������-���$��/r{Io��(��om�0�
�)�.�
�v��lT'4���\���L��?��.��6�U��(^�5s)fA���l��T���K����O>�>�%8k2wf<%�.��kk�V�,�XUQ������J�����%��E��t<���������	���B�}�k�U�i����9���qK�+��/�A��j������'x
G���B�]9H��g�[�����
��K�\�K<~���j�)�w�J�/���f)��,DB��2QVf�%��K����uv��b�w��"[�~J�6h���%������=qK���5o��\+�t��$8��R�'+)�K����VcuB�,�PM2�S�;g� �
w,�K�dH�'j���Ox,Jd\
a�����$� ������k�P��;�6��Q)��i6r�,�A.��!.~�K�L����8w�����.�E2�e�$eh�6�V�|��;�h�-�T�����XT��,��q��c�^��!X�~���U*�uG�����<���D &'����R�"^S���l7��&��
���:�	���1h��;���N���_���n�L��t���!����t�P�PK��K��q��u����;�>fw,,k���������u�/��e��#��\
�������Y�Xd��x�n�����K)p�������5iS����������k���iKS^�7�_�b�?�!��
dL��xy%\�>V��}��Q�4�L"���u�`�$P����n1��i?E��+��@-�ok�0�C������^(4H���/��7Q����O�~j���J	\�i���(�DY��P���D�<j
���^b"-�"|�B���jE���dp������0	�"�����l�����kR%e����d�����E��<+_�{���K(�n�
ne47"������j������I�������Q���@z���Qz=���Ryq^B���1�����"Y��L��!��9s�0�k�la+Xn�����\"P��JQ�a��B��:�l������#�b���~@i�2
pZ?h�<�}N�Va����n_
p�^��P������W��M��9�0wd�WR�&K#t�.1�U�{����hX��:�5��e���+��A��������2*���S���i_ce����0��h'�xyn5fp���<��e���D?-��OU���C7xT��)S�G�m��{[����m�XZ��I�$�-�F�s�j�S�2V����1���*���������c�v��"�:�?��(�����EI��$6�|��Uvm$�w��!��d6�?%��b��w�u�h���y�C4�z����bg��8v���6eu�a�V����]iT�D�'�[��H���������w�s{N������|��=�X�����Y���e�6W�d�H������C�`����7M��>������W'�%<x��(y������������;���5�	�K��2����0f��[w�X�*���jUpJ{OK$��|�Q�=w�a!�^V������D��z@P�_J�A#�������N�*��EG_��G�����������	�.U�`���sb�~^ ��������C��H(��
x�z�����z���i�^��t�
�3
����;2#�6��^�*����K�F���#.�*T���_k���y
�}�*hP �9�������_W8c�:�T-S0E��Tu'}�DRZ�OT�'�]������7���d���/fi|=d����fi|%l��Wgi|
t�F����hQf��dF$JT�D�����3�E�v;z�6���Gb��c2�����b'��10������fms���L�
�[����������GjCE������	\�A�x���9N&CXs��F\���mf��Z�TD���>�����(D��g���Ol�P��+~�L���uq�3-h�f>���.8E�HR���G_]�Q���?���K��;T��f\�(�M;1�Q������G���C���,Rr�������
��g:|��B����&'�>Mb��/o��Do����d)�b�y)���������� %�xw�R�jg�
H������R��G<���@���I�L1�N/����f�i�SqE����7���G;h��
N5���+J[�k$�h�����Y���~{s�����%j�6n����a9������a��!V�)��R7L��R/	S*���������U��.������.�t�E������#����GW&��d���M2�]�@s=4sf!$=R�Ba�W��o6���.?��Z�����p��G���?�(�b��*����TBF"4r��H��I5Z���	.����8D�TgB�B�4	�`�z���a��M6W�<�6��9�7��s$�f������5����u��f���
��)����Uc�9�HGr	�bT7	������0��b�kca%c������c�����
H������z)��G.X����trI��@�%��]&tf
{���@c��o�O���"FD�<s�z�7@�A�����u���}�]h}Nq�VB��<�4;�����~�������s7�5�����e�^��UGT�{�TTJ���	4��F���'9�D�'*��-�fT��r�@K���v���`5��!�_�FuK\aq�B���AB�FXe�/����t�����R���}�3qbl�jV���|>��3�{Nf����9�H�`"g�����o����:�T���#���h�s�������.L������i���["e9s7��8��$��cD�)!���p���*��%�N���W�������#I�!�T��+@�a��K��rb�����,��r;P6'IF����k>�D��/%�h����M9�0�qj8��
n��T�������JQ��1�A%�A&����0B�/�H�rHC���Jh,������1��e'��T�:���C/�����s�V��#����N����k��m���rF�.��Y|y��8|U�K=0�;�C������`���~�����A����p�g���E�GB��X����-��U{5HX���+e�X������b�avH�<��{�)� ���5�
��lvJ	0���k]?63�����D'���8�U&L����j�qU�QU~�Y��r��Z�
�V�W�X��h�����66�����XC?����\�������t�H��kW���N
�`�X�z�x�/����H,��-��m�2��������j3��E�=?5q���I��U�K�~/���S�G��G7��,�%�n-��Z��b��
}A-�u]��{�4�Y'�.{�o6����4[���$��0��y���{�����I!�=?������$�� f�C������C��=0')5e�-�i���p��O���+������f�� g��@_��)������~R���\|��7�f�5����y����Zi\���p��^�>N:�|���1��u��y������/1�8Hu�:��3�J(�7U<��DI����gv����R
1W�LH�Q"w���;��(FF�%s��j�%����Fc)�U��		:�/��3��u97nb��A^jm�+���:���W��\�\E�M��p�>3�\A�$9��W�u|r(����H!�i"���B����V��D������JE?�F���3�D!*�8P���Y�^������%���r�`�=���I�3�.�|�t���rI�%\�N�<��	9��v
��e,��p�KjkJ���q���;J�y����$kW��U�8��V��K�Y��y�A�
k��{��\d�������=�!�5��SE�"���c����I2��c�0�)��f���<��N�c�a�������o�����a��n�+Ebb������B��i�>�b/�VY����r�����CF���q���^��U��������!�?����O���6�l�d��I(]Y�<2r���Mw�t����6���x5�U���H�o�x�^�h])������*���b���F�����Y��G�S�8��:�y ����1�R
���G���'r����a,8tO�q1Q6���FT9h�T�M���YP.�����#��{H�f����9!T1&	���~!����B/�����D�Nx�6e����I���8��st�p��a�[4�j��@�N�T�lm���Y�t=p�AB�����z��k��0��]�|�v�z�y���[�[E@�����To��nG�
AE(�/���b�C����D�4��qN����x?�x=�h<��71b
��%���CRO�q6��\+�x�]���J	���WE�!�p-G��Y�d��c����pv��qh�}!�=a L����,�2�V9)*�I��'��X�d��Tm�����Q�k]��5���|��o���L�+'*��K5�A,������R�y^�~�-���.�����F)[mlV|��T�bU=���`���Z%������n�2Epi~$L����������gW��:5kZK�p\�5�`&+N���$��w�1�\�^���\�r�u|���������4�Z��8+TT�K����Mm��2CI�
y���/L�q��r55������T?�{�*�q#��VT��8��V�V����������X0��wL�����i�z<'/
,����j^��-�m������q��$ �"��}Pv��&!���������?��@������L���)��=�@��&�/q����'W�]�n�����u�q_�8�C��u�'.�r�������Z�(�Q($�Xf�F�o��+��+a��Db5�BBA!�C��n���"�������zQ�A�������b.����n���������{�6��k�X�yA�dT0{k��{9Vz@�%��h�N�d�������A������A��yT@������{��WE����u������1���G����|����&�#{8����+g��*jM�m���(�BGO��>������*�M���Q��zk���.��0��Q�B��{�>;���M;����;aD7N��8� )d��A��������-~�S:�+���&��WWy2{�:��������?�	)hn:�X��yu�6������UX���I���?�7���R0�����<���%���G�������O�=��XOm��i��:;��:�{�^V�g��,���h���J)��*K�g�<��`l�/���NhJ7��,���!\/�~�U���Y��K���<4{��v�t��z�_�,
�27ck'fHx��4&i�J�\���(b��R��5�3�j8����&�����(O�3�\��r��C)��&%S�r�eC����I��v66���^����xcFv�u�3�P�����*���/Ke������������ ZU���hY�,:�j�1���0 ��v~0�Vj<W�?r�DR6���a�����n���y7�KIR���T���f6�_NCu��e��G���Ci������������/t[D���@�����*�=p�Z~�A���*J�F�^S[����+�H���N������2�0:n�[��U:�����C��J�E ������������a�_���0�z�_����v )�loo�����d����~X�7��W�R��I6���Rh�$#���%F��G�8����$�a�����mADI�/�������M�OY)~,��������KA7������mX�a�U�)zM8BE�jb����^"bJ�_���%�����rQ(���E"���c��b\��R|dEY726+�v!�/XK�WEWAF�:�<C#���_@�R+�K�;������������U��5�������g�Yd\M�Ms�K[�)�L�+*\�*�I����`d�$����*��������<�&������
�~S�}��n�l���z2�s�g�j������H\0B��>u���T��:�G�S�}�M�S]�SEj�y���y������E�1[�Q6a�o�����\;���Y��v�n��u�;�$�7�f@�9��tlx��"����M(f�l�B�������"6��5�FkP�����S�{��E���.�����L���Z�uAW�F]��^������5�G��!Z�o��j5;��g�����^�~��Y[n�}��C�]]o�/F�b<^��Pt^����G��bF��wQ���T�i��z������G�[*��C�/�V��;�e���W�G�|��u����
�:��o8��e�ck��5����`|6�)�� ������p�)[��ps���J�[F&���|�CH����K�����������]2��
Q�]�������.�^��6|��Iw�6~���~+T�2�^���3��������p�������B�$�]���tI^1��	����|�q���uAZ��^���&c��X��?�y��
��_
P&������|�*��O�)&�t2������.Wi�(����N�rgcs}}��������;[[�:��jo{�j�V|#�k�ng�3�R��!���YQ��N�l���P�F��I�`w��(�}*�x!!�����Q��u}�)��
�E>�B����������J��
j;��>A�P��"�g����W�/�_���������?���=�/�����K��������G�4���Ur�X9Te%�Y�1i�Q��_+��*���{�'�4�~\\ v������/��n�������b�Ub�]N�������N��)��)������������`�B�&���o}��@tO���/���=Z'����T�����u��p�����0h'�f��w����������vW���_�a[�k�Gp��A8���I�L�|6��g�)?�X`���E��I�t���1��<H�j����B*�w	�Y��\p.E�v��KF��O��kUx��.x���SS^���&��<���(��e�8?�0���C���M��0�(��N��LFhtD��LG�lD���_��>1����$����9����xFhm���y��p���]9��E.���j<�J��*|�=�=�~�
���D>��e�Q�:�Y�H�S}��2
$TTb/����v8�]���:���u$0�A{�U���`��t��i�
��;�h/n�a�!��\����b������:����$C|���H��&�����	{�+���_���(���V�wv�`��{;|:���Z�[>2s�N����'P(#r�����&n�$A��/h�]3����G���P>��t`im{�N
b���|�:DT/5����C3���v|�=g�x����3��N��F�����>UB�%`(:axo������,�}[%��5X���EQ^�[B��h���TA��\H��rAS�c�������Ei�%�K�����(G]a�l,(�+�Z��X��w&�JT��.���R��|G���1���d��P�tI	G���
B�G���C���^��mvi{?��Mv,W�l�>[�����og�u�Gf	g����W�.X�B�nsA���YA8��D�s�K����')^E�0�m*u��5W�7���:��T7�kW=H���fm�o����Mr�Z�T�VDg$:%3#���Z��1�[�]5��5�������FR.�����;�������E�j'����>�����-�9|��������ja8l#�� ��]�v����WIw��]��V�.�c�s�����|4���V�z�h�@�u�����{mbH/�E<J�������!���,+<BF��"��w\������m���x���X@��-�:�e Gv,"�ztd"'�R��0Sus��w�Ba�s�u=�;�/�fkn2��rJ��~�,k�)J�%�%s�D?V��GA%��k�
�_BO�+����
b�(���FpbL*��I2�,����"��s&��Gv�Q��bp�!S���}T=�G-�1�*C��g����n����_��k��Q�thA:|�3$�q�%U��,�Fb���5���l������Aj^?1��p�WJ��e##rs��i�)�#���d���������[�;AEC�TW[�$6�jk{�?�o�������+�1���g���Gz��"B���,�]O����u����d�}_'S��:�1�
:���V���S�a�Kb�a����uR����2t�*W�$�?��q���z@����j�uH���:�����S�QjK�"����S�����|%��qxs!����P"'�p;�c,Y�����s��Ad
�ffxT������"��3��,`��[����%T��_���(Z�)���E�A��B��2�LS�4HK�Yz�	����0�f���cl�����f��Z>c����T��8Ylz��.�4N
oo&�����H
�L)�8��Q:.��|��{L���@�o�A|�[`�6#�M��12�lhT�Y:&�*�����P.r`vP��a�@!�)��v����3A�e��P04���l�dX�\�\�,�C��{%x(P&�#Z��
����y�r�8�Q� N'Gq�yT�$�G(�>�dSR��~9������0�L�����0L�m��:}���f+�� o"A`�7�|1[�f#K*(E%����
���{�<��P�������F�y���m��G�O�	U`���Y�5��	�
��P\*�D��*6+����_�������Q�"�u�6^�nk�}�[��)��E�����$@��c��A���g���{�b^�����X$�����+U�i��� ��+Z=�CJ�=�3���/�h����y��"U������a;/����~aK`PG����p7��������2����������L��!L���,S8B����``�����-�������d�����EK��h�������3������eA��1K�������(�g�W��Q0)r"�9���o(i'>_=h�U�q���r�Q��M��Z������6��v��.?qW/!(f*��y�dIO[��4�&:~�~�R��!d�����qKN@{S�����S&�X��lb��d����o/���iu��7-�M����K����(�����c�sC�8�������t(�h����r#����'Q��8l����H�O��83���Ks!�/����8���x�*_&���L��[�2rH�'9D<
0 `/^����7���
(�2���5[4Vb�+�#�P��2zi�z���&*H�83�=�hQo]�0Dr�!�������y�/E@z`x0wORg����������������w�?��<|����~���������o��������nx�9��W��6������P������D���R'�[L��q2��x�/��8~��*� PZ�G����f��W����[Y��p�lW(��e��E���g��/����X�}���[���e|�J�G[�"�Ba���������+�.�p-N8
I��i�_��	���>������6j
3y�����R���������	�~)(�v�����-��������_�F��,������Xz���}������O�?��7�p,�Z}���;�����BB|4��<��������#����3�!L�Z*S�:(|���Y"tW�Z�
��0���������]��T�����qQ���n*��=,�����-h����T(y�s[k���Agk�kk��8z|
��������l���0�3�j����\����~y!73�E�������d�O�ukT3�n�;���:k]�������vG(�����F�^��<B�	R���+���d!TBB�x���q4���,��s4�h����S�����E������Z.z@H��-}����GhhL1#=��p�42���CT$�_�Bj�)�@��1�qwN�S�6�;>l4{5�~|u�7|���c��[��~7��><��9s��w�[����!��&w���h'h��e��m������y�s��b����N���hlU��O<�����Q��\�!���"�AL��S��K�'P3~o$g'��8��T^���D��k��d�V�ht�z*�p��V����c���z����wEBXx����C<�W����/Pe�SZ�HI<��+���)�1��S����B���
�_8s��h���]���+=W"�pF/���Mz��^u0%��s�}*�]��QLL��Z��K�����[�e�u�(I�����GnK�h!|4��R��94��������0TU�c�������=���4���el�Am�$�M����	K�Q&m�K���  j����ZH����@�gi��Z��5(�������_��tVD��o?������f�d��f�5���z�%���|  41F��AN�2H����=�&���{JG�d��
��7�aZ�r���Xg���<B������Q��(���F������%D��0y�;F�u)iC�$%TW!��1V��1�+B�c�D�g�`�*��9����+���
LPW\�����Q���)$����{5��a'�eB���H�wDxW�y�������	-���&U)�l$�m�d��z�� 0dag����R����t|��� ]�w�ht���� �-��A�����a|
������.
DP�����eJ�d���l���+X��z�Qm�#Q��A!V
���-�(,�a�y�������p�OO_�:�^����������OR���^��*�����?��N.�ia�A�_8�p�0���O�bgs�
���Q��+,���^�x��3'����0
z��}��~�j��o�}����l����~������~�N����H\��}���D����i5uS\4���Y6���������qJ�%�?�
`�����fg�,����x@�bE�}D�!��&�Z�$����C$����PW�`� �c� �PXX@A����P��S�Z������p%�o�K�=�w�2����yw�����X�I�vH'U�u�	�w�d�o��FWx���b�
���w��Wn������k~D���MM�3~��xz�mG�Oz5�R�J����W��WEg�X2#Qh�����p��.f�EN��%A�<[s�t
=����������!���2"	E����i�����wS��F{s+jmn��wv<����iV�!��
�J������J��a����R���������_{����/{�S����^��n�sD���DfR�-��6����^��9�<3Ph\���x�j�e���kzm����~
��V`�����}(��X
5���>�X�Y�i{���"��a$�4��
bP�4D���k"\"��+�KA�S�N���4"��� vy2G�j�v.�"�{3�z����+��!r	"������/���*����<�FO������Vo�����H5b�?6������-���	4���ZkY5��a�Q�P���8�6�zu��tk��;��Be,�-CI�C����m���l�������~;�pl�����
�����4K�R]q�b���[_��U.Q�GJ�W��pz],t!u�����7��p�9]�}l�)��t(�Td
s�����Ow]�=���IMP��sR������	N�#[�[�"Pi�9�{��
�Q���}���'/s��J�(X�����Q9�����w���gW�j����9����NiA�$�-J ���Z�1�|����6u��u�3=I�����I�����EV|�����e���
{k���%��������[��`���F�<��R���p���"�Kl����R�q2�3��D�,��l���F����t���
N��)�y�"���mn~2G����59�pa��L�5����an�=�O��I�1�<`|���|�����(<��_�3{��\mYC_�4|��*����4���*,{�����p�FA�p�y��+���A<{{��t����/���`���pZ
������.sK��K>q�,UT3��9�����Z��W��!��e@�U�I%R���`k>sLb?��o�k�5c2��|���Z�!�!��e2�7��������Y�3����pj	���(COC���BF���������������9��$�4�p���	Hv6�K����[��H0|�E�$�O������/j_�~&�H}�� ��$_k.����2"�xL��������C+1�\O�`����<h�):-��.ud��V4*^�4/�,��.7�A
��{<X/h�Chp�aK���zG�_R����hT��D�����WF�����w����mx����]�'TpaE~Eg�t1yb?�
S?IQN���k��H�jr�-��3�{��X�[sC��vZXUn�{����j M�(����p�h>���� ��in��$��n"��a���OH9(�/��F�{���F�[�Y���_�X�h�:���P�7b��~*�T����hCT&�B��w�g�  E�R�"�
��S��T��UJ>�&:W8�o�������IG�K�z[���������X�Lg���8��:���P\*9�Y�90�i�ef�g��$_���s��5��p�����d���d����E��uM�a� �B�� 1��`�-P[�Z�+Y�<�b��`�o��)�{kk,��v��/v�|9�6������?[_�2O$l,�	�G 
����RC�TUUTW�������/n����^AV�K�A%B��K�3K��f�|l�|rBe3���-��e��>����o�}
.�|5��� ���k5�+k�'2#��2�d���J����7"�/%�1�����E6rr�k
��PlKm������f�W�gI���{-���7
������X](I��Q�����_����[��:Q,#35��CQ�1��!�{/�l�`<jh_rPjg��xs��A��N�����7W|�L�-���\��^D�r�t�vwH�����u>l\�<�hOY*�pTA������v�G�=������?v�����e9���V
Z����C������S�W*lrHH�������F/�%�970�z�S~]�'*���D!�]���E���-.��G*R���H�&�L����;�:�n����>�p���PO�s��5S������3�k�)$��d }��H����s	��k;6x���F��/�����	'){|
S�!Ez�(��(�N���(&R#}�M��^y�_(���>
��jf���u�l*d����,:����{"���
�	�zO��r����=�_[���p��������D4oyD*�v`�i�F��c�s���;�!����B�5�g�p6���oom����n��k������%|�h(����,U�
�~���>$_|p;���QF-�f�)���I�R�Psa�4p�����PNea8�K��0j��.�*��'��x��3h��?����S�7�,R�-_
����it�i�\����
D�e6��z
��Aq�d��B�w���(;���&A���N�%QAgT<
FZ@ta�Yl�,J('�t&pt5��;�`���GK�O��u������q�����o�-r|&��G��Q`��yM��t�B�d���?r4o��;��J��T{�=����i��F���Z�	3�3E]�k�&\��FmLE��2N���@�=�^��L��R�����Lh��O�)zBN��-2�������A�	b�
hNi���r�y��
���9%�o�:��/��^�D@��O��lmX��9b�%{F��?QW�4���9/���S�6X�����I�7��0�<e�$����u,Cb�cW?�{��������|$��=^v��dA� 
����Y��P���d��"�M����j��O	�i<0��l={�P��M��g@kw��U��T���L����j�6���L���}P�3NJ-��?�JL����E�#�,z9NZph��������<�3����dr�T7!8�����_���{e�]f��By5�H�c������)��
�xUDxg�T����
��}]��*j
#+�\�y����oN��l��p��F��+�L��������x#YL��B~Ph�OH>�jO�
�e�'�G���,
�����{�/?�����O����W.(��+(���#��`���v`{�R���NUy&����;.3�_C�J����>��
�rT���������Z|�SI�PS�-���b2)#���9��-�����!�r�k#uX�����o�:����g~����E V4G��e��j���>�k����&�,��{{�zp�[9$c��QMrIvv6�~cS���������~!�G�M�����u*B��@F@?2z�Z0Vy��_!�Nm�g�5,���a�),Y=_$ RD�����P������'�V�,b�bGc�
X�M������&��<�%��%Dg�2�����Xy�E^<�f�L'�X�)��s�� ����S���0Y��1���g�`����*s�m���zc8*�12c ~�����go�6(�>L
e�o�9�qQ��yn��xl*r�|�{cA�Y�
�>�HK��Y��3��Sx�7�����%{�_����m}�y����C�b.
��#�(���>;?<=�8:=9�8;>:?y����^Z�r���;/vAc������6���.#������g<M/��.-s�`wcA����-��=x���4,q}��I���!���[�����W�����c'�z���O�9�y~��������&�������� }�b;�-rNQU���s��i��N�c7�m)�GU�.P��J+��+J1{I��uN�cD�c��{@����:�����R����o�����1
"�	�y��\�����-�c��0A-����,�[���.�	Za��v�{��j`����&c�Q����t��
�[�"J�<���I���s�{U�{R�|m��C�f���U����j����{}�rO^��������E���T�q��7�-Ny2�N���i�����f��sfK`�Z��_��*���}��@aH669O�J�{��e���G�f\ )����9��.�6E�1����54�x�U6�����	0�T}�9��>���y��T�x����p_\��Y��LZ��j>�!-�.����z?�es���s�:]VKb(0?(���Zl�����Ri{���$�#�x�G�P�*(|��?��bK-&�90����y����?���V+���b\�*��K�P��V�0�BrDeS����<�+ZKB��\��������B��?/�H��I~�<��C��\z��=���~2lH8I��C]$]1z�h6�� b~�@/�v�snD�q��
�k�y$���b��Z�]���^����?h2-H�[�E)��n����\B�20��5��OT�4<N����G���"�*�0)��������(���x3u���|�D�tvg���p��^w�d��]C�������')�8�C�x��W!���l^�3����P����G��e$���i�i'�}��L]�c��k*�9���o�s��
���� a�j��������E�[~�1�Jf��p�\u����[�#F$&P����|�a1�a��R�����(B;>=}{jEj���-�2+��{���+X��?�zv��c��?S�_HE���Fm+��"���9�����u�i��������:��q�f:��i������J�0.�S/Oy�
}�j�.�6z������L^�K�<�z��R
4���f��0
�����A�F
���@
�0�������z�%yM�q��%�p.���y��'p�}?%K�@/*�h��,��M��F����L

���C������j_����P1�E/x�����E�KMR�Ir?�Q2j��+KAd6C�')�EvTYc�"0T��&���
[�'�����	�]��`I�@S�9W0�Q����/	q�����`���r�t��ro/����Y���g���JW*���,���0�O��~6�������a���T��a����&��t(�_1t�d���B[�����K��-�������H�����E�jF��Ai ��O�kai��0�
*���X�|�[�~���}�����`)�n*����
/w��;�=�o��D��fg�����z��+�����l@zX6�g���J����r�2�N�\	!�D;t�'(~K�y3�P��L��'&9�q4O����Q����.��}�loo��!oi���3s�\�3�h���������`#�����<<�xu���������u������l�����H�u@�$�W���FtP��(��S����x�|Ay}�4G�#�*^7�H�>�F�K�"|h�o3�N�������<�xw�������N^��C�8>~y�r�>�F�u��_����>��0���b<T�6r�R��;��	&�:4S�������������h�����||�9��	-A�����$2�����H������?v�G������F8Z
,��
��fp��Zu����E��?^Sg$!����� o�Xe��
���g��_Y�I�[|��W�?���px����OU�D�'�"j���
f������$@���?���5�m���**N�������\RC&����*�D�I'f��c�:�Pn������+�ur�K��*�SYQm�<����F%�
���F�����]�Nh�c!#�������|�0c���Yk�Ib~�ff��������A���
��PB��
��\h���P� ����EkS5��k��Vh[�-����h�#0������j��Yo&`t!�k�7kw�'��n���%���>h����B�.��C����e��9B@�
��-J��� $�tQ��b
9�����������h��d���)�-Y����c����!-���a<EAu�|e|K��p_F������P1���,F-�nE��E,r~9�� S�����\&�)'S�����8����v9��$%�B�V^�������K��)���5���V������%����h��Y:�{;8{�n*n���\�]�7���{�U.z��b>	��JZ�C��-�#*%*v�U8�sF�Y�)g(i'T0��J4wI
=�BB?�n+e�����a��&~�19b�Jf ��� �%r�P����P"
KE0W&��ZL���S��OG�sd�
����8"�F�z�p��>���t��wr��3da�
Ho�1���o��<�7�r�G	<�H���D��d!*C%to9�)�bQ,6	�,l}8��x�Oj�������#$uytE�\��c�\�H���h�9��y2C����>��<��~�����L��e����3��4��oG���T�����~l���P���R��n�����������UV} ��j��b4�X����!��m�#Z�>��F'��/���j���g7����?a�M'br�G��s�����y����[#.������a�X^�g�B`<p�E?n��A��*�Aq/c9D�d��NV]���+O��K�K��P~k�L���9��wy1��-�I6�Ic?�f!;��U����^{��������W���Nf4>�ZC��y�������K����%Y�?�82
�w� ���~���"���A�^�tSZ9VnL���~��\'�����Q��I���m�����y�y���x�2UI�4�%��H%'������2F���f�@���a7�������m;�����C��H	'���j3u�%!{��~��������z���;�/���^�.�[�P���^����6��n��X����4����!��\�y���-K��
�f������H�2��a,�#��M���q�N>i���R3��c/��-�i�P�f��_�������$��rv<r����#��"`P�!4��Nq.a��2�����G���Y�@���x���L�8nd�|*P���W,'���,82�� ����n}%�,k�'�g��0��a����c���H�G�
��B�|���OF���tP^�3��r:-��l��)4��a�}�N0@(�H�"��!2�$#W�	Xj���0�������\$����%g�x�p��:��QHX��u����7������a�b�C&*�c��|")?��4:�`aCP�<�y�i�	5��d�D����$v�{b�F���l����H�@
��D�%�x�U&/�1v��^�}������'�3�<���M�?
?>hQ\N�7cV�t6������!Lg�X�(�����Y����u��~SI�,f���u���E��0�������<$�m6&zqJ��X�:8$,�-��\�Jg��y����nyj����,L�1lg�'��]�dZ�;a��'X���%eqb��~��d��A
�?�$��������Ki���������bJ<��kLexc.����n�Y������f3�w�6�>*�N��G�5�������QW����(��tXR�j�,�d�C�
��K�Q�}�����	���x�D�(��xmwv��X�������D�Y-y;�,u�6)����e)��d������e����|���/k�U������U�Z�mA�}-X�����7�$�h�'[t<�{�M���5�o��Q�tP:�H8�������M,�m�e����D��i	�������g����K��_������F������jF���	h- t5�����s�b=F�x���<�?E�����e����G�bq� 
U�x�
5��DP�[>����\o�/�������b�����~����J�8<����R�����?��F�����kn��k�Il����?��82���~���Q-�4YQ��������O_q������$K�����~���HP�knLg�?^��]�xrzv~t��U������q�'c���/���0
92�5��'��_����������{���;?3
X��<kJYz}gZ^�g������O p�-b����������������u*\O 8�2y2w�U}� �����A�-����8��
%]����l6}C��Z�5�����}<y�yp>l��?�(�tx~��<�����FJK��Q&�}�Y?�������;}��W}v�'��+�_.��U�'��Lk.�$��w��DCg����5���`����k9x������mX��9�'4���h^6���M.��/�o�'������dj��U�61[��������J�f]�Tp�����>l�h%�!�^�������4�Q?y���m�����}��i)`��uj6����CW�q;��iH�s����m71��Q�A��*�/=�}!C������cKo�2�p�UV-go�Ri[��N�Kk��@f.�T���!�9�&8k���y�0t��]��B��a��nL�E1��5��[���o,�<�4H��R0� �J���������W^�",v!�H'�z�r}���������G����:��Z;����wu�;�����>a����Bs=<�/�?pc�5���X�!Z(2K�Z����(2Q{�"�u7�=�s�{[RQ�9��']3�k6�����mf���������+�����V��N��e/�#v����~3�����U�
-�3�{fUh�����'��-��7=��S�����1�� *Z�<�9��q~��o��M����B�s������a���U��|�+���L(�����F���6��lV
p��s2
}#1]�^�U���Es���GT�����$��xj	��m	���&���Bo�kmY.A��Azo���Z<y��q����c��l��7�%2��f-���P�E�
��������j�����:T��muw%8��>������4C�&![�}�-�6�@��Q�C����AWE)����D����\�����������!L'{0@���e%1��'1r�$0/����%�
���=�j�w���U��{�T�]��)l���z��J���K-a�L�;�v&���-�R&��X��?���+�Q	��J%��(�o�e4��~����TS��g�K��$����`z/���S��y���r���m������������r�'+�W�SW�t����P�6���^�m��
]��u�
�peW��1�=�����q|	Y���E�..y�{|3���B<�z����U1�{���z�-�D��g�PJ�3��:*�����N$g0�S,��Y�r�������&B��v��;;��?p��j���ub�
��tH���=SQ$-��~dY���r�`e��>u����x)�����q5�u�0j�:��N�����EX?�U��H+	~V�(�a"��T�)�F����a���{+u"��
O
��T�P?��%����d��>�z�d���|5������t��IV�|�����.�y|������<��q�#Fl�O�xs�'� >\���j^E����Ul�������D�cC&�F���#g��:,�N2�[zW�C�$�n/*��N�K?��Z���.��3��$e�D���Pq��
����h��|��4��m�'��H����U��e������C���%�Y�?;Kzp��9^���S��l>U��8��!4�3��o(fP��{u��6_��_�������+
��eX1��et���-�/�Yf��0���Q��f�Y��h1J]*��^� �&���de�i0���K��3���o����W��G�X�����{!�i�]8����y6	>FZ���`������z�s�
�7���t���X�=��T��'��p"(g�CA$?9�	��� ��j��D�}st�g<
��fF����8�hy+���O��L7��,��iy9L����ay���{LQ�a}�3�z��O}�����kl�'-���VW|gA!��d2���0>�
��9���C��^�h��3/���C�8J&�}�r����o����G�R���g�[d��)��J(��E0�	�gxw���/!��b���E��s(N��}�R/��S��v?����)�co���HB5v
�BPM
�e��5i_^R1�K���a�����aA&Y�V
��p���o�����cc����@��i2�S`�Z�X���7�A��qXJ���i::2'���s�Y��s�)|���ig��83���'
������U��tP���`� ��!s�&�}M����U��c�C����"�HC�����=(Z��+����k��i����6���xu����u�X��Z��S�X���h\��6�G�}��'~P������ �����4C���`'w����m|��t�P��t$0���K�o���'< L&���+��{��\f�e)����vE^%p?��4�g�)�kH��]���"O�z��
�B�����.�h[�}�R����|6�n�%f����Ib�KEc�D�O@�;��]��q�
�����K�d���'MW�1:�K�I2T�_�2��&�������s���E���U�[��-�NO�p-���7/_�1�>$��
bY>Hq�K�j�}�#\-?%|����`W�?Y\*��O;p����v@m�G�}�d�w����zm��;��7K�@���RR�zJG}s����o�s
q:/�+����n��������.������m=�)�*)
��i���ED�wT���������e�	��7�9��4EYw�	�u����8����(U�����%AS�e�3
��`�"w��� 9R��wZQ����f>���!1���|f]W�T\Z	F%�/�Ot���E�������\��'�k$�Z����b2����I�a�7�X���������������wo��:=>��W~A����������s��S
����]���w7���^Px�W����-���yY�2E������L��pOH��Q���2��(���&mO�,T���z'�Q��;G}�^3
�1��SI�'�R.��|$�N~m���+�u�#�]�FO����'W,���eJ!��� �K����g}�_�[����]����4+
G��]�a����n�cQt�J�������W@��X4���O��O2$����cK\<y��&�J����a>��H��<�f�TE����&D��f���U>?�-��#u�R�'�dX��/_B�f����lf&t�5��jv3�nA�^�{2����X�#�MJ)��d���Ha���Cq��&
��:����?D]�dz�+��jU�z[���=�t	���B<b������e����}����
����G�.��n�������ZuV�*�W*
*�7�� 11�@8���8�D���	=�.��z��_R�pO
�Lc\�T��*������y�u����kz
w�+���_��F��O^&A8�U$�Q��~��G.��MUWq:nVjn[C���GH���)_�F��m����e�,��GH�0�X�#�6Q�3�P�n�0����|-�?����W�u6��E��r'������
CJ`u��e�k#���+�"������Y^6v
'���9���T:���sk�Xxnm�{s}}�����k��ea�ZF\�-G��
s��K�vg�A�8�
W��zU+aZ�����LBL���B-���}�H>
�j���u�
W�+<H�^ ST=T\�z!��6�e���Ge9�u������2��%_\�\6�Uvm�jx+5w��
X!lV��#X^H��|h��A�.���.��E	���+�xuP���']�(��Aj�(OT800�*�+� �����=����#��0r�M�����y��B:����,���6'XQ&7����8�x�[��h�~����E����x*�:�l�H���|���
����Rb��X�nt�d�*���Q"\����9��F���;�A��|���wE
+yI���&���gU=��Tc��xp��������2�@�����M4`d���,y��&a?���b{
6� stS�4���j�&�-1u>��������(�Lm�;#_/R����X���f.�*�����W�����4��3�f�	��R�^�n��m�������B��BD�DR�8�2��\���8���^H��*x����%8D_\�_�T�5`�� ��@�K��=
6��B��*5�>��n�������vgV^��X��A1��+WZb�Z�jr<V�}R�:��s�tXN�4~B �<����zD�'W�l�z]��a���=�JE����;��O:2���D&�U��8�P
�7�H����{�����XL�D��
�/�H�$bwc��.0�wkd1�$���$9X���J7M�um�^��!:���o%��&�v���8L��(�g�(2�]�K5����&�G�6������J�71�:�?���I7�wGi���f�������"���F4�|;
	�D�u'6A&{�.yY�,�������5�"'ha&�&f�Q������I�8Hy��B�mP�����f�G�K^�5"�$�O�J�b����Y�'�i�!]�>�d�BT�l��>�#�z[����o��9��&�Q���>B��p���_SZrmX��dM=���M�]a��:����cs��b���.;�?������1��Z1r��O��p��b��P�S��*�X\w�A���R����v(\.�|��S�ABW�h��Eg���C&B|[m��aLII�5��,T�k��Fk�1&�����AH>����:�n�a`
�B����D��
&�/R@�&hdZ�O��c��|EQ��8��*��G��y3�W��C*� ������H,���
U��\�B	RMV��^T	����g~�]|���(H,����t��$�9��]�]�3#g��B�}�:��ZDD��������N �0G�t�|������-�0[U������c�B��N)�3����������U��lN=B��gE\�F#���]``S�.[�������J.����=��\�z�����M���/
3i�H�}yg���L9��Y-�ZU�/T�C��j����(Vt�z�p>�����A����0lZ��z[��m��w(����F��e�%�O4��C�p��h@OU��W���k;�����rAwr��s�R�a����k8�g��]�A���������o���s���1t6���
�L���M9�}�h���>s��d���"��&1�}g���=jy�;����t��7G�a�Gxg!~��G�\��� Q����[���~�lL�1�r��C���:?T�d�N7����4��O|j�;�]8t����99�)�~�c;9���iX[,����}=A��XP�����< E�RV���Y�����x�W�O���J������e6D���_�4��2g��y��*�����)P�Z�Wt�������4[������6{>������am9�nb��g���n�Nft!W�UR�]ld��l4������*:m'T���l�����X�i�	qJ�3Gs�T�
)�A
��_/=��G��QT�
D�r���@�r�j�^h�e��!�n	����})u5��0��w�</��h�:���!���$P��_r��@�Lh�5����J<�2�������%�|��}-x�O+{���g���V��!f	B_���������� ����/ �"C\o�Adx��	�V����3�� �h�	J?y�nS�^C�N���[A
�x�b���hI4�zE�
h���4X�����l��C�X��H����!��W$��6}Y("��"�^��K�*$�h�����2���f���\":4n�e����1+
LK�G��>C����C�&����iDa�ll)��h��ZqG����x������55����Q����#�������	���} ��f Q�N������6��(�O����XG$u�D<�����*�^�Ph���L��*�X���s�A����P�y�^����hQ��zS������7t]������#�������gg�I�E|s��"�f���f���(T�+�#%���L���|k��r�
������������(�-�����_���D5��������ua%�f��.�_{����}V���'K���/�����h���d�F��*��Z�X��*�%����n;*}�s�)7{l�7�?�$����Kn�wao��	�O�����hlz�_��5��<M����v$�W��}�k�\�o�p���;A(~��������K��<�����K����^H���vM�x�qS�p��%�u��9������v�7�R�G��
=�H�@0�f�"S�6?*2��e�K��6n�|F����
K���W4�����zkG�mgQ�uF��Y����2���	nTw7{�h�����*�x���y�+��$�����\����cJf�#�O^F#�&�^I1���I�������n��������>R?��v��p���1L�O_��o}�����9"��9#C�[��9�:;���V!���`w�����8��5u�c�/Wh��r�	����jf����f�2�_o{����v�P��P\"pA��=��0D�<�����YFd����yX�2�
���cN��vn���g���G������*��&j� �?�����U�X0t��g�$Y~W���x�G����0���$�5�y��o�~xl ��;���p�����T����������i>��1b�V���9��olm_]�����q+F����������O��Z��8=�����%�~BN�mp~5	�l~�Zd��b����P���3�am~��9}c�v�~$cV]����p����d�!2*s�g���	|���
w$�����.ca�u�PlW���45��U2�	��Q�j��k������@1���
������u����j�o��(��7�����c<M��%4>:"�m�����4�e��m����v��{������d�jceJ�^�����mww��3�I��"��s@�u�6�|��+�q�`U�#]dO{�MG�0X��G(�D����|��%����Q�jS����`��p/�
���b�F�^���*�{�G��S���u��y��g�z��h. �|l����~�t�����M��~�!������M	����������-�������3�� �22���lA_|�kr1��S��$��}�����#��vzFO3kn~�06���+^�����e1<��R��c����g��c8�����{-� �.J*��k^��~������U����	�#����~w���Hf�,����p�8��7������ d�:����U�%�d���B��s-�#5��0�W�81�q��A�������	�����.J��C��CQ�����[�x<%�F�]��+O����f��VU<�-J��3����F�H��Y�)�'����N
����������H{o{{�I7����{��[�e�����k����#��c��hQ��mL a
��A'�"8���g�b��v�7X����W,�]f�Y�e=���1����+z--q$���v~p��P�C��L�^���sN6�,�C/��If���EB�o�����l����RI\������j�C#��������c�]?�
�t]Mn�:{����e����D�'���E����� O��V��tG����w{�<.=\���[��X�u,�=�� ����Ga�s7Ju�Q���y�{���y�8z=�*3*�w�m�v���`�+P� ��Od��/_�m*7O_]�Q��?H��i�Q:��������aQ�)��fcd<=�j�N�%v�$]6|�_���r�����{������^-<^V?
�6H
����`������*��E��F3jP�:����lZ��(��_j�,��fO���1���]
�<����D #������R��p<]�/���&�u���r"��it��-u9����vwA�����\�Km��KVx�N�������>AwX]�(�z�CU�
�����(��X���k��S�64�7�_jMd��G����a?���;�l�K#+�;{`���w����m���g�b�����?��yya~�
5�a)��������9Wx~��V�v{�o���������+�"��K���-$�������L-�=��Q��vU�����..D���-	pd���d�K<���jU�{g&@<�|[��`w6M`%��N����/$��I��a]
���8�9�-���!�����FqW�)"��)�,�ps(���26��b?���W�855,����1��o>�t���F��
7"��c���k�SN�=�z��&A�x�\�T�[]�D�[����;T��K���2��2�h���M���/Q}���t�����)��w��n>N��`������f9��e�+
���]�e~��5����(���WGH���$�b'o��O�/N�\�;}��3���	=�|�����=�N
�����[$�z!������.��n}*"��~�������?!��b���||�������'g���bk��g(���lu����i���Q�������?�Y�F|�|�gQe�;b�:��5k*V��J��<��i�.d��xo��9h?��v0���D�9���]rV����6��F� ���,�8a��)�6���S������)����,%`U���
�fj�hWr�����A(D��x*�Ix������I2?|��S^��PM�N�e;��/��g������� M���i��;�+F�a���W0�=�XF���p�q�hRd�7O��[/:���5j�C-z!���m������C���lO ��F����	9���~�8?��b���y�m������2�.5���$;�F����H�A�.*�x��R!��
F>�g7d�us��������g�H)3����Z��b�d2��c���H�O������P�����_���ga�e��L�����>!�1E{������^�\##��9�r����]��|��4|FL@��[f�GI�b�n���>i���!?�����y%����J�R����llx���q��l��,[���}nA�nu����91�"'���<m{�EOh>��f�����`��,^��$)����WL��$�*^*��e��m���<}J
~�
��F@j�3�V
J�5�k�������.��[}A�N*��]�``Zc
��UYAm�`���� \��U�@������������9�nl�V����|�5bX������i��<M����V�������CP9������e!��}hY�:c"]��$1<����%�}F�0J����e����r���$��H�x{���I����` ��	�������y�>|29�5��;��oCff�H����G�{��m}����3o�X�M�J�	6�rs
OE������"xd��6���`w��������K�w�c`���m�S#?��|�lq����
��&.,��PE���~���� ������:&\�p�Ym�X��=4����������'6�"�R��\�4�,gf���_[��w������D�>`��6�c�iQ����qC��4H����6��x��#E��eA=<�Q���Opr��w��������w�J������y�s�Tc��|����$����R�:���7����#��*RI�\�1��9:��A`0B,/3&�tK�/x��U6�jV�����+?���5$�����]����5@�/`��,�U�q����$�'ka������7��������K�GM�AI�+R�WX�4��k�~^��N��>��?N�&���(]'��Mo�������H����4Y��Rsq����KAC{����|X��"7�(��	�:o.ex�"Tv���s!�9'�z���skjx���H�<[+54������
eD��y"�ke.
�|��aPW��iH���x�{�@����
���/{}B�7��Eu�>�'�<�/n������5*X�S'c8��u�U����Q�;��OEp�}��w2�>?M�L�8�fa.�G���>:��6^�Z��dA+eI��I9�������;m��m������'����h_��g�,-����_&}@���pX���q�ay�hq����#R��s��NN0��N����QI���#��qIh��x��t6���G�_a]�m��f��D���m�wg�������X����]z6�b�j*��-�s��:
��v�<$e��4
�����'y�^��
@����Q�t��\Pr)!=����G����}q���f*�_�\�F�.���'����[�>�JOZ�(8z�|i���i�9B,�@���b���4Gw����Y�6z����l��L��8�!V�h/M�9�����#<����}.D}+���������Bt�.�%����v0�?�h����VG�;i�)*��� j���d��rC��s����x������jnh�[�<(���=������N��g����I����8Y������.<	��D���B�2�K}��3�+�k#v����������\���)�����,M�QP�|�n���>��T!,)�5w`��D�bnA���]4YnA�Z���������5T��,UT������k�`�]&��0�,.����"lq���������yP%����������,yX���>Rg0p��"��V}08h�Ed�H3G+%�v���dN]N=�5?��
Ea	�o��`p���{	�6���v���������xkwGA��_A��c>9�3����J�%IzM:������������.3�����>�4�U[J@#�>��I2���w���>�<E��C$�e�����W���a�zW��.Trx�!����R� ���I9�Ipm><��C�t��3`�@�,��f�&���90�	;�K�n@f�U��2��:�v�y�����o��#��]����93�������q|�C�'�����=�5�����L��� �r������H�4#))��R��wl�ihfO�kv�1qR1|R5�+����x����1���_fd���D���f�:�]��+,���f���%�v�rX������m�����J��h��x|z���KS�#��|xd���{#L�x����������g�kl���|����{l"��F&�M6�`��Z�/�8���0hp&i@������kw|kz��������Q�������Bx}�~��>E�.@�����(|w{�75��3�\2�'N7Js�[^d�`�'��K����!t�M�1��i|��oG����gx��O���<���C���&��H��5|�Q�I���V��gm�%�i�#���]�}���������P���p�s�����yE�����]�]�{�M�~Oxk�l����gl�C�
g�sZ�5{�v7H~��������8���.����[��r*�V��	r>���-���syi�n������_l��J&K�F�4��dC�����f���g�?h%���}���b�:y�K�/�N�jj
�mp	S������6�F*���SB�W3|�'��K�-c��K�Vx����m�7/�a�B_L^�V���:5����{��_�����n��[%���c�B��b3Q��o}�8��(�s�~���5�d� a���fj��P��rW�4����L��23��!_��,�J�>r�`�����Eihm�Y����N�h���Bj�,�Ps(e>�'*��h�Yp���t)a��a�������c���=���v}r�,����>�'�/�J��T��3|�*E�E�I��H���}����j�F<`�f��gd�m
X��!\DO.�W�9�kl�nGi����L��_u�����AP��f�f�����+.�u������1�7,E0|�E�0
{���r��2�>��{��Z5�?�o�3����U���������7����_����\����Y��&j���no�!��j#��\%[��dk��-��Px
��=B������"k�g�{�6��`~������,����$?��O���
���iX>.HN
0E]i�Z� 1��	��
.�5P@��M�+���n�V-��{���h�g���
�"�cf����'�)_
S3��$
1���gA�k�0�7� �����P���<��O�ui.��u�a����[R��c?���,��������3��d0Gys�j76s1��Sv������x���O�����.���y�=/�����OH��i���B����=^�����b���Z�[��F�H�K��R�q�z�y��]��.pC��o�<i�&�e6�fs�G���)!3�����u�������B?�$�e�p���`���	����b�����[q��xa�����X����U\����PZ��"�(p9I6�e�tAA�Xpj����A�ro}}{c0���^��],��l#�w6��O��p6��_��8�E�h���D�$��B�E}�pe>vX�K\���.}6�H�,���A�_d��d>��;�?���LcO�K�lU4{/"�M���3��o�&&�]|��r�ag�P���g+l)����~�yJ������h�W��*j{X���
m'Ir�T�
}��W
����FN�V	��6��X��f�Oe��,@Pg�5��p4�q��)P���|����lET�,���-'��;d� ���Hs���?�Pc����_x�F�U9�v���a'(��z����H��Ub��
�M�>	t�q(coH���.n�������`����nm���uQw]���6	��j���������5�7pp��j���]�Q��������q�'�R�����:���r�09
]�E����v�w�E�Z��j��P#�}����Z�3��B�$��#�i���%������C�e���xh��F��7T?�&�����*�����-nF^X+wG��[Q��������{�:�@wF����n�����m"*#��0KG8���������N�����F���G8�l������Pt)�!7%@D��D2{��9x�;�
)$_���f�9L.���W���b��@�<+�GB0$-�2�����Z�uU���T%B�4�E�}�=LE�����s�8!o�����C����8`�/	,��|���������u����*v��O�M��\��l������V4�Z5�U������+X:��R��
�rhg{������H^"0O��R�Umb�}��0o��[;{[?��DO^S��gK,w��~=�*�!Cn��T-�5�����|
�)�Kg����Y�������������,�|�j;w"%skg��*3.Oj+z��$���H��o����2�l��v�9G�K��������J��Z�zH�����������2d~w�������QA�(O�hLA�T-��.����+��W��:r���������L}���9�I�gc����KIU����vo�p��h�w9UkE�5�}`>Vi� �,B��X_^1k�~g��'�|A\���?�+zl��1R��s9���ff+����H�me1���f�qJb�OZ�����Y�.�qa
1F!4�&(.95�����������������6#��qs;���'�1�ak��^���=�
����o��M6>�_1A6������\�+�!��$7Dq���|���M�f���@��G��dR��Fn�)vz����t����U[�4DO7�pb�@:w����G���f�R�<F)�����F'c����K(\�s>��aj���h�y_;8�b%,�|��"��M�����V>h�i��� u��I�kIA�v��\=H"��^hj#�h�5*��p_��<p���,��M�x��FW	�>raY��.)9H|du�	7�E��E3
��4_+�+\RV�`QI$>��RI�����!W����
[[�.�5�v�W$#�����3������qX;�������2�>��M.mW���F+��Mi������v�R�w������S*DN��U�T���v�G��`Jb��P]���t�Uf�43�a�R�Q���V�����X��?�a����w*�����J��0��_�|C\�Q�ZJ'���1������,l�<z}�,�f�N#n����)�t������<�R�OE�y_��+��e�'�l����3���+R!r��B���9�c�Z�~:>?;?}t�t�c�P����/���~��ve��\�5��L���~AU�mo==��#��8�N����B5'[|(4��������+��(����gq��*���� �6�y��#e��G����8:Be1�o�������Pg_���='�QA,�f�'�`��
k�_������'=����F�1T)����*�A�G�~�����a����zY��������iR��Z�g�Y����C�J�[�kP�j�-C��������/�����{��������"�Xw�YL����n�-C�6�����6�����wgVk���;�k�E ,���rF�S���U����ow�\��F�������~��w{��������,u�J;rQ���������@���?%���wm�KtW����)&�(���C/_-@�l��s�9��/V#�����v��#z���sRZ��"�Zs����0����8�[s��QiI����r�&����f�(�	��v�CD�O�a��5�x��?M���-Xm�$?4-yn��/���4��5�"�^e��+�`.??<G�Z�=o����\�Gu�Y���C:��E���!Z��]�]�Q�����X%�V�h��B�����[1)��� (cT}$Z�:�� �g���1N���~_��$��T���l+<��W���E�p�/�*��b*-y�d�t����/��i�m����*m	<\<G���m�c;?g��|��3�r���I��mX:�\�5�����^,�X��6g�m��ly����0 K������3���@S��X��Z$���C��Q��0�^I������!�������?��9�q��cs�
�g�����7�����j!�7���G\���-�u�<�Z�5����������J�F��OfS
��?�a�o�����]��5DiG2[�����
���(���MsB��������:��g�M%Z]FT���Aa g�:EI��G�� U����&�"�f�H���_Y���X�����y}��I�_S����{^���w�PE�������	'�.o!�3�H�K��O�Y6��!�k|���_���m�#h�+���"��=H��8��\�<�v}����?%����t�J�t<n��2	��gS�������T��h��A��g�����2�.������T�b�yDc�����Al$�cy��\7�}�L�����?�WF�M�B�D��~yqv������K��0����7�o����oO���^�?=�����35� v����8W/�v3"�m�tj-���8��_���������������0���*r�������7)}�h�=bH
`�9=pB����|��u���;��J{�k`���3n�B��pi������z;�_i��O}�~�����K��'%�sae��h8*�1o����\���}���'�S����}��2���)"7���8BP�:t��B���sH���~���l*���3���H'���������0k�+����/�?�zv���/i��Uw)�i����	g������L�Ez�����G��5��Q�^�G2��>�-l��\���x@K���	v������|�a�e%g.���H�mI�Db�RF�t���2YV�f��
��������4=��peW*D��by86w���f�������O&���y���z�H�e����-3�X�}�Vbp�y
TZ���<���Z7u8��Z�	aV���H��S]��r5&�4�"o����^�����n����a�����H8�90b�,+?f�/`!�r��g������+8\
I�R>;����K���i����VB������/����(���4��_o�,Pb�	K�M���tf)&J�:��r�z�78*�&J8[Q�c��U���5�D��������z?\.>��g�Th)���	�2�{Tzv�I?���'�&�*��@����������e��wm
����CDx�A�	�Rz�c��������J���c�����g��^b��=�Y�-F�vU�H���DV�����w��v�ZO�Y?�e���t��j��y��E'z<��	t�,X�E~����y�t|��(��9O���Gq#���5�t��g}aF]�q�i���}�!V>�����o�I������o]�m��r��]n����h�O����X���@��!I'�~/|x/�.�;y����������tqvt��������W/�S��u*�12�8�D�{$��g7���_�����_";<��n��@���K�?uI@}�f
�$��-I���%	Z�K�(R<4P(�e����P�	G2��LF	�����o�n�����X���f�j��^�E=�h�z��/?C��G�=��^��lX��R�����~g���
����Ia�*ZT��x�Y ��o���
��@���
����~���N�B���Ed��V����u�����~�?�\����H�k��Sm�'��iR��lj�8A��Y�J"����s���?�;n>n�����^�?;~,d����f������*7��>~��ua#Y��1���[��m��z[L�[n)B��^���T�F��xS��O�����s
�����@�`��Whi{�k�����^<X_����dm�=����B�`S�.� �Q
A>=>|yah���Y�A�W�}^�I��}�����E��Ve=@�K�Oaz
C_��J;U�PA���A?�4Z�`c�w�@\�Uy�4��YdxvQz�~�R����d�W����;��N�d>�f��%"� �����{{��������As��I��a�
�n��@�6���P!`A��`����}U���=ra��$���1�(���!�^�(��!�3��0�������Q���,2gbh:������%��(��@d�����3�HG���8��������1�Q%E9��Y�T|W����<�_����Mj����x`'�[#:�����W����l{���)�G#T|�!�63$M+z{s'IV�a��n�U����|�#���;��SCX�
Zc/WJ�@n4�`�E�8�_�D��K\f��L�{\�[��W��F���s��������r��$	W%sv�arE��b��-#��x|x�����������==?~i�PX��:��P�%�Sv�,'��kk����O�����+�XM��_�������V��n)�-��~�2P]3��{�������a�����WdmGK��kJ�EUc�d��%�].����O��2FO���vDN��	Q[�����J�G����<�����C��C$�x��B�v�_�V�R�W��%XVd$�as��AWp	`���J,�
H�����R;1�F��&�&b2b�����{�?����#�!��������i�	G5M�12�r����W���6M����Xp�.bl��-��3�R������>���/d�fsx�����kp����4����������m�����2������k�u��%((�PR����e^�U�8����(m���u!�um��s�o��y��o�����8H2`�x������F�>>=3==�xt����/���K78���|y����_���e*�J,���D����`HG��)��M��`�_B��V��t�a���	5�#gV�O|���_�e���c.�	A�W��hw����(��G�c3�`��4h�������`\&r������;�C�F�7�e]B��{$��O���L!���jF]s��+����y��8�������8� ��d%�����N�bn����	 ���[��9)Wf���a�FFI?��9�k�yvt~�2�.R�Z�"��|�Ip���P,=�$�������*�Suv����!��/�Y:�����hb��������T�o��m�w����GBc��1D�l;zix~�����uR��T�jI�95Q_�0������+���d����j��9��D�=������s?�UT\7]pWtA��	!��tJ�����`�"@s�'���F�MyD�J<�O��R�5]����\hI.��L�f������8���	G�L�����M! ������4��F]�54��N�F0Y:������J��19�-�;��O��/�"Bt�P���Q9K���Je`&l�z����!����s��(4N�>J2y��
������9�qo���Ko�~Ok�;��wql� ��A�������f��h��V��E�_�P�Kz��Z�`�S�I����*"���O�9���T
q���.��!�
�2T@���VjmO����i����:W����2D���`+4*�����Bh��Oki�.���jB���5�������V.�R�Z�����������V���-@�-uP6i���;����I�i>�4M$�&�#R������Ij<�����.d�B]gWI<�#����P��K|��k��nQ!|ao����E����Y$�����q�e|��4��
a��Ix=�7���XP��|Hc?��Nq6����;������I�I����j���2�T���>�>P�tK4 L���[?y��\��'g�'oLkV�~�5�|��8������O�o���^���ce*��������C�����
+��e����'o~*.�W_�����^_�CyL|}=M�q�XL���Dj�.�s�f�?�?��\�[���T������(
�Z�����T5Z7V���Ga���hf`�S��NlURl�����y�aD�zr���_��H����jM�V�!���U��������	�0����$��A���D�H�VQ\���F'���qQ�>���1�G���&�^4J���l�)�"���'��@&��X��;��QmP�8��l�I(������_��C��l+U.������C_����N.t���^�1S~��;��H;���e�P���'o^Bu;>�������W���#*�+E!@����������EhGA�N�����}�G"� ���]b{������+I��l.T��J3����c��^�����?=�g���E��d�)q�������&�l_�31!����E������G�}hK.8�`�z�g�MM���14����l�: �=�C �v�kdg+Z?J�0�{����#��2�n�����q��1HP|	�w�m�P�	����,��&O`)m��N�9���b-���q+����?�9?�xA���������'I?�B�O#s�K�vI7��5�� _�=���9l��0��dy�6M]l�Z�f2[�c��
����n�������p��M����0f��z��<WQ�������H�q;`E_Z��]D-�Y����6���@�4�yt%~��c�8!�t8���^z��T�e�o����#I��S�z?�R�
�9�<����\z��po�����_~��m�1�n}k��CN���c����t��t������������d��*�o���+~��������g�Lk�#����u�����"b�s,h}89��%���\��|���<f�;[�����[	���l��|�����O��Y�,b[IV@���Qc�!���R�����X[��W��f;������0�O(v=h����I�����\!�B���>!w*��8�B���
>������'���0�HJ��H��J�,$�q�	/���R�/���M��FM��@�
m�a��'m��|GH�ur��6E�*���\f�����<�����x�����(j���Hm������:���b�K���RS:�;�"�Q�	��q�m��Qb��'��t`��� {g}M"r���`�����H�&�fK74e
������|v��JW<���W:qO��R����|H$�P�l��������sxr�D��n��*_'+���w��4H.cV�`���i��2kg��Y�A�P��QFT�j>� �����������Y��
f&�M����O��";J�c��:s��S�d&
�b�x~� `��AhO;
��^D���'��&7���{����/k�/k����2:�k6��jX�Q�^!PD��cI���
���T�����A��[�����|N6��y4D��x�>Gx����#���������X�N�q����^�������{;���8�tm$�|O).;��+?�G�/,���"s����������7.x:����/�p��O�k���6.�N������������D�>��|��N��Z�Ko�;�_�-����=d����Q�1�aQn�|�Q6�]��[�'���d0��t�z�['V������������H�T���9;��dK.N���bkg<u@��P��V�d�����>��{;W����`75-�:����b�����c3�����"���YI$W��e"�[�u�$��������	�OS�]���g�(�_P�����O?���k�������=9?>}��2W�Q62\?5�`4��r9D���	�LQ~5G-j]��3��h��*+��U�U����k^�<�
�/�������KV��ma��x`������O1��/�l��5$(�!������ ynS1����>�5�!P����b�Ni��xpA:����|
�\���5(���K-|�xw
����|qL���w����7&[�cc�+Tl�n3��*�zqKE/,��ih��K0���(1R���o~�g`�M��l���8~��������"�9����\�?�v6X�O�Kz�QF��G���E}Nq� �����NR/LZ����C����"ju�6�����jg!�6w�������V�<�B�G(�+PyE!o%'W�uF�����J����C|��x��"<�ZJ���,������L�_*��HG7���������~�
�kD�F�*���T;�3�&����������G���f���Gt$�
Co���Y���{�e�KAqho�Kh��Z�����F����,m����g�-cW���u���o�[�����?+��=��cI����i�V�ul�������CI���,iE)�wO��;�I��|I�T��m	��`n��*��u�:	�c��w����*����+2y�yH#�2,��zn���pU�RW
�������3�!&b�S���{��>�p�7l	p�
y�[O.���h2�e>���SG
�H>���d+�S�%'��|�������pX�lJ����Q�
�d��L����dd0=�f3�o:���N�sj�@A�������r�P���<�W���O���V]>���5��L�xF������]��+E/��"������)A��>�8��}�����M0$�Kn@`m,�WA�I�������z�X�y�����_���E�x��+Y�4c�[���1o�&�	�����5���v���`n����:�v�J�]�k;�|�T��I�E~c`�&S\�e�w/e�%�����>}b��AY��j�-.��2��d\+NQXw��&����3����o����8���������+`J����n [rB��#
�
'>�8�AJ.��h��Z�Yz�%E6d!1n�EDy��)���A�.g�c��|1/�.� T���H��a1P��H���m��d'��;nE�r1���C��
*����m�M��f���HSn7Y�0-_�+-M�R���V��<\�4,���������L����T��9/"��DU�_���$}����m5�l���p.���N�%��yE+�m���i?�_$�H�o)%"�E�[P��N��:�wN��G���G��
��X����J/
O5��l��h*����=6C
��Z�4l%��|/D���2�F���2)���0~,���o)�H2�WZ3i��G��mW*���j�����T@IkXjS��E�f�H�^�e+��D������(����?��p�'� ���n�h������+��P ��i�m����_��|�Zl���bBe&�H��g\�Ro�g��O������W���TD��rLe��>a��r��d���H��W#o���!��|�r���/)k�n`-�+@!�HW���++2�*�*u�0���p�v�0�!��tF,}	�Z/.�X&2�n�2.G����2i�r�OC�[�F�k�]ez�cJ�3����m����L��8o�>�J2]-R=���}P�A�V��z��J�J&�p����>�V�v����5.�����PVM��N�������f�M�A�`���!�����*:!����Vp���Fex��Q��0�<(�A��m�����W����x;�z*����l}��6��B�(���� �d2���$��E�_\�������6�E'����5��
�SZ�d-.j�gS~:Ce��IQ��C��N�L�T��A��-G�����	9Sr��2���|���N�N?�`J�ByXe�1����A����:k��Qa� ���%�1i�=_p6�a�������qF��T��s��S~�'e����\��qp]DgD��bu0/$�|0�8�Ro#�zd���0y�
J|nh�o���v;���n9G����V��
y���K�^�x��M�|�<HF�8>f�{�.e(����$�f��%7a�������&)/������}��
#q���s��>��z�pCC�D���s�3�X>����&�sH�g�7pa��E����(����.�+�&�]���N����@����HMe&'
3����=�v��0����U���^���)D^���~�C�;�#C\�����Q�H�<�Hh��e�����f�e�^gy"bx�<�������l�|��_�Y�c#����2��k&���}�~M�)i�e7x��������]W[�MX��n�I�*�u����X���a6�P#�<�������x�1�Br������Jv���/��j��8�W���E3�2�|��5����_ns�t��X>N��s����O���SN� A���To�;My��<S���d�{�?������|�q�xr�Op��B�`�l5w{���Sg�/��7��u~���s��3��h�Drc��P$wh����� S����S��t��*�s�>_]�������ZH���n
G�R����nF90�������l5m�������t��{�����%�N�N��2Rw�
\��a,���E�Fg��@��6�����r
Ho�}����,�|���US��eg��$��W�k�P���g�;��E�������J"��
���2R�D��S)sE������z���n��t�oO~�p�� ����$��_i����i�����"Oa��JZM��[���Z	�(����9hLK�G�S���1�z�F�g2P"'y���&�P6�j��e�5�7��J�>6��ZNB���V�)Ij�8hY�����?w�T�D���rQ$A��'3�&����w�o;{��O�:���~��{�K���w�<���"�H+�
�������b���oj�X�]��>����#��{pZ��u���?�y�w���y��yy���d�Lg��o�-n��RU����A�$�n���~:���'	5��d�����>��O&�)w�q�$$.��Y��TK>��2
�������I�|?\�:C�!,g�JH��blv���2!�8�=J��N���@��j*��Q�� `�_��/6��������:)_�)�L�}�Ia0���m��}�57�t������
jW��HV�m1�f������*8�?�{��$_o��I�S:�&m�2�J�\,��"e�8�@Y�+�
RS)�ZC�H�.��g@���l����)0�$�������F&�h�e
��������~R��I?yGf�%�,K0�n?����r��l��4mN�=��T��3\<�{�����p����)�T��a�5���0������5ip��MZK.U��R��v�{��{�#Gf��M�O��w����v1�6�I�m0�f ������r��� ����c�{OVO�>eo�(��'ws}.9]�9ja�����i���c��
n��DK�	��0(���8�^/�P�g�>����sk�U����#=w�?��?� [����}@���Fk
U��7�'���]A.���'�G��og��V|G�����C��k�Q�^�����/�W)e�X��.S_���K���`��Q��@%�}�?�4� ,��e���|9�pnA�����:D0� ��3*�:C�&2V�|,*9}�+���V�
}G���\�U?���zf�;���O1-������T*��H�T��<� ���8�J��7���!��G�t(g����U�=����g�	� �X.@z`�#<Bk�������H��\�����m�?�zC�W�7����k�V�2y���e������$�������.1ZK�e4�7��R?G���&E��L�$$K�`�8Ya(��l
�h�M&\D@z����a�X10�����-� Kb�"�S�|��+����)���s \CZn�y�{��Pw�;3y�Z�$t��G�d�Q
��;�O�F�'d\�NwF_���fFi��&��H�-2�������j�FN��bOKEy��X���+D�js���*w�+�k�4qM��K�J����W��B*O��*����������.�p{��D��v-���0��l8D�!�����J���4Q�U�Y2�+v���i�k������������j6��_|V�D���Z�Ll����4���f\����r���L[��m���Dn����f�LI�n�^��7|"�'�8H�c���r��m�s�Y�;-wP���u�u2F��d�F�v�/m�:���\�G���c��r,���g<�����O�/l��'��T+�e����cn��P�9x"�?P��C�c1N�J�e��>��(�}j�x/Y��gu��Si�_�!�Z��l�<%��
�p�/v�^��JJ����,#t�RjO����0�D_O��b��-��I_>=�t��t�����AI$?�������g8v��n4<������9�{�g��/7�@~�YF����u�s�z��"��M�%�w��{R1ozz5�� �����R	����M\^�����o��@_�C��a�/|�8/�I��:����'��c�%��7RQ��v�Qu+�Q��~}�=��v%��C�e��Qt4�,��P��e��3��7>�R+��r:�9�O%�J�����#,$�>����Hy���:&��#w��Q�0�D�y������N�d�6U-����]������J��!�e���%�1���}�	+QDR�a:��O�8�&�l�T���?1L�.�_��;-NBcKO����j�>��5Vf;������~g[|���HP��!�w���\�4��	gAl�����	��`�U����A���1�P��5��T�N1��T[Q4b�Md3g��(�W��j��x�T��{� �43R	n��X��s�>~m���
`!���T����2�||Q��H3^��UT	oJ��A�7����Ob��\���4����ZG��X�L'?enW���z�^$��M�')�q�����s���b!��.36A*���2u%X��B����Hl�=�oQN���|:��������a"z,'�0� i�a�Fz/C��)hb���+x:aZ.|u�������aD~���zJ!p��������>��[������������W���@�3x��
����/mYr���|���_M(�t�y���
��T�M�4��j(\=70���Ns��Xm������)�	b����������pA��S&�w.,�F
h<|\|�KLb�~�('f���6��b�pA�Ci���&��������/�A?n�`:�I��R�E�*��E�|N�<����^; 
���%.�P��G��)b���^�h[�����n�-�SgH�lU�8g�U��L��M1d��q��-�p�h������&��� �Nai�T��N,�F�r}uB����]Q���ep�����Z����
g��hw9�����!�G�~g���B)�UF��|q�Y�@����2�^������;�[���v�`�08I��*����95�O�t)o/�I0^'x���^������R����~�x�^��j����������)
�'�q�h��tLe��m��3F�m�E.�+<�ml���:������p�vaO(K^��|r��@���R�T�	�DUYD�@E���8��\�Q��t:!�r�K��8�P��x���)�0�-WI
b[��@�����BEk�V��;����6��&��f��#��*�����-�H��*�?_�Q�U�������C�+�Md�.^�?�5�������H����'^
?��e1z}�s���8cJK�D�P*���d6vn8W�2������P��t�zi��YaW�<���
�O�0��k��3��v�3�6�������%��o���[ O�����7��Ui�c������C����g�u�H��eS�����d:| R(�r�2$9���I��/�	�5��w�wQ����w'?u���S���9���P��r���r�G��?���������RSsy��`�:����O��:�9y��j���>(As�]`	�,��
$�������V*����e��5��$X�������`��������#���A�4�+>?��(~��&)��0��TA���s�����w{�{��?����Q!V����,� �J�
i5x17t���T�u�{lQ���������Rr�m#��Y�������������f��1�C�ND�����'�?�#�.;�Z]��;^l�A*�z2IC��B�
*=��/B �
�#R�7�?�/d������9cx������5��5XV6�m9�;�'B�v#e��"u:woA!�/���G��<f�`Vo��6Y4Ku�A�~�[3(X���.��&����h?���1��W��&���z
�g�����.<�rqX�R�O�E�Ik��ik<�^s�*�aB�}��W���!�Oew������e0��K�t'
��f	`�q89�O/P�Xk�o���HO$H�9P���f?��d�=���e!K��[�(���)'��$����oJ�{w��e,e��PO�;�!��[�
-��-K'�C�	(�!��o��@�h����n������&��MU��H��z%u�B5_?����2,&�H�0����9���K
�N�RQ���)����N��)���������u�U�-|���~��>�_�J������4^�l�<%��^14�r����l�3����x��vG�mB&UE��}�k���6z���s�LUq���TE|�Q%Y���	A@$3�'��������y2�N�z����^�������Su�;�����5��Z^�m�j),���4�
���*�xo�]0�;����S>MOd�z����u����a���D:��2����$����!5��H�#�e00W�@K"��q�%/��n��w��R��x����1�d�n�g�����gK*{���wL'���c,s-"7�"�||%?�t���W�'��o�U%���F����y9����[�L���w[z0��(9����Yj��q�L����������\��R�r"�y`����_����/vB���;�t"�C������j�:�&�k�`J8X��z���S�G�Q�����(���	�I��A��4K�m������k���J����zL6��
��*����b�
�&-v���/n�����p�F��/���?}@��/����w�ANa�k�/����:��bnu��SQq�;�0�<��������O�L�IL��D89�DG�&��X�i�r��������r����V��x�S���e�����J}��d/�DT�S�������>��3�7�"��1�U���xdov����}��['.2�Zma�z`C�V�c������\��U��>�����`A�*L+���?�������-q�8�5��)���f����Q��t}��{��}<� +PG���F������<���d��K����n�)E��)�r[p"j�=q�{�aU����iB��
�w��v�]����n!{�M�"�p��^�u��n���5YI �u�[�u�d��0�K�~�Aj�+������m��|��'�z?�T�'?�(�W |���Y�*�J�(�p��r
���o����]KH�{2e�a����U6��U��$e�����4���8]��MJQ�w/*+v��xz�����.������9����XG�����������F�V)��$d�7�w�\�9����:G�n'
���A�d�F�g�y��o�L���������]�����<�[�\8c%��	0�'�uez4�c���������R��Z�]j���S�i�{�Mx����]�Y[_M
���Hbd<��;I�$W�#���jfed�i�@�\����M�	h|��(\\d��;z����K�lQ���>�^s^���������
~�f;�gXD@��`H���&�f���
H��U�eF>)=������l��@��b'�q~	N�r�G�^�|���@���z���#��7��7�Q�6����B���	��%za�nIJ�?]e���������;�EYp;��w��G�����L�D����(y�sz4k�Z���uRe�)�7������I.������n-�kJ��t�\�E��'�K�OH�~���\t��lc��\6����b�C`:g��.�J��~h�q���"�K(�	�)��_���rpqH'X�V7s�����Ms
��d!��tyq��?��K@����r<�G�w�����!&�D����R=�
:�I�����~��+9)�s�f�?�J��]���k����8��YrZY��Y�\�Xw�m.�Z������t?F�s^t3P
/]a�1���MBX&I2�feJ�S��m�M�aQ��d1���bi��0����2���Z�Z5�RU�j��k�v����:GkJ�g7�f����+��������$���~��P+��g(X��B����T-����7���r�s�fT�������%��|s�=�������B�m����v)�@���L��i[l'�`�u�����W�o5P�]�����C>����:��S��|.���{�M������7����T��C���g{���VkZ|O/"��@Z+U��6�v�J�a����v*���I�~jKzw�@����m>�PE?�0��G�G�to>��:���~�F$���{�s5[��?:ycl���7��A*<������y�����N���]U�P%~���dI
7�3��7���H-=.�F��N��~8
?��	�WWA�,�R��p�.�,\���<�Jb�{
��RS|��m�g)A3�SE�x��3�Za-���*�j�@��0�7W�<g�����,��+Ng�R�s1[��z�tS��Xk�����?�T~�����V���j���Xs���G ��Q65)S���/��	�!���<�����X��km���]r�PA������	��_���?�D��)URb��������i�x\���$�Fa_v�V��^O6�������F�j���H��ZC�\�	���n�`	1�0�-Q��`�GH�TI)��^��D�|��X��W�����3��%�� ��Ry�����~(/_:��ap��?5��y.
�QK����:�8�A�~�]�Avj�f���HOw����8a��7�
vV���oX�	
;��e�
z����#q M�zS��l�WS<%�/$d���������t���@�WB�����GS���Q!*C��������i"d���Ld�3�!�1�X�r����|i�#�0g�����/�/���*��paU.�������I,��~^�/dw���F�Ve�����}	�'�O)=�
D�EuBHlq1����e����S���������H�$��P�?�T?3�B�H�+"�m�hG��-���9�����^�V���G�����|Y��;�#)k�����	r���'�x�|��^���e� 
!F��CEN��M�to��
���K�@8Z�����/��q�LuW�s�����(S������[�,2d"&~�����C@E�K��%����E���c�=:,�><���;od�+���w��z�L����'���_������sVg��$��_K|���	�$�O���$4)��.W3&Q�M�u����^����C!-E�ED�;`h����
8	V�p"N^�>�tW���PK 	3�,2������.]��*�����4;��Z�U�U*ugd7FV����?47�� ����6j�(�-nf�����,�����C��B+�{��i�C���qI�������]}5�]'BuG�N�Q���
�*���e{3�����:a	>pS J�����~W�>9?t~�o��:,f=~poN)���:����{�N�,����e��	��n���`:^^M�XvV&�����$D���%�O�'|�01�k�|i���=�l����8�>�	�9()�)�o)R�`�W��r:T��T_��q�z82.����?���$7��!���(L%����/��Y	Q�,N>�^;M���i���&��3p{�Mr��A!�K���������0��J�q��>X�p���XL)�>9'����}9�{��nV��W034�q���<��Wa<�k:�4�8\�W$��*=����Q]�!�{	�������.�xC2���u&��X�g&:`��ped�hm9���X����j�5[���_�Y������Y���o�N�����G,�V��|^)�1�,V�@&�%�hw�u>���
��������j}�z2�v�k'�����tj���!Z�[�F���
���(O�����a�I�]u���!_����I������O{g?t�
���j������A��{��+_{��eX{��o�u��y����I8u���s���j@ ����������;X����<wL�@e����mAe���������{������W�R{�=X�I�N�)���r�6AX��03����b C��S���?�	P�zd�;���I$Q��hE�$�x�F*�{�������2J*�\�c�E'g����U�J��yo������R�-���'�����@Em�;1h��a�|���1��*���d��yx�N����3=|M*��%�WFQO;�S����/b����]!��F�&����W��(��`����8��9|.��l�1��	r�p�fxt�y����O1���������@������X�7�7e���q���
�<����������$_�*n��ZR����H��u	�����F��;G�����Q�G�����7��vdQz�Lj<JQ�������X�Y�vG�s�iq���D��
I��~��S����A�,������M��f�~�t����1^�\���di��3�H��x�E�p0"G]-Q;��Hi�#`!����f�{����5�b��c��Q8��v�C����~�TiYL�dUae:
/H_��|��j-%07z��
,s��TX�C��;��t�\��]��9��1**�����
}�=w���L��\�k��+G&NUO�
�j�Do$�-�Z��%�e�2�X���rtv�-�Ky�J�gu��dh	r�
���eI�	`���F���Z�b����
n�K�/�5������$2.��j�����cx�6�].����,H���K��(���GWDsj�s�?Sc�����f�$�b�\��&1�����@ghH���<�O!*i�\y�h�W
s���?p(��ig�s�	�����u`��0� �[1�������y��p&��O/s����;�-.	���:� ��"����0yo��5�s�e�pNF������s<t����l�/��2��av���W&A�U#�7�����qj�=�%�c�L���r;"
�j.%�H�����GT�O*�/v�4���z�M��:{�?��|��{�������W���a[����B�F5\���P��o�c��1�=�l������#��C>u2>�.Y!�jczNm��n�q������9.��fs���=!V���B������V=���t[�zAM������a����c�
���Z�������Q�TYZ7��*!��IT�����4�V����(�
���W[|k���������c�����[�'oX�H+w!�K�V
��Eu��M�:B+�t`&|���;�?2�r���������8��Z|�7a5�&J-��
{j�sX/6��H���U(�����A6�b��k���r^�7\H�(���lGB��F���mM��	L_�
��������"�3	.���g/l���A'�P
�.�%�>4&��D��;�8��>!��yr?2
x
���5�U<���� ����4�|�H����r��V�*YV�cc*�#�9aI!a�|�8p����f7�.k*�����w�:g��������b�+�o;�s|#}�a
���/Er+�WI���
�wM���0-O�����09�MDW��t��=��d��v�Z�Z��O���T�7��L�Q����	l�a���������f
��Nvw�B����9x$����<~C����Of�0T3�%�2�!���D��DAi�Z2VG.
�����Z����S>u�$X`s�o����TZn|�A�aI�#�t�?<��
@���PN�����86��?��h>����i���a2��[���l�
�v"�{U>Y�2������&�v�=�����F���LS�N�,���b��w��&����4Z��q�jc�6�T�V��ne�Hu����G��S,5d��_e�%��K��-��@���^9�8;���F$.���,���`H&)���a��N��i�(���E�w/8F�k��~����=I5U9�h>�"x���P�^�i�,#�b�U�xa����(�[&�����m�ZG$�����KQ�����Yo��@��w`m~C�����-B|��^���5\��B���]�����4���X��X�[>=���&#����t9�-p��l�=��v���@�R�R�D�D#C�_fl�B����@�LHGl��Y(;D$��$���\��t��d0��c�Z26�ZX�����mn��p����	�dV���k1��������hdM��.Ar�g�*�g$zLb�j��k�u�����������4~������c�&�-�j2��@h��u@��Y�@����dxHt����ma�h,�j����m`
W��W��r6j�:��7>FR���u�Q�2��=�����-8�����z�5��j�����1vQfM��:�>�Z���?����)��s���V�������4��E�(����Y������H=��c~����Q�y����H�b|B�V��� ��0����q�f-�IYJ��k�����x\|fc�j6�����b��8,)M��2�v$<b:IN�����.�L����D�U�A+�����W�2����-�/�%jcf���4�H��_3{�n�5����;�0�����(�<}E�`����3#PxPC�1*�quLq;��<}JG;���`�CS�.5G�>������� Bb+kF����AB���s�MzA�8��f0�d�d;���:��m��b��x/��N<���S ��3���9�I�U�.����z�h�i��#�*F\4����7������4�@A�=�������|9A��"{�4x
�7���s)����i)��^��-p�OoO����TR���&8�I���.kmn5���}�`b���~��GZ��1���<�Md�8�I��Y�0�T����2����<�/@��]:���I��`�2��E0������f��;�a������|�)��Hi��F����*�v�����J�DH�}"��t],��nM)x�qs�9����m�s�w/�	V��a���{��'��a�����aK�{-����R{��8�%7�"���!�W�����;����%7�E���a��Nz:�lW{g ��T]���JY�'L�?��'AY�&������v�F���Wk��e��,c3zVY�w��&Q��?t������H`��^w=�?u��O�{�'�j���ei>��V��8��Jb��������h���f�����]��}�t�i�H������X����,����~8��:9��w��N��������vz'��V�[����������?;�c,h��A)��@
S�`�"������pS�����7�3l����}������{h��&�o�0��|�Ui��p�}�-x���ZP�~}X�M��2����}?�a~������l")��a�^n&��z#��������&� d����%8I�B��<w�'��s4���j�sB?����;88��3�t�tr�
Ll����)U��*����w�#����������r�k��t��DsL���&bH����G{��2$���;����5�>��k�,A��,��J(�@2`dy�dV��?���43�6h�����������Ue�W�kb��id�f[���8;��t"b�wC6i�Z3U�M���H^��X+��3g���f�N�T���(3�e����HH@c_���P���]�h����8������&�$��nF��$I���J�(��I�h��3����W|�8���m�l�����^�Q
V����m1Cv#�4�#��C�>��$-�+��?zi�RXv}W��i��������DU����Tc��&fb.j�������D��=����K��}���}�8:v������dH	(�}$�.���q�9�?+nm�M�8z[�D��4V�������c�j�����l>(��V���]Mw:��V��-�9|���{�[k���4N��3�&Jk��m����
GC;C�7���f���B���eP�C�����`O�G� (��<��c�>HzC}1��"|Ff:��a������b��>�C9*�w$PN���9�b	�>��"eO@�-
An��P5�0q���^�aJUq	.�<d�f�Ega��jT��F�2pv��];�iP2)Mk!���������z��?�:G{]P�j0�VT��]�E�Ph@��1o8��8N&��JRu+�e��d*���N\xuM%m^z��"��00Eg	h�p�����Wi=xe�YCJJ�Y]��i��%-P�$Q\V�9@���>'�A��=�� �z�����ej��;��|��E��
����h��?�QDJ���[1��SB�	�E����0C�L�J��C�m��"
���a���1���}�`���F�d0���Jej�)�x��:�������`P����)��[���UM*Fz��l����U}�L�5J7�yL9r
u�P�	���K�!�������7b�h��r���� ��Qd�(V����4���C��o��p
	>H��0��4�������z��b��Yw/�-{�o�[m��v.\��-	!���m���j��E?I<2���kIy�!p����N���+�7�?��@���:�UBq}��aB�a�j3���<�Q\`�����F}hh��v�d7�y��%�	���NK�?���n�t�
Kj���z����a�BK).'&��
B5aaq=-��-RNa�L��������x�T$V^dX2��Bb��(�`��?�i�,�<��e���V��%N�JE���FM)� �@�u{��D�<���K	_�Fd7`�M���m�g./6��������;Yx#O�����q$S�lez����L��I��h�x����F�Rq�����g�00�d#
A�"o�U�o>�z�%���kgVD�3l��xH�L!��y6��b=��"�"��b
�l���f�H�z$����}���J�|������ +���_�S��{L��������[c��N9Mq����������6A/��v����0)	!cg�6d�l�8q���7���fhIEv�2��`��t��5
��!��x�� ��'E=|�4��j�O�W�.����e��*n%�a�a��d��I>%�����c��j���D��#L���~3�N��LMMxv�K<����4�a���-����#�*
`��"]�5�]I��Hnz����-RO
>	������8)�������ZU���Q�5L?)I���lC
}����G�z��x�
y��M��9��E�IT5�s�^�i\:4��"|���c���M�g�����_~3������g*�<E?�z���,K��8�l��|�o_8��Y����%'f����3������mv���/�M��8pg�5�[``ACI��Y����Ag�o�������p��H0������3��s`>?��������0�D8C�P�<dzs���7���!����!�x�Q���c���������
2�����c<�*'yG�T����_����0����9�^_������T���B3c���t8��

@:q���1��0-��/&��Q�3y��\���8�I�^�a�����I�"��L�	���v%L�+������P����<��Y�*
��#�I�
T��~�����n���Q�<c���#�(��Na������������Y�����!}�pL��{����#��Qo����Z�P�������:�I(�@��q���y�=/X6�f���D�h:�cb����J��}U����j�R1�������/�	���X���
.2i�@2�G?�_�q�q��j�?��aT Pe���d�\��>��FB�*�������c=������eP���o�Yg������:�\���J����
�~��Be��l2�$�dh�0W��m��x�9�*���J�`'�a�K�E���?,\����4I��E �}z�L���q6�F&e{4�T[b�&'��;���+|&%�/�I�8b�#��LI�r\G2D�$�� "8���
O�I���]@0��������rAL[�^P�yH������p+q�;��:�T@?9sf�p=&�~�5~(a��i���~�V@�R�"�G�8��������	�?��L�r���3��'Hv�� ���|>�������9y3��#�������l�J���TDg6����p
��s<���E�����RJ��Q{��0)zmo���	�GL$�u.
l����6���sn�v�G�$��|��R�7�#���L$����d��9�|�v���&S|�$����������4��I�qi5mzE������v�Kzz���R�T�Q��D�sLN7[.n!r���C��[��d<���3���Gc,�g���
�����u��X+"(����N	�2���9,��JH��p'nrB2w��c$I���������sV��	Qr� �%V�>�^c�B�Y���K��2���]����:1������Z��/@�s�#������7��%����Yb��������r�F�'�G��t�R%#�L���MJ�7k:q�����iw�0X�7��F���v�
#`�M���(����y���rD0k���p��C�0��bysJx��|V,pocF�{a����e�p�y���w��s1�Q����e��:4�2�jY��
�i��5�V���(��m�R�2��J��J�H^=dl�o�.&��6�-|�8��60�2�!JH0�S�{w3u�A
X�r��V�o����*B6vC��X�t��<,M>����l�H���D����4N�2rB�"!:�1����D��K�J��&`	X��
�-��AC(T�i�.��D�I�<A�D�+k	E'0C�KA��K��~�� V�H�d;����{���~��c�����7?U*�$��_�0�1S���	F{����Z�t�)�}�s
�E�J��:����u��U����:����"�6��a@�u���2������eN���������3��x�p���
������H�tqGp
{z�q�T���a��&�U}����=4��^�NjsQax���R8�d7�h�>+�DkH [�y��-���3q��2 5D\,>X��m�9(�
�����!�3W�P�3�c���b�?)���~����J����3�����[�-o�~����+�A���~������M��Oa?������Np������3�/��"��|J��s�M�,�X�sw�R-��)��{~w&�Z�� Q�y����@����4&��.��s�P�F���w�w���vVx�gSq17d�r�u�=�����;jV��q��a��/�������#ZI&���R�}��]P��{LG�n��V�����9/��� c����J����-|����1F��D�ys�IM���b��8{ �qj��8T�M�Us�a�����X%��TfRw�
:�3�������4���4]��jm��d�[�uO�~�Q@��w�GD�����ID��������wTY�vP����E
��
Jd��zJ��t.�:��>0�Gw�
wZ8��6S5���d����}��t(��U��������d�C���	p@F?����1�w��}DE�RI`�������:�$�~�kI����u����Z����0��$!$�����w���dfp�M����������oz�<��w	���7��� ��G-^=S�C�	Oh<4���S1�/���
�#M�9)��1��
�pqw2�I�n,��<�{"T���7r���j}`;�J��j���U;�}�6���z�u��j=%s	r,���{A�@%b�j�T�z76�5�������_�s��O��%��n��W�t��3�
>P������A�I��1	�r����MX��4b�1wG�P�*�d\��u/��@�G�,0�����2M�c*��������s
|����$ �.���#@7�JK���rL������~�?E�G��N@�!����*|tTk�[�J��V�a#*T����P��C(���V�*2�+��<�4_��t�,��I�2���B�'srCD������0��PUK)�h��*m��s7Rv"�V~\��@�z� �����6�_a�'���
n���_�8��&����M��_^��4�w��y-.�9����
%i���
�3�e���1I3U|����1E��=?����G�@�������� �{<�T&]���4����"Q5W-Q��}�N�	���da��
��4m�y �����?��)�������Q,������
�������Oi`QXR,�L��U*�V����������kLd�����*\q%{�R(���::���dB������|��4��o���s�[�?k	����@������=�.�[<�{?uNO��b�"�|�\��G�)*T����&�����������������<��N��H�jh������pU������6x.�`0��N��'�{/l�_�^|^��v��{a�f/���'�^���<��3S��w.�^|�s�5�������W�&��qGe�^����3���q������*��w�v�[��,
T���/������vo�l�i������M�W���m��i����y\�I�&�z#�{�:���*��u|f	&k��|��k�D~��5cb�S�h1��e��������9`����i){���Z���Ev���/�{b%&LD��?+&�������������MK�^���Y�����x��/������=�=�6>i���+�5�����r��_���K�{j���{/6~����_���_:N�_������e��_z]�<B�������bf����6~�/�=n�������Ko�����8��|:�73�&0�?��������������{�����t����
������?6�����������qvo��y0��tvo����Q�����(=�C9G���e�����<>��~����~�u��^��Q5?�=<�&��q:�s$j�����,�@�i�`���I��� '�bi3{�?��-'�?;�e����@��
	d2�����N����7';����(����Mw�n'���}��]wTo����|,*o~���!��YYI�"�P�n�����[_@��Q�����n��R���>�-����'�|[f���?h���1��v���,[��?��C!�M����$��fk8i�Anz\���;�[c�^��u��x:���$��L�uw�k+�j�j�v
���C\��$�P�W���[������l�����K��[w<���������j&[��0�>a���sY�~���\��w�c�\�������F��w��fn�6t�}�.��q��'k!m
���*��/����Z�e�FV��l;v���E]x�h+����T=�����p�����N*��B��%���(5
.��0�'�$��A��w$����8���7@��(�����5�2��3�� Ny�=Ay�����'��(V3�j+�.��x���r�=����{��q�n������j1
�if'�c+3P�*�
��[^f���{�6r��>���e����9h*7��6g�������l��]����������V����>��n���:����u��]�L���;�:wDR�����Tc��m��<q3��J�����T����Z�JN��%/��3�b�����&0/��0����?��UK���sp���'��d��E��o6���rw�� �9�=5WK�����h�ZoqV^l�p���4C�)���l�4��e(���04{E5��������������Y��2L�x���pcs���r��������&��Iv.ZP�)�������]^k���-��]���]�r��.?�u�$�]��4u����_k��}�oCV�a�}��{��/��4ml���v_><-���}5�8��?�8���.[[wd��>���R�#����(%����k�I	)������.���V����u�L/�efml��9��T���4�q�]���v�R����Z�u;s����Lv"�Y�����h��&�����������|!s��p�������d:w�b9Yxc�N�X�V���9n�m_����l������o��dL����6�h���m�K�
��?�s���z�-\�8i���
�s5�%jb���8n$���6v�������:�ZG?�����nl�_w�Xr��m���*?�y���,K����;��og^7�r�^�f����kg�0�'��6��|C#�(��1;���h���=�����V9��r [�C"�5���<��{��v��uI��2��r��&��� �0�}��Y�w��/v{t���n�����
����[2�����|�)[��<���L�1�=����8��R�������0�7z��|q��1��etY7&/�7pv5��T��z��eH�m"���<���8���&�K�{��X:i�p�{�}�y?�`���4o�|�	4RLo�s6�t���Rsi=��&E�����a6y6fm��N&�d;nx[����H���
��Ak�4�5gwh�% ���s���Vx�4�v�^��3�Zy��\2�����?(
�Yo�%�
�����?��y���������Y��[m@L�z�
x@a%����K��3����P��p��������J\X���V�����3��I���7��T�3u�%�ZN�j8���5�������jrix��*_��y�VK��D���A	z�������U�nk�Kp�xw�3����o�b4�h@�Q�f�t(b��s�aoo�y�5�tm������Aa5����G�0&}&z��/���4�O��6\%J�&�|���Ig_�5�tNo�vb�2�!�*��r�:�Qk��oq�%��)��>>r_�h)���]���4w�uU�v��\��j���!��O9����?Kz(l$�q�����W�7������h�{xr,�:?u��Y�����{u��_�B�Q=B��
%�^�c��P0S���K��'a�Y��������?g�u���7{����5��w�A
L��j
4���s-BM��6i=���? �?����$��2�[�E }p��3^S2���'��v����pU���o�H�L{�'8$��:��`���cS�o{0hs��F�[�\��%P��(��$�7��AudW*�M�����`c���`hK���R
h���J��s�j�$�P��
��36�<;�����L�4����	��R��(�����E�We[})����s���w7������2X�D�L�'�����J�X�]���@�7.h���h�JM��?((|���4^T�B�sd^Oic���*�� J����S(���B� �_b4�^����+�'���q�E�n9�`�����
6�N�7����D�!L��i�L��<`�	����9���e�_�Qcl~6��*F�!�D%��eE��dk������b���1,��
_�W(%�\�i�����,Ao�7����#���:Nu��
�O��jlI������G,���!�B\:>l%|��t���v���#2e��/I �'��f��3�t���d	T�(�-;�T��r�l>�A��.����u��e��d��Ov����m�WA�
�_(�18=;|�w�^M%���!�{n� ,"�/��|�f����9����i8�j����om�Y
��W��$o������R���K��,�B��_�����u{�����N�w�}����'�W@&�����f�J��f7���U��������������'5�W���V�N���i�S?�uyb��h9��>��
1>���U��$,$)$����5��3�9�Y�<����5��G.�|N���)rr��������|�����m����0�����U��x�'OG�e�vG5�}�!m���������������;s��h��G�@v��x�7u��+�������e��]��!\�]�|��@h��K|��.]������W��m���a3��L����9�8�<v�[��y��z=�����cH��6d����<�d�`I��2�3�d������)�s,�^EE�C��\�LS�|���LYfFO����DO9�-0��g:��{����g.B�;�@������%`^��H-�U7�������jX�J�uG���]iJ�BHv@�n�={��1b\Z��%II����H~�q6���C"\�WH�)��H���9:%&�8��z�1h��*s~j�����+s~�N���%�&8x�N�
0��;���|�D�N���w�����_�
6�Z���:�lp03�>�O��+U������h�������[���S�������J��K5���N���q�P�^��fz8��qX��K��Q���@�b�Kj�b�\�>i��InoQBh�����^����F,����B �`�1��$��q�YD��%�r�z{���L�`\�������y����$�}��\%���X����/h�.1���}lJ��Oz��d��:jKi�+=�I�w�������3�]Y�m�-S�V��T��%E�c�I>�O��ne��S��3����>����j|���;������Tn��tNU�:K�x��#�����a��:l6�+��`����W��3[{�����G��x��7��e:���d����'�i,
#133Alvaro Herrera
alvherre@commandprompt.com
In reply to: Simon Riggs (#78)
Re: foreign key locks, 2nd attempt

Excerpts from Simon Riggs's message of lun mar 05 15:28:59 -0300 2012:

On Mon, Feb 27, 2012 at 2:47 AM, Robert Haas <robertmhaas@gmail.com> wrote:

Regarding performance, the good thing about this patch is that if you
have an operation that used to block, it might now not block.  So maybe
multixact-related operation is a bit slower than before, but if it
allows you to continue operating rather than sit waiting until some
other transaction releases you, it's much better.

That's probably true, although there is some deferred cost that is
hard to account for.  You might not block immediately, but then later
somebody might block either because the mxact SLRU now needs fsyncs or
because they've got to decode an mxid long after the relevant segment
has been evicted from the SLRU buffers.  In general, it's hard to
bound that latter cost, because you only avoid blocking once (when the
initial update happens) but you might pay the extra cost of decoding
the mxid as many times as the row is read, which could be arbitrarily
many.  How much of a problem that is in practice, I'm not completely
sure, but it has worried me before and it still does.  In the worst
case scenario, a handful of frequently-accessed rows with MXIDs all of
whose members are dead except for the UPDATE they contain could result
in continual SLRU cache-thrashing.

Cases I regularly see involve wait times of many seconds.

When this patch helps, it will help performance by algorithmic gains,
so perhaps x10-100.

That can and should be demonstrated though, I agree.

BTW, the isolation tester cases have a few places that in the unpatched
code die with deadlocks and with the patched code continue without
dying. Others are cases that block in unpatched master, and continue
without blocking in patched. This should be enough proof that there are
"algorithmic gains" here.

There's also a test case that demostrates a fix for the problem (pointed
out by docs) that if you acquire a row lock, then a subxact upgrades it
(say by deleting the row) and the subxact aborts, the original row lock
is lost. With the patched code, the original lock is no longer lost.

I completely agree with the idea that we need some mitigation against
repeated lookups of mxids that contain committed updates.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#134Alvaro Herrera
alvherre@commandprompt.com
In reply to: Simon Riggs (#88)
Re: foreign key locks, 2nd attempt

Excerpts from Simon Riggs's message of mar mar 06 18:33:13 -0300 2012:

The lock modes are correct, appropriate and IMHO have meaningful
names. No redesign required here.

Not sure about the naming of some of the flag bits however.

Feel free to suggest improvements ... I've probably seen them for too
long to find them anything but what I intended them to mean.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#135Alvaro Herrera
alvherre@commandprompt.com
In reply to: Simon Riggs (#85)
Re: foreign key locks, 2nd attempt

Excerpts from Simon Riggs's message of mar mar 06 17:28:12 -0300 2012:

On Tue, Mar 6, 2012 at 7:39 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

We provide four levels of tuple locking strength: SELECT FOR KEY UPDATE is
super-exclusive locking (used to delete tuples and more generally to update
tuples modifying the values of the columns that make up the key of the tuple);
SELECT FOR UPDATE is a standards-compliant exclusive lock; SELECT FOR SHARE
implements shared locks; and finally SELECT FOR KEY SHARE is a super-weak mode
that does not conflict with exclusive mode, but conflicts with SELECT FOR KEY
UPDATE.  This last mode implements a mode just strong enough to implement RI
checks, i.e. it ensures that tuples do not go away from under a check, without
blocking when some other transaction that want to update the tuple without
changing its key.

So there are 4 lock types, but we only have room for 3 on the tuple
header, so we store the least common/deprecated of the 4 types as a
multixactid. Some rewording would help there.

Hmm, I rewrote that paragraph two times. I'll try to adjust it a bit
more.

My understanding is that all of theses workloads will change

* Users of explicit SHARE lockers will be slightly worse in the case
of the 1st locker, but then after that they'll be the same as before.

Right. (We're assuming that there *are* users of SHARE locks, which I'm
not sure to be a given.)

* Updates against an RI locked table will be dramatically faster
because of reduced lock waits

Correct.

...and that these previous workloads are effectively unchanged:

* Stream of RI checks causes mxacts

Yes.

* Multi row deadlocks still possible

Yes.

* Queues of writers still wait in the same way

Yes.

* Deletes don't cause mxacts unless by same transaction

Yeah .. there's no way for anyone to not conflict with a FOR KEY UPDATE
lock (the strength grabbed by a delete) unless you're the same
transaction.

The possibility of having an update within a MultiXact means that they must
persist across crashes and restarts: a future reader of the tuple needs to
figure out whether the update committed or aborted.  So we have a requirement
that pg_multixact needs to retain pages of its data until we're certain that
the MultiXacts in them are no longer of interest.

I think the "no longer of interest" aspect needs to be tracked more
closely because it will necessarily lead to more I/O.

Not sure what you mean here.

If we store the LSN on each mxact page, as I think we need to, we can
get rid of pages more quickly if we know they don't have an LSN set.
So its possible we can optimise that more.

Hmm, I had originally thought that this was rather pointless because it
was unlikely that a segment would *never* have *all* multis not
containing updates. But then, maybe Robert is right and there are users
out there that run a lot of RI checks and never update the masters ...
Hm. I'm not sure that LSN tracking is the right tool to do that
optimization, however -- I mean, a single multi containing an update in
a whole segment will prevent that segment from being considered useless.

VACUUM is in charge of removing old MultiXacts at the time of tuple freezing.

You mean mxact segments?

Well, both. When a tuple is frozen, we both remove its Xmin/Xmax and
any possible multi that it might have in Xmax. That's what I really
meant above. But also, vacuum will remove pg_multixact segments just as
it will remove pg_clog segments.

(It is possible, and probably desirable, to remove a Multi much earlier
than freezing the tuple. The patch does not (yet) do that, however.)

Surely we set hint bits on tuples same as now? Hope so.

We set hint bits, but if a multi contains an update, we don't set
HEAP_XMAX_COMMITTED even when the update is known committed. I think
we could do this in some cases.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#136Simon Riggs
simon@2ndQuadrant.com
In reply to: Alvaro Herrera (#132)
Re: foreign key locks, 2nd attempt

On Sat, Mar 17, 2012 at 10:45 PM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:

Here is v11.  This version is mainly updated to add pg_upgrade support,
as discussed.  It also contains the README file that was posted earlier
(plus wording fixes per Bruce), a couple of bug fixes, and some comment
updates.

The main thing we're waiting on are the performance tests to confirm
the lack of regression.

You are working on that, right?

--
 Simon Riggs                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services

#137Peter Geoghegan
peter@2ndquadrant.com
In reply to: Simon Riggs (#136)
Re: foreign key locks, 2nd attempt

On 25 March 2012 09:17, Simon Riggs <simon@2ndquadrant.com> wrote:

The main thing we're waiting on are the performance tests to confirm
the lack of regression.

I have extensively benchmarked the latest revision of the patch
(tpc-b.sql), which I pulled from Alvaro's github account. The
benchmark was of the feature branch's then-and-current HEAD, "Don't
follow update chains unless caller requests it".

I've had to split these numbers out into two separate reports.
Incidentally, at some future point I hope that pgbench-tools can
handling testing across feature branches, initdb'ing and suchlike
automatically and as needed. That's something that's likely to happen
sooner rather than later.

The server used was kindly supplied by the University of Oregon open
source lab.

Server (It's virtualised, but apparently this is purely for sandboxing
purposes and the virtualisation technology is rather good):

IBM,8231-E2B POWER7 processor (8 cores).
Fedora 16
8GB Ram
Dedicated RAID1 disks. Exact configuration unknown.

postgresql.conf (this information is available when you drill down
into each test too, fwiw):
max_connections = 200
shared_buffers = 2GB
checkpoint_segments = 30
checkpoint_completion_target = 0.8
effective_cache_size = 6GB

Reports:

http://results_fklocks.staticloud.com/
http://results_master_for_fks.staticloud.com/

Executive summary: There is a clear regression of less than 10%. There
also appears to be a new source of contention at higher client counts.

I realise that the likely upshot of this, and other concerns that are
generally held at this late stage is that this patch will not make it
into 9.2 . For what it's worth, that comes as a big disappointment to
me. I would like to thank both Alvaro and Noah for their hard work
here.

--
Peter Geoghegan       http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training and Services