armink / FlashDB

An ultra-lightweight database that supports key-value and time series data | 一款支持 KV 数据和时序数据的超轻量级数据库
Apache License 2.0
1.93k stars 435 forks source link

现有迭代器函数很难覆盖更多应用,一些新迭代器函数的实现探讨 #260

Open guoweilkd opened 1 year ago

guoweilkd commented 1 year ago

目前的TSDB提供了一些迭代器函数如fdb_tsl_iter/reverse(),通过它们虽然可以实现遍历,读取等很多功能。但是在回调中处理tsl,很难满足很多应用的使用,比如这个例子: tsdb中有100个数据,需要通过网络进行上送到服务器

  1. 用户在回调函数读取最老的tsl,进行联网上送,上送成功后,标记此tsl
  2. 用户在回调函数读取下一个tsl,进行联网上送,上送成功后,标记此tsl
  3. 循环上面的步骤

上述例子中,用户必须在回调函数中一气呵成,否则:

  1. 每次读取时都要遍历数据库,这样效率会变的很差
  2. 回调函数是在互斥锁中的,太多的应用代码逻辑会影响异步append
  3. 回调函数会打断用户的正常使用流程,没有kv那样直接读取方便

总之,如果在数据库中的某个中间点上进行顺序的向下读取,使用上述函数就会变的很困难且低效。

因此,我们是否可以增加一些更便利的迭代器函数,使其实现如下功能:

  1. tsl = fdb_tsl_next_find(db, cur_tsl):根据当前的tsl来读取下一个tsl
  2. tsl = fdb_tsl_prev_find(db, cur_tsl):根据当前的tsl来读取下一个tsl 这样就可以完美的解决如下功能。

但因为追加可能是异步的,上面的函数可能会有面临如下一些问题:

  1. 在使用当前cur_tsl后, 异步append()造成cur_tsl所在扇区被重写,此时调用fdb_tsl_next_find()会出现错误情况
  2. 如何检测cur_tsl所在扇区被重写? 检测到后如何处理?

如何检测cur_tsl所在扇区被重写,我的思路如下:

  1. fdb_tsl_next_find()内部,先读取cur_tsl所在地址的信息,如果发现id不一致,则表明此扇区被重写了

检测到后如何处理,我的思路如下:

  1. 检测到被重写后,直接返回下一个最老的tsl,或者直接告诉用户被覆盖了?

当然,我认为,用户应该在使用时尽量避免这种情况

对于上面的问题,你有更好的解决思路吗?下面是我的一些简单实现思路,代码进行了简单测试,目前看起来可以实现上述问题

/* 代码实现 */
struct fdb_tsl fdb_tsl_next_find(fdb_tsdb_t db, fdb_tsl_t prestl)
{
    bool index_flag = false;
    struct tsdb_sec_info sector;
    uint32_t sec_addr, traversed_len = 0;
    struct fdb_tsl tsl;

    tsl.status = FDB_TSL_UNUSED;
    if (!prestl) return tsl;

    if (!db_init_ok(db)) {
        FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
    }

    db_lock(db);

    tsl.addr.index = prestl->addr.index;
    read_tsl(db, &tsl);
    if (tsl.status != FDB_TSL_UNUSED && tsl.time == prestl->time) {
        sec_addr = FDB_ALIGN_DOWN(tsl.addr.index, db_sec_size(db));
        index_flag = true;
    } else {
        sec_addr = db_oldest_addr(db);
    }

    do {
        traversed_len += db_sec_size(db);
        if (read_sector_info(db, sec_addr, &sector, false) != FDB_NO_ERR) {
            index_flag = false;
            continue;
        }
        if (sector.status == FDB_SECTOR_STORE_USING || sector.status == FDB_SECTOR_STORE_FULL) {
            if (sector.status == FDB_SECTOR_STORE_USING) {
                /* copy the current using sector status  */
                sector = db->cur_sec;
            }
            if (index_flag) {
                index_flag = false;
                tsl.addr.index = get_next_tsl_addr(&sector, &tsl);
            } else {
                tsl.addr.index = sector.addr + SECTOR_HDR_DATA_SIZE;
            }
            if (tsl.addr.index != FAILED_ADDR) {
                read_tsl(db, &tsl);
                db_unlock(db);
                return tsl;
            }
        }
        if (sec_addr == db->cur_sec.addr) {
            break;
        }
    } while ((sec_addr = get_next_sector_addr(db, &sector, traversed_len)) != FAILED_ADDR);

    db_unlock(db);

    tsl.status = FDB_TSL_UNUSED;
    return tsl;
}
armink commented 1 year ago

想法非常赞的,考虑的也很完善

检测到被重写后,直接返回下一个最老的tsl,或者直接告诉用户被覆盖了

我觉得可能要看当前 TSDB 模式,我觉得可以增加一个入参,比如回滚模式。为真时,可以直接返回最老的 TSL ,否则是不是直接 返回空