Skip to content

Latest commit

 

History

History
604 lines (490 loc) · 17.3 KB

README.md

File metadata and controls

604 lines (490 loc) · 17.3 KB

11. Disk Manager (5th Week)



1. 구조체

구조체
struct disk_cache
{
  int nvols_perm;                               /* number of permanent type volumes */
  int nvols_temp;                               /* number of temporary type volumes */DISK_CACHE_VOLINFO vols[LOG_MAX_DBVOLID + 1]; /* volume info array */

  DISK_PERM_PURPOSE_INFO perm_purpose_info; /* info for permanent purpose */
  DISK_TEMP_PURPOSE_INFO temp_purpose_info; /* info for temporary purpose */

  pthread_mutex_t mutex_extend; /* note: never get expand mutex while keeping reserve mutexes */
#if !defined(NDEBUG)
  volatile int owner_extend;
#endif /* !NDEBUG */
};
struct disk_stab_cursor
{
  const DISK_VOLUME_HEADER *volheader; /* Volume header */

  PAGEID pageid;      /* Current page ID */
  int offset_to_unit; /* Offset to current unit in page. */
  int offset_to_bit;  /* Offset to current bit in unit. */

  SECTID sectid; /* Sector ID */

  PAGE_PTR page;        /* Fixed table page. */
  DISK_STAB_UNIT *unit; /* Unit pointer in current page. */
};
struct vsid
{
  int32_t sectid;	/* Sector identifier */
  short volid;	/* Volume identifier where the sector resides */
};
struct disk_cache_vol_reserve
{
  VOLID volid; // Volume identifier
  DKNSECTS nsect; // 예약할 섹터 수
};
struct disk_reserve_context
{
  int nsect_total; //예약 요청된 섹터 수VSID *vsidp; // 섹터의 배열, 예약과정에서 산출된 최종 섹터들의 위치DISK_CACHE_VOL_RESERVE cache_vol_reserve[VOLID_MAX]; // 볼륨별 사전예약 섹터 수의 배열
  int n_cache_vol_reserve; // 사전예약한 섹터들이 포함된 볼륨들의 수, cache_vol_reserve의 길이
  int n_cache_reserve_remaining; // 아직 사전예약 되지 못한 섹터들의 수, nsect_total - n_cache_vol_reserve = nsect_reserveDKNSECTS nsects_lastvol_remaining; //실제 예약 처리시에 해당 볼륨에서 남은 섹터 예약량

  DB_VOLPURPOSE purpose; // 예약 목적
};


2. 함수 구조

disk_reserve_sectors ()
disk_reserve_sectors ()
│
├── disk_reserve_from_cache ()
│   ├── disk_reserve_from_cache_vols ()
│   │	└── disk_reserve_from_cache_volume ()
│   │
│   └── disk_extend ()
│		├── disk_volume_expand ()
│		├── disk_reserve_from_cache_volume ()
│		├── disk_add_volume ()
│		└── disk_reserve_from_cache_volume ()
│
└── 🤔 disk_reserve_sectors_in_volume () 🤔
disk_reserve_sectors_in_volume ()
│
├── disk_get_volheader ()
├── disk_log ()
└── disk_stab_iterator_unit ()
	└── disk_stab_unit_reserve ()


3. 함수 분석

1. initial
/*
 * disk_reserve_sectors_in_volume () - Reserve a number of sectors in the given volume.
 *
 * return	    : Error code.
 * thread_p (in)    : Thread entry.
 * vol_index (in)   : The index of volume in reserve context
 * context (in/out) : Reserve context
 */

