rdnf 0.2 思路
indradb
indradb图数据库是基于kv存储引擎,主要是基于rocksdb。基本元素主要有三:Edge、Vertex、Property(包含edge_property、vertex_property)。
原理如下
-
VertexManager
- key:vertex.id
- value:vertex.identifier
-
EdgeRangeManager
- key:[edge.outbound_id,edge.identifier,edge.inbound_id]
- value:null
因为key是排序过的,故可通过迭代的方式查找对应 inbound_id的集合,即vertex.id集合。
同理可得ReversedEdgeRangeManager
- key[edge.inbound_id,edge.identifier,edge.outbound_id]
- value:null
-
VertexPropertyManager
- key:[vertex.id,identifier]
- value:json
-
EdgePropertyManager
- key:[edge.outbound_id,edge.identifier,edge.inbound_id,identifier]
- value:json
-
VertexPropertyManager
- key:[identifier,json,vertex_id]
- value:null
通过迭代遍历排序过的key,得到对应的vertex_id,或得到对应的json和vertex_id
-
EdgePropertyManager
- key:[identifier,json,edge.outbound_id,edge.identifier,edge.inbound]
- value:null
同上
图数据库建模
最初的方案设计是name、arch单独作为一个(identifer,value),但是这会造成大量kv键值对,数据库的写入压力非常大。故而软件包的元数据信息包装成为pkgdetail和formatdetail。同样的,将requires由类似与provides的边关系,转变为property。
每个package provides多个entry,每个package requires多个entry。
Repo
按照优先级有三层repo
- cmd_repo:有这样的场景: dnf install file:///…x86_64.rpm https://…i686.rpm 将url的rpm下载到本地,将这些本地的rpm文件,解析其头文件,将元数据导入到cmd_repo,由于是用户指定的rpm文件,故而优先级最高。
- installed_repo: 操作系统将已经安装后的rpm包的元数据信息,导入到rpmdb.sqlite文件中,路径一般为 /var/lib/rpm/rpmdb.sqlite ,由于操作的不便,目前采用的是将rpmdb.sqlite的数据导入到installed_repo,根据rpmdb.sqlite的sqlite_sequence 和installed_seq中的记载来判断是否需要同步。在使用rdnf的同时,对rpmdb.sqlite文件加锁。
- repos: 将多个远程的软件包仓库的元数据xml文件解析成Vec<Repo>
SAT依赖解析
每个package requires多个Entry
对每个requier_entry:<rpm:entry name=“0ad-data” flags=“EQ” epoch=“0” ver=“0.0.26”/>,将其解析为SolveItem
pub(self) struct SolveItem {
entry_name: Arc<String>,
ver: Version,
flag: Option<String>,
}
其中,pub struct Version {
pub epoch: Option<u32>,
pub version: Option<String>,
pub release: Option<String>,
}
依次从cmd_repo、installed_repo、repos查找能够提供满足需要的Entry的Package ( P i , i = 1 , 2 , . . m P_i,i=1,2,..m Pi,i=1,2,..m)。
- provide 语义
( P 1 ∨ P 2 ∨ P 3 . . ∨ P m ∨ ¬ E n t r y 1 ) 当 E n t r y 1 为 T r u e ,则 P 1 . . P m 中至少有一个必须为 T r u e (P_1 \vee P_2 \vee P_3..\vee P_m \vee \neg Entry_1)\\ 当 Entry_1 为True,则P_1..P_m中至少有一个必须为True (P1∨P2∨P3..∨Pm∨¬Entry1)当Entry1为True,则P1..Pm中至少有一个必须为True
- require 语义:对于Package的reuqire Entry
( E n t r y i ∨ ¬ P a c k a g e ) 当 P a c k a g e 为 T r u e , 则 E n t r y i 必为 T r u e 由公式 ( 1 ) 可得,提供 E n t r y i 的多个 p a c k a g e 至少有一个为 T r u e . (Entry_i \vee \neg Package)\\ 当Package为True,则Entry_i必为True \\ 由公式(1)可得,提供Entry_i的多个package至少有一个为True. (Entryi∨¬Package)当Package为True,则Entryi必为True由公式(1)可得,提供Entryi的多个package至少有一个为True.
- conflict : P a c k a g e A Package_A PackageA对于conflict entry,满足entry条件的多个package ( P i , i = 1 , 2 , . . m P_i,i=1,2,..m Pi,i=1,2,..m)
( ¬ P A ∨ ¬ P i ) ∧ ( ¬ P A ∨ ¬ P 2 ) . . . ∧ ( ¬ P A ∨ ¬ P m ) (\neg P_A \vee \neg P_i) \wedge (\neg P_A \vee \neg P_2) ...\wedge (\neg P_A \vee \neg P_m) (¬PA∨¬Pi)∧(¬PA∨¬P2)...∧(¬PA∨¬Pm)
- obsolete同上。
当Require为Term时,即类似于**((feh and xrandr) if Xserver)**的
(
¬
P
a
c
k
a
g
e
∨
T
e
r
m
o
u
t
)
∧
(
¬
T
e
r
m
o
u
t
∨
¬
E
n
t
r
y
X
s
e
r
v
e
r
∨
T
e
r
m
i
n
)
∧
(
¬
T
e
r
m
i
n
∨
E
n
t
r
y
f
e
h
)
∧
(
¬
T
e
r
m
i
n
∨
E
n
t
r
y
x
r
a
n
d
r
)
(\neg Package \vee Term_{out})\wedge (\neg Term_{out} \vee \neg Entry_{Xserver}\vee Term_{in}) \\ \wedge (\neg Term_{in} \vee Entry_{feh}) \wedge (\neg Term_{in} \vee Entry_{xrandr})
(¬Package∨Termout)∧(¬Termout∨¬EntryXserver∨Termin)∧(¬Termin∨Entryfeh)∧(¬Termin∨Entryxrandr)
- 对于or 语义 (a or b or c),a、b、c既可以是Entry_id也可以是Term_id
( A ∨ B ∨ C ∨ ¬ T e r m ) (A \vee B \vee C \vee \neg Term) (A∨B∨C∨¬Term)
-
对于and 语义(a and b and c)
( ¬ T e r m ∨ A ) ∧ ( ¬ T e r m ∨ B ) ∧ ( ¬ T e r m ∨ c ) (\neg Term \vee A)\wedge (\neg Term \vee B) \wedge (\neg Term \vee c) (¬Term∨A)∧(¬Term∨B)∧(¬Term∨c) -
对于 if 语义 (m if p)
T e r m → ( P → M ) ( ¬ T e r m ∨ ¬ P ∨ M ) Term \rightarrow (P \rightarrow M) \\ (\neg Term \vee \neg P \vee M) Term→(P→M)(¬Term∨¬P∨M) -
对于 if else 语义 (m if p else n)
T e r m → ( P → M ) T e r m → ( ¬ P → N ) ( ¬ T e r m ∨ ¬ P ∨ M ) ∧ ( ¬ T e r m ∨ P ∨ N ) Term \rightarrow (P \rightarrow M) \\ Term \rightarrow (\neg P \rightarrow N) \\ (\neg Term \vee \neg P \vee M) \wedge (\neg Term \vee P \vee N) Term→(P→M)Term→(¬P→N)(¬Term∨¬P∨M)∧(¬Term∨P∨N)
unless unless else 同上。
- 对于with 语义,通过满足多个Entry的多个pkg 的交集,without 即减集
下一步目标
- makecache 构建缓存后,再次调用 rdnf其他命令,可能会出现报错(某个文件不存在)(Bug)
- 目前使用sat算法求解器是 varisat,该求解器的策略是,用最少数量为True的变量满足所有的Clause,不符合要求。在or 语义和provide语义中,package应该是存在优先级的,即先按arch(x86_64一定是优于i686)、然后是按repo排序(cmd_repo > install_repo > repos,其中repos是按配置文件中的priority排序)。需要对sat求解算法改进。
- 在本地建立缓存,使用的rocksdb效果不是很理想,kv分离的lsm树存储引擎比较理想,例如badger。