Esempio n. 1
0
/*
 * RecordPageWithFreeSpace - update info about a page.
 *
 * Note that if the new___ spaceAvail value is higher than the old value stored
 * in the FSM, the space might not become visible to searchers until the next
 * FreeSpaceMapVacuum call, which updates the upper level pages.
 */
void
RecordPageWithFreeSpace(Relation rel, BlockNumber heapBlk, Size spaceAvail)
{
	int			new_cat = fsm_space_avail_to_cat(spaceAvail);
	FSMAddress	addr;
	uint16		slot;

	/* Get the location of the FSM byte representing the heap block */
	addr = fsm_get_location(heapBlk, &slot);

	fsm_set_and_search(rel, addr, slot, new_cat, 0);
}
Esempio n. 2
0
/*
 * RecordAndGetPageWithFreeSpace - update info about a page and try again.
 *
 * We provide this combo form to save some locking overhead, compared to
 * separate RecordPageWithFreeSpace + GetPageWithFreeSpace calls. There's
 * also some effort to return a page close to the old page; if there's a
 * page with enough free space on the same FSM page where the old one page
 * is located, it is preferred.
 *
 * For very small heap relations that don't have a FSM, we update the local
 * map to indicate we have tried a page, and return the next page to try.
 */
BlockNumber
RecordAndGetPageWithFreeSpace(Relation rel, BlockNumber oldPage,
							  Size oldSpaceAvail, Size spaceNeeded)
{
	int			old_cat;
	int			search_cat;
	FSMAddress	addr;
	uint16		slot;
	int			search_slot;
	BlockNumber nblocks = InvalidBlockNumber;

	/* First try the local map, if it exists. */
	if (FSM_LOCAL_MAP_EXISTS)
	{
		Assert((rel->rd_rel->relkind == RELKIND_RELATION ||
				rel->rd_rel->relkind == RELKIND_TOASTVALUE) &&
			   fsm_local_map.map[oldPage] == FSM_LOCAL_AVAIL);

		fsm_local_map.map[oldPage] = FSM_LOCAL_NOT_AVAIL;
		return fsm_local_search();
	}

	if (!fsm_allow_writes(rel, oldPage, InvalidBlockNumber, &nblocks))
	{
		/*
		 * If we have neither a local map nor a FSM, we probably just tried
		 * the target block in the smgr relation entry and failed, so we'll
		 * need to create the local map.
		 */
		fsm_local_set(rel, nblocks);
		return fsm_local_search();
	}

	/* Normal FSM logic follows */

	old_cat = fsm_space_avail_to_cat(oldSpaceAvail);
	search_cat = fsm_space_needed_to_cat(spaceNeeded);

	/* Get the location of the FSM byte representing the heap block */
	addr = fsm_get_location(oldPage, &slot);

	search_slot = fsm_set_and_search(rel, addr, slot, old_cat, search_cat);

	/*
	 * If fsm_set_and_search found a suitable new block, return that.
	 * Otherwise, search as usual.
	 */
	if (search_slot != -1)
		return fsm_get_heap_blk(addr, search_slot);
	else
		return fsm_search(rel, search_cat);
}
Esempio n. 3
0
/*
 * Recursively update the FSM tree from given address to
 * all the way up to root.
 */
static void
fsm_update_recursive(Relation rel, FSMAddress addr, uint8 new_cat)
{
	uint16		parentslot;
	FSMAddress	parent;

	if (addr.level == FSM_ROOT_LEVEL)
		return;

	/*
	 * Get the parent page and our slot in the parent page, and
	 * update the information in that.
	 */
	parent = fsm_get_parent(addr, &parentslot);
	fsm_set_and_search(rel, parent, parentslot, new_cat, 0);
	fsm_update_recursive(rel, parent, new_cat);
}
Esempio n. 4
0
/*
 * RecordPageWithFreeSpace - update info about a page.
 *
 * Note that if the new spaceAvail value is higher than the old value stored
 * in the FSM, the space might not become visible to searchers until the next
 * FreeSpaceMapVacuum call, which updates the upper level pages.
 *
 * Callers have no need for a local map.
 */
