Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ttl 的默认值不正常 #1756

Closed
scruel opened this issue Jun 17, 2024 · 19 comments
Closed

ttl 的默认值不正常 #1756

scruel opened this issue Jun 17, 2024 · 19 comments

Comments

@scruel
Copy link

scruel commented Jun 17, 2024

问题现象

ttl 相关的默认值在文档中为“远程查询结果”:
image
但实测却发现并非是这样,rr-ttl-min 的默认值貌似为 600。

另外对于上游 ttl 为一天的记录,设置 rr-ttl-min 60 后,下发的 ttl 也会变为 60……,这不是 rr-ttl 的效果嘛,怎么 rr-ttl-min 也这样……

运行环境

version: '3'

services:
  smartdns:
    container_name: smartdns
    image: pymumu/smartdns:latest
    restart: always
    network_mode: host
    volumes:
      - ./:/etc/smartdns

重现步骤

bind [::]:53

log-level debug
log-size 64K

speed-check-mode none
cache-persist no
server 223.5.5.5

本机配置 dns server 到 smartdns,
配置上游域名 test1m.scruel.com 的 ttl 为一分钟,随后执行 dig,可见 dig 结果中的 ttl 是十分钟即 600,而不是上游的 60:

$ dig test1m.scruel.com
; <<>> DiG 9.18.18-0ubuntu0.22.04.2-Ubuntu <<>> test1m.scruel.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 56757
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;test1m.scruel.com.             IN      A

;; ANSWER SECTION:
test1m.scruel.com.      600     IN      A       1.1.1.2

;; Query time: 289 msec
;; SERVER: 127.0.0.42#53(127.0.0.42) (UDP)
;; WHEN: Mon Jun 17 17:16:12 CST 2024
;; MSG SIZE  rcvd: 51

改用 1.1.1.1 做 dig 则正常获得 ttl 为 60 的结果:

$ dig test1m.scruel.com @1.1.1.1

; <<>> DiG 9.18.18-0ubuntu0.22.04.2-Ubuntu <<>> test1m.scruel.com @1.1.1.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51053
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;test1m.scruel.com.             IN      A

;; ANSWER SECTION:
test1m.scruel.com.      60      IN      A       1.1.1.2

;; Query time: 299 msec
;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP)
;; WHEN: Mon Jun 17 17:12:14 CST 2024
;; MSG SIZE  rcvd: 62

信息收集

