Skip to content

project3 : Buffer Manager

Sonny edited this page Feb 25, 2022 · 1 revision

Data Structure in Buffer Manager

1. Control Block

typedef struct Control
{
	page_t* frame;
	int64_t table_id;
	pagenum_t page_num;
	bool is_dirty;
	bool is_pinned;
	struct Control* next;
	struct Control* prev;

} Control;

The Control block has its own frame pointer and metadata about that frame.

2. Frame Block

page_t* frame_block;
frame_block = (page_t*)malloc(sizeof(page_t) * num_buf);

Each Frame block has page from disk manager and they are controlled by each control block.

3. LRU list

typedef struct
{
	Control* head;
	Control* tail;
} LRU_list;

The LRU list is used for replacement. The head pointer points to MRU frame and the tail pointer points to LRU frame.

4. Hash Table

Bucket** hash_table;
typedef struct Bucket
{
	Control* control;
	struct Bucket* link;
} Bucket;

The hash table is used for fast search in buffer pool. It uses page number as key. Each bucket has control pointer and next bucket link.

Implementation of Buffer Manager

0. Overview

  • Each control block has a frame pointer for ease of frame management.

  • The number of buffer frames must be at least four. This is because the index manager requires an internal page (self), a neighbor page, a parent page, and a child page when proceeding with the internal page's merge or redistribute. In addition, insert or deletion can proceed recursively.

  • In order to eliminate the difference between the header page in the buffer pool and the header page on the disk when the page is allocated and freed, I flushed immediately if it is a dirty header page in the buffer pool. And if there is a change in the header page on the disk in the buff_alloc_page() or buff_free_page(), the contents were matched through file_read_page() if there was a header page in the current buffer pool.

1. buf_init_db()

int buf_init_db(int num_buf)
{
	if ( num_buf < 4 ) return -1;

	// Buffer pool initialize.
	control_block = (Control*)malloc(sizeof(Control) * num_buf);
	if ( control_block == NULL ) return -1;

	frame_block = (page_t*)malloc(sizeof(page_t) * num_buf);
	if ( frame_block == NULL ) return -1;

	for( int i = 0; i < num_buf; i++ )
	{
		control_block[i].frame = &frame_block[i];
		control_block[i].table_id = -1;
		control_block[i].page_num = 0;
		control_block[i].is_dirty = false;
		control_block[i].is_pinned = false;
		control_block[i].next = NULL;
		control_block[i].prev = NULL;
	}
	
	// Hash table initialize
	init_hash_table();

	// LRU list initialize
	init_lru_list(num_buf);
	return 0;
}

This function is used to initialize control block and frame block, LRU list, and hash table. If num_buf is less than 4, return -1.

2. buf_read_page()

void buf_read_page(int64_t table_id, pagenum_t page_num, page_t* dest)
{
	Control* control = search_hash_table(table_id, page_num);

	// cache miss
	if ( control == NULL )
	{
		control = lru_list.tail;
		// if all frames are pinned, device another way.
		while(control->is_pinned)
			control = control->prev;
		
		del_hash_table(control);

		if (control->is_dirty)
			file_write_page(control->table_id, control->page_num, control->frame);
		

		file_read_page(table_id, page_num, control->frame);
		control->table_id = table_id;
		control->page_num = page_num;
		control->is_dirty = false;
		control->is_pinned = true;
	
		add_hash_table(control);
	}

	control->is_pinned = true;

	update_lru_list(control);
	memcpy(dest, control->frame, PAGE_SIZE);
}

This function first finds pages corresponding to table_id and page number in the current buffer pool through the hash table when a page read request is received from index manager. If there is, put the pin and send the frame to the front of the lru_list and send over the contents of the frame to index manager.
If there is no page corresponding to the hash table, find the victim without pin from the tail of lru_list to head and delete it from the hash table. If vitim is dirty, write down the dirty content on the disk through file_write_page(), and read the page through new file_read_page(), pin it, and hand over the frame content to the index manager.

3. buf_pin_out()

