Ejemplo n.º 1
0
/*
 * Any change of global outstanding allocation balance should come
 * from the combination of active memory account balance increase
 * and any shared header allocation (SharedChunkHeadersMemoryAccount
 * balance increase)
 */
void
test__MemoryAccounting_Allocate__ChargesOnlyActiveAccount(void **state)
{
	MemoryAccount *newActiveAccount = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_Exec_Hash);

	/* Make sure we have a new active account other than Rollover */
	MemoryAccount *oldActiveAccount = MemoryAccounting_SwitchAccount(newActiveAccount);

	assert_true(ActiveMemoryAccount == newActiveAccount);

	uint64 prevOutstanding = MemoryAccountingOutstandingBalance;

	uint64 prevSharedHeaderAlloc = SharedChunkHeadersMemoryAccount->allocated;

	void *testAlloc = palloc(NEW_ALLOC_SIZE);

	/*
	 * Any change of outstanding balance is coming from new allocation
	 * and the associated shared header allocation
	 */
	assert_true((newActiveAccount->allocated - newActiveAccount->freed) +
			(SharedChunkHeadersMemoryAccount->allocated - prevSharedHeaderAlloc) ==
					(MemoryAccountingOutstandingBalance - prevOutstanding));

	/*
	 * We need to pfree as we have allocated this memory
	 * in the TopMemoryContext with a short-living memory account
	 */
	pfree(testAlloc);
}
Ejemplo n.º 2
0
/* Tests whether a large allocation updates balance of the active memory account */
void
test__AllocSetAllocImpl__LargeAllocInActiveMemoryAccount(void **state)
{
	MemoryAccount *newActiveAccount = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_Exec_Hash);

	/* Make sure we have a new active account other than Rollover */
	MemoryAccount *oldActiveAccount = MemoryAccounting_SwitchAccount(newActiveAccount);

	uint64 prevOutstanding = MemoryAccountingOutstandingBalance;

	uint64 prevSharedHeaderAlloc = SharedChunkHeadersMemoryAccount->allocated;

	uint64 prevActiveAlloc = ActiveMemoryAccount->allocated;

	int chunkSize = ALLOC_CHUNK_LIMIT + 1;
	/* This chunk should record newAccount as the owner */
	void *testAlloc = palloc(chunkSize);

	/*
	 * All the new allocation should go to ActiveMemoryAccount, and the
	 * SharedChunkHeadersMemoryAccount should contribute to add up to
	 * the outstanding balance change
	 */
	assert_true((newActiveAccount->allocated >= prevActiveAlloc + chunkSize) &&
			(SharedChunkHeadersMemoryAccount->allocated > prevSharedHeaderAlloc) &&
			(newActiveAccount->allocated - newActiveAccount->freed) +
			(SharedChunkHeadersMemoryAccount->allocated - prevSharedHeaderAlloc) ==
					(MemoryAccountingOutstandingBalance - prevOutstanding));

	/*
	 * We need to pfree as we have allocated this memory
	 * in the TopMemoryContext with a short-living memory account
	 */
	pfree(testAlloc);
}
Ejemplo n.º 3
0
/*
 * serializeNode -
 * This is used on the query dispatcher to serialize Plan and Query Trees for
 * dispatching to qExecs.
 * The returned string is palloc'ed in the current memory context.
 */
