Beispiel #1
0
/*
 * Acquire an entry from the cache. If the cache is full (reached gp_workfile_max_entries),
 * trigger evictions and try again.
 * If the cache remains full after max_retries, give up and error out.
 *
 * populate_param is the parameter to be passed to Cache_AcquireEntry. It
 * will be used to populate the entry before being returned.
 */
static CacheEntry *
acquire_entry_retry(Cache *cache, workset_info *populate_param)
{
	CacheEntry *localEntry = Cache_AcquireEntry(cache, populate_param);

	int crt_retry = 0;
	while (NULL == localEntry && crt_retry < MAX_EVICT_ATTEMPTS)
	{
		/*
		 * We reached maximum number of entries in the cache. Evict something
		 * then try again.
		 */
		int64 size_evicted = workfile_mgr_evict(MIN_EVICT_SIZE);
		elog(gp_workfile_caching_loglevel, "Hit cache entries full, evicted " INT64_FORMAT " bytes", size_evicted);

		localEntry = Cache_AcquireEntry(cache, populate_param);
		crt_retry++;
	}

	if (NULL == localEntry)
	{
		/* Could not acquire another entry from the cache - we filled it up */
		elog(ERROR, "could not create workfile manager entry: exceeded number of concurrent spilling queries");
	}

	return localEntry;
}
Beispiel #2
0
/*
 * Create a new file set
 *   type is the WorkFileType for the files: BUFFILE or BFZ
 *   can_be_reused: if set to false, then we don't insert this set into the cache,
 *     since the caller is telling us there is no point. This can happen for
 *     example when spilling during index creation.
 *   ps is the PlanState for the subtree rooted at the operator
 *   snapshot contains snapshot information for the current transaction
 *
 */
workfile_set *
workfile_mgr_create_set(enum ExecWorkFileType type, bool can_be_reused, PlanState *ps)
{
	Assert(NULL != workfile_mgr_cache);

	Plan *plan = NULL;
	if (ps != NULL)
	{
		plan = ps->plan;
	}

	AssertImply(can_be_reused, plan != NULL);

	NodeTag node_type = T_Invalid;
	if (ps != NULL)
	{
		node_type = ps->type;
	}
	char *dir_path = create_workset_directory(node_type, currentSliceId);


	if (!workfile_sets_resowner_callback_registered)
	{
		RegisterResourceReleaseCallback(workfile_set_free_callback, NULL);
		workfile_sets_resowner_callback_registered = true;
	}

	/* Create parameter info for the populate function */
	workset_info set_info;
	set_info.file_type = type;
	set_info.nodeType = node_type;
	set_info.dir_path = dir_path;
	set_info.session_start_time = GetCurrentTimestamp();
	set_info.operator_work_mem = get_operator_work_mem(ps);

	CacheEntry *newEntry = Cache_AcquireEntry(workfile_mgr_cache, &set_info);

	if (NULL == newEntry)
	{
		/* Clean up the directory we created. */
		workfile_mgr_delete_set_directory(dir_path);

		/* Could not acquire another entry from the cache - we filled it up */
		ereport(ERROR,
				(errmsg("could not create workfile manager entry: exceeded number of concurrent spilling queries")));
	}

	/* Path has now been copied to the workfile_set. We can free it */
	pfree(dir_path);

	/* Complete initialization of the entry with post-acquire actions */
	Assert(NULL != newEntry);
	workfile_set *work_set = CACHE_ENTRY_PAYLOAD(newEntry);
	Assert(work_set != NULL);

	elog(gp_workfile_caching_loglevel, "new spill file set. key=0x%x prefix=%s opMemKB=" INT64_FORMAT,
			work_set->key, work_set->path, work_set->metadata.operator_work_mem);

	return work_set;
}
/*
 * Add or update an entry in the Global MDVSN cache for a versioning event
 * found in the event list. Reconcile with current contents of the cache
 * if needed.
 *  event: The event containing the versioning information for an update
 */