[2024-06-17 09:16:07,735][NOTICE][       smartdns.c:640 ] smartdns starting...(Copyright (C) Nick Peng <[email protected]>, build: 1.2024.05.09-1452)
[2024-06-17 09:16:07,737][ INFO][     dns_server.c:9013] bind ip [::]:53, type 0
[2024-06-17 09:16:07,737][ INFO][     dns_server.c:9013] bind ip [::]:853, type 2
[2024-06-17 09:16:07,747][ INFO][     dns_server.c:6404] ICMP ping is disabled, no ipv6 icmp check feature
[2024-06-17 09:16:07,747][ INFO][     dns_server.c:9330] IPV6 is not ready or speed check is disabled, disable IPV6 features
[2024-06-17 09:16:07,749][ INFO][     dns_client.c:1272] add server 223.5.5.5:53, type: udp
[2024-06-17 09:16:11,427][DEBUG][     dns_server.c:7155] recv query packet from fe80::eb23:cfdf:83ae:3adf, len = 46, type = 0
[2024-06-17 09:16:11,427][DEBUG][     dns_server.c:7171] request qdcount = 1, ancount = 0, nscount = 0, nrcount = 0, len = 46, id = 23986, tc = 0, rd = 1, ra = 0, rcode = 0
[2024-06-17 09:16:11,427][DEBUG][     dns_server.c:7196] query test1m.scruel.com from fe80::eb23:cfdf:83ae:3adf, qtype: 1, id: 23986, query-num: 1
[2024-06-17 09:16:11,427][DEBUG][     dns_client.c:3855] send query to server 223.5.5.5:53
[2024-06-17 09:16:11,427][ INFO][     dns_client.c:4252] request: test1m.scruel.com, qtype: 1, id: 44466, group: default
[2024-06-17 09:16:11,457][DEBUG][     dns_server.c:7155] recv query packet from 192.168.1.22, len = 46, type = 0
[2024-06-17 09:16:11,457][DEBUG][     dns_server.c:7171] request qdcount = 1, ancount = 0, nscount = 0, nrcount = 0, len = 46, id = 23986, tc = 0, rd = 1, ra = 0, rcode = 0
[2024-06-17 09:16:11,457][DEBUG][     dns_server.c:7196] query test1m.scruel.com from 192.168.1.22, qtype: 1, id: 23986, query-num: 2
[2024-06-17 09:16:11,503][DEBUG][     dns_server.c:7155] recv query packet from fe80::eb23:cfdf:83ae:3adf, len = 32, type = 0
[2024-06-17 09:16:11,503][DEBUG][     dns_server.c:7171] request qdcount = 1, ancount = 0, nscount = 0, nrcount = 0, len = 32, id = 20183, tc = 0, rd = 1, ra = 0, rcode = 0
[2024-06-17 09:16:11,712][DEBUG][     dns_client.c:1848] domain: test1m.scruel.com qtype: 1  qclass: 1
[2024-06-17 09:16:11,712][DEBUG][     dns_server.c:4644] query result from server 223.5.5.5:53, type: 0, domain: test1m.scruel.com qtype: 1 rcode: 0, id: 23986
[2024-06-17 09:16:11,712][DEBUG][     dns_server.c:3865] domain: test1m.scruel.com TTL: 60 IP: 1.1.1.2
[2024-06-17 09:16:11,712][ INFO][     dns_server.c:2613] result: test1m.scruel.com, qtype: 1, rtt: -0.1 ms, 1.1.1.2
[2024-06-17 09:16:11,712][DEBUG][     dns_server.c:2336] reply test1m.scruel.com qtype: 1, rcode: 0, reply: 1
[2024-06-17 09:16:11,712][DEBUG][     dns_server.c:1189] result: test1m.scruel.com, rtt: -0.1 ms, 1.1.1.2
[2024-06-17 09:16:11,712][DEBUG][     dns_server.c:1666] cache test1m.scruel.com qtype: 1 ttl: 600
[2024-06-17 09:16:11,712][ INFO][     dns_server.c:2391] result: test1m.scruel.com, client: fe80::eb23:cfdf:83ae:3adf, qtype: 1, id: 23986, group: default, time: 286ms
[2024-06-17 09:16:11,712][ INFO][     dns_server.c:4500] result: test1m.scruel.com, client: 192.168.1.22, qtype: 1, id: 23986, group: default, time: 255ms
[2024-06-17 09:16:11,712][DEBUG][     dns_client.c:1600] result: test1m.scruel.com, qtype: 1, has-result: 1, id 44466
[2024-06-17 09:16:11,856][DEBUG][     dns_client.c:2609] recv udp packet from 223.5.5.5:53, len: 103, ttl: 118, latency: 352
[2024-06-17 09:16:11,856][DEBUG][     dns_client.c:1836] qdcount = 1, ancount = 0, nscount = 1, nrcount = 0, len = 103, id = 35659, tc = 0, rd = 1, ra = 1, rcode = 3, payloadsize = 1408
@scruel
Copy link
Author

scruel commented Jun 17, 2024

另外假设这个问题已被解决,默认的 ttl 变为同上游一致,那么缓存的预获取功能,究竟会被哪些选项所影响?
例如,上游 ttl=60,配置中写入 rr-ttl-min 3600cache-persist yes,我目前理解的流程应该如下:

client dns query -> record uncached -> upstream query -> cache record -> return record
client dns query -> record cached -> record ttl unexpired -> return record
client dns query -> record cached -> record ttl expired -> return expired record with ttl=3 -> query upstream -> update cached record

如果追加写入以下配置:

serve-expired yes
serve-expired-ttl 345600

那么 smartdns 会主动进行轮询,效果应当如下:

smartdns -> iterate cached records -> record excceed serve-expired-ttl -> delete record

考虑文档说明:

开启过期缓存的情况下,仅当域名要从缓存中过期时,才进行预读取,而不是TTL超时获取。 所以,开启了过期缓存的情况下,推荐开启域名预获取功能。

如果追加写入以下配置:

serve-expired yes
serve-expired-ttl 345600
prefetch-domain yes

效果应当如下:

client dns query -> record uncached -> upstream query -> cache record -> return record
client dns query -> record cached -> record cache unexpired -> return record
client dns query -> record cached -> record cache expired -> return expired record with ttl=3 -> query upstream -> update cached record
smartdns -> iterate cached records -> record excceed serve-expired-ttl -> query upstream -> update cached record

而如果此时增加 serve-expired-prefetch-time 43200
则主动查询部分将变为,客户端发起的被动查询则不变:

smartdns -> iterate cached records -> record excceed serve-expired-ttl -> delete record
smartdns -> iterate cached records -> record unexcceed serve-expired-ttl -> excceed serve-expired-prefetch-time -> query upstream -> update cached record