static int
disk_reserve_sectors_in_volume(THREAD_ENTRY *thread_p, int vol_index, DISK_RESERVE_CONTEXT *context)
{
  VOLID volid; // volume id
  PAGE_PTR page_volheader = NULL; // Volume header page
  DISK_VOLUME_HEADER *volheader = NULL; // Volume header
  DISK_STAB_CURSOR start_cursor = DISK_STAB_CURSOR_INITIALIZER; // {NULL, 0, 0, 0, 0, NULL, NULL}
  DISK_STAB_CURSOR end_cursor = DISK_STAB_CURSOR_INITIALIZER; // {NULL, 0, 0, 0, 0, NULL, NULL}
  int error_code = NO_ERROR;

  volid = context->cache_vol_reserve[vol_index].volid; // volume id
  if (volid == NULL_VOLID)
  {
    assert_release(false);
    return ER_FAILED;
  }
    // number of sectors to reserve
  context->nsects_lastvol_remaining = context->cache_vol_reserve[vol_index].nsect;
  assert(context->nsects_lastvol_remaining > 0);

  //... ellipsis
2. disk_get_volheader (), disk_log ()
	//... ellipsis

  /* fix volume header */
  error_code = disk_get_volheader(thread_p, volid, PGBUF_LATCH_WRITE, &page_volheader, &volheader);
  if (error_code != NO_ERROR)
  {
    ASSERT_ERROR();
    return error_code;
  }
	// disk_log ()
  disk_log("disk_reserve_sectors_in_volume", "reserve %d sectors in volume %d.", context->nsects_lastvol_remaining,
           volid);

	//... ellipsis
disk_get_volheader_internal ()
/*
 * disk_get_volheader_internal () - get volume header page and header
 *
 * return                   : error code
 * thread_p (in)            : thread entry
 * volid (in)               : volume id
 * latch_mode (in)          : latch mode for volume header page
 * page_volheader_out (out) : output volume header page
 * volheader_out (out)      : output volume header
 * file (in)                : (debug only) caller file
 * line (in)                : (debug only) caller line
 */
STATIC_INLINE int
disk_get_volheader_internal(THREAD_ENTRY *thread_p, VOLID volid, PGBUF_LATCH_MODE latch_mode,
                            PAGE_PTR *page_volheader_out, DISK_VOLUME_HEADER **volheader_out
#if !defined(NDEBUG)
                            ,
                            const char *file, int line
#endif /* !NDEBUG */
)
{
  VPID vpid_volheader;
  int error_code = NO_ERROR;

  vpid_volheader.volid = volid;
  vpid_volheader.pageid = DISK_VOLHEADER_PAGE;

  *page_volheader_out =pgbuf_fix(thread_p, &vpid_volheader, OLD_PAGE, latch_mode, PGBUF_UNCONDITIONAL_LATCH);
  if (*page_volheader_out == NULL)
  {
    ASSERT_ERROR_AND_SET(error_code);
    return error_code;
  }

  disk_verify_volume_header(thread_p, *page_volheader_out);
  *volheader_out = (DISK_VOLUME_HEADER *)(*page_volheader_out);

  return NO_ERROR;
}
3. reserve all possible sectors. (✅ disk_stab_iterate_units () )
  • if have hint
    // ... ellipsis
    
      /* reserve all possible sectors. */
      if (volheader->hint_allocsect > 0 && volheader->hint_allocsect < volheader->nsect_total)
      {
        /* start with hinted sector */
        DISK_SECTS_ASSERT_ROUNDED(volheader->hint_allocsect);
        disk_stab_cursor_set_at_sectid(volheader, ✅ volheader->hint_allocsect, &start_cursor); // at hint
        disk_stab_cursor_set_at_end(volheader, &end_cursor);
    
        /* reserve sectors after hint */              // ...0000000111✅00000111... 8 free sectors
        error_code = disk_stab_iterate_units(thread_p, volheader, PGBUF_LATCH_WRITE, &start_cursor, &end_cursor,
                                            disk_stab_unit_reserve, context);
        if (error_code != NO_ERROR)
        {
          ASSERT_ERROR();
          goto exit;
        }
        if (context->nsects_lastvol_remaining > 0) // 가용섹터가 힌트 앞에 있을 경우.
        {
          /* we need to reserve more. reserve sectors before hint */
          end_cursor = start_cursor;
          ✅ disk_stab_cursor_set_at_start(volheader, &start_cursor); // 처음부터 이터레이팅
          error_code = disk_stab_iterate_units(thread_p, volheader, PGBUF_LATCH_WRITE, &start_cursor, &end_cursor,
                                              disk_stab_unit_reserve, context);
          if (error_code != NO_ERROR)
          {
            ASSERT_ERROR();
            goto exit;
          }
        }
      }
  • if not
    else // 처음부터 이터레이팅
    {
      /* search the entire sector table */
      disk_stab_cursor_set_at_start(volheader, &start_cursor);
      disk_stab_cursor_set_at_end(volheader, &end_cursor);
      error_code = disk_stab_iterate_units(thread_p, volheader, PGBUF_LATCH_WRITE, &start_cursor, &end_cursor,
                                           disk_stab_unit_reserve, context);
      if (error_code != NO_ERROR)
      {
        ASSERT_ERROR();
        goto exit;
      }
    }
    
    // ... ellipsis
  • disk_stab_iterate_units ()
    /*
    * disk_stab_iterate_units () - iterate through units between start and end and call argument function. start and end
    *                              cursor should be aligned.
    *
    * return           : error code
    * thread_p (in)    : thread entry
    * volheader (in)   : volume header
    * mode (in)        : page latch mode
    * start (in)       : start cursor
    * end (in)         : end cursor
    * f_unit (in)      : function called on each unit
    * f_unit_args (in) : argument for unit function
    */
    static int
    disk_stab_iterate_units(THREAD_ENTRY *thread_p, const DISK_VOLUME_HEADER *volheader, PGBUF_LATCH_MODE mode,
                            DISK_STAB_CURSOR *start, DISK_STAB_CURSOR *end, DISK_STAB_UNIT_FUNC f_unit,
                            void *f_unit_args)
    {
      DISK_STAB_CURSOR cursor;
      DISK_STAB_UNIT *end_unit;
      bool stop = false;
      int error_code = NO_ERROR;
    
      assert(volheader != NULL);
      assert(start->offset_to_bit == 0);
      assert(end->offset_to_bit == 0);
      assert(disk_stab_cursor_compare(start, end) < 0);
    
      /* iterate through pages */
      for (cursor = *start; cursor.pageid <= end->pageid; cursor.pageid++, cursor.offset_to_unit = 0)
      {
        assert(cursor.page == NULL);
        disk_stab_cursor_check_valid(&cursor);
    
        error_code = disk_stab_cursor_fix(thread_p, &cursor, mode);
        if (error_code != NO_ERROR)
        {
          ASSERT_ERROR();
          return error_code;
        }
    
        /* iterate through units */
    
        /* set end_unit */
        end_unit = ((DISK_STAB_UNIT *)cursor.page) + (cursor.pageid == end->pageid ? end->offset_to_unit : DISK_STAB_PAGE_UNITS_COUNT);
    
        /* iterate */
        for (; cursor.unit < end_unit;
            cursor.unit++, cursor.offset_to_unit++, ✅ cursor.sectid += (DISK_STAB_UNIT_BIT_COUNT - cursor.offset_to_bit),
            cursor.offset_to_bit = 0) // unit 단위로 이터레이팅 하려고 안에서 오프셋을 건드려도 일정하게 해주려고
        {
          disk_stab_cursor_check_valid(&cursor);
    
          /* call unit function */
          error_code =f_unit(thread_p, &cursor, &stop, f_unit_args);
          if (error_code != NO_ERROR)
          {
            ASSERT_ERROR();
            disk_stab_cursor_unfix(thread_p, &cursor);
            return error_code;
          }
          if (stop)
          {
            /* stop */
            disk_stab_cursor_unfix(thread_p, &cursor);
            return NO_ERROR;
          }
        }
    
        disk_stab_cursor_unfix(thread_p, &cursor);
      }
    
      return NO_ERROR;
    }
    
  • disk_stab_unit_reserve ()
    /*
    * disk_stab_unit_reserve () - DISK_STAB_UNIT_FUNC function used to lookup and reserve free sectors
    *
    * return        : NO_ERROR
    * thread_p (in) : thread entry
    * cursor (in)   : disk sector table cursor
    * stop (out)    : output true when all required sectors are reserved
    * args (in/out) : reserve context
    */
    static int
    disk_stab_unit_reserve(THREAD_ENTRY *thread_p, DISK_STAB_CURSOR *cursor, bool *stop, void *args)
    {
      DISK_RESERVE_CONTEXT *context;
      DISK_STAB_UNIT log_unit;
      SECTID sectid;
    
      /* how it works
      * look for unset bits and reserve the required number of sectors.
      * we have two special cases, which can be very usual:
      * 1. full unit - nothing can be reserved, so we early out
      * 2. empty unit - we can consume it all (if we need it all) or just trailing bits.
      * otherwise, we iterate bit by bit and reserve free sectors.
      */
    
      if (*cursor->unit == BIT64_FULL) // 1. full unit
      {
        /* nothing free */
        return NO_ERROR;
      }
    
      context = (DISK_RESERVE_CONTEXT *)args;
      assert(context->nsects_lastvol_remaining > 0);
    
      // 2. empty unit // *cursor->unit == bit64 of free sectors
      if (*cursor->unit == 0)
      {
        /* empty unit. set all required bits */
        int bits_to_set = MIN(context->nsects_lastvol_remaining, DISK_STAB_UNIT_BIT_COUNT);
        int i;
    
        if (bits_to_set == DISK_STAB_UNIT_BIT_COUNT) // full unit
        {
          /* Consume all unit */
          *cursor->unit = BIT64_FULL;
        }
        else // partial unit
        {
          /* consume only part of unit */
          *cursor->unit = bit64_set_trailing_bits(*cursor->unit, bits_to_set);
        }
        /* what we log */
        log_unit = *cursor->unit;
    
        /* update reserve context */
        context->nsects_lastvol_remaining -= bits_to_set;
    
        /* save sector ids */
        for (i = 0, sectid = disk_stab_cursor_get_sectid(cursor); i < bits_to_set; i++, sectid++)
        {
          context->vsidp->volid = cursor->volheader->volid;
          context->vsidp->sectid = sectid;
          context->vsidp++;
          //log가 먼저 찍혀야 되는거 아닌가?
          disk_log("disk_stab_unit_reserve", "reserved sectid %d in volume %d.", sectid, cursor->volheader->volid);
        }
      }
      else // already be set
      {
        /* iterate through unit bits */ // one by one
        log_unit = 0;
        // offset_to_bit <= count bit. bit should 연속적으로 찍혀야함. 아니면 거의 0 나옴.
        // 그런경우에는 그냥 순회 돌게 한듯.
        for (cursor->offset_to_bit = bit64_count_trailing_ones(*cursor->unit), cursor->sectid += cursor->offset_to_bit;
            cursor->offset_to_bit < DISK_STAB_UNIT_BIT_COUNT && context->nsects_lastvol_remaining > 0;
            cursor->offset_to_bit++, cursor->sectid++)
        {
          // disk_stab_cursor_is_bit_set() 안에서 체크 한번 더함.
          disk_stab_cursor_check_valid(cursor);
    
          if (!disk_stab_cursor_is_bit_set(cursor)) // check offset_to_bit is set
          {
            /* reserve this sector */
            disk_stab_cursor_set_bit(cursor); // set offset_to_bit
    
            /* update what we log */
            log_unit = bit64_set(log_unit, cursor->offset_to_bit);
    
            /* update context */
            context->nsects_lastvol_remaining--;
    
            /* save vsid */
            context->vsidp->volid = cursor->volheader->volid;
            context->vsidp->sectid = cursor->sectid;
            context->vsidp++;
            //log를 먼저찍는게 유리하지 않나요?
            disk_log("disk_stab_unit_reserve", "reserved sectid %d in volume %d.", cursor->sectid,
                    cursor->volheader->volid);
          }
        }
      }
    
      /* safe guard: we must have done something, so log_unit cannot be 0 */
      assert(log_unit != 0);
      /* safe guard: all bits in log_unit must be set */
      assert((log_unit & *cursor->unit) == log_unit);
      if (context->purpose == DB_PERMANENT_DATA_PURPOSE)
      {
        /* log changes */
        log_append_undoredo_data2(thread_p, RVDK_RESERVE_SECTORS, NULL, cursor->page, cursor->offset_to_unit,
                                  sizeof(log_unit), sizeof(log_unit), &log_unit, &log_unit);
      }
      /* page was modified */
      pgbuf_set_dirty(thread_p, cursor->page, DONT_FREE);
    
      if (context->nsects_lastvol_remaining <= 0)
      {
        /* all required sectors are reserved, we can stop now */
        assert(context->nsects_lastvol_remaining == 0);
        ✅ *stop = true;
      }
      return NO_ERROR;
    }
    • disk_stab_cursor_check_valid ()
          /*
      * disk_stab_cursor_check_valid () - Check allocation table cursor validity.
      *
      * return      : void.
      * cursor (in) : Allocation table cursor.
      *
      * NOTE: This is a debug function.
      */
      STATIC_INLINE void
      disk_stab_cursor_check_valid(const DISK_STAB_CURSOR *cursor)
      {
        assert(cursor != NULL);
      
        if (cursor->pageid == NULL_PAGEID)
        {
          /* Must be recovery. */
          assert(!LOG_ISRESTARTED());
        }
        else
        {
          /* Cursor must have valid volheader pointer. */
          assert(cursor->volheader != NULL);
      
          /* Cursor must have a valid position. */
          assert(cursor->pageid >= cursor->volheader->stab_first_page);
          assert(cursor->pageid < cursor->volheader->stab_first_page + cursor->volheader->stab_npages);
          assert((cursor->pageid - cursor->volheader->stab_first_page) * DISK_STAB_PAGE_BIT_COUNT + cursor->offset_to_unit * DISK_STAB_UNIT_BIT_COUNT + cursor->offset_to_bit == cursor->sectid);
        }
      
        assert(cursor->offset_to_unit >= 0);
        assert(cursor->offset_to_unit < DISK_STAB_PAGE_UNITS_COUNT);
        assert(cursor->offset_to_bit >= 0);
        assert(cursor->offset_to_bit < DISK_STAB_UNIT_BIT_COUNT);
      
        if (cursor->unit != NULL)
        {
          /* Must have a page fixed */
          assert(cursor->page != NULL);
          /* Unit pointer must match offset_to_unit */
          assert((int)((char *)cursor->unit - cursor->page) == (int)(cursor->offset_to_unit * DISK_STAB_UNIT_SIZE_OF));
        }
      }
4. assert, update hint and exit
// ... ellipsis

  if (context->nsects_lastvol_remaining != 0)
  {
    /* our logic must be flawed... the sectors are reserved ahead so we should have found enough free sectors */
    assert_release(false);
    error_code = ER_FAILED;
    goto exit;
  }

  /* update hint */
  volheader->hint_allocsect = (context->vsidp - 1)->sectid + 1;  // next sector to be allocated
  volheader->hint_allocsect = DISK_SECTS_ROUND_DOWN(volheader->hint_allocsect); // round down to sector boundary, bit단위로는 하지 못해용
  /* we don't really need to set the page dirty or log the hint change. */

exit:
  if (page_volheader != NULL)
  {
    pgbuf_unfix(thread_p, page_volheader);
  }
  return error_code;
}