static void
mdver_globalhandler_add_version(mdver_event *event)
{
    Assert(NULL != event);

    Cache *glob_mdvsn = mdver_get_glob_mdvsn();

    if (mdver_is_nuke_event(event))
    {
        mdver_glob_mdvsn_nuke();
        return;
    }

    mdver_entry mdver = { InvalidOid, INVALID_MD_VERSION, INVALID_MD_VERSION };
    mdver.key = event->key;
    mdver.ddl_version = INVALID_MD_VERSION;
    mdver.dml_version = INVALID_MD_VERSION;

    /* FIXME gcaragea 04/14/2014: Trigger evictions if cache is full (MPP-22923) */
    CacheEntry *acquired_entry = Cache_AcquireEntry(glob_mdvsn, &mdver);
    Assert(NULL != acquired_entry);

    /*
     * We're about to look-up and insert/update a shared cache entry.
     * Grab writer lock in exclusive mode, so that no other backend
     * tries to insert or update the same entry at the same time.
     */
    LWLockAcquire(MDVerWriteLock, LW_EXCLUSIVE);

    CacheEntry *cached_entry = Cache_Lookup(glob_mdvsn, acquired_entry);

    if (NULL != cached_entry)
    {
        mdver_globalhandler_reconcile(event, cached_entry);

        /* Done with the looked-up entry. Release it */
        Cache_Release(glob_mdvsn, cached_entry);
    }
    else
    {
        /* Entry not found, insert new entry */
        mdver_entry *new_mdver_entry = CACHE_ENTRY_PAYLOAD(acquired_entry);

#ifdef MD_VERSIONING_INSTRUMENTATION
        elog(gp_mdversioning_loglevel, "Inserting into GlobalMDVSN entry %d: (%d,%d)",
             event->key,
             (int) event->new_ddl_version, (int) event->new_dml_version);
#endif

        new_mdver_entry->ddl_version = event->new_ddl_version;
        new_mdver_entry->dml_version = event->new_dml_version;

        Cache_Insert(glob_mdvsn, acquired_entry);
    }

    Cache_Release(glob_mdvsn, acquired_entry);
    LWLockRelease(MDVerWriteLock);
}
/*
 * Look up an entry in the Global MDVSN component.
 * To avoid any concurrency issues, this returns a copy of the entry,
 * palloc'ed in the current memory context. The caller is responsible
 * for freeing this copy.
 *
 * 	 Returns a copy of the entry if found, NULL otherwise.
 *
 */
mdver_entry *
mdver_glob_mdvsn_find(Oid oid)
{

	Assert(NULL != mdver_glob_mdvsn);

	mdver_entry mdver_info;
	mdver_info.key = oid;

	/* FIXME gcaragea 03/18/2014: Trigger evictions if cache is full (MPP-22923) */
	CacheEntry *localEntry = Cache_AcquireEntry(mdver_glob_mdvsn, &mdver_info);
	Assert(NULL != localEntry);

	CacheEntry *cachedEntry = Cache_Lookup(mdver_glob_mdvsn, localEntry);

	/* Release local entry. We don't need it anymore */
	Cache_Release(mdver_glob_mdvsn, localEntry);

	mdver_entry *mdver_copy = NULL;
	if (NULL != cachedEntry)
	{
		/* Found a match. Make a local copy */
		mdver_entry *shared_mdver = (mdver_entry *) CACHE_ENTRY_PAYLOAD(cachedEntry);
		mdver_copy = (mdver_entry *) palloc0(sizeof(mdver_entry));

		/* Lock entry to ensure atomicity of copy */
		Cache_LockEntry(mdver_glob_mdvsn, cachedEntry);

		memcpy(mdver_copy, shared_mdver, sizeof(mdver_entry));

		/* Got the copy, unlock entry */
		Cache_UnlockEntry(mdver_glob_mdvsn, cachedEntry);

		/*
		 * We're also done with the entry, release our pincount on it
		 *
		 * TODO gcaragea 05/02/2014: Are there cases where we need to hold the
		 * entry past this point? (MPP-22923)
		 */
		Cache_Release(mdver_glob_mdvsn, cachedEntry);
	}

	return mdver_copy;
}
Beispiel #5
0
/*
 * When a backend is requesting the more recent version of an object,
 * if the Local MDVSN cache doesn't have the version, and if a NUKE event
 * hasn't been encountered in the current transaction, it is looked up
 * in the Global MDVSN shared cache.
 *
 * If the object is found in Global MDVSN, return the global version.
 * If the object is not found, generate a new version, record it in Global MDVSN
 * and then return it.
 *
 *   key: The key of the looked-up object
 *   ddl_version: used to return the ddl version for the object
 *   dml_version: used to return the dml version for the object
 *
 */