char *
serializeNode(Node *node, int *size, int *uncompressed_size_out)
{
	char	   *pszNode;
	char	   *sNode;
	int		   uncompressed_size;

	Assert(node != NULL);
	Assert(size != NULL);
	START_MEMORY_ACCOUNT(MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_Serializer));
	{
		pszNode = nodeToBinaryStringFast(node, &uncompressed_size);
		Assert(pszNode != NULL);
	
		if (NULL != uncompressed_size_out)
		{
			*uncompressed_size_out = uncompressed_size;
		}
		sNode = compress_string(pszNode, uncompressed_size, size);
		pfree(pszNode);

		if (DEBUG5 >= log_min_messages)
		{
			Node * newnode = NULL;
			PG_TRY();
			{
				newnode = deserializeNode(sNode, *size);
			}
			PG_CATCH();
			{
				elog_node_display(DEBUG5, "Before serialization", node, true);
				PG_RE_THROW();
			}
			PG_END_TRY();

			/* Some plans guarantee these differences (see serialization
			 * of plan nodes -- they avoid sending QD-only info out) */
			if (strcmp(nodeToString(node), nodeToString(newnode)) != 0)
			{
				elog_node_display(DEBUG5, "Before serialization", node, true);

				elog_node_display(DEBUG5, "After deserialization", newnode, true);
			}
		}
	}
	END_MEMORY_ACCOUNT();

	return sNode;
}
Ejemplo n.º 4
0
/* Tests whether a free operation decreases balance of only the owning account */
void
test__AllocFreeInfo__FreesOnlyOwnerAccount(void **state)
{
	MemoryAccount *newAccount = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_Exec_Hash);
	MemoryAccount *oldActiveAccount = MemoryAccounting_SwitchAccount(newAccount);

	/* This chunk should record newAccount as the owner */
	void *testAlloc = palloc(NEW_ALLOC_SIZE);

	MemoryAccounting_SwitchAccount(oldActiveAccount);

	assert_true(ActiveMemoryAccount != newAccount);

	uint64 originalActiveBalance = ActiveMemoryAccount->allocated - ActiveMemoryAccount->freed;
	uint64 newAccountBalance = newAccount->allocated - newAccount->freed;
	uint64 newAccountFreed = newAccount->freed;
	uint64 sharedFreed = SharedChunkHeadersMemoryAccount->freed;
	uint64 prevOutstanding = MemoryAccountingOutstandingBalance;

	pfree(testAlloc);

	/*
	 * Make sure that the active account is unchanged while the owner account
	 * balance was reduced
	 */
	assert_true(ActiveMemoryAccount->allocated - ActiveMemoryAccount->freed == originalActiveBalance);
	/* Balance was released from newAccount */
	assert_true(newAccount->allocated - newAccount->freed <= newAccountBalance - NEW_ALLOC_SIZE);

	/* The shared header should be released */
	assert_true(SharedChunkHeadersMemoryAccount->freed - sharedFreed > 0);

	/* All the difference in outstanding balance should come from newAccount
	 * (which was the owner of the chunk) balance change and the resulting
	 * shared header release
	 */
	assert_true(prevOutstanding - MemoryAccountingOutstandingBalance ==
			newAccount->freed - newAccountFreed + SharedChunkHeadersMemoryAccount->freed - sharedFreed);
}
Ejemplo n.º 5
0
/*
 * Tests whether we charge shared chunk header account during an allocation
 * when header sharing is not possible
 */
void
test__AllocAllocInfo__ChargesSharedChunkHeadersMemoryAccount(void **state)
{
	MemoryAccount *newActiveAccount = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_Exec_Hash);

	/* Make sure we have a new active account to force a new shared header allocation */
	MemoryAccount *oldActiveAccount = MemoryAccounting_SwitchAccount(newActiveAccount);

	uint64 prevSharedBalance = SharedChunkHeadersMemoryAccount->allocated - SharedChunkHeadersMemoryAccount->freed;

	void *testAlloc = palloc(NEW_ALLOC_SIZE);

	uint64 newSharedBalance = SharedChunkHeadersMemoryAccount->allocated - SharedChunkHeadersMemoryAccount->freed;

	/*
	 * A new sharedHeader should record the associated memory overhead
	 * in the SharedChunkHeadersMemoryAccount
	 */
	assert_true(prevSharedBalance < newSharedBalance);

	pfree(testAlloc);
}
Ejemplo n.º 6
0
/* Tests whether we are promptly freeing shared header that is no longer shared */
void
test__AllocFreeInfo__FreesObsoleteHeader(void **state)
{
	MemoryAccount *newActiveAccount = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_Exec_Hash);

	/* Make sure we have a new active account to force a new shared header allocation */
	MemoryAccount *oldActiveAccount = MemoryAccounting_SwitchAccount(newActiveAccount);

	uint64 prevSharedBalance = SharedChunkHeadersMemoryAccount->allocated - SharedChunkHeadersMemoryAccount->freed;

	void *testAlloc = palloc(NEW_ALLOC_SIZE);

	uint64 newSharedBalance = SharedChunkHeadersMemoryAccount->allocated - SharedChunkHeadersMemoryAccount->freed;

	assert_true(prevSharedBalance < newSharedBalance);

	StandardChunkHeader *header = (StandardChunkHeader *)
		((char *) testAlloc - STANDARDCHUNKHEADERSIZE);

	AllocSet set = (AllocSet)CurrentMemoryContext;

	SharedChunkHeader *headerPointer = header->sharedHeader;
	assert_true(set->sharedHeaderList == headerPointer);

	/*
	 * As no one else is using the sharedHeader of testAlloc,
	 * we should release upon pfree of testAlloc
	 */
	pfree(testAlloc);

	/*
	 * The sharedHeaderList should no longer store the sharedHeader
	 * that we have just released. Note: the headerPointer is now
	 * and invalid pointer as we have already freed the shared header
	 * memory
	 */
	assert_true(set->sharedHeaderList != headerPointer);
}
Ejemplo n.º 7
0
/*
 * deserializeNode -
 * This is used on the qExecs to deserialize serialized Plan and Query Trees
 * received from the dispatcher.
 * The returned node is palloc'ed in the current memory context.
 */