即一旦配置预获取,ttl 就会变得无意义?不知道理解是否有误?

smartdns 在何时会进行预获取?或者说 record expired ttl 的值此时应该等于多少?
最终的目的无非是当上游 IP 更新时,希望 smartdns 能尽快返回更新后的 IP,直接查询主上游服务器的话(不考虑全球分发,只考虑主上游),返回的记录应当总是最新的 IP,所以判断是否记录过期用的 ttl 应当越低越好(不考虑性能消耗)。
那么,是否有办法能让 smartdns 以上游 ttl 为预获取的基准值,或者能否有个选项能让是否请求上游的判断基准值变为 min(配置的 ttl, 上游 ttl)?预期如下:

client dns query -> record uncached -> upstream query -> cache record -> return record
client dns query -> record cached -> MIN unexpired -> return record
client dns query -> record cached -> MIN expired -> return expired record with ttl=3 -> query upstream -> update cached record
smartdns -> iterate cached records -> record excceed serve-expired-ttl -> delete record
smartdns -> iterate cached records -> record unexcceed serve-expired-ttl -> excceed serve-expired-prefetch-time -> query upstream -> update cached record

另外文档提到频繁更新的 dns 记录会有问题,请问具体是什么问题呢?没太读懂,还望解释一下,谢谢。

@PikuZheng
Copy link
Contributor

首先我不主张修改ttl,ttl应该有域名所有者自主决定。

我的设置

rr-ttl-min 1
rr-ttl-max 3600
rr-ttl-reply-max 3600

以域名 ntp.17xiu8.com 为例,域名所有者(也就是我)设置ttl为3600。使用上游 119.29.29.29 查询ttl为3600。
使用smartdns,将上游设置为doh.pub时,首次查询(无缓存时)返回给客户端的ttl也是3600;间隔2秒后再次查询,ttl为3598。
以域名 blog.17xiu8.com 为例,域名所有者(也就是我)设置ttl为86400。使用上游 119.29.29.29 查询ttl为86400。
使用smartdns,将上游设置为doh.pub时,首次查询(无缓存时)返回给客户端的ttl也是3600;间隔2秒后再次查询,ttl为3598。

综上根据我的测试结果,我认为rr-ttl-min没有将所有的域名ttl改为1,符合预期;rr-ttl-max 和 rr-ttl-reply-max 也符合预期。

@scruel
Copy link
Author

scruel commented Jun 17, 2024

并不希望修改 ttl,主要是 rr-ttl-min 的默认值并非是预期的。

@PikuZheng
Copy link
Contributor

并不希望修改 ttl,主要是 rr-ttl-min 的默认值并非是预期的。

你就把它设的足够小嘛。

@scruel
Copy link
Author

scruel commented Jun 17, 2024

你就把它设的足够小嘛。

关于 workaround 的话,#1756 (comment) 里已经补充过了,但非预期行为属于 bug,可以看下怎么能修掉。

@PikuZheng
Copy link
Contributor

PikuZheng commented Jun 17, 2024

rr-ttl-min 3600 会导致写入缓存的ttl最小值为3600
serve-expired-ttl 345600 是指缓存的记录ttl降到0后仍会保留 345600 秒,和记录的ttl值无关

最终的目的无非是当上游 IP 更新时,希望 smartdns 能尽快返回更新后的 IP,

建议将这部分经常改变的域名不缓存,且不能使用阿里作为上游

但非预期行为属于 bug,可以看下怎么能修掉。

能否明确的说一下 哪个配置导致了哪个非预期行为。1楼说得当上游返回ttl大于 rr-ttl-min 时也会被改成rr-ttl-min 在我这没法重现,我自己也没遇到过这个问题。

serve-expired yes
serve-expired-ttl 345600
prefetch-domain yes
serve-expired-prefetch-time 43200

这样设置的话会有两种可能。一是在缓存的ttl降到0后的43200秒发起预读,此期间如果有客户端查询将收到ttl=5(由serve-expired-reply-ttl 定义)的结果;二是这个域名一段时间没有客户端查询不触发预读,缓存ttl降到0后的345600(会生效吗?就我所知最大有效值应该是65534)秒从缓存中删掉这个记录。

即一旦配置预获取,ttl 就会变得无意义?不知道理解是否有误?

我认为ttl值在记录没有过期时是有意义的,它指示客户端应保留记录(缓存)多久。预读是增加有效缓存,加快下游查询应答速度的手段,不能改变或代替记录ttl值的功能。