static void
mdver_request_from_global(Oid key, uint64 *ddl_version, uint64 *dml_version)
{

	Assert(NULL != ddl_version);
	Assert(NULL != dml_version);

	Cache *mdver_glob_mdvsn = mdver_get_glob_mdvsn();
	Assert(NULL != mdver_glob_mdvsn);

	mdver_entry entry = {key, INVALID_MD_VERSION, INVALID_MD_VERSION};

	/* FIXME gcaragea 06/03/2014: Trigger evictions if cache is full (MPP-22923) */
	CacheEntry *localEntry = Cache_AcquireEntry(mdver_glob_mdvsn, &entry);

	Assert(NULL != localEntry);

	/*
	 * We're about to look-up and insert a shared cache entry.
	 * Grab writer lock in exclusive mode, so that no other backend
	 * can insert or update the same entry at the same time.
	 */
	LWLockAcquire(MDVerWriteLock, LW_EXCLUSIVE);

	CacheEntry *cachedEntry = Cache_Lookup(mdver_glob_mdvsn, localEntry);

	if (NULL != cachedEntry)
	{
		/* Not found in LVSN, not nuke happened, eventually found in GVSN */
		mdver_entry *crt_entry = CACHE_ENTRY_PAYLOAD(cachedEntry);

		*ddl_version = crt_entry->ddl_version;
		*dml_version = crt_entry->dml_version;

#ifdef MD_VERSIONING_INSTRUMENTATION
		elog(gp_mdversioning_loglevel, "Found version in Global MDVSN: (%d, " UINT64_FORMAT ", " UINT64_FORMAT "). Adding it to Local MDVSN",
				key, crt_entry->ddl_version, crt_entry->dml_version);
#endif

		/*
		 * We're also done with the entry, release our pincount on it
		 *
		 * TODO gcaragea 05/02/2014: Are there cases where we need to hold the
		 * entry past this point? (MPP-22923)
		 */

		Cache_Release(mdver_glob_mdvsn, cachedEntry);
	}
	else
	{
		/* Not found in LVSN, not nuke happened, not found in GVSN either */

		/* Generate new version */
		*ddl_version = mdver_next_global_version();
		*dml_version = mdver_next_global_version();

		/* Add to GVSN */
		mdver_entry *new_entry = CACHE_ENTRY_PAYLOAD(localEntry);
		new_entry->ddl_version = *ddl_version;
		new_entry->dml_version = *dml_version;

#ifdef MD_VERSIONING_INSTRUMENTATION
		elog(gp_mdversioning_loglevel, "Inserting new version in Global MDVSN: (%d, " UINT64_FORMAT ", " UINT64_FORMAT "). Adding it to Local MDVSN",
				key, new_entry->ddl_version, new_entry->dml_version);
#endif

		Cache_Insert(mdver_glob_mdvsn, localEntry);

	}

	LWLockRelease(MDVerWriteLock);

	/* Release local entry. We don't need it anymore */
	Cache_Release(mdver_glob_mdvsn, localEntry);
}