void
RecordPageWithFreeSpace(Relation rel, BlockNumber heapBlk,
						Size spaceAvail, BlockNumber nblocks)
{
	int			new_cat;
	FSMAddress	addr;
	uint16		slot;
	BlockNumber dummy;

	if (!fsm_allow_writes(rel, heapBlk, nblocks, &dummy))
		/* No FSM to update and no local map either */
		return;

	/* Get the location of the FSM byte representing the heap block */
	addr = fsm_get_location(heapBlk, &slot);

	new_cat = fsm_space_avail_to_cat(spaceAvail);
	fsm_set_and_search(rel, addr, slot, new_cat, 0);
}
Esempio n. 5
0
/*
 * RecordAndGetPageWithFreeSpace - update info about a page and try again.
 *
 * We provide this combo form to save some locking overhead, compared to
 * separate RecordPageWithFreeSpace + GetPageWithFreeSpace calls. There's
 * also some effort to return a page close to the old page; if there's a
 * page with enough free space on the same FSM page where the old one page
 * is located, it is preferred.
 */
BlockNumber
RecordAndGetPageWithFreeSpace(Relation rel, BlockNumber oldPage,
							  Size oldSpaceAvail, Size spaceNeeded)
{
	int			old_cat = fsm_space_avail_to_cat(oldSpaceAvail);
	int			search_cat = fsm_space_needed_to_cat(spaceNeeded);
	FSMAddress	addr;
	uint16		slot;
	int			search_slot;

	/* Get the location of the FSM byte representing the heap block */
	addr = fsm_get_location(oldPage, &slot);

	search_slot = fsm_set_and_search(rel, addr, slot, old_cat, search_cat);

	/*
	 * If fsm_set_and_search found a suitable new___ block, return that.
	 * Otherwise, search as usual.
	 */
	if (search_slot != -1)
		return fsm_get_heap_blk(addr, search_slot);
	else
		return fsm_search(rel, search_cat);
}
Esempio n. 6
0
/*
 * Search the tree for a heap page with at least min_cat of free space
 */
static BlockNumber
fsm_search(Relation rel, uint8 min_cat)
{
	int			restarts = 0;
	FSMAddress	addr = FSM_ROOT_ADDRESS;

	for (;;)
	{
		int			slot;
		Buffer		buf;
		uint8		max_avail = 0;

		/* Read the FSM page. */
		buf = fsm_readbuf(rel, addr, false);

		/* Search within the page */
		if (BufferIsValid(buf))
		{
			LockBuffer(buf, BUFFER_LOCK_SHARE);
			slot = fsm_search_avail(buf, min_cat,
									(addr.level == FSM_BOTTOM_LEVEL),
									false);
			if (slot == -1)
				max_avail = fsm_get_max_avail(BufferGetPage(buf));
			UnlockReleaseBuffer(buf);
		}
		else
			slot = -1;

		if (slot != -1)
		{
			/*
			 * Descend the tree, or return the found block if we're at the
			 * bottom.
			 */
			if (addr.level == FSM_BOTTOM_LEVEL)
				return fsm_get_heap_blk(addr, slot);

			addr = fsm_get_child(addr, slot);
		}
		else if (addr.level == FSM_ROOT_LEVEL)
		{
			/*
			 * At the root, failure means there's no page with enough free
			 * space in the FSM. Give up.
			 */
			return InvalidBlockNumber;
		}
		else
		{
			uint16		parentslot;
			FSMAddress	parent;

			/*
			 * At lower level, failure can happen if the value in the upper-
			 * level node didn't reflect the value on the lower page. Update
			 * the upper node, to avoid falling into the same trap again, and
			 * start over.
			 *
			 * There's a race condition here, if another backend updates this
			 * page right after we release it, and gets the lock on the parent
			 * page before us. We'll then update the parent page with the now
			 * stale information we had. It's OK, because it should happen
			 * rarely, and will be fixed by the next vacuum.
			 */
			parent = fsm_get_parent(addr, &parentslot);
			fsm_set_and_search(rel, parent, parentslot, max_avail, 0);

			/*
			 * If the upper pages are badly out of date, we might need to loop
			 * quite a few times, updating them as we go. Any inconsistencies
			 * should eventually be corrected and the loop should end. Looping
			 * indefinitely is nevertheless scary, so provide an emergency
			 * valve.
			 */
			if (restarts++ > 10000)
				return InvalidBlockNumber;

			/* Start search all over from the root */
			addr = FSM_ROOT_ADDRESS;
		}
	}
}