Repository: incubator-hawq Updated Branches: refs/heads/master f0e95fc4e -> aaa2c8cdf
HAWQ-1618. Segment panic at workfile_mgr_close_file() when transaction ROLLBACK Project: http://git-wip-us.apache.org/repos/asf/incubator-hawq/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-hawq/commit/aaa2c8cd Tree: http://git-wip-us.apache.org/repos/asf/incubator-hawq/tree/aaa2c8cd Diff: http://git-wip-us.apache.org/repos/asf/incubator-hawq/diff/aaa2c8cd Branch: refs/heads/master Commit: aaa2c8cdf289d0cf50e025cf5eef2197871f4951 Parents: f0e95fc Author: interma <inte...@outlook.com> Authored: Wed May 30 14:26:01 2018 +0800 Committer: rlei <r...@pivotal.io> Committed: Wed Jun 6 16:53:37 2018 +0800 ---------------------------------------------------------------------- src/backend/executor/execWorkfile.c | 53 ++++----- src/backend/executor/test/Makefile | 2 +- src/backend/executor/test/execWorkfile_test.c | 119 --------------------- src/backend/utils/sort/tuplestorenew.c | 90 +++++++--------- 4 files changed, 59 insertions(+), 205 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/aaa2c8cd/src/backend/executor/execWorkfile.c ---------------------------------------------------------------------- diff --git a/src/backend/executor/execWorkfile.c b/src/backend/executor/execWorkfile.c index 2e68efe..7e8ec58 100644 --- a/src/backend/executor/execWorkfile.c +++ b/src/backend/executor/execWorkfile.c @@ -60,8 +60,8 @@ ExecWorkFile_Create(const char *fileName, bool delOnClose, int compressType) { - ExecWorkFile *workfile = NULL; - void *file = NULL; + ExecWorkFile *workfile; + void *file; /* Before creating a new file, let's check the limit on number of workfile created */ if (!WorkfileQueryspace_AddWorkfile()) @@ -70,14 +70,6 @@ ExecWorkFile_Create(const char *fileName, workfile_mgr_report_error(); } - /* - * Create ExecWorkFile in the TopMemoryContext since this memory context - * is still available when calling the transaction callback at the - * time when the transaction aborts. - */ - MemoryContext oldContext = MemoryContextSwitchTo(TopMemoryContext); - - switch(fileType) { case BUFFILE: @@ -88,10 +80,10 @@ ExecWorkFile_Create(const char *fileName, file = (void *)bfz_create(fileName, delOnClose, compressType); break; default: - ereport(LOG, + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("invalid work file type: %d", fileType))); - Assert(false); + return NULL; /* keep compiler quiet */ } workfile = palloc0(sizeof(ExecWorkFile)); @@ -102,8 +94,6 @@ ExecWorkFile_Create(const char *fileName, workfile->size = 0; ExecWorkFile_SetFlags(workfile, delOnClose, true /* created */); - MemoryContextSwitchTo(oldContext); - return workfile; } @@ -158,9 +148,9 @@ ExecWorkFile_Open(const char *fileName, bool delOnClose, int compressType) { - ExecWorkFile *workfile = NULL; - void *file = NULL; - int64 file_size = 0; + ExecWorkFile *workfile; + void *file; + int64 file_size; switch(fileType) { @@ -170,32 +160,30 @@ ExecWorkFile_Open(const char *fileName, delOnClose, true /* interXact */ ); if (!file) - { - elog(ERROR, "could not open temporary file \"%s\": %m", fileName); - } + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not open temporary file \"%s\": %m", + fileName))); + BufFileSetWorkfile(file); file_size = BufFileGetSize(file); - break; + case BFZ: file = (void *)bfz_open(fileName, delOnClose, compressType); if (!file) - { - elog(ERROR, "could not open temporary file \"%s\": %m", fileName); - } + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not open temporary file \"%s\": %m", + fileName))); file_size = bfz_totalbytes((bfz_t *)file); break; + default: - ereport(LOG, + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("invalid work file type: %d", fileType))); - Assert(false); - } - - /* Failed opening existing workfile. Inform the caller */ - if (NULL == file) - { - return NULL; + return NULL; /* keep compiler quiet */ } workfile = palloc0(sizeof(ExecWorkFile)); @@ -219,6 +207,7 @@ ExecWorkFile_Open(const char *fileName, */ bool ExecWorkFile_Write(ExecWorkFile *workfile, + void *data, uint64 size) { http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/aaa2c8cd/src/backend/executor/test/Makefile ---------------------------------------------------------------------- diff --git a/src/backend/executor/test/Makefile b/src/backend/executor/test/Makefile index 8f84984..a7d4124 100644 --- a/src/backend/executor/test/Makefile +++ b/src/backend/executor/test/Makefile @@ -18,7 +18,7 @@ subdir=src/backend/executor top_builddir=../../../.. -TARGETS=nodeSubplan nodeShareInputScan execAmi execWorkfile execHHashagg +TARGETS=nodeSubplan nodeShareInputScan execAmi execHHashagg # Objects from backend, which don't need to be mocked but need to be linked. nodeSubplan_REAL_OBJS=\ http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/aaa2c8cd/src/backend/executor/test/execWorkfile_test.c ---------------------------------------------------------------------- diff --git a/src/backend/executor/test/execWorkfile_test.c b/src/backend/executor/test/execWorkfile_test.c deleted file mode 100644 index 5a279df..0000000 --- a/src/backend/executor/test/execWorkfile_test.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -#include <stdarg.h> -#include <stddef.h> -#include <setjmp.h> -#include "cmockery.h" - -#include "c.h" -#include "postgres.h" -#include "storage/buffile.h" -#include "storage/bfz.h" -#include "executor/execWorkfile.h" -#include "utils/memutils.h" - -/* Ignore elog */ -#include "utils/elog.h" - -#undef elog -#define elog - -/* Provide specialized mock implementations for memory allocation functions */ -#undef palloc0 -#define palloc0 execWorkfile_palloc0_mock -void *execWorkfile_palloc0_mock(Size size); - -#undef pstrdup -#define pstrdup execWorkfile_pstrdup_mock -char *execWorkfile_pstrdup_mock(const char *string); - -#include "../execWorkfile.c" - -/* - * This is a mocked version of palloc0 to be used in ExecWorkFile_Create. - * It asserts that it is executed in the TopMemoryContext. - */ -void * -execWorkfile_palloc0_mock(Size size) -{ - assert_int_equal(CurrentMemoryContext, TopMemoryContext); - return MemoryContextAllocZero(CurrentMemoryContext, size); -} - -/* - * This is a mocked version of pstrdup to be used in ExecWorkFile_Create. - * It asserts that it is executed in the TopMemoryContext. - */ -char *execWorkfile_pstrdup_mock(const char *string) -{ - assert_int_equal(CurrentMemoryContext, TopMemoryContext); - return MemoryContextStrdup(CurrentMemoryContext, string); -} - - -/* ==================== ExecWorkFile_Create ==================== */ -/* - * Test that the ExecWorkFile struct is allocated in TopMemoryContext - */ -void -test__ExecWorkFile_Create__InTopMemContext(void **state) -{ - - char *test_filename = "foo"; - - will_return(WorkfileQueryspace_AddWorkfile, true); - - expect_value(BufFileCreateFile, fileName, test_filename); - expect_value(BufFileCreateFile, delOnClose, true); - expect_value(BufFileCreateFile, interXact, false); - will_return(BufFileCreateFile, NULL); - - expect_value(BufFileSetWorkfile, buffile, NULL); - will_be_called(BufFileSetWorkfile); - - /* - * All the memory context stuff is mocked, so the TopMemoryContext is NULL - * at this point. Set it to something specific so we can distinguish it from - * the CurrentMemoryContext. - */ - TopMemoryContext = (MemoryContext) 0xdeadbeef; - CurrentMemoryContext = (MemoryContext) 0xfeadbead; - - /* - * ExecWorkFile_Create will call our mocked palloc0 function execWorkfile__palloc0_mock - * and our mocked pstrdup function execWorkfile_pstrdup_mock. - * These functions will assert that the allocation of the result happens - * in the TopMemoryContext. - */ - ExecWorkFile *ewf = ExecWorkFile_Create(test_filename, BUFFILE, true /* delOnClose */, 0 /* compressType */); - -} - -/* ==================== main ==================== */ -int -main(int argc, char* argv[]) -{ - cmockery_parse_arguments(argc, argv); - - const UnitTest tests[] = { - unit_test(test__ExecWorkFile_Create__InTopMemContext) - }; - - return run_tests(tests); -} http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/aaa2c8cd/src/backend/utils/sort/tuplestorenew.c ---------------------------------------------------------------------- diff --git a/src/backend/utils/sort/tuplestorenew.c b/src/backend/utils/sort/tuplestorenew.c index f88a3e2..7d3cb4a 100644 --- a/src/backend/utils/sort/tuplestorenew.c +++ b/src/backend/utils/sort/tuplestorenew.c @@ -191,6 +191,8 @@ struct NTupleStore List *accessors; /* all current accessors of the store */ bool fwacc; /* if I had already has a write acc */ + MemoryContext mcxt; /* memory context holding this tuplestore, and the page structs */ + /* instrumentation for explain analyze */ Instrumentation *instrument; }; @@ -383,21 +385,9 @@ static void nts_return_free_page(NTupleStore *nts, NTupleStorePage *page) nts->first_free_page = NTS_PREPEND_1(nts->first_free_page, page); } -static inline void *check_malloc(int size) -{ - void *ptr = gp_malloc(size); - if(!ptr) - ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("NTupleStore failed to malloc: out of memory"))); - return ptr; -} - /* Get a free page. Will shrink if necessary. * The page returned still belong to the store (accounted by nts->page_cnt), but it is * not on any list. Caller is responsible to put it back onto a list. - * - * Page is allocated/freed with gcc malloc/free, not palloc/pfree. */ static NTupleStorePage *nts_get_free_page(NTupleStore *nts) { @@ -464,7 +454,7 @@ static NTupleStorePage *nts_get_free_page(NTupleStore *nts) if(nts->page_cnt >= page_max) { - gp_free2(page_next, sizeof(NTupleStorePage)); + pfree(page_next); --nts->page_cnt; } else @@ -486,7 +476,7 @@ static NTupleStorePage *nts_get_free_page(NTupleStore *nts) } Assert(page_next == NULL && nts->page_cnt < page_max); - page = (NTupleStorePage *) check_malloc(sizeof(NTupleStorePage)); + page = (NTupleStorePage *) MemoryContextAlloc(nts->mcxt, sizeof(NTupleStorePage)); init_page(page); ++nts->page_cnt; @@ -614,30 +604,24 @@ static NTupleStorePage *nts_load_prev_page(NTupleStore *store, NTupleStorePage * } } -static void ntuplestore_cleanup(NTupleStore *ts, bool fNormal, bool canReportError) +void +ntuplestore_destroy(NTupleStore *ts) { NTupleStorePage *p = ts->first_page; + ListCell *cell; - /* normal case: for each accessor, we mark it has no owning store. - * This do not need to, actually, cannot be called in error out case, - * because the memory context of ts->accessor has already been - * cleaned up - */ - if(fNormal) + /* For each accessor, we mark it has no owning store. */ + foreach(cell, ts->accessors) { - ListCell *cell; - foreach(cell, ts->accessors) - { - NTupleStoreAccessor *acc = (NTupleStoreAccessor *) lfirst(cell); - acc->store = NULL; - acc->page = NULL; - } + NTupleStoreAccessor *acc = (NTupleStoreAccessor *) lfirst(cell); + acc->store = NULL; + acc->page = NULL; } while(p) { NTupleStorePage *pnext = nts_page_next(p); - gp_free2(p, sizeof(NTupleStorePage)); + pfree(p); p = pnext; } @@ -645,18 +629,18 @@ static void ntuplestore_cleanup(NTupleStore *ts, bool fNormal, bool canReportErr while(p) { NTupleStorePage *pnext = nts_page_next(p); - gp_free2(p, sizeof(NTupleStorePage)); + pfree(p); p = pnext; } if(ts->pfile) { - workfile_mgr_close_file(ts->work_set, ts->pfile, canReportError); + workfile_mgr_close_file(ts->work_set, ts->pfile, false); ts->pfile = NULL; } if(ts->plobfile) { - workfile_mgr_close_file(ts->work_set, ts->plobfile, canReportError); + workfile_mgr_close_file(ts->work_set, ts->plobfile, false); ts->plobfile = NULL; } @@ -666,18 +650,14 @@ static void ntuplestore_cleanup(NTupleStore *ts, bool fNormal, bool canReportErr ts->work_set = NULL; } - gp_free2(ts, sizeof(NTupleStore)); -} - -static void XCallBack_NTS(XactEvent event, void *nts) -{ - ntuplestore_cleanup((NTupleStore *)nts, false, (event!=XACT_EVENT_ABORT)); + pfree(ts); } NTupleStore * ntuplestore_create(int maxBytes) { - NTupleStore *store = (NTupleStore *) check_malloc(sizeof(NTupleStore)); + NTupleStore *store = (NTupleStore *) palloc(sizeof(NTupleStore)); + store->mcxt = CurrentMemoryContext; store->pfile = NULL; store->first_ondisk_blockn = 0; @@ -696,7 +676,7 @@ ntuplestore_create(int maxBytes) if(store->page_max < 16) store->page_max = 16; - store->first_page = (NTupleStorePage *) check_malloc(sizeof(NTupleStorePage)); + store->first_page = (NTupleStorePage *) MemoryContextAlloc(store->mcxt, sizeof(NTupleStorePage)); init_page(store->first_page); nts_page_set_blockn(store->first_page, 0); @@ -715,7 +695,6 @@ ntuplestore_create(int maxBytes) store->instrument = NULL; - RegisterXactCallbackOnce(XCallBack_NTS, (void *) store); return store; } @@ -749,7 +728,8 @@ ntuplestore_create_readerwriter(const char *filename, int maxBytes, bool isWrite } else { - store = (NTupleStore *) check_malloc(sizeof(NTupleStore)); + store = (NTupleStore *) palloc(sizeof(NTupleStore)); + store->mcxt = CurrentMemoryContext; store->work_set = NULL; store->cached_workfiles_found = false; store->cached_workfiles_loaded = false; @@ -764,7 +744,6 @@ ntuplestore_create_readerwriter(const char *filename, int maxBytes, bool isWrite 0 /* compressType */); ntuplestore_init_reader(store, maxBytes); - RegisterXactCallbackOnce(XCallBack_NTS, (void *) store); } return store; } @@ -791,7 +770,7 @@ ntuplestore_init_reader(NTupleStore *store, int maxBytes) if(store->page_max < 16) store->page_max = 16; - store->first_page = (NTupleStorePage *) check_malloc(sizeof(NTupleStorePage)); + store->first_page = (NTupleStorePage *) MemoryContextAlloc(store->mcxt, sizeof(NTupleStorePage)); init_page(store->first_page); bool fOK = ntsReadBlock(store, 0, store->first_page); @@ -923,17 +902,19 @@ ntuplestore_flush(NTupleStore *ts) } } -void -ntuplestore_destroy(NTupleStore *ts) -{ - UnregisterXactCallbackOnce(XCallBack_NTS, (void *) ts); - ntuplestore_cleanup(ts, true, true); -} - NTupleStoreAccessor* ntuplestore_create_accessor(NTupleStore *ts, bool isWriter) { - NTupleStoreAccessor *acc = (NTupleStoreAccessor *) palloc(sizeof(NTupleStoreAccessor)); + NTupleStoreAccessor *acc; + MemoryContext oldcxt; + + /* + * The accessor is allocated in the same memory context as the tuplestore + * itself. + */ + oldcxt = MemoryContextSwitchTo(ts->mcxt); + + acc = (NTupleStoreAccessor *) palloc(sizeof(NTupleStoreAccessor)); acc->store = ts; acc->isWriter = isWriter; @@ -952,6 +933,8 @@ ntuplestore_create_accessor(NTupleStore *ts, bool isWriter) if(isWriter) ts->fwacc = true; + MemoryContextSwitchTo(oldcxt); + return acc; } @@ -1338,6 +1321,7 @@ bool ntuplestore_acc_current_tupleslot(NTupleStoreAccessor *tsa, TupleTableSlot { NTupleStoreLobRef *plobref = (NTupleStoreLobRef *) tuple; Assert(len == -(int)sizeof(NTupleStoreLobRef)); + tuple = (MemTuple) palloc(plobref->size); len = ntuplestore_get_lob(tsa->store, tuple, plobref); @@ -1370,7 +1354,7 @@ bool ntuplestore_acc_current_data(NTupleStoreAccessor *tsa, void **data, int *le { if (tsa->tmp_lob) pfree(tsa->tmp_lob); - tsa->tmp_lob = palloc(plobref->size); + tsa->tmp_lob = MemoryContextAlloc(tsa->store->mcxt, plobref->size); tsa->tmp_len = plobref->size; }