关于freebsd:cannot-use-devda1-must-be-a-block-device-or-regular-file

FreeBSD在创立zpool时,呈现cannot use '/dev/da1': must be a block device or regular file谬误,通过gpart show查看发现da1硬盘是 MBR 格局,而非GPT。 解决的办法是将 MBR 格局的硬盘变更 GPT: => 63 1953525105 da1 MBR (932G) 63 1 - free - (512B) 64 409599936 1 ntfs [active] (195G) 409600000 1543925168 - free - (736G)解决命令如下: $ gpart destroy -F da1$ gpart create -s gpt da1接下来便能够应用zpool创立ZFS 存储池了.

May 15, 2023 · 1 min · jiezi

关于freebsd:FreeBSD使用deamon将frpc添加到开始启动项

FreeBSD最大的特点就是稳,让你难以置信的稳。提到FreeBSD,还须要感激带我意识它的刘仕臣老师。 frpc默认状况下会随着shell的退出而主动终止,而且咱们也心愿在服务器启动时可能主动启动frpc服务,以及在frpc产生谬误时,服务器可能主动重启该服务。 步骤如下: 下载frpc下载frpc并将相干的配置项写入frpc.ini中,而后咱们将frpc与frpc.ini一并上传到FreeBSD服务器。其实这个地位不重要,只须要保障frpc可执行以及frpc.ini可读即可。 比方我上传到了:/usr/local/opt/frp_0.48.0_freebsd_amd64 创立启动脚本切换到root用户,并创立/etc/rc.d/frpc文件:$ ee /etc/rc.d/frpc,而后粘入以下内容: #!/bin/sh # PROVIDE: frpc# REQUIRE: LOGIN# KEYWORD: frpc . /etc/rc.subr name="frpc"rcvar=frpc_enable load_rc_config $name : ${frpc_enable="NO"}: ${frpc_user="nobody"}: ${frpc_flags="-c /usr/local/opt/frp_0.48.0_freebsd_amd64/frpc.ini"}daemon_pidfile="var/run/frpc_daemon.pid" pidfile="/var/run/frpc.pid"command="/usr/local/opt/frp_0.48.0_freebsd_amd64/frpc"start_cmd="/usr/sbin/daemon -r -R 5 -u $frpc_user -P $daemon_pidfile -p $pidfile -t $name $command $frpc_flags"start_postcmd="${name}_poststart"stop_cmd="${name}_stop"frpc_poststart(){ echo "${name}_daemon running pid `cat ${daemon_pidfile}`." echo "${name} running pid `cat ${pidfile}`."}frpc_stop(){ if [ -f "$daemon_pidfile" ]; then pid=`cat $daemon_pidfile` echo "Stopping pid ${pid}." kill $pid else echo "${name} not running?" fi} run_rc_command "$1"保留后为其增加执行权限:$ chmod +x /etc/rc.d/frpc ...

May 13, 2023 · 1 min · jiezi

关于freebsd:Freebsd配置服务开机自启动

freebsd因为没应用systemd,所以会采纳配置/etc/rc.d/文件的形式配置服务启动 服务模仿脚本开机自启动脚本执行的时候不能阻塞,freebsd服务启动的时候是串行执行的,一条命令卡住,所有后续服务不能执行 新建脚本/root/test-boot.sh,留神最初面要加上&免得阻塞 #!/bin/shsh -c "while true;do date >> /root/test-boot.log; sleep 1; done" &上面执行脚本 $ chmod a+x test-boot.sh$ ./test-boot.sh查看成果 $ tail -f /root/test-boot.log配置开机启动如下配置是照抄freebsd官网文档Starting Services 次要的不同是把原文中的配置文件从utility参数全副替换为test_boot,即utility服务替换为test_boot服务,该服务会在DAEMON pseudo-service之后启动 留神不要携带后缀.sh /etc/rc.d/test_boot #!/bin/sh## PROVIDE: test_boot# REQUIRE: DAEMON# KEYWORD: shutdown. /etc/rc.subr# 配置服务名称是test_bootname=test_boot# 配置服务是否开机自启动参数rcvar=test_boot_enable# 配置启动命令地位command="/root/test-boot.sh"load_rc_config $name## DO NOT CHANGE THESE DEFAULT VALUES HERE# SET THEM IN THE /etc/rc.conf FILE#test_boot_enable=${test_boot_enable-"NO"}pidfile=${test_boot_pidfile-"/var/run/test_boot.pid"}run_rc_command "$1"更多配置项或者应用能够参考/etc/rc.d/sshd文件 减少执行权限 $ chmod a+x /etc/rc.d/test_boot编辑/etc/rc.conf,新增一行数据容许开机自启动 test_boot_enable="YES"最初重启查看/root/test-boot.log文件验证 拓展浏览配置freebsd软件包源为国内中科大源 创立配置文件/usr/local/etc/pkg/repos/FreeBSD.conf FreeBSD: { url: "pkg+http://mirrors.ustc.edu.cn/freebsd-pkg/${ABI}/quarterly",}更新索引 $ pkg update -f装置vim ...