void buf_pin_out(int64_t table_id, pagenum_t page_num)
{
	Control* control = search_hash_table(table_id, page_num);
	if ( control != NULL ) control->is_pinned = false;
}

This function is used for pulling out the pin in the page in index manager that is called only for reading data not updating.

4. buf_write_page()

void buf_write_page(int64_t table_id, pagenum_t page_num, page_t* src)
{
	Control* control = search_hash_table(table_id, page_num);

	control->is_dirty = true;
	control->is_pinned = false;
	memcpy(control->frame, src, PAGE_SIZE);

	update_lru_list(control);

	// fflush header page
	if ( page_num == 0 )
	{
		file_write_page(control->table_id, 0, control->frame);	
		control->is_dirty = false;
	}
}

This function is called by index manager to change the content of the frame. First, find the control block corresponding to the frame in the hash table, mark dirty, remove the pin, and write the contents imported by the index manager to the frame indicated by the control block. And send the control block to the head of the lru list. If the header page is written, do a flush immediately.

5. buf_alloc_page()

pagenum_t buf_alloc_page(int64_t table_id)
{
	// find lru page
	Control* control = lru_list.tail;
	while(control->is_pinned)
       	control = control->prev;

   	del_hash_table(control);

	// fflush
   	if (control->is_dirty)
    	file_write_page(control->table_id, control->page_num, control->frame);

	pagenum_t new_page_num = file_alloc_page(table_id);
	
	Control* header_control = search_hash_table(table_id, 0);
	if ( header_control != NULL )
		file_read_page(table_id, 0, header_control->frame);


	memset(control->frame, 0x00, PAGE_SIZE);
	control->table_id = table_id;
	control->page_num = new_page_num;
	control->is_dirty = false;
    control->is_pinned = true;

 	add_hash_table(control);
     
	update_lru_list(control);

	return new_page_num;
}

This function finds a frame that is not pinned from the tail of the lru list, evicts it, and calls file_alloc_page() to get a free page to that frame. If the page is a dirty page, do flush first and evict. If there is a header page in the current buffer pool, match the contents of the header page on the disk. And update the control block and add it to the hash table, update the lru list, and return the corresponding page number.

6. buf_free_page()

void buf_free_page(int64_t table_id, pagenum_t page_num)
{
	Control* control = search_hash_table(table_id, page_num);
	
	del_hash_table(control);

	memset(control->frame, 0x00, PAGE_SIZE);
	control->table_id = -1;
	control->page_num = 0;
	control->is_dirty = false;
	control->is_pinned = false;
	
	update_lru_list(control);

	file_free_page(table_id, page_num);

	Control* header_control = search_hash_table(table_id, 0);
	if ( header_control != NULL )
		file_read_page(table_id, 0, control->frame);

}

This function first finds the control block corresponding to table_id and page number in the hash table and erases it from the hash table. And initialize the control block and its frame pointer. And update lru_list and free the page through file_free_page(). If there is a header page in the current buffer pool, match the contents with the header page on the disk through file_read_page().

7. buf_shutdown_db()

int buf_shutdown_db()
{
	Control* temp = lru_list.head;
	while( temp )
	{
		if ( temp->is_dirty ) 
			file_write_page(temp->table_id, temp->page_num, temp->frame);
		
		temp = temp->next;
	}

	free_hash_table();
	
	free(control_block);
	free(frame_block);
	return 0;
}

This function is used to flush all currently dirty pages and release dynamic allocation of hashtable, control block, and frame block. Returns 0 when free is normally completed.

Changed functions in Index Manager.

1. init_db()

int init_db(int num_buf)
{
	header_page = (Header_page*)malloc(sizeof(Header_page));
	if ( header_page == NULL ) return -1;
	if ( TABLE_CNT >= 20 ) return -1;
	return buf_init_db(num_buf);
}

buf_init_db() is added.

2. shutdown_db()

int shutdown_db()
{
	free(header_page);
	int ret = buf_shutdown_db();
	file_close_table_files();
	return ret;
}

buf_shutdown_db() is added.

ETC

Each function now calls the buffer manager's functions without calling the disk manager's functions directly.