/* * 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); }
/* 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); }
/* 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); }
/* * 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); }
/* 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); }
/* * CreateMemoryAccountImpl * Allocates and initializes a memory account. * * maxLimit: The quota information of this account * ownerType: Gross owner type (e.g., different executor nodes). The memory * accounts are hierarchical, so using the tree location we will differentiate * between owners of same gross type (e.g., two sequential scan owners). * parent: The parent account of this account. */ static MemoryAccount* CreateMemoryAccountImpl(long maxLimit, MemoryOwnerType ownerType, MemoryAccount* parent) { /* We don't touch the oldContext. We create all MemoryAccount in MemoryAccountMemoryContext */ MemoryContext oldContext = NULL; MemoryAccount* newAccount = NULL; /* Return value */ /* Used for switching temporarily to MemoryAccountMemoryAccount ownership to account for the instrumentation overhead */ MemoryAccount *oldAccount = NULL; /* * Rollover is a special MemoryAccount that resides at the * TopMemoryContext, and not under MemoryAccountMemoryContext */ Assert(ownerType == MEMORY_OWNER_TYPE_LogicalRoot || ownerType == MEMORY_OWNER_TYPE_SharedChunkHeader || ownerType == MEMORY_OWNER_TYPE_Rollover || ownerType == MEMORY_OWNER_TYPE_MemAccount || (MemoryAccountMemoryContext != NULL && MemoryAccountMemoryAccount != NULL)); if (ownerType == MEMORY_OWNER_TYPE_SharedChunkHeader || ownerType == MEMORY_OWNER_TYPE_Rollover || ownerType == MEMORY_OWNER_TYPE_MemAccount || ownerType == MEMORY_OWNER_TYPE_Top) { /* Set the "logical root" as the parent of two top account */ parent = MemoryAccountTreeLogicalRoot; } /* * Other than logical root, no long-living account should have children * and only logical root is allowed to have no parent */ Assert((parent == NULL && ownerType == MEMORY_OWNER_TYPE_LogicalRoot) || (parent != RolloverMemoryAccount && parent != SharedChunkHeadersMemoryAccount && parent != MemoryAccountMemoryAccount)); /* * Only SharedChunkHeadersMemoryAccount, Rollover, MemoryAccountMemoryAccount * and Top can be under "logical root" */ Assert(parent != MemoryAccountTreeLogicalRoot || ownerType == MEMORY_OWNER_TYPE_LogicalRoot || ownerType == MEMORY_OWNER_TYPE_SharedChunkHeader || ownerType == MEMORY_OWNER_TYPE_Rollover || ownerType == MEMORY_OWNER_TYPE_MemAccount || ownerType == MEMORY_OWNER_TYPE_Top); /* Long-living accounts need TopMemoryContext */ if (ownerType == MEMORY_OWNER_TYPE_LogicalRoot || ownerType == MEMORY_OWNER_TYPE_SharedChunkHeader || ownerType == MEMORY_OWNER_TYPE_Rollover || ownerType == MEMORY_OWNER_TYPE_MemAccount) { oldContext = MemoryContextSwitchTo(TopMemoryContext); } else { oldContext = MemoryContextSwitchTo(MemoryAccountMemoryContext); oldAccount = MemoryAccounting_SwitchAccount(MemoryAccountMemoryAccount); } newAccount = makeNode(MemoryAccount); InitializeMemoryAccount(newAccount, maxLimit, ownerType, parent); if (oldAccount != NULL) { MemoryAccounting_SwitchAccount(oldAccount); } MemoryContextSwitchTo(oldContext); return newAccount; }
/* * 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); }
/* * 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); }