March 7, 2023 · 1 min · jiezi

关于freebsd:FreeBSD-ext2-文件系统磁盘块和-inode-的申请

前言磁盘块与文件 inode entry 的申请和开释的解决机制总体上是统一的,所以就放到一起进行分享。在之前的文章中介绍了ext2文件系统磁盘的的总体布局,为了不便阐明,这里就假如磁盘只有一个块组。 块组布局 块组描述符: 与超级块作用相似,记录该块组的根本属性信息数据块位图/inode位图: 以 bit 作为基本操作对象,示意一个数据块或者 inode entry 的状态。置 0 示意闲暇,置 1 示意占用inode表: 集中寄存 inode entry数据块: 存储文件的数据与元数据发问1: 超级块保留的是磁盘整体的信息,为什么每个块组中都蕴含一份? 这样做就是为了保留多个备份,有利于磁盘被损坏时的数据恢复。有的磁盘文件系统并没有采纳此种设计,就比方 ufs,它是在某些特定磁盘块寄存超级块数据的备份 (印象中是有3份)。发问2: 每块区域的大小是如何确定的? 个别是采纳估算法来确定的。用户能够依据本身的理论应用状况,预设每个文件均匀占用的磁盘块数。因为文件肯定会惟一对应一个 inode 数据结构,这样就能够计算出 inode entry 的大抵个数。再进一步,能够计算失去 inode 位图和 inode 表的大小。超级块和块组描述符个别会占用残缺的一个或者几个磁盘块,确定之后就能够计算失去数据块位图和数据块两个区域的大小。每个区域的大小尽量设置为磁盘块大小的整数倍。如果存储空间比拟拮据,倡议保障磁盘块大小是数据结构大小的整数倍(可能会造成一些空间节约),益处是升高代码实现的复杂度。 次要函数剖析咱们次要关注 freebsd/usr/src/sys/fs/ext2fs 下的 ext2_alloc.c 和 ext2_balloc.c 两个文件。int ext2_alloc(...) :从磁盘申请一个闲暇数据块 /* * Allocate a block in the filesystem. * * A preference may be optionally specified. If a preference is given * the following hierarchy is used to allocate a block: * 1) allocate the requested block. * 2) allocate a rotationally optimal block in the same cylinder. * 3) allocate a block in the same cylinder group. * 4) quadradically rehash into other cylinder groups, until an * available block is located. * If no block preference is given the following hierarchy is used * to allocate a block: * 1) allocate a block in the cylinder group that contains the * inode for the file. * 2) quadradically rehash into other cylinder groups, until an * available block is located. */intext2_alloc(struct inode *ip, daddr_t lbn, e4fs_daddr_t bpref, int size, struct ucred *cred, e4fs_daddr_t *bnp){ struct m_ext2fs *fs; struct ext2mount *ump; e4fs_daddr_t bno; int cg; *bnp = 0; fs = ip->i_e2fs; ump = ip->i_ump; mtx_assert(EXT2_MTX(ump), MA_OWNED);#ifdef INVARIANTS if ((u_int)size > fs->e2fs_bsize || blkoff(fs, size) != 0) { vn_printf(ip->i_devvp, "bsize = %lu, size = %d, fs = %s\n", (long unsigned int)fs->e2fs_bsize, size, fs->e2fs_fsmnt); panic("ext2_alloc: bad size"); } if (cred == NOCRED) panic("ext2_alloc: missing credential");#endif /* INVARIANTS */ if (size == fs->e2fs_bsize && fs->e2fs_fbcount == 0) goto nospace; if (cred->cr_uid != 0 && fs->e2fs_fbcount < fs->e2fs_rbcount) goto nospace; if (bpref >= fs->e2fs_bcount) bpref = 0; if (bpref == 0) cg = ino_to_cg(fs, ip->i_number); else cg = dtog(fs, bpref); bno = (daddr_t)ext2_hashalloc(ip, cg, bpref, fs->e2fs_bsize, ext2_alloccg); if (bno > 0) { /* set next_alloc fields as done in block_getblk */ ip->i_next_alloc_block = lbn; ip->i_next_alloc_goal = bno; ip->i_blocks += btodb(fs->e2fs_bsize); ip->i_flag |= IN_CHANGE | IN_UPDATE; *bnp = bno; return (0); }nospace: EXT2_UNLOCK(ump); ext2_fserr(fs, cred->cr_uid, "filesystem full"); uprintf("\n%s: write failed, filesystem is full\n", fs->e2fs_fsmnt); return (ENOSPC);}参数剖析: ...

January 27, 2023 · 10 min · jiezi

关于freebsd:FreeBSD启用ZFS快照功能

FreeBSD的 ZFS 果然不让人悲观,在保障数据安全的前提下,效率也十分的高。4块硬盘组成的raidz较NVME固态硬盘的速率略低,但不显著。 而快照则是ZFS更外围的性能,ZFS能够达到秒级创立快照,这个速度重大的超出了本人的认知。 创立数据集(档案零碎)zfs并不是能够间接对所有的文件夹设置快照的,要想对某个文件夹设置快照,则须要先建设档案零碎(数据集),比方我以后存在存储池yzpool,则能够应用以下命令建设存储池: # zfs create yzpool/data# zfs create yzpool/data/hosts# zfs create yzpool/data/hosts/20pro留神:在建设数据集时,须要由父到子顺次建设。 此时咱们便建设了3个数据集,别离是data, data/hosts以及data/hosts/20pro。而后咱们便能够对这个20pro文件夹设置快照了。 创立快照此时进入data/hosts/20pro文件夹,并创立一个测试文件:echo "sfsdf" >> hello.text,而后执行 # zfs snapshot yzpool/data/hosts/20pro@22-11-19 便胜利的创立了第一个快照,快照创立的地位位于yzpool/data/hosts/20pro文件夹下的.zfs/snapshot子文件夹,能够应用ls命令来间接查看。 # ls -a -l .zfs/snapshot/ total 1dr-xr-xr-x+ 3 root wheel 3 Nov 20 01:50 .dr-xr-xr-x+ 3 root wheel 3 Nov 20 01:48 ..drwxr-xr-x 2 root wheel 3 Nov 20 01:49 22-11-19顺便看一下快照大小: # du .zfs/snapshot/7 .zfs/snapshot/22-11-197 .zfs/snapshot/创立复原快照接着咱们新增一个文件,并且批改原文件: root@nfs:/yzpool/data/hosts/20pro # echo "123" >> hello.text root@nfs:/yzpool/data/hosts/20pro # cat hello.text sfsdf123root@nfs:/yzpool/data/hosts/20pro # echo "456" >> text.textroot@nfs:/yzpool/data/hosts/20pro # cat text.text 456而后咱们再建设个快照: ...

November 19, 2022 · 2 min · jiezi

关于freebsd:FreeBSD系统安装NFSNetwork-File-System服务并启用基于-IP-认证的机制

Network File System (NFS)网络文件系统:能够把网络上的某个资源做为本地硬盘来应用的一种零碎。 该服务次要依赖于:nfsd、mountd以及rpcbind nfsd: 接管 NFS 客户端发动的申请mountd: 解决由nfsd接管的请潮州rpcbind: 容许客户端发现以后的 NFS 服务端口服务端配置编辑/etc/rc.conf并退出以下配置: rpcbind_enable="YES"nfs_server_enable="YES"mountd_flags="-r"mountd_enable="YES"接着编辑/etc/exports,配置文件服务内容: /yzpool/hosts/20pro -maproot=root 192.168.1.20上述配置实现了: 可将本地/yzpool/hosts/20pro映射给192.168.1.20客户端。且当192.168.1.20客户端中的root权限等同于本机的root权限。 而后咱们重启一下服务器,以及下面的服务全副失效。 如果编辑过/etc/exports文件,则须要执行:/etc/rc.d/mountd reload客户端配置客户端操作系统以debian11为例,该客户机的 IP 地址为在服务端设置的192.168.1.20 首先咱们装置nfs客户端利用: # sudo apt install nfs-common而后创立一个挂载点,比方我创立一个位于根门路下的yz做为持载点: # sudo mkdir /yz最初咱们实现挂载: # sudo mount -t nfs 192.168.1.2:/yzpool/hosts/20pro /yz如果咱们心愿在系统启动的时候同步进行挂载,则须要编辑/etc/fstab文件来实现。 192.168.1.2:/yzpool/hosts/20pro /yz nfs defaults 0 0而后重新启动服务器进行测试. 其它dir client1 (options) [client2(options)...]详解: ro / rw : a) ro: 只读 b) rw: 写入加读取sync / async : a) sync: 服务端应答完上次申请后,才会应答下次申请; b) async: 异应应答。wdelay / no_wdelay a) wdelay服务器预测是间断申请时,将提早提交上次的写申请(晋升写入效率,数据断电易失落)no_all_squash / all_squash a) no_all_squash: 不扭转客户端的参考文档https://vitux.com/debian-nfs-... ...

November 19, 2022 · 1 min · jiezi

关于freebsd:FreeBSD-ext2-文件系统基本数据结构分析下

寒假日常凌晨,Douyiya 被短促的脚步声吵醒了,室友 狒狒 因为要加入电子设计大赛,所以早早起床拾掇明天所要用到的材料。简略寒暄两句之后,他便急匆匆地走出了寝室。Douyiya 起身观望,又是再相熟不过的场景: 老马 通宵未归,阿狗 和 龙哥 还在睡梦中。想到 Nanami 和 Nanase 因为天气太过酷热而回家避暑,Douyiya 也只能微微叹了口气,“又是无聊的一天呀!” 随后就约着在计算机爱好者社团结识的敌人 Ling,一块去图书馆分享最近学习的感悟。 Douyiya 的汇报Ling 是一个谈话语速慢慢悠悠,但思维十分麻利,并且涉猎宽泛的技术大佬,所以 Douyiya 也十分喜爱向她求教问题。两人碰面之后互道了早安,随后便找了一个宁静的地位坐了下来。“最近浏览了 ext2 文件系统的源码,想跟你分享一下,顺便帮我看看有没有了解上的谬误。” Douyiya 笑眯眯地说到。“我之前读过这些代码,你间接开始讲吧。” Douyiya 点了拍板:咱们能够认为操作系统治理着一棵微小的 文件树,无论是目录文件,还是一般文件,都是树上的一个节点。如果用户指定的是文件的绝对路径,操作系统会从根节点逐级查找,直到定位指标文件;如果是相对路径,则从当前目录下开始查找。所以,目录文件必须要蕴含一些信息,用来示意出它的子文件到底有哪些。ext2 文件系统的设计如下: /* * Structure of a directory entry */#define EXT2FS_MAXNAMLEN 255struct ext2fs_direct { uint32_t e2d_ino; /* inode number of entry */ uint16_t e2d_reclen; /* length of this record */ uint16_t e2d_namlen; /* length of string in e2d_name */ char e2d_name[EXT2FS_MAXNAMLEN];/* name with length<=EXT2FS_MAXNAMLEN */};/* * The new version of the directory entry. Since EXT2 structures are * stored in intel byte order, and the name_len field could never be * bigger than 255 chars, it's safe to reclaim the extra byte for the * file_type field. */struct ext2fs_direct_2 { uint32_t e2d_ino; /* inode number of entry */ uint16_t e2d_reclen; /* length of this record */ uint8_t e2d_namlen; /* length of string in e2d_name */ uint8_t e2d_type; /* file type */ char e2d_name[EXT2FS_MAXNAMLEN]; /* name with * length<=EXT2FS_MAXNAMLEN */};e2d_ino: 文件对应的惟一 inode numbere2d_name: 寄存文件名的字符数组,最大不超过 255 bytese2d_namlen: 文件名的长度e2d_type: 文件类型,比方一般文件、设施文件、目录、链接文件等等e2d_reclen: 与文件名的长度无关,sizeof(uint64_t) + e2d_namelen ...

September 12, 2022 · 4 min · jiezi

关于freebsd:云服务器FreeBSD系统zfs-zpool扩容的一种方案

近期服务器因为磁盘空间满而产生了谬误,数据盘有了扩容的需要。以后的零碎采纳了稳如磐石的FreeBSD零碎,文件系统应用了更加平安、牢靠的ZFS。 扩容计划云硬盘满,咱们首先想到的是去服务商那扩容。但应用了zfs文件系统后,扩容的最并没有反馈到零碎中,也就是说尽管云服务端给咱们扩容到了100G,但咱们可能应用的依然为40G。 搜寻了相干的扩容关键字,但基本上所有的文章都在讲如何对实体的硬盘进行扩容(老的硬盘换了新的更大的硬盘),却没有一篇文章提及如何在云盘扩容后同步扩容zpool。 最终查阅了oracle的官网文档失去了答案: 计划一查看zpool状态,失去zpool名称以及退出到zpool的硬盘名称: root@xxx:/dev # zpool status pool: zroot state: ONLINE scan: none requestedconfig: NAME STATE READ WRITE CKSUM ①zroot ONLINE 0 0 0 ②vtbd1 ONLINE 0 0 0失去①zpool的名称为zroot ②以后挂载的硬盘为vtbd1. 而后执行扩容命令: root@PingTai:/dev # zpool online -e ①zroot ②vtbd1计划二查看zpool状态,失去zpool名称: root@xxx:/dev # zpool status pool: zroot state: ONLINE scan: none requestedconfig: NAME STATE READ WRITE CKSUM ①zroot ONLINE 0 0 0 vtbd1 ONLINE 0 0 0失去zpool的名称为zroot,将zpool的autoexpand属性设置为on: # zpool set autoexpand=on ①zroot而后当磁盘扩容后,重新启动服务器,扩容主动实现。 ...

April 19, 2021 · 1 min · jiezi

freebsd下实现mysql的自动备份

沟通很重要,沟通的渠道很重要。当团队需要配合的时候,要想办法将自己当下做的事情,以最有效的方式通知给团队其它成员,以避免不必要的伤害。 比如,刚刚发生了如下事情:有一测试系统,历史的任务是完成上线前的最后一次测试,然后:A 为了客户演示系统的使用,在此系统上添加了大量的供演示用的支撑数据。B 为了删除生产环境下冗余数据,用生产环境下的数据覆盖了测试的数据。 导致:A很无奈:覆盖数据前竟然不和我打招呼!B很无辜:测试系统不就是用来测试而可以随时覆盖的吗? 以上是闲谈,如果我们每日都对数据进行备份呢?是不是就可以解决A、B两个人的问题? 脚本#!/bin/bash# # Set these variablesMyUSER="" # DB_USERNAMEMyPASS="" # DB_PASSWORDMyHOST="" # DB_HOSTNAME# Backup Dest directoryDEST="" # /home/username/backups/DB# Email for notificationsEMAIL=""# How many days old files must be to be removedDAYS=3# Linux bin pathsMYSQL="$(which mysql)"MYSQLDUMP="$(which mysqldump)"GZIP="$(which gzip)"# Get date in dd-mm-yyyy formatNOW="$(date +"%d-%m-%Y_%s")"# Create Backup sub-directoriesMBD="$DEST/$NOW/mysql"install -d $MBD# DB skip listSKIP="information_schemaanother_one_db"# Get all databasesDBS="$($MYSQL -h $MyHOST -u $MyUSER -p$MyPASS -Bse 'show databases')"# Archive database dumpsfor db in $DBSdo skipdb=-1 if [ "$SKIP" != "" ]; then for i in $SKIP do [ "$db" == "$i" ] && skipdb=1 || : done fi if [ "$skipdb" == "-1" ] ; then FILE="$MBD/$db.sql" $MYSQLDUMP -h $MyHOST -u $MyUSER -p$MyPASS $db > $FILE fidone# Archive the directory, send mail and cleanupcd $DESTtar -cf $NOW.tar $NOW$GZIP -9 $NOW.tarecho "MySQL backup is completed! Backup name is $NOW.tar.gz" | mail -s "MySQL backup" $EMAILrm -rf $NOW# Remove old filesfind $DEST -mtime +$DAYS -exec rm -f {} \;

July 2, 2019 · 1 min · jiezi

TAILQ-队列之一二事

TAILQ队列是FreeBSD内核中的一种队列数据结构,在一些著名的开源库中(如DPDK,libevent)有广泛的应用。TAILQ队列的定义TAILQ队列有HEAD和ENTRY两种基本的数据结构 #define TAILQ_HEAD(name, type) \struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \}#define TAILQ_ENTRY(type) \struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev;/* addr of previous next element*/ \} 注意:数据结构中的filed都是type类型的指针(或者是二级指针),这里的type是用户的队列元素类型,,将ENTRY结构内嵌到用户的QUEUE_ITEM结构中: struct QUEUE_ITEM{ int value; TAILQ_ENTRY(QUEUE_ITEM) entries; }; TAILQ_HEAD(headname,QUEUE_ITEM) queue_head; 这和Linux中list的组织方式不一样,后者是单纯地将struct list_head作为链表的一个挂接点,并没有用户的信息,具体差别可以看下图: TAILQ队列的操作TAILQ提供了多种操作队列的API,比如: TAILQ_HEAD(name, type)TAILQ_ENTRY(type)TAILQ_EMPTY(head)TAILQ_FIRST(head)TAILQ_FOREACH(var, head, field) TAILQ_INIT(head)TAILQ_INSERT_AFTER(head, listelm, elm, field)TAILQ_INSERT_BEFORE(listelm, elm, field)TAILQ_INSERT_TAIL(head, elm, field).....这些接口的实现和更多的操作接口可以参考 FreeBSD queue ...

June 19, 2019 · 4 min · jiezi

Golang-Failpoint-的设计与实现

作者:龙恒 对于一个大型复杂的系统来说,通常包含多个模块或多个组件构成,模拟各个子系统的故障是测试中必不可少的环节,并且这些故障模拟必须做到无侵入地集成到自动化测试系统中,通过在自动化测试中自动激活这些故障点来模拟故障,并观测最终结果是否符合预期结果来判断系统的正确性和稳定性。如果在一个分布式系统中需要专门请一位同事来插拔网线来模拟网络异常,一个存储系统中需要通过破坏硬盘来模拟磁盘损坏,昂贵的测试成本会让测试成为一场灾难,并且难以模拟一些需要精细化控制的的测试。所以我们需要一些自动化的方式来进行确定性的故障测试。 Failpoint 项目 就是为此而生,它是 FreeBSD failpoints 的 Golang 实现,允许在代码中注入错误或异常行为, 并由环境变量或代码动态激活来触发这些异常行为。Failpoint 能用于各种复杂系统中模拟错误处理来提高系统的容错性、正确性和稳定性,比如: 微服务中某个服务出现随机延迟、某个服务不可用。存储系统磁盘 IO 延迟增加、IO 吞吐量过低、落盘时间长。调度系统中出现热点,某个调度指令失败。充值系统中模拟第三方重复请求充值成功回调接口。游戏开发中模拟玩家网络不稳定、掉帧、延迟过大等,以及各种异常输入(外挂请求)情况下系统是否正确工作。……为什么要重复造轮子?Etcd 团队在 2016 年开发了 gofail 极大地简化了错误注入,为 Golang 生态做出了巨大贡献。我们在 2018 年已经引入了 gofail 进行错误注入测试,但是我们在使用中发现了一些功能性以及便利性的问题,所以我们决定造一个更好的「轮子」。 如何使用 gofail使用注释在程序中注入一个 failpoint: // gofail: var FailIfImportedChunk int// if merger, ok := scp.merger.(*ChunkCheckpointMerger); ok && merger.Checksum.SumKVS() >= uint64(FailIfImportedChunk) {// rc.checkpointsWg.Done()// rc.checkpointsWg.Wait()// panic("forcing failure due to FailIfImportedChunk")// }// goto RETURN1// gofail: RETURN1:// gofail: var FailIfStatusBecomes int// if merger, ok := scp.merger.(*StatusCheckpointMerger); ok && merger.EngineID >= 0 && int(merger.Status) == FailIfStatusBecomes {// rc.checkpointsWg.Done()// rc.checkpointsWg.Wait()// panic("forcing failure due to FailIfStatusBecomes")// }// goto RETURN2// gofail: RETURN2:使用 gofail enable 转换后的代码: ...

May 5, 2019 · 6 min · jiezi

TiKV 源码解析(五)fail-rs 介绍

作者:张博康本文为 TiKV 源码解析系列的第五篇,为大家介绍 TiKV 在测试中使用的周边库 fail-rs。fail-rs 的设计启发于 FreeBSD 的 failpoints,由 Rust 实现。通过代码或者环境变量,其允许程序在特定的地方动态地注入错误或者其他行为。在 TiKV 中通常在测试中使用 fail point 来构建异常的情况,是一个非常方便的测试工具。Fail point 需求在我们的集成测试中,都是简单的构建一个 KV 实例,然后发送请求,检查返回值和状态的改变。这样的测试可以较为完整地测试功能,但是对于一些需要精细化控制的测试就鞭长莫及了。我们当然可以通过 mock 网络层提供网络的精细模拟控制,但是对于诸如磁盘 IO、系统调度等方面的控制就没办法做到了。同时,在分布式系统中时序的关系是非常关键的,可能两个操作的执行顺行相反,就导致了迥然不同的结果。尤其对于数据库来说,保证数据的一致性是至关重要的,因此需要去做一些相关的测试。基于以上原因,我们就需要使用 fail point 来复现一些 corner case,比如模拟数据落盘特别慢、raftstore 繁忙、特殊的操作处理顺序、错误 panic 等等。基本用法示例在详细介绍之前,先举一个简单的例子给大家一个直观的认识。还是那个老生常谈的 Hello World:#[macro_use]extern crate fail;fn say_hello() { fail_point!(“before_print”); println!(“Hello World~”);}fn main() { say_hello(); fail::cfg(“before_print”, “panic”); say_hello();}运行结果如下:Hello World~thread ‘main’ panicked at ‘failpoint before_print panic’ …可以看到最终只打印出一个 Hello World~,而在打印第二个之前就 panic 了。这是因为我们在第一次打印完后才指定了这个 fail point 行为是 panic,因此第一次在 fail point 不做任何事情之后正常输出,而第二次在执行到 fail point 时就会根据配置的行为 panic 掉!Fail point 行为当然 fail point 不仅仅能注入 panic,还可以是其他的操作,并且可以按照一定的概率出现。描述行为的格式如下:[<pct>%][<cnt>*]<type>[(args…)][-><more terms>]pct:行为被执行时有百分之 pct 的机率触发cnt:行为总共能被触发的次数type:行为类型off:不做任何事return(arg):提前返回,需要 fail point 定义时指定 expr,arg 会作为字符串传给 expr 计算返回值sleep(arg):使当前线程睡眠 arg 毫秒panic(arg):使当前线程崩溃,崩溃消息为 argprint(arg):打印出 argpause:暂停当前线程,直到该 fail point 设置为其他行为为止yield:使当前线程放弃剩余时间片delay(arg):和 sleep 类似,但是让 CPU 空转 arg 毫秒args:行为的参数比如我们想在 before_print 处先 sleep 1s 然后有 1% 的机率 panic,那么就可以这么写:“sleep(1000)->1%panic"定义 fail point只需要使用宏 fail_point! 就可以在相应代码中提前定义好 fail point,而具体的行为在之后动态注入。fail_point!(“failpoint_name”);fail_point!(“failpoint_name”, || { // 指定生成自定义返回值的闭包,只有当 fail point 的行为为 return 时,才会调用该闭包并返回结果 return Error});fail_point!(“failpoint_name”, a == b, || { // 当满足条件时,fail point 才被触发 return Error})动态注入环境变量通过设置环境变量指定相应 fail point 的行为:FAILPOINTS="<failpoint_name1>=<action>;<failpoint_name2>=<action>;…“注意,在实际运行的代码需要先使用 fail::setup() 以环境变量去设置相应 fail point,否则 FAILPOINTS 并不会起作用。#[macro_use]extern crate fail;fn main() { fail::setup(); // 初始化 fail point 设置 do_fallible_work(); fail::teardown(); // 清除所有 fail point 设置,并且恢复所有被 fail point 暂停的线程}代码控制不同于环境变量方式,代码控制更加灵活,可以在程序中根据情况动态调整 fail point 的行为。这种方式主要应用于集成测试,以此可以很轻松地构建出各种异常情况。fail::cfg(“failpoint_name”, “actions”); // 设置相应的 fail point 的行为fail::remove(“failpoint_name”); // 解除相应的 fail point 的行为内部实现以下我们将以 fail-rs v0.2.1 版本代码为基础,从 API 出发来看看其背后的具体实现。fail-rs 的实现非常简单,总的来说,就是内部维护了一个全局 map,其保存着相应 fail point 所对应的行为。当程序执行到某个 fail point 时,获取并执行该全局 map 中所保存的相应的行为。全局 map 其具体定义在 FailPointRegistry。struct FailPointRegistry { registry: RwLock<HashMap<String, Arc<FailPoint>>>,}其中 FailPoint 的定义如下:struct FailPoint { pause: Mutex<bool>, pause_notifier: Condvar, actions: RwLock<Vec<Action>>, actions_str: RwLock<String>,}pause 和 pause_notifier 是用于实现线程的暂停和恢复,感兴趣的同学可以去看看代码,太过细节在此不展开了;actions_str 保存着描述行为的字符串,用于输出;而 actions 就是保存着 failpoint 的行为,包括概率、次数、以及具体行为。Action 实现了 FromStr 的 trait,可以将满足格式要求的字符串转换成 Action。这样各个 API 的操作也就显而易见了,实际上就是对于这个全局 map 的增删查改:fail::setup() 读取环境变量 FAILPOINTS 的值,以 ; 分割,解析出多个 failpoint name 和相应的 actions 并保存在 registry 中。fail::teardown() 设置 registry 中所有 fail point 对应的 actions 为空。fail::cfg(name, actions) 将 name 和对应解析出的 actions 保存在 registry 中。fail::remove(name) 设置 registry 中 name 对应的 actions 为空。而代码到执行到 fail point 的时候到底发生了什么呢,我们可以展开 fail_point! 宏定义看一下:macro_rules! fail_point { ($name:expr) => {{ $crate::eval($name, |_| { panic!(“Return is not supported for the fail point "{}"”, $name); }); }}; ($name:expr, $e:expr) => {{ if let Some(res) = $crate::eval($name, $e) { return res; } }}; ($name:expr, $cond:expr, $e:expr) => {{ if $cond { fail_point!($name, $e); } }};}现在一切都变得豁然开朗了,实际上就是对于 eval 函数的调用,当函数返回值为 Some 时则提前返回。而 eval 就是从全局 map 中获取相应的行为,在 p.eval(name) 中执行相应的动作,比如输出、等待亦或者 panic。而对于 return 行为的情况会特殊一些,在 p.eval(name) 中并不做实际的动作,而是返回 Some(arg) 并通过 .map(f) 传参给闭包产生自定义的返回值。pub fn eval<R, F: FnOnce(Option<String>) -> R>(name: &str, f: F) -> Option<R> { let p = { let registry = REGISTRY.registry.read().unwrap(); match registry.get(name) { None => return None, Some(p) => p.clone(), } }; p.eval(name).map(f)}小结至此,关于 fail-rs 背后的秘密也就清清楚楚了。关于在 TiKV 中使用 fail point 的测试详见 github.com/tikv/tikv/tree/master/tests/failpoints,大家感兴趣可以看看在 TiKV 中是如何来构建异常情况的。同时,fail-rs 计划支持 HTTP API,欢迎感兴趣的小伙伴提交 PR。 ...

April 1, 2019 · 2 min · jiezi

FreeBSD服务器选型

稳如磐石的FreeBSD在硬件选型上,并不理想,因为大多的服务器,官方的驱动列表中,都找不到它的身影。首先,如果我们选择了FreeBSD操作系统,那么服务器的选型上,就可以和Dell说再见了。本文给出联想的几种选型方案。RS 低端系列RS240 RS260 RS140 RS160RD 高端系列RD530 RD540 RD630 RD7XX RD8xx 基本上所有的RD系列的服务器。其它BD530FC等BD系列、DC5000、Converged HX2710-E等Converged系列、SS108 G1、RQ750、 SD330等SD系统详询:http://support.lenovo.com.cn/EsWeb/Index.aspx

March 12, 2019 · 1 min · jiezi

阿里云freebsd如何由11.1升级到12.0

阿里云的freebsd官方镜像版本为11.1,但很遗憾,该版本官方已经停止了支持。所以在进行ports安装软件时,会遇到版本过期的问题。支持的日期到:2017年7月。背景既然11.1不被支持,那我们将其升级到被支持的版本就好了。当前官方最新的release版本为11.2以及12.0,发布时间均为2018年,且在新的版本未发布前,会一直得到官方的支持。12.0过期日期:12.1-release发布日期 + 3个月。 11.2过期日期:11.3-release发布日期 + 3个月。目标本文的目的,即是将操作系统由11.1升级到12.0。解决问题的整个方法,仍然是以官方文档为主。在进行服务器升级时,需要逐步升级:比如你的系统当前是10.0,则需要依次看下面的文档:即升级过程为:10.1 -> 10.2 -> 10.3 -> … -> 11.1 -> 11.2我们当前想由11.1升级,那么要先看11.2的文档,升级完成后,再看12.0的文档。注意:我们不能由11.1直接升级到12.0,这点在官方文档的升级手册上也写明了。在升级12.0时,要求当前系统为11.2-release.点击各个版本的Installation Instructions并找到:即,我们需要的升级教程。操作步骤升级当前版本先fetch到最新版本,再安装# freebsd-update fetch 此过程会有些长,最后显示的信息过多,会提示以下信息:Applying patches… done.–More–(END)此时,按q退出日志显示,回到终端。得到以下信息,它告诉我们说11.1-release已过期了。WARNING: FreeBSD 11.1-RELEASE HAS PASSED ITS END-OF-LIFE DATE.Any security issues discovered after Mon Oct 1 08:00:00 CST 2018will not have been corrected.下一步:执行官方文档中的freebsd-update install# freebsd-update installsrc component not installed, skippedNo updates are available to install.Run ‘/usr/sbin/freebsd-update fetch’ first.上述操作保证了,我们在11.1版本下是最新的。11.1 -> 11.2参考官方文档:https://www.freebsd.org/releases/11.2R/installation.html#upgrade-binary先获取11.2-release的安装包,再升级。# freebsd-update upgrade -r 11.2-RELEASE期间会有个提示:The following components of FreeBSD do not seem to be installed:kernel/generic-dbg world/base-dbg world/doc world/lib32 world/lib32-dbgDoes this look reasonable (y/n)? 按y继续。下面的过程有些缓慢,主要是由于要下载的包太多了,有10000多个,需要耐心等待。升级过程中,有个冲突的提示:The following file could not be merged automatically: /etc/ntp.confPress Enter to edit this file in vi and resolve the conflicts此时,按回车编辑文件/etc/ntp.conf,解决冲突 。我的做法是:删除current version中的所有数据,保留11.2-release的。编辑以后,回到shell 按两次y确认自己的更改,然后按q完成安装过程。安装升级包# freebsd-update installsrc component not installed, skippedInstalling updates…Kernel updates have been installed. Please reboot and run"/usr/sbin/freebsd-update install" again to finish installing updates.重启# shutdown -r now待系统重启后继续安装# freebsd-update install如果我们是一个纯净的系统,由于未使用ports安装软件,所以此过程会简单很多。【猜想】如果我们不是纯净的系统,那么此时以前使用port安装的软件应该会随之升级,并可能需要一些人工干预,干预完毕后,最后再执行一次安装升级包,随后重新启动。# freebsd-update installsrc component not installed, skippedNo updates are available to install.Run ‘/usr/sbin/freebsd-update fetch’ first.# shutdown -r now查看freebsd版本以确定升级是否成功# freebsd-version 11.2-RELEASE-p911.2 -> 12.0官方文档:https://www.freebsd.org/releases/12.0R/installation.html#upgrade以下的步骤,基本和11.1 -> 11.2 相同# freebsd-update fetch# freebsd-update install# freebsd-update upgrade -r 12.0-RELEASE出现提示后按y# freebsd-update install# shutdown -r now# freebsd-update install# freebsd-update install# shutdown -r now备份最后,将系统盘做个镜像,以后再安装的时候,直接使用此空白镜像即可。总结1.官方文档很重要。 2.看提示很重要。3.积累很重要。任何的努力,都将在特定的时间点上闪光。 ...

March 8, 2019 · 1 min · jiezi