Node *
deserializeNode(const char *strNode, int size)
{
	char		*sNode;
	Node		*node;
	int 		uncompressed_len;

	Assert(strNode != NULL);

	START_MEMORY_ACCOUNT(MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_Deserializer));
	{
		sNode = uncompress_string(strNode, size, &uncompressed_len);

		Assert(sNode != NULL);

		node = readNodeFromBinaryString(sNode, uncompressed_len);
	
		pfree(sNode);
	}
	END_MEMORY_ACCOUNT();

	return node;
}
Ejemplo n.º 8
0
/*
 * InitMemoryAccounting
 *		Internal method that should only be used to initialize a memory accounting
 *		subsystem. Creates basic data structure such as the MemoryAccountMemoryContext,
 *		TopMemoryAccount, MemoryAccountMemoryAccount
 */
static void
InitMemoryAccounting()
{
	Assert(TopMemoryAccount == NULL);
	Assert(AlienExecutorMemoryAccount == NULL);

	/*
	 * Order of creation:
	 *
	 * 1. Root
	 * 2. SharedChunkHeadersMemoryAccount
	 * 3. RolloverMemoryAccount
	 * 4. MemoryAccountMemoryAccount
	 * 5. MemoryAccountMemoryContext
	 *
	 * The above 5 survive reset (MemoryAccountMemoryContext
	 * gets reset, but not deleted).
	 *
	 * Next set ActiveMemoryAccount = MemoryAccountMemoryAccount
	 *
	 * Note, don't set MemoryAccountMemoryAccount before creating
	 * MemoryAccuontMemoryContext, as that will put the balance
	 * of MemoryAccountMemoryContext in the MemoryAccountMemoryAccount,
	 * preventing it from going to 0, upon reset of MemoryAccountMemoryContext.
	 * MemoryAccountMemoryAccount should only contain balance from actual
	 * account creation, not the overhead of the context.
	 *
	 * Once the long living accounts are done and MemoryAccountMemoryAccount
	 * is the ActiveMemoryAccount, we proceed to create TopMemoryAccount
	 *
	 * This ensures the following:
	 *
	 * 1. Accounting is triggered only if the ActiveMemoryAccount is non-null
	 *
	 * 2. If the ActiveMemoryAccount is non-null, we are guaranteed to have
	 * SharedChunkHeadersMemoryAccount, so that we can account shared chunk headers
	 *
	 * 3. All short-living accounts (including Top) are accounted in MemoryAccountMemoryAccount
	 *
	 * 4. All short-living accounts are allocated in MemoryAccountMemoryContext
	 *
	 * 5. Number of allocations in the aset.c with nullAccountHeader is very small
	 * as we turn on ActiveMemoryAccount immediately after we allocate long-living
	 * accounts (only the memory to host long-living accounts are unaccounted, which
	 * are very few and their overhead is already known).
	 */

	if (MemoryAccountTreeLogicalRoot == NULL)
	{
		/*
		 * All the long living accounts are created together, so if logical root
		 * is null, then other long-living accounts should be the null too
		 */
		Assert(SharedChunkHeadersMemoryAccount == NULL && RolloverMemoryAccount == NULL);
		Assert(TopMemoryAccount == NULL && MemoryAccountMemoryAccount == NULL && MemoryAccountMemoryContext == NULL);

		MemoryAccountTreeLogicalRoot = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_LogicalRoot);

		SharedChunkHeadersMemoryAccount = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_SharedChunkHeader);

		RolloverMemoryAccount = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_Rollover);

		MemoryAccountMemoryAccount = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_MemAccount);

		/* Now initiate the memory accounting system. */
		MemoryAccountMemoryContext = AllocSetContextCreate(TopMemoryContext,
											 "MemoryAccountMemoryContext",
											 ALLOCSET_DEFAULT_MINSIZE,
											 ALLOCSET_DEFAULT_INITSIZE,
											 ALLOCSET_DEFAULT_MAXSIZE);

		/*
		 * Temporarily active MemoryAccountMemoryAccount. Once
		 * all the setup is done, TopMemoryAccount will become
		 * the ActiveMemoryAccount
		 */
		ActiveMemoryAccount = MemoryAccountMemoryAccount;
	}
	else
	{
		/* Long-living setup is already done, so re-initialize those */
		/* If "logical root" is pre-existing, "rollover" should also be pre-existing */
		Assert(MemoryAccountTreeLogicalRoot->nextSibling == NULL &&
				RolloverMemoryAccount != NULL && SharedChunkHeadersMemoryAccount != NULL &&
				MemoryAccountMemoryAccount != NULL);

		/* Ensure tree integrity */
		Assert(MemoryAccountMemoryAccount->firstChild == NULL &&
				SharedChunkHeadersMemoryAccount->firstChild == NULL &&
				RolloverMemoryAccount->firstChild == NULL);

		/*
		 * First child of MemoryAccountTreeLogicalRoot should be Top, which
		 * had been obliterated during MemoryAccountMemoryContextReset. So,
		 * we don't attempt to verify the first child of MeomryAccountTreeLogicalRoot
		 */
		Assert(MemoryAccountMemoryAccount->nextSibling == RolloverMemoryAccount &&
				RolloverMemoryAccount->nextSibling == SharedChunkHeadersMemoryAccount &&
				SharedChunkHeadersMemoryAccount->nextSibling == NULL);

		/*
		 * We will loose previous TopMemoryAccount during InitMemoryAccounting. So,
		 * we need to readjust the "logical root" children pointers.
		 */
	 	MemoryAccountTreeLogicalRoot->firstChild = MemoryAccountMemoryAccount;
	 	MemoryAccountMemoryAccount->nextSibling = RolloverMemoryAccount;
	 	RolloverMemoryAccount->nextSibling = SharedChunkHeadersMemoryAccount;
	 	SharedChunkHeadersMemoryAccount->nextSibling = NULL;
	 	RolloverMemoryAccount->firstChild = NULL;
	 	SharedChunkHeadersMemoryAccount->firstChild = NULL;
	}

	TopMemoryAccount = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_Top);
	/* For AlienExecutorMemoryAccount we need TopMemoryAccount as parent */
	ActiveMemoryAccount = TopMemoryAccount;

	AlienExecutorMemoryAccount = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_Exec_AlienShared);
}
Ejemplo n.º 9
0
/*
 * Tests whether we correctly allocate a new shared header and insert it
 * into the sharedHeaderList
 */
void
test__AllocAllocInfo__InsertsIntoSharedHeaderList(void **state)
{
	AllocSet set = (AllocSet)CurrentMemoryContext;

	/*
	 * This should create or reuse one of the sharedHeader
	 * in the current context's sharedHeaderList
	 */
	void *testAlloc1 = palloc(NEW_ALLOC_SIZE);

	MemoryAccount *newActiveAccount1 = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_Exec_Hash);
	/* Make sure we have a new active account to force a new shared header allocation */
	MemoryAccounting_SwitchAccount(newActiveAccount1);

	/* This will trigger a new sharedHeader creation because of the new ActiveMemoryAccount */
	void *testAlloc2 = palloc(NEW_ALLOC_SIZE);

	StandardChunkHeader *header1 = (StandardChunkHeader *)
		((char *) testAlloc1 - STANDARDCHUNKHEADERSIZE);

	StandardChunkHeader *header2 = (StandardChunkHeader *)
		((char *) testAlloc2 - STANDARDCHUNKHEADERSIZE);

	/* Now we are triggering a third sharedHeader creation */
	MemoryAccount *newActiveAccount2 = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_Exec_Hash);
	/* Make sure we have a new active account to force a new shared header allocation */
	MemoryAccounting_SwitchAccount(newActiveAccount2);

	void *testAlloc3 = palloc(NEW_ALLOC_SIZE);
	StandardChunkHeader *header3 = (StandardChunkHeader *)
		((char *) testAlloc3 - STANDARDCHUNKHEADERSIZE);

	/* Verify that the sharedHeaderList linked list looks good */
	assert_true(set->sharedHeaderList == header3->sharedHeader &&
			set->sharedHeaderList->next == header2->sharedHeader &&
			set->sharedHeaderList->next->next == header1->sharedHeader &&
			set->sharedHeaderList->next->next->next == NULL);

	/*
	 * Create one more account to force creation of another sharedHeader, which
	 * should replace the earliest sharedHeader in the sharedHeaderList. We only
	 * look ahead up to depth 3, but we maintain all the sharedHeaders in the
	 * sharedHeaderList
	 */
	MemoryAccount *newActiveAccount3 = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_Exec_Hash);
	MemoryAccounting_SwitchAccount(newActiveAccount3);

	void *testAlloc4 = palloc(NEW_ALLOC_SIZE);
	StandardChunkHeader *header4 = (StandardChunkHeader *)
		((char *) testAlloc4 - STANDARDCHUNKHEADERSIZE);

	/*
	 * Verify that the sharedHeaderList linked list can go beyond
	 * 3 levels of lookahead by testing linked list up to depth 4
	 */
	assert_true(set->sharedHeaderList == header4->sharedHeader &&
			set->sharedHeaderList->next == header3->sharedHeader &&
			set->sharedHeaderList->next->next == header2->sharedHeader &&
			set->sharedHeaderList->next->next->next == header1->sharedHeader &&
			set->sharedHeaderList->next->next->next->next == NULL);

	pfree(testAlloc1);
	pfree(testAlloc2);
	pfree(testAlloc3);
	pfree(testAlloc4);
}
Ejemplo n.º 10
0
/*
 * Tests whether header sharing works even if the desired header
 * is not at the head of sharedHeaderList
 */
void
test__AllocAllocInfo__LooksAheadInSharedHeaderList(void **state)
{
	AllocSet set = (AllocSet)CurrentMemoryContext;

	/*
	 * This should create or reuse one of the sharedHeader
	 * in the current context's sharedHeaderList
	 */
	void *testAlloc1 = palloc(NEW_ALLOC_SIZE);

	MemoryAccount *newActiveAccount1 = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_Exec_Hash);
	/* Make sure we have a new active account to force a new shared header allocation */
	MemoryAccount *oldActiveAccount = MemoryAccounting_SwitchAccount(newActiveAccount1);

	/* This will trigger a new sharedHeader creation because of the new ActiveMemoryAccount */
	void *testAlloc2 = palloc(NEW_ALLOC_SIZE);

	StandardChunkHeader *header1 = (StandardChunkHeader *)
		((char *) testAlloc1 - STANDARDCHUNKHEADERSIZE);

	StandardChunkHeader *header2 = (StandardChunkHeader *)
		((char *) testAlloc2 - STANDARDCHUNKHEADERSIZE);

	/* The old shared header should not be at the head of sharedHeaderList */
	assert_true(set->sharedHeaderList != header1->sharedHeader);
	assert_true(header1->sharedHeader != header2->sharedHeader);

	/*
	 * This should restore old active account, so any further allocation
	 * should reuse header1, which is now *not* at the head of sharedHeaderList
	 */
	MemoryAccounting_SwitchAccount(oldActiveAccount);

	void *testAlloc3 = palloc(NEW_ALLOC_SIZE);
	StandardChunkHeader *header3 = (StandardChunkHeader *)
		((char *) testAlloc3 - STANDARDCHUNKHEADERSIZE);

	/* As we switched to previous account, we should be able to share the header */
	assert_true(header3->sharedHeader == header1->sharedHeader);

	/* Now we are triggering a third sharedHeader creation */
	MemoryAccount *newActiveAccount2 = MemoryAccounting_CreateAccount(0, MEMORY_OWNER_TYPE_Exec_Hash);
	/* Make sure we have a new active account to force a new shared header allocation */
	oldActiveAccount = MemoryAccounting_SwitchAccount(newActiveAccount2);

	void *testAlloc4 = palloc(NEW_ALLOC_SIZE);
	StandardChunkHeader *header4 = (StandardChunkHeader *)
		((char *) testAlloc4 - STANDARDCHUNKHEADERSIZE);

	/* Make sure header4 got a new sharedHeader as we switched to a *new* memory account */
	assert_true(header4->sharedHeader != header1->sharedHeader &&
			header4->sharedHeader != header2->sharedHeader && header4->sharedHeader != header1->sharedHeader);

	/* And the latest sharedHeader should be at the head of sharedHeaderList */
	assert_true(header4->sharedHeader == set->sharedHeaderList);

	/*
	 * Now restore the original account, making the very original
	 * sharedHeader eligible to be reused (a 3 level reuse lookahead)
	 */
	MemoryAccounting_SwitchAccount(oldActiveAccount);

	void *testAlloc5 = palloc(NEW_ALLOC_SIZE);
	StandardChunkHeader *header5 = (StandardChunkHeader *)
		((char *) testAlloc5 - STANDARDCHUNKHEADERSIZE);

	/*
	 * Make sure we were able to dig up the original sharedHeader
	 * by digging through the sharedHeaderList
	 */
	assert_true(header5->sharedHeader == header1->sharedHeader);

	pfree(testAlloc1);
	pfree(testAlloc2);
	pfree(testAlloc3);
	pfree(testAlloc4);
	pfree(testAlloc5);
}