@pymumu
Copy link
Owner

pymumu commented Jun 18, 2024

tll-min的值默认设置为600这个是为了避免上游返回TTL过小,导致的客户端频繁查询问题。
大部分情况,上游返回一个IP,不会说10分钟就失效的,大部分情况IP失效,生效都是要48~72小时。

如果是你自己内部的一些域名要IP频繁变化,可以用domain-rule来设置ttl值。

@scruel
Copy link
Author

scruel commented Jun 18, 2024

@PikuZheng
缓存的记录ttl 是指什么呢?是指经过 rr-ttl-xxx 调整的 ttl?

建议将这部分经常改变的域名不缓存,不能使用阿里作为上游

大部分域名倒是不会频繁改动,这里主要是顺便多问一些,毕竟目前没有监看面板等功能,也没法知道哪些域名会频繁改变。另外,多久变一次算得上经常改变?是否能简单说一下原因为何不能?cloudflare 呢?

能否明确的说一下 哪个配置导致了哪个非预期行为。1楼说得当上游返回ttl大于 rr-ttl-min 时也会被改成rr-ttl-min 在我这没法重现,我自己也没遇到过这个问题。

owner 已回复该问题,既然算预期行为,那么便属于是文档未同步,修改下即可。

一是在缓存的ttl降到0后的43200秒发起预读,此期间如果有客户端查询将收到ttl=5(由serve-expired-reply-ttl 定义)的结果;

那么在缓存的 ttl 降到 0 后,未到 serve-expired-prefetch-time 规定的秒数,客户端就发起了查询,是否也会收到 ttl=5(由 serve-expired-reply-ttl 定义),并同时向上游做查询?
那这里若是将 serve-expired-prefetch-time 改为零,是否一旦缓存 ttl 超时,就会发起预读?例如将会有如下循环:

smartdns -> check_and_update -> check_and_update -> check_and_update
check_and_update: cache ttl expired -> serve-expired-prefetch-time(=0) reached -> upstream query -> update ttl

@pymumu

tll-min的值默认设置为600这个是为了避免上游返回TTL过小,导致的客户端频繁查询问题。

看来默认值确实为 600,建议把文档更新一下,否则容易产生误导,谢谢。

@pymumu
Copy link
Owner

pymumu commented Jun 18, 2024

@scruel
Copy link
Author

scruel commented Jun 18, 2024

@pymumu 这个已经读过了,就是读后才有上面的疑问

@PikuZheng
Copy link
Contributor

PikuZheng commented Jun 18, 2024

@PikuZheng 缓存的记录ttl 是指什么呢?是指经过 rr-ttl-xxx 调整的 ttl?

建议将这部分经常改变的域名不缓存,不能使用阿里作为上游

大部分域名倒是不会频繁改动,这里主要是顺便多问一些,毕竟目前没有监看面板等功能,也没法知道哪些域名会频繁改变。另外,多久变一次算得上经常改变?是否能简单说一下原因为何不能?cloudflare 呢?

是的,大部分域名解析都不会频繁改变。腾讯云认为“频繁改变的域名,应(域名所有者)将ttl设为60”(但是允许最小设置为1)。
我认为多久变一次不重要,但变了以后能及时更新很重要。阿里云和移动在这做得就非常不好,因为它有巨大的过期缓存,这使得(尤其是ddns)记录变化后不能及时更新。根据我自己的使用经验,一般ddns域名的记录改变2-4小时后才能从移动获得到新的结果,阿里云需要十几分钟。cf我自己不用,不了解。

一是在缓存的ttl降到0后的43200秒发起预读,此期间如果有客户端查询将收到ttl=5(由serve-expired-reply-ttl 定义)的结果;

那么在缓存的 ttl 降到 0 后,未到 serve-expired-prefetch-time 规定的秒数,客户端就发起了查询,是否也会收到 ttl=5(由 serve-expired-reply-ttl 定义),并同时向上游做查询?

正确

那这里若是将 serve-expired-prefetch-time 改为零,是否一旦缓存 ttl 超时,就会发起预读?例如将会有如下循环:

smartdns -> check_and_update -> check_and_update -> check_and_update
check_and_update: cache ttl expired -> serve-expired-prefetch-time(=0) reached -> upstream query -> update ttl

设置为0逻辑上不通,应该是无效值

@PikuZheng
Copy link
Contributor

smartdns/src/dns_server.c

Lines 1611 to 1616 in 07c1382

if (timeout == 0) {
timeout = request->conf->dns_serve_expired_ttl / 2;
if (timeout == 0 || timeout > EXPIRED_DOMAIN_PREFETCH_TIME) {
timeout = EXPIRED_DOMAIN_PREFETCH_TIME;
}
}

serve-expired-prefetch-time 0 时,将其值改为 serve-expired-ttl 的一半且不超过28800

@scruel
Copy link
Author

scruel commented Jun 18, 2024

阿里云需要十几分钟。cf我自己不用,不了解。

原来如此,阿里云的具体缓存行为倒是没注意到,cf 生效非常快,可算是即时更新的。

设置为 0 逻辑上不通,应该是无效值
serve-expired-prefetch-time 0 时,将其值改为 serve-expired-ttl 的一半且不超过28800

了解,这里只是假设,意思就是一旦缓存 ttl 过期,就尽快发起预读,比如假设这个值是 1 的话,是不是有一个周期为 ttl+1 的循环,不断进行预读?

@PikuZheng
Copy link
Contributor

一旦缓存 ttl 过期,就尽快发起预读,比如假设这个值是 1 的话,是不是有一个周期为 ttl+1 的循环,不断进行预读?

时间上是正确的,但是否预读取决于下游查询这个域名的频率。一段时间没有下游查询,就不预读了

@scruel
Copy link
Author

scruel commented Jun 18, 2024

一段时间没有下游查询,就不预读了

这个应该意思是缓存超时后就不预读吧,还是另有其他值,比如在未过期前预读 n 次?伪代码应该类似下面这样?

while 1:
  if cache_timedout:
    break
  check
  sleep ttl+1

然后其中的 ttl 的值会被外部改变,即当客户端请求时,依据 rr-ttl 和上游 ttl 做更新。

@PikuZheng
Copy link
Contributor

一段时间没有下游查询,就不预读了

这个应该意思是缓存超时后就不预读吧,还是另有其他值,比如在未过期前预读 n 次?伪代码应该类似下面这样?

while 1:
  if cache_timedout:
    break
  check
  sleep ttl+1

然后其中的 ttl 的值会被外部改变,即当客户端请求时,依据 rr-ttl 和上游 ttl 做更新。

就我所知是否预读有复杂算法 参考 #1016 (comment)

@scruel
Copy link
Author

scruel commented Jun 29, 2024

@PikuZheng 感谢解惑,还有一个问题,如果禁用缓存,是不是意味着每次客户端查询,都将会进行一次向上游的查询,并且会附带 经过计算的 ttl

@scruel
Copy link
Author

scruel commented Jun 29, 2024

下面给出我的配置和理解(如果有误欢迎指正),供以后他人参考:

rr-ttl-min 60
cache-persist yes
serve-expired yes
serve-expired-ttl 345600
prefetch-domain yes
serve-expired-prefetch-time 43200
serve-expired-reply-ttl 3

不考虑是否做预读的这一层判断,假设过期前会一直更新。

rr-ttl-min 60 是为了防止类似 ddns 之类“更新频繁/更新需要尽快刷新”的域名,无法及时更新
有些主机频繁更新的域名的 ttl 会很小(=60),而启用预读后是依照计算后的 ttl 为间隔做循环的,所以如果这类域名很多,将会增加 CPU 的负担,故而建议为这类域名禁用缓存(虽看起来光禁用预读就够了,毕竟如果应用于子网络中的设备,某一时刻某个设备能访问,而同时某个又不能访问,行为就会比较奇怪)。
serve-expired-ttl 345600,过久没访问的域名就释放缓存
serve-expired-prefetch-time 43200,表示意味着当 ttl 过期后,经过多久时间做预读。用于冷处理,对于客户端来说,这个值越小就越不可能获取到过期记录,必须大于 0
serve-expired-reply-ttl 3 如果缓存的 ttl 已过期(不过期的话当然就直接返回记录了),先返回指定 ttl 为 3 的记录(不确定是否过期),并且同时向上游进行查询,如果此时记录已经更新,那么客户端将会在 3 秒后重查记录,从而获得上游更新后的记录,这个操作同时会刷新下一次的预读时间(serve-expired-prefetch-time 的值)。这个值如果很小,可能会导致查询风暴,如果很大的话,一旦发生过期,则会导致客户端较长时间无法访问。

@PikuZheng
Copy link
Contributor

@PikuZheng 感谢解惑,还有一个问题,如果禁用缓存,是不是意味着每次客户端查询,都将会进行一次向上游的查询,

正确

并且会附带 经过计算的 ttl

查询没有ttl,应答才有。在不缓存的情况下,ttl值应该是上游应答的值

@scruel scruel closed this as completed Jun 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants