有2种可供回放的格式
blkparse
的bin
文件, 支持的比较多iolog
格式, 常用v2
和v3
正常使用bin文件的方式如下
1 | blktrace /dev/sdb1 |
1 | fio --direct=1 --read_iolog="dd.bin" --replay_redirect=/dev/sdc1 --name=replay --replay_no_stall=1 --numjobs=1 --ioengine=libaio --iodepth=32 |
目前初步实验来看, 多个job的时候, 最好让不同job的write_log
文件独立, 否则可能存在因同时追加写入冲突, 导致部分格式错误.
然后得到独立的iolog
文件后, 再使用fio --read_iolog="<file1>:<file2>" --merge_blktrace_file="<output_file>"
来进行多个job文件的合并
然后就可以正常使用1个iolog
文件进行read_log测试了.
rbd引擎生成的iolog
是v2协议
文件头指定fio version 2 iolog
然后声明job
对应的action
1 | filename action |
1 | filename action offset length |
1 | fio version 2 iolog |
magic
解析2类格式较直观
1 |
|
SNIA - Storage Networking Industry Association: IOTTA Repository Home
从这里可以下载到对应的iolog.bin的replay文件.
分别对应哪些rbd接口呢?
1 |
|
1 | static enum fio_q_status fio_rbd_queue(struct thread_data *td, |
这里方案2社区有样例 ### lttng采集
RBD Replay — Ceph Documentation
Capture the trace. Make sure to capture pthread_id context: 1
2
3
4
5
6
7
8
9
10
11
12
13
14mkdir -p traces
lttng create -o traces librbd
lttng enable-event -u 'librbd:*'
lttng add-context -u -t pthread_id
lttng start
# run RBD workload here
lttng stop
# Process the trace with rbd-replay-prep:
rbd-replay-prep traces/ust/uid/*/* replay.bin
# Replay the trace with rbd-replay. Use read-only until you know it’s doing what you want:
rbd-replay --read-only replay.bin
这里的rbd-replay
代码基本上就是单独解析的lttng
采集到的埋点格式了
两种格式, fio代码里针对两种格式分别处理, 通过解析文件头, 是否是blktrace来判断触发哪种解析.
blkparse
得到的bin
文件是前八位是7407 6561
, 而不是那个代码中的0xffffff00
呢fio不支持多线程回放. 只能是用merge-blktrace-file
合并后再进行处理.
目前也算是在分布式存储领域折腾了有一些时间了, 虽然还是比较入门的程度.
偶然又看到在讨论分布式存储与 集中式存储 应用场景的文档, 回想起来, 做个草草的初步思考.
最早的时候, 我给领导汇报测试数据时, 总是拿着跑bench
时只管把depth
往上加的最终数据汇报, 只要能看到IOPS上涨, 就继续加. 当时理解为理解为集群的性能是以depth跑满, 比如把集群的cpu
或者硬盘的util
用尽, 出现了硬件或者源码锁的逻辑瓶颈为止, 以该结论来作为集群水平汇报. 看上去数据确实挺美好, 看着至少也有业界的水准了的样子.
但是这里其实当时忽略了延时的因素, 延时应该如何理解呢?
这里的理解方式, 就是p99
,p90
等划分一个比例的延时, 要低于指定值, 比如5ms
内, 这个情况下的IOPS才是一定程度上用户有机会发挥出来的.
为什么我说是有机会呢? 因为这里还存在一个问题, depth
是否真的用户能跑出来?
诸如数据库或者自己开发的代码, 队列深度以及压力, 一般情况下能达到8/16已经算是比较可以的了.
所以实际上, 比如以ceph
为例, 假设我现在3节点各3 ssd
, rbd跑出了2W IOPS
性能, 还需要细分, 延时是多少, 如果是3ms
, 则代表我单队列深度时, IOPS只有333
, 8队列深度时, 此时底层物理盘利用率尚未达到瓶颈, 则可以达到2664 IOPS
.
而2664 IOPS
一般是无法满足数据库类业务的, 比如说某个现场的一组客户需求, 需要平均5000 IOPS
, boost 15000 IOPS
, 该数据是在psync 16队列深度下跑出的
, 即基本可以换算为需要937.5 IOPS
, 即1.06ms
. 这个量级基本上是SATA固态的裸盘性能了.
从需求反推, 可以得知这个延时数量级, 只有当我们使用集中式存储的技术(诸如RAID, 且不能是EC类), 或是采用了nvme
这类物理延时十位数微秒级别的硬件, 分布式存储才有希望可以满足这类用户的需求.
所以, 换句话说. 我们评估一个集群的实际可用性能时, 往往需要考虑用户的实际使用场景, 如果技术上可以做到, 肯定是最好像裸盘的硬件规格一样, 报出的延时和IOPS, 基本上是队列深度2-8
就能跑出的水平, 从而完全可以保障任意用户都能够取得满足需求的性能.
以腾讯云的数据为例,
我的一块100G的云硬盘, 1.3ms, 6000 IOPS
队列深度 | IOPS | 延时(ms) | 延时(p999) | 延时(p99) | 延时(p90) |
---|---|---|---|---|---|
1 | 713 | 1.3 | 146 | 81 | 26 |
2 | 1495 | 1.3 | 146 | 75 | 23 |
4 | 2828 | 1.4 | 146 | 77 | 25 |
8 | 6042 | 1.3 | 130 | 73 | 24 |
上面这组数据可以得到的一个推论是, 底层存储能够给予QoS稳定提供1.3ms
延时, 并且可以支持极大的队列深度.
主要就体现在, 提供了稳定的1.3ms
延时后, 横向继续加设备,能够不停给这个集群扩充, 让往这个集群申请资源的业务, 能够几近无上限的扩展.
在实际使用luminous
, 尚未引入QoS
的版本的ceph
中会发现, 一个集群只跑单卷时, 16队列比如能提供4000 IOPS
, 当2->4->8->16
个卷并发时, 平均单卷的性能降到3000
, 直到最终可能就1000
不到.
TODO: 按照我目前的理解, 这个问题的关键点可能主要是集中在锁抢占或者说op之间排队出队的顺序之类的问题? QoS可能可以缓解这个问题. 亦或是rocksdb
的一些解决长尾延时问题的技术也有所帮助? 这部分暂未投入了解了.
根据目前理解, 应用一致性可以粗分为
很简单粗暴, 不管是否有 pending IO, 直接保存当前的硬盘状态, 做COW/ROW链接, 完成快照.
采用崩溃一致性的备份来恢复云服务器,在数据恢复后,由于没有保证数据库事务的一致性,通常需要依赖数据自身的保护机制做自动的日志回滚,数据才能正常启动,数据是恢复到离备份时间点最近的一个一致性状态,相比应用一致性备份的恢复,RPO(Recovery Point Objective,指的是最多可能丢失的数据的时长)会更大,RTO(Recovery Time Objective 指的是从灾难发生到整个系统恢复正常所需要的最大时长)也更长。
基本上表达的一种实现方式可能就是会建多个快照, 然后会识别多个快照内业务的可用性, 无法通过数据库/文件系统修复工具回滚继续提供服务, 则去触发下一个快照. 直到找到一个能用的快照? ## 应用一致性快照. (Application-consistent backup)
以qemu和qcow2为例, qcow2支持内存快照, 但是是依赖于虚拟机先由qemu控制进入paused状态的. 这部分一般由于内存一般划的比较大, 内存完整写入, 一般比较慢. 停机时间特别长, 一般都是不太能接受的. 虽然应用一致性确实保障了.
TODO: 这个暂时还支持猜想, 还没去细找是否有基于这类实现的.
诸如drbd/rbd mirror等, 本身除了这个业务本身, 还提供了其他设备进行备份, 其实应该算得上是备份技术了. 它确实能保障应用一致性, 但是其实是双活了. 针对快照时间点, 是在对端做flush, 然后创建快照了.
这个快照点, 基本上可以是在mirror对端按事件进行冻结, 然后flush, 然后创建快照了. 对本端无影响.
按照目前的认知, 这个可能是目前用的比较多的方案.
主要就是在创建快照前和后分别提供了一个hook接口. 针对文件系统类具有一定的通用约束的, 可以直接默认使用fsfreeze
类的偏通用的接口完成静默, 然后再进行对应的pending IO的持久化.
非文件系统类业务, 则需要上层业务自己去触发了, 就是根据对应的数据库开发对应的持久化方式, 完成快照的一致性保障. 如果是明确指定类型的数据库, 可能还能直接配合对应的方案做自动化检测和识别去完成对应数据库内的持久化快照, 无需用户自己实现.
不过当客户机上是windows 时, 则直接可以通过VSS来实现应用一致性快照. 无需开发介入了.
看到阿里云的文档里也写的很清楚, 如下表格.
类型 | 说明 | 实现方式 |
---|---|---|
应用一致性快照 | 应用一致性快照在快照创建时刻备份内存数据及正在进行中的数据库事务,保证应用系统数据和数据库事务的一致性。通过应用一致性快照,没有数据的损坏及丢失,避免数据库启动时日志回滚,确保应用处于一致性的启动状态。 应用一致性快照以标签APPConsistent:True标识。 | 根据操作系统类型,实现方式如下: Windows:通过卷影复制服务VSS(Volume Shadow Copy Service)实现。 Linux:通过执行自定义Shell脚本(需要您根据应用自行编写脚本)实现。应用一致性的效果,由您自己编写的脚本负责保证。 |
文件系统一致性快照 | 如果开启应用一致性功能,但不满足相关条件,系统将会为您创建文件系统一致性快照。 文件系统一致性确保在快照创建时刻同步文件系统内存和磁盘信息,冻结文件系统写操作,使得文件系统处于一致性的状态。通过文件系统一致性快照,可以避免操作系统在重启后进行chkdsk或fsck等磁盘检查修复操作。 文件系统一致性快照以标签FsConsistent:True标识。 | 根据操作系统类型,实现方式如下: Windows:如果无Windows操作系统上特定应用的VSS Writer参与时,默认创建的为文件系统一致性。 Linux:如果无对应的应用脚本,默认创建的为文件系统一致性。 |
看到阿里云的描述, 基本就知道了, 也是通过由用户自身的shell脚本内的行为来完成在创建快照前内存缓存持久化到硬盘, 确保硬盘此刻是完整的. 文件系统侧则通过默认的fsfreeze等通用方案来进行持久化.
If the volume contains a file system, the file system should be in an internally consistent state before a snapshot is taken. Snapshots taken without write quiescing could need an fsck pass before they are mounted again. To quiesce I/O you can use fsfreeze command. See the fsfreeze(8) man page for more details.
Snapshots — Ceph Documentation
rbd的文档里已经提供了fsfreeze的方案.
ceph在16版本librbd提供了quiesce
能力, 主要描述的就是在创建快照前和后分别提供了一个hook接口.
然后暴露给上层用户, 可能就比较完善了.
参考What is Application Consistent Backup and How to Achieve It 可知几个方案的比较结果
Operation | timebased (inconsistent) | Crash-consistent | Application-consistent |
---|---|---|---|
Consistent point in time backup of files | No | Yes | Yes |
Application consistency | No | yes | Yes |
Aware of in memory and pending I/O | No | no | Yes |
Requires no special steps for application data restore | No | yes | Yes |
主要是针对rbd->openstack相关, 所以cephfs/rgw/dashboard等其他的特性相关的几乎都跳过了. 仅整理了rbd及以下模块, 从luminous以后到pacific为止的.
RADOS
bluestore-rocksdb-sharding
, which reduces disk space requirements.RBD
osd_client_message_cap
has been set to 256, to provide better flow control by limiting maximum number of in-flight client requests.https://hub.nuaa.cf/ceph/ceph/pull/42616
https://docs.google.com/spreadsheets/d/1dwKcxFKpAOWzDPekgojrJhfiCtPgiIf8CGGMG1rboRU/edit#gid=0
这里的实测结论居然是256比较好? 还没细看.
RADOS
RBD
This release sets bluefs_buffered_io
to true by default to improve performance for metadata heavy workloads. Enabling this option has been reported to occasionally cause excessive kernel swapping under certain workloads. Currently, the most consistent performing combination is to enable bluefs_buffered_io and disable system level swap.
The default value of bluestore_cache_trim_max_skip_pinned
has been increased to 1000 to control memory growth due to onodes.
bluestore_min_alloc_size_ssd
has been changed to 4K to improve performance across all workloads. > https://github.com/ceph/ceph/pull/32998 > common/options: Set bluestore min_alloc size to 4K by markhpc · Pull Request #30698 · ceph/ceph > > > I'm not sure that having 4K min_alloc_size for both ssd and spinner is a good choice. From my benchmarks 4K MAS is undoubtedly beneficial in all-flash setupы only. It shows performance boost for certain small R/W scenarios and no regression for other onesceph osd numa-status
command, and configured via the osd_numa_node
config option. Bug #42411: nautilus:osd: network numa affinity not supporting subnet port - RADOS - CephRADOS:
Trimming of PGLog dups is now controlled by the size instead of the version. This fixes the PGLog inflation issue that was happening when the on-line (in OSD) trimming got jammed after a PG split operation. Also, a new off-line mechanism has been added: ceph-objectstore-tool got trim-pg-log-dups op that targets situations where OSD is unable to boot due to those inflated dups. If that is the case, in OSD logs the “You can be hit by THE DUPS BUG” warning will be visible. Relevant tracker: https://tracker.ceph.com/issues/53729
RBD: rbd device unmap command gained --namespace option. Support for namespaces was added to RBD in Nautilus 14.2.0 and it has been possible to map and unmap images in namespaces using the image-spec syntax since then but the corresponding option available in most other commands was missing. rados namespaces can be used to provide isolation between rados clients within a pool. For example, a client could only have full permissions on a namespace specific to them. This makes using a different rados client for each tenant feasible, which is particularly useful for rbd where many different tenants are accessing their own rbd images. 即主要用途其实算是更精细的权限控制? 确保底层即便拿到其他用户的权限, 也因为namespace无法使用?
Accept
header (starting with v1.0).Account lock-out after a configurable number of failed log-in attempts.
Improved cookie policies to mitigate XSS/CSRF attacks.
Reviewed and improved security in HTTP headers.
Sensitive information reviewed and removed from logs and error messages.
TLS 1.0 and 1.1 support disabled.
Debug mode when enabled triggers HEALTH_WARN.
SPECIFYING EXPECTED POOL SIZE 支持多池共用osd, 做健康检查, 感知是否使用超过该池原定使用空间上限, 会有warning信息. ceph osd pool set {pool-name} recovery_priority {value} rbd 性能监控 - Image live-migration feature has been extended to support external data sources. Images can now be instantly imported from local files, remote files served over HTTP(S) or remote S3 buckets in raw
(rbd export v1
) or basic qcow
and qcow2
formats. Support for rbd export v2
format, advanced QCOW features and rbd export-diff
snapshot differentials is expected in future releases.
librbd.dll
and rbd-wnbd
(Windows Network Block Device) daemon. It allows mapping, unmapping and manipulating images similar to rbd-nbd
.--max <n>
option is available with the osd ok-to-stop
command to provide up to N OSDs that can be stopped together without making PGs unavailable.msgr2下的arm/x86不兼容修复 * A long-standing bug that prevented 32-bit and 64-bit client/server interoperability under msgr v2 has been fixed. In particular, mixing armv7l (armhf) and x86_64 or aarch64 servers in the same cluster now works.
OSD:
A new OSD daemon command, 'dump_recovery_reservations', reveals the recovery locks held (in_progress) and waiting in priority queues.
A health warning is now generated if the average osd heartbeat ping time exceeds a configurable threshold for any of the intervals computed. The OSD computes 1 minute, 5 minute and 15 minute intervals with average, minimum and maximum values. New configuration option mon_warn_on_slow_ping_ratio
specifies a percentage of osd_heartbeat_grace
to determine the threshold. A value of zero disables the warning. New configuration option mon_warn_on_slow_ping_time
specified in milliseconds over-rides the computed value, causes a warning when OSD heartbeat pings take longer than the specified amount. A new admin command, ceph daemon mgr.# dump_osd_network [threshold]
, will list all connections with a ping time longer than the specified threshold or value determined by the config options, for the average for any of the 3 intervals. Another new admin command, ceph daemon osd.# dump_osd_network [threshold]
, will do the same but only including heartbeats initiated by the specified OSD
Ceph will now issue health warnings if daemons have recently crashed. Ceph has been collecting crash reports since the initial Nautilus release, but the health alerts are new. To view new crashes (or all crashes, if you've just upgraded)::
ceph crash ls-new
To acknowledge a particular crash (or all crashes) and silence the health warning::
ceph crash archive <crash-id>
ceph crash archive-all
num_rados_handles
has been removed. If you were using a value of num_rados_handles
greater than 1, multiply your current objecter_inflight_ops
and objecter_inflight_op_bytes
parameters by the old num_rados_handles
to get the same throttle behavior.MDS:
Cache trimming is now throttled. Dropping the MDS cache via the
"ceph tell mds.<foo> cache drop"
command or large reductions in the cache size will no longer cause service unavailability.
The monitor daemon uses significantly less disk space when undergoing recovery or rebalancing operations.
CephFS:
对nfs的使用支持如何?
Wallaby对应Mimic-Pacific
rocky到2023.1后面那个版本
当前业务已升级到train版本, 局部还在N版本
The Hyper-V driver can now attach Cinder RBD volumes. The minimum requirements are Ceph 16 (Pacific) and Windows Server 2016.
The Hyper-V virt driver can now attach Cinder RBD volumes.
New [glance]/enable_rbd_download
config option was introduced. The option allows for the configuration of direct downloads of Ceph hosted glance images into the libvirt image cache via rbd when [glance]/enable_rbd_download= True
and [glance]/rbd_user
, [glance]/rbd_pool
, [glance]/rbd_connect_timeout
, [glance]/rbd_ceph_conf
are correctly configured.
Added params [libvirt]/rbd_destroy_volume_retries
, defaulting to 12, and [libvirt]/rbd_destroy_volume_retry_interval
, defaulting to 5, that Nova will use when trying to remove a volume from Ceph in a retry loop that combines these parameters together. Thus, maximum elapsing time is by default 60 seconds.
Nova tries to remove a volume from Ceph in a retry loop of 10 attempts at 1 second intervals, totaling 10 seconds overall - which, due to 30 second ceph watcher timeout, might result in intermittent object removal failures on Ceph side (bug 1856845). Setting default values for [libvirt]/rbd_destroy_volume_retries
to 12 and [libvirt]/rbd_destroy_volume_retry_interval
to 5, now gives Ceph reasonable amount of time to complete the operation successfully.
The libvirt RBD image backend module can now handle a Glance multistore environment where multiple RBD clusters are in use across a single Nova/Glance deployment, configured as independent Glance stores. In the case where an instance is booted with an image that does not exist in the RBD cluster that Nova is configured to use, Nova can ask Glance to copy the image from whatever store it is currently in to the one that represents its RBD cluster. To enable this feature, set [libvirt]/images_rbd_glance_store_name
to tell Nova the Glance store name of the RBD cluster it uses.
Glance multistore configuration with multiple RBD backends is now supported within Nova for libvirt RBD-backed images using [libvirt]/images_rbd_glance_store_name
configuration option.
[workarounds]/never_download_image_if_on_rbd
which helps to avoid pathological storage behavior with multiple ceph clusters. Currently, Nova does not support multiple ceph clusters properly, but Glance can be configured with them. If an instance is booted from an image residing in a ceph cluster other than the one Nova knows about, it will silently download it from Glance and re-upload the image to the local ceph privately for that instance. Unlike the behavior you expect when configuring Nova and Glance for ceph, Nova will continue to do this over and over for the same image when subsequent instances are booted, consuming a large amount of storage unexpectedly. The new workaround option will cause Nova to refuse to do this download/upload behavior and instead fail the instance boot. It is simply a stop-gap effort to allow unsupported deployments with multiple ceph clusters from silently consuming large amounts of disk space.The reporting for bytes available for RBD has been enhanced to accomodate unrecommended Ceph deployments where multiple OSDs are running on a single disk. The new reporting method takes the number of configured replicas into consideration when reporting bytes available.
The libvirt driver’s RBD imagebackend no longer supports setting force_raw_images to False. Setting force_raw_images = False and images_type = rbd in nova.conf will cause the nova compute service to refuse to start. To fix this, set force_raw_images = True. This change was required to fix the bug 1816686.
Note that non-raw cache image files will be removed if you set force_raw_images = True and images_type = rbd now.
[libvirt]/rbd_connect_timeout
configuration option has been introduced to limit the time spent waiting when connecting to a RBD cluster via the RADOS API. This timeout currently defaults to 5 seconds.This aims to address issues reported in bug 1834048 where failures to initially connect to a RBD cluster left the nova-compute service inoperable due to constant RPC timeouts being hit. ### stein
Adds support for extending RBD attached volumes using the libvirt network volume driver. ### rocky The [workarounds]/ensure_libvirt_rbd_instance_dir_cleanup
configuration option has been introduced. This can be used by operators to ensure that instance directories are always removed during cleanup within the Libvirt driver while using [libvirt]/images_type = rbd
. This works around known issues such as bug 1414895 when cleaning up after an evacuation and bug 1761062 when reverting from an instance resize.
Operators should be aware that this workaround only applies when using the libvirt compute driver and rbd images_type as enabled by the following configuration options:
[DEFAULT]/compute_driver = libvirt
[libvirt]/images_type = rbd
### queens
Nova now requires Ceph/librados >= 11.1.0 if running under Python 3 with the RBD image backend for the libvirt driver. Requirements for Python 2 users or users using a different backend remain unchanged.
QEMU 2.6.0 and Libvirt 2.2.0 allow LUKS encrypted RAW files, block devices and network devices (such as rbd) to be decrypted natively by QEMU. If qemu >= 2.6.0 and libvirt >= 2.2.0 are installed and the volume encryption provider is ‘luks’, the libvirt driver will use native QEMU decryption for encrypted volumes. The libvirt driver will generate a secret to hold the LUKS passphrase for unlocking the volume and the volume driver will use the secret to generate the required encryption XML for the disk. QEMU will then be able to read from and write to the encrypted disk natively, without the need of os-brick encryptors.
rbd_secret_uuid
configuration option.total_capacity
reported by the driver to the scheduler on Ceph clusters that have renamed the bytes_used
field to stored
. (e.g., Nautilus).When the Ceph backup driver is used for the backup service, restoring a backup to a volume created on a non-RBD backend fails. The cinder team has developed a fix but decided to do more thorough testing before including it in a release. When ready, the solution is expected to be backported to a future release in the Yoga series. The issue is being tracked as Bug #1895035.
RBD driver: Enable Ceph V2 Clone API and Ceph Trash auto purge
In light of the fix for RBD driver bug #1941815, we want to bring the following information to your attention.
Using the v2 clone format for cloned volumes allows volumes with dependent images to be moved to the trash - where they remain until purged - and allow the RBD driver to postpone the deletion until the volume has no dependent images. Configuring the trash purge is recommended to avoid wasting space with these trashed volumes. Since the Ceph Octopus release, the trash can be configured to automatically purge on a defined schedule. See the rbd trash purge schedule
commands in the rbd manpage.
RBD driver bug #1941815: Fixed deleting volumes with snapshots/volumes in the ceph trash space.
RBD driver bug #1916843: Fixed rpc timeout when backing up RBD snapshot. We no longer flatten temporary volumes and snapshots.
RBD driver bug #1947518: Corrected a regression caused by the fix for Bug #1931004 that was attempting to access the glance images RBD pool with write privileges when creating a volume from an image. ### Xena
Bug #1931004: Fixed use of incorrect stripe unit in RBD image clone causing volume-from-image to fail when using raw images backed by Ceph.
已知问题 - Anomalies with encrypted volumes
For the most part, users are happy with the cinder feature [Volume encryption supported by the key manager](https://docs.openstack.org/cinder/wallaby/configuration/block-storage/volume-encryption.html). There are, however, some edge cases that have revealed bugs that you and your users should be aware of.First, some background. The Block Storage API supports the creation of volumes in gibibyte (GiB) units. When a volume of a non-encrypted volume type of size _n_ is created, the volume contains _n_ GiB of usable space. When a volume of an encrypted type is requested, however, the volume contains less than _n_ GiB of usable space because the encryption metadata that must be stored within that volume in order for the volume to be usable consumes an amount of the otherwise usable space.Although the encryption metadata consumes less than 1% of the volume, suppose that a user wants to retype a volume of a non-encrypted type to an encrypted type of the same size. If the non-encrypted volume is “full”, we are in the position of trying to fit 101% of its capacity into the encrypted volume, which is not possible under the current laws of physics, and the retype should fail (see [Known Issues](https://docs.openstack.org/cinder/wallaby/configuration/block-storage/volume-encryption.html) for volume encryption in the cinder documentation).(Note that whether a volume should be considered “full”, even if it doesn’t contain exactly _n_ GiB of data for an _n_ GiB volume, can depend upon the storage backend technology used.)A similar situation can arise when a user creates a volume of an encrypted volume type from an image in Glance. If the image happens to be sized very close to the gibibyte boundary given by the requested volume size, the operation may fail if the image data plus the encryption metadata exceeds the requested volume size.So far, the behavior isn’t anomalous; it’s basically what you’d expect once you are aware that the encryption metadata must be stored in the volume and that it consumes some space.We recently became aware of the following anomalies, however, when using the current RBD driver with a Ceph storage backend.- When creating an encrypted volume from an image in Glance that was created from a non-encrypted volume uploaded as an image, or an image that just happens to be sized very close to the gibibyte boundary given by the requested volume size, the space consumed by the encryption header may not leave sufficient space for the data contained in the image. In this case, the data is silently truncated to fit within the requested volume size. - Similarly, when creating an encrypted volume from a snapshot of an encrypted volume, if the amount of data in the original volume at the time the snapshot was created is very close to the gibibyte boundary given by the volume’s size, it is possible for the data in the new volume to be silently truncated. Not to put too fine a point on it, silent truncation is worse than failure, and the Cinder team will be addressing these issues in the next release. Additionally (as if that isn’t bad enough!), we suspect that the above anomalies will also occur when using volume encryption with NFS-based storage backends, though this has not yet been reported or confirmed.
RBD driver: Prior to this release, the Cinder project did not have a statement concerning what versions of Ceph are supported by Cinder. We hereby announce that:
For a given OpenStack release, Cinder supports the current Ceph active stable releases plus the two prior releases.
For any OpenStack release, it is expected that the versions of the Ceph client and server are in alignment.
The Ceph RADOS Block Device (RBD) driver documentation has been updated to reflect this policy and explains it in more detail.
Ceph/RBD volume backends will now assume exclusive cinder pools, as if they had rbd_exclusive_cinder_pool = true
in their configuration.
This helps deployments with a large number of volumes and prevent issues on deployments with a growing number of volumes at the small cost of a slightly less accurate stats being reported to the scheduler.
RBD driver bug #1907964: Add support for fast-diff on backup images stored in Ceph. Provided fast-diff is supported by the backend it will automatically be enabled and used. With fast-diff enabled, the generation of diffs between images and snapshots as well as determining the actual data usage of a snapshot is speed up significantly.
Bug 1913449: Fix RBD driver _update_volume_stats() failing when using Ceph Pacific python rados libraries. This failed because we were passing a str instead of bytes to cluster.mon_command()
Ceph/RBD: Fix cinder taking a long time to start for Ceph/RBD backends. (Related-Bug #1704106)
Ceph/RBD: Fix Cinder becoming non-responsive and stats gathering taking longer that its period. (Related-Bug #1704106)
Supported Ceph versions
The Cinder project wishes to clarify its policy concerning what versions of Ceph are supported by Cinder.
For a given OpenStack release, Cinder supports the current Ceph active stable releases plus the two prior releases.
For any OpenStack release, it is expected that the versions of the Ceph client and server are in alignment.
The Ceph RADOS Block Device (RBD) driver documentation has been updated to reflect this policy and explains it in more detail.
RBD driver Bug #1898918: Fix thread block caused by the flatten operation during cloning a volume. Now the flatten operation is executed in a different thread.
RBD driver bug #1901241: Fixed an issue where decreasing the rbd_max_clone_depth
configuration option would prevent volumes that had already exceeded that depth from being cloned.
RBD driver: the rbd_keyring_conf
configuration option, which was deprecated in the Ussuri release, has been removed. If it is present in a configuration file, its value will silently be ignored. For more information, see OSSN-0085: Cinder configuration option can leak secret key from Ceph backend.
Bug #1828386: Fix the bug that a volume retyped from another volume type to a replicated or multiattach type cannot have replication or multiattach enabled in rbd driver.
Bug #1873738: RBD Driver: Added cleanup for residue destination file if the copy image to encrypted volume operation fails. ### Ussuri
RBD driver: support added for reverting a volume to the most recent snapshot taken.
Please be aware of the following known issues with this operation and the Ceph storage backend:
Rolling back a volume to a snapshot overwrites the current volume with the data from the snapshot, and the time it takes to complete this operation increases with the size of the volume.
It is faster to create a new volume from a snapshot. You may wish to recommend this option to your users whose use cases do not strictly require revert-to-snapshot.
The efficiency of revert-to-snapshot is also dependent upon the Ceph storage backend in use, namely, whether or not BlueStore is being used in your Ceph installation.
Please consult the Ceph documentation for details.
Catch argument exceptions when configuring multiattach for rbd volumes. This allows multiattach images with flags already set to continue instead of raising an exception and failing.
Rbd replication secondary device could set different user and keyring with primary cluster. Secondary secret_uuid value is configed in libvirt secret, and libvirtd using secondary secret reconnect to secondary cluster after Cinder failover host.
Fixed issue where all Ceph RBD backups would be incremental after the first one. The driver now honors whether --incremental
is specified or not. ### stein
RBD driver has added multiattach support. It should be noted that replication and multiattach are mutually exclusive, so a single RBD volume can only be configured to support one of these features at a time. Additionally, RBD image features are not preserved which prevents a volume being retyped from multiattach to another type. This limitation is temporary and will be addressed soon.
Add support for deferred deletion in the RBD volume driver. ## glance
除Newton版本全部没有cephfs/rbd/cephfs关键词日志
Red Hat Ceph Storage Life Cycle - Red Hat Customer Portal
应该是17版本
Chapter 3. New features Red Hat Ceph Storage 6.0 | Red Hat Customer Portal
librbd
plugin named persistent write log cache to reduce latencylibrbd
plugin named Persistent Write Log Cache (PWL) provides a persistent, fault-tolerant write-back cache targeted with SSD devices. It greatly reduces latency and also improves performance at low io_depths
. This cache uses a log-ordered write-back design which maintains checkpoints internally, so that writes that get flushed back to the cluster are always crash consistent. Even if the client cache is lost entirely, the disk image is still consistent; but the data will appear to be stale.这个是在quincy版本才支持的. 所以这个版本还是太新了. * - The allocation metadata is removed from RocksDB and now performs a full destage of the allocator object with the OSD allocation. - With cache age binning, older onodes might be assigned a lower priority than the hot workload data. See the Ceph BlueStore for more details.
不看6了...
应该是15或者16
只支持容器化集群
bluestore支持了4K粒度, With this release, the default value of BlueStore’s min_alloc_size
for SSDs and HDDs is 4 KB. This enables better use of space with no impact on performance.
Sharding of RocksDB database using column families is supported
rbd
Improved librbd small I/O performance
Previously, in an NVMe based Ceph cluster, there were limitations in the internal threading architecture resulting in a single librbd client struggling to achieve more than 20K 4KiB IOPS.
With this release, librbd is switched to an asynchronous reactor model on top of the new ASIO-based neorados API thereby increasing the small I/O throughput potentially by several folds and reducing latency.
Built in schedule for purging expired RBD images
Previously, the storage administrator could set up a cron-like job for the rbd trash purge
command.
With this release, the built-in schedule is now available for purging expired RBD images. The rbd trash purge schedule add
and the related commands can be used to configure the RBD trash to automatically purge expired images based on a defined schedule.
See the Defining an automatic trash purge schedule section in the Red Hat Ceph Storage Block Device Guide for more information.
支持内建的trash周期清理等逻辑.
Overriding read-from-replica policy in librbd clients is supported
Online re-sparsification of RBD images
Previously, reclaiming space for image extents that are zeroed and yet fully allocated in the underlying OSD object store was highly cumbersome and error prone. With this release, the new rbd sparsify
command can now be used to scan the image for chunks of zero data and deallocate the corresponding ranges in the underlying OSD object store.
ocf:ceph:rbd cluster resource agent supports namespaces
Previously, it was not possible to use ocf:ceph:rbd cluster resource agent for images that exist within a namespace.
With this release, the new pool_namespace
resource agent parameter can be used to handle images within the namespace.
RBD images can be imported instantaneously
With the rbd import
command, the new image becomes available for use only after it is fully populated.
With this release, the image live-migration feature is extended to support external data sources and can be used as an alternative to rbd import
. The new image can be linked to local files, remote files served over HTTP(S) or remote Amazon S3-compatible buckets in raw
, qcow
or qcow2
formats and becomes available for use immediately. The image is populated as a background operation which can be run while it is in active use.
Snapshot-based mirroring of RBD images
The journal-based mirroring provides fine-grained crash-consistent replication at the cost of double-write penalty where every update to the image is first recorded to the associated journal before modifying the actual image.
With this release, in addition to journal-based mirroring, snapshot-based mirroring is supported. It provides coarse-grained crash-consistent replication where the image is mirrored using the mirror snapshots which can be created manually or periodically with a defined schedule. This is supported by all clients and requires a less stringent recovery point objective (RPO).
看起来是13
bluestore稳定
Asynchronous recovery for non-acting OSD sets Previously, recovery with Ceph was a synchronous process by blocking write operations to objects until those objects were recovered. In this release, the recovery process is now asynchronous by not blocking write operations to objects only in the non-acting set of OSDs. This new feature requires having more than the minimum number of replicas, as to have enough OSDs in the non-acting set.
The new configuration option, osd_async_recovery_min_cost
, controls how much asynchronous recovery to do. The default value for this option is 100
. A higher value means asynchronous recovery will be less, whereas a lower value means asynchronous recovery will be more.
Introduction of diskprediction
module
Erasure coding for Ceph Block Device
RBD performance monitoring and metrics gathering tools
Cloned images can be created from non-primary images
Creating cloned child RBD images from mirrored non-primary parent image is now supported. Previously, cloning of mirrored images was only supported for primary images. When cloning golden images for virtual machines, this restriction prevented the creation of new cloned images from the golden non-primary image. This update removes this restriction, and cloned images can be created from non-primary mirrored images.
Segregating RBD images within isolated namespaces within the same pool
RBD images can now be segregated within isolated namespaces within the same pool. When using Ceph Block Devices directly without a higher-level system, such as OpenStack or OpenShift Container Storage, it was not possible to restrict user access to specific RBD images. When combined with CephX capabilities, users can be restricted to specific pool namespaces to restrict access to RBD images.
Moving RBD images between different pools within the same cluster
This version of Red Hat Ceph Storage adds the ability to move RBD images between different pools within the same cluster. For details, see the Moving images between pools section in the Block Device Guide for Red Hat Ceph Storage 4.
Long-running RBD operations can run in the background
Long-running RBD operations, such as image removal or cloned image flattening, can now be scheduled to run in the background. RBD operations that involve iterating over every backing RADOS object for the image can take a long time depending on the size of the image. When using the CLI to perform one of these operations, the rbd
CLI is blocked until the operation is complete. These operations can now be scheduled to run by the Ceph Manager as a background task by using the ceph rbd task add
commands. The progress of these tasks is visible on the Ceph dashboard as well as by using the CLI.
Red Hat Ceph Storage 3 这个比较像是12
Chapter 3. New features Red Hat Ceph Storage 3.3 | Red Hat Customer Portal
ceph-ansible
The new device_class
Ansible configuration option
With the device_class`feature, you can alleviate post deployment configuration by updating the `groups_vars/osd.yml
file in the desired layout. This feature offers you multi-backend support by avoiding to comment out sections after deploying Red Hat Ceph Storage.
The default BlueStore and BlueFS allocator is now bitmap
New omap
usage statistics per PG and OSD
Listing RADOS objects in a specific PG
]]>filestore 在 Pacific 和 Quincy 中被弃用,在 Reef 中被完全移除
bluestore目前暂不知道什么时候会移出, 但是期望是将来seastore能够替代他. 也许会发生一些不可预料的新内容让他变得更好.
目前, 根据tracker和telemetry的统计, bluestore已经成熟了. 目前进入维护阶段
其中telemetry提供的错误会进入tracker队列的顶端. 而一些只提了单子, 但没有信息的单子就暂时没有办法处理.
更进一步的, 则是在bluestore的performance meeting例会和头脑风暴例会上进行讨论.
关于Onode的引用计数, 关于Onode的缓存. 很恼人, 不过不够critical, osd自动重启后数据没问题.
问题点 * 引用技术为0时自动清理, 很多版本中都很普遍而且我们甚至无法向后移植它. * 在Octopus版本中, 在引入技术为2时, 我们需要额外的操作. * 除了引用计数外, 还存活在了缓存中. 如果被pinned, 无法修改他. 只有unpined才能修改, 但是unpin依赖lock.
这个问题花了9条PR, 其中4条真的进入的后续版本, 直到Igor真的解决了, 但不是从技术上解决的.
做了个从缓存中lazy unpining的功能. 所以可以使他正常工作.
我们之所以不能回滚掉他, 是因为基于他有太多内容已经变更. 如果有个解决方案, 我们会考虑, 但是Pacific和Quincy版本的解决方案将有所不同.
rocksdb损坏后, 没有办法还原时, OSD只能重新部署, 没有办法绕过他. 有两个原因
问题来源非常早, 来源于linux manual中说的不应该同时使用direct和buffered mode.
我们没有这么做, 但是我们保证打开同一个设备, 但永远不会使用read buffered和read direct读取同一个block device. 除了bluefs log.
这个让我们付出了代价, 当你重启设备/osd, 他启动的非常快时, 你可能无法取得你刚才写到bluefs log上的数据. 其中一些对rocksdb表的更新,
结果是我们在Nuatilus中引入了另一个问题, 我们修改了bluestore和bluefs只建传输的内存的部分. 目前为止这部分是不可见的.
TODO: 暂未找到这条PR的单子.
os/bluestore: enable 4K allocation unit for BlueFS by ifed01 · Pull Request #48854 · ceph/ceph
这个问题今年刚在master分支修复, 会backport到pacific分支, 但尚未完毕, 值得关注.
具体指代的是block 上仍有空间, 但是碎片化, 没有能提供给bluefs的满足对齐要求的空间.
这个问题最早在luminous版本, 我们做了个flip-flop
触发器, 在block和bluefs之间gifting
和reclaiming
数据, 1M对齐显然是可怕的, 因此我们选择了64K 的chunk. 这部分持续了很久, 最终我们现在有个支持4K对齐的bluefs.
可能有人会问为什么设置一个4K的bluefs chunk 需要这个久. 这是个好问题.
最早创造bluefs的时候, 有一个bluefs启动的4K的超级块. 当启动时, 从一个4K扇区读取重放日志. 如果chunks大小很大, 你的log就不可能会非常大.
但是如果是4K大小的chunks, logs就很有可能被用尽. 很快就可以得到非常零碎的bluefs log. 因此,4k大小时, 只是不适合那个大小.
所以现在, 通过special trampoline multi-stage booting of bluefs log
可以实现了(待理解, 暂未在PR中找到解释的关键词), 性能在大量碎片时当然不太好, 但是不会再失败了. 不再会有半夜因多个osd bluefs空间不足被叫起来, 不得不去迁移数据的情况.
为什么这些很重要, 因为这样我们就有更多时间去给bluestore添加新功能.
这里有一张完整功能规划的图. 这些如果实现了将提供巨大的质量改进. 其中有一些次要优先级, 但是简单一些的.
早期我们引入了stupid 分配器, 实际上这个名字我觉得不太好, 功能很简单表现也很不错.
在我们测试过程中发现他创建了很多碎片, 可用空间碎片化之后, 只能期望你的新对象也是碎片化的, 自然性能就会下降, 甚至会导致bluefs内部空间也无法回收.
一般在性能下降之前, bluefs就会告诉你空间不足了.
这是个现实中的例子, 可能是你们中的一个集群.
在固定地点长期运行之后的图
这个图不是截断的, 就是19个allocation unit. 会看到长期运行之后, 大部分对象都在少数分配单元中.
这张图表达的不是对数也不是线性刻度, 单纯表达的有数量级差异.
之所以这样说, 是因为当时用户需要我们从no space的情况下将数据救回来.
然后, 如果你想要你的对象分段像这样连续, 选择bitmap分配器. 但是你的对象会像下面这样, 所以最终并没有性能提升
我们真的不知道, 因为有个基于分片数量的wired碎片打分. 这个确实告诉了你bluefs的内部空间条件, 不过现在他不再有用.
所以我们需要一张直方图.不过即便我们有碎片, 也并不是很明显有多少成本. 如果你正在运行RBD, 你是以4MB的块以某种方式碎片化. 他们在不同的时间被覆盖. 如果RBD的对面正在运行任何文件系统, 仍旧是没问题.
如果chunks代表不同的文件, 只要不发生同时读取他们的事情, 你仍旧不会受到碎片的影响.
TODO: why?
当然Romin会抱怨, 在scrub的时候, 所有的对象都是red, 会拖慢scrub的速度.
我认为从这个过程中, 我们会得到一个感觉, 也许应该只修复真正热点的对象的碎片化.
待会会有张关于碎片的幻灯片.
现在让我讲下其他的主题 #### rocksdb
当我们合并rocksdb给bluestore时, 我们实现了我们自己的block cache , 因为我们需要内存管理. 现在我们的block cache和其他的cache之间循环,这部分要感谢Mark Nelson, 目前这部分是个非常好的动态过程, 对于最热点地区的goals和gifts是最令人难忘的.
但是似乎有些东西在和block cache运作时令人感到strinky(恶臭的). 在一些情况下我们似乎并没有真正使用block cache , 但是莫名其妙的提供大量相等于Block cache数量的linux page cache可以解决性能问题.
所以要么是我们错误的实现了它, 我们只是缓存了它, 然后我们并没有在rocksdb想要时, 给回它.
亦或是其他的一些错误导致我们无法归还它. 这个问题已经很长时间了.
为什么block cache非常重要呢? 因为如果不缓存数据, 迭代是慢的. 并且在rocksdb中迭代可能会花很多时间, 并且很多人抱怨甚至到了试图删除空的collection的度. 这个是删除PG的最后一步, 它最后一次尝试遍历只是为了检查是否真的是空的. 当所有内容被删除时, 有很多墓碑, 这些只是thmb stones, 但它仍然需要花很多时间来做这个.
em, 我听到一个好消息, 我认为是时候承认Corey Snyder在改进迭代器方面迈出了非常好的一步.
因为他的一条PR, 使用range iterators, 在大部分场景下, 可以把事情分开进行.
但如果我们遇到迭代器问题, 我们可能会使用collection_list 的方案来解决它.
不过暂时还不知道怎么去做他, 不颠覆当前的架构.
因为我们当前确实从把所有内容放在一个地方并使用rocksdb迭代中收益良多.
也许其中一个解决方案可能是用speedb替换rocksbd. speedb有一个模式, 他们告知了我, 我也测试了一下, 姑且让我们假设它正常工作, 当你区分了key 流和 value 流, 它能够做什么?
如果我们把他用在onode collection, 即便我们只有某个范围内的墓碑, 它会是个非常短的表. 它只包含名称, 而名称在rocksdb中可以被很好的压缩. 所以它应该会快得多. 当然这还只是个想法
另一个spliiover revert. 溢出是我们很不喜欢的一种情况, 但你有一个非常慢的HDD, 你用nvme/ssd的一部分来给他备份. 这不是一个很好的组合,
然后没有空间放你的rocksdb的meta, 它必须使用hdd上的临时空间, 这个不会出问题. 只是非常慢, 问题发生在当他只是个过渡性质的溢出. 因为如果你空间真的多于容量, 那没有什么可以解决的. 但是如果你压缩一个db刚好比你的数据库空间一半大一点. 它可能会溢出到慢速设备, 并且它会在那停留很长一段时间. 因为如果它碰巧是一个非常深的压缩, 影响了很多层级. 你的新SSd文件将驻留很长时间, 而溢出恢复只是简单的从慢速设备移动回来到db的概念.
正如你们所知, bluefs files可以驻留在这些设备中的任何一个上, 只是物理上执行复制并切换extents到新的形状上, 这部分没问题.
棘手的部分是如何做出复制什么的决定, 这很困难. 因为我们没有跟踪, 并不区分hot SST tables ,也许叫做SSD tables. 亦或是我期望的情况, 甚至可能是某些部分的一部分SSD tables很热, 与此同时其他表很慢. 但我们可能会忽略它, 只是为了简化一些其他的东西.
可能有人会问, 为什么这里会有这个深奥的功能
因为任何原因, bluestore失败时, 你想要救回一些PG数据, 真的很难使用ceph-objectstore-tool来做到这件事.
举例来说, 你进入了一个真的没有空间继续写的情况下, rocksdb需求更多空间, 而你的设备确实没有了. 你不能用ceph-objectstore-tool去挂载rocksdb, 并解压导出数据.
因为bluestore期望待定名字
完成所有pending压缩, 不同的权限等等. 所以你被困在了这个点上, 只是想做一点点修改.
所以你想得到一个只读的bluestore, 只需要保持冻结状态, 可以加载到内存里, 在内存里的rocksdb, 从而可以将数据提取到另一个osd的superblock中.
我期望这件事不会发生在你们身上, 但是确实发生在了很多人身上.
OSD无法启动, 因为OSD写入的第一个对象是OSD超级块,因为对于他来说, 选中它知道他自己的状态并继续是需要的. 有一些技术可以恢复, 不过这部分还是相对困难的, 成功率不是100%. 现在OSD 超级块, 再次感谢Igor, 将位于多个地方, 包括rocksdb本身, 并且代码也变了, 对象通常处在磁盘开头的对象, 现在也可能存在不同的地方. 在大多数环境中, 超级块都处于磁盘头, 因为它需要占用第一个分配单元, 并且它总是被更新, 且他很小. 它总是进入不同的权限路线, 所以它只是被覆盖了, 难怪osd开头的任何损坏都会引起osd的超级块崩溃.
一个我最喜欢的例子是, 你如何下放一个功能, 很长时间以来真的不可能挂载bluestore 2次, 我们使用flock, 管理资源独占锁来实现挂载bluestore 2次. 但当容器化的时候, 它就变得无用了. 因为容器化环境也区分了那些锁, 所以如果你有一个讨厌的情况, 你尝试运行你的容器2次, 你得到它, 并且你的超级块损坏了, 因为其中一个覆盖了第二个, 这也是一个问题我们已经解决了的.
我现在除了locking之外, 我们还使用了额外的方式.
为什么不使用内核级特殊处理, 是因为打开块设备独占, 对于其他所有块设备的工作方式梧桐, 我无法打破它. 如果有人能够打破那个保护, 麻烦告知我.
TODO: osd启动时有peek_meta, 不知道是不是这个
在CD中, 我们需要清理的, mount/umount顺序是一点, 我们存放可配置的地方bluefs, rocksdb ,RO, 关闭/打开, 读分配. 现在可以简称 free list的清理.
以及Greg实现的一个非常好的功能, 删除rocksdb关于分配的升级. 所以我们现在节省了很多时间在kv sync thread上, 不更新位置.
我真正想做的事一对对检查, 我不知道admin socket 最有可能或者不同的类型, 对我来说, 就是这样, 好奇我需要看看我的元数据是什么样的, 我希望能在运行时关联它, 我不想运行一些调试登记的bluestore只是为了看看对象在磁盘上是如何结构化的, 此时所有osd不得不回复我.
我自己的笔记, 我感兴趣的事我的SharedBlobs是如何拆分, 等等.
和运行时检查相同, 但是也区分在线或者撩闲. 当然runtime cache/buffers 现在基本上更好, 也很有价值对于碎片化的报告.
我想要直方图, 以及分裂的热点区域是什么, 碎片化的可能成本是多少.
所以这部分是接下来碎片化最有可能完成的事情, 它将包括自由空间的碎片和应该可以再一次运行中完成的对象, 我们正在考虑使用一种算法来迭代的提供一些迭代改进, 我们不希望一个全局的操作来操作OSD每周半小时, 期望它内联的运行, 或者如果我们提供更好的分配器和更好的策略, 我们跳过他, 新的OSD对于现有的部署不会那么碎片化, 也许我们在想, 对于本地化分配会有所帮助. 意味着, 吸纳关系那改一下, 你把可用磁盘空间分配给某些区域, 如果你需要空间, 你想要获得接近分配大小的卡攻坚, 但是你真的想要接近这个对象的空间爱你, 你已经知道对象的所有其他数据, 是否相信他不是这种情况, 我们只是在分配时, 我们不知道请求它的对象在哪里. 我们的想法是即便对于spinner, 如果有个相同的cyliner, 我们仍然大体上还可以紧挨着他.
当然, 对于固态的策略并没有真正发挥作用, 更重要的是落盘可能不是以正确的顺序, 而是由于无论硬盘内部的架构如何, 你都可以一口气读到他.
以及如何做一些额外特别的scrub优化的一部分来完成, 事实上我们不知道.
目前我们正在和GSoC合作, 我们会创建一个模拟器, 尽可能模拟一些工作集. 想看到运行成年累月之后的垃圾数据之后的表现.
因为很不想bluestore代码对于实验不够好. 你只有实现模拟器才能让我们有可能以最小的成本来测试不同的分配器或则和不同的策略.
最后一个是碰巧针对HDD, 我有一个问题如何通过微调来预测实际延时.
这是一种模式允许你, 构造删除很多数据, 只更新元数据的集群, 当我想要看看我的硬盘在一年的数据碎片化之后的表现, 我只需要切换模式, 插入真实设备, 他就会执行真实操作, 这样就可以作为真实的长时间运行后的测试工具.
我们可能会抛弃当前的数据结构, 摆脱我们目前的所有缺陷, 并让他离线.
这样我们可以平衡你想要让他使用多少cpu, 选择只有冷数据进行压缩, 这样我们就不再会遇到那种奇怪的情况, 我们应该使用3个分配单元, 但是实际却使用了6个. 这是我们内部组织blob的方式. 这对我们来说更好. 而且在适应之后, 来自前面那一部分的模拟器应该能够给我们预测使用惰性压缩有多好.
然后以一个额外的概念, 来自Igor. 这是一个自定义WAL, 意味着我们删除了当前场景, rocksdb正在实现一个headlock , 我们在这里有延时. 在这条竖线后面, 我们可以通知用户事务已经结束.
我们可以修改它, 并提供我们自己的WAL日志, 我们在kvsync线程总并行处理主表, 同时我们也可以压缩他. 我们队数据做了一个fdat, 所以他更早的匹配, 是为了发出事务最终完成的信号, 取决于这里写操作可以像读其他任何我可能错的内容一样执行.
这个概念是为了进一步减少经过rocksdb的数据量. 我们将为PG log创建一个特殊通道,而不是像当前那样讲PG log更新作为新的简直更新到rocksdb中, 每个他们都会写入一些特殊的环形对象, 也许带有一个写headlock. 目前这部分还没有决定, 但我们会卸载掉那个关键的东西,
因为我想大家都知道kvsync线程是个关键的部分. 在bluestore中产生延时.
这是个简单的解释, 我们当前的默认机制太粗糙了, 想象一下, 你追加到你已经写了一些数据的对象, 你追加数据, 现在有一个扇区需要被替换, 但是那个进入了不同的机制, 但是所有其他的只是分配, 这是讲不通的. 因为如果你有spinner, 意味着你必须访问2个地方. 但是实际上你可以只访问其中一个地方
毫无疑问, 这是一个完全不同的东西, 我们正在考虑制作一个新的延迟机制. 不进保留在rocksdb中derred的对象, 那部分应该只保留一部分时间, 不然会不得不被rocksdb写入SST, 产生巨大的写惩罚, 我们想保留一个特殊的延时缓存, 并添加一个长期使用的快速分区, 这样你就可以有10/20/50G的数据, 将你的数据缓存在一个慢速设备中, 我们将允许保留它, 只在有一些迭代过程时, 才触发清理. 这样有序的进行, 好的部分是缓存的任何对象都指向已经分配到某处扇区, 所以没有注入什么奇怪的逻辑.
]]>Mark Nelson: Performance / CBT模块成员
8192*3/60=409.6
个pg, 相比正常理解里的100, 差挺多的2048*3/60=102.4
, 倒是正常pg数少时的效果似乎与蒙特卡洛算法有关?
Random distributions look clumpy at low samples counts. Higher PG counts results in a more even workload and data distribution across OSDs. The new primary workload balancer being developed by Josh Salomon and Laura Flores will attempt to improve the distribution even at low PG counts.
Is distribution quality the only explanation for the performance differences though?
主pg的均衡, 有助于在pg数少时提高性能.
似乎是在shared-blob上做了一些努力, 碎片整理等? 以及不用遍历所有的元数据了.
Why does performance level off, then drop, as the number of threads per shard increases? A key point is that there is only 1 messenger thread in this test. With more messenger threads, the effect diminishes.
shard_lock contention? Possibly. We might spend too much time in notify_one / notify_all with relation to the wait_lock. More testing needed.
主要是msgr线程数也需要考虑匹配. 具体原因还需要更多实验.
主要集中在下述2个场景, 针对2个的冲突, 修改了一些rocksdb逻辑, 会在reef版本上车.
期望能达成无需使用rocksdb的 head log, 初步看到效果单OSD 12W
可能会出现一个情况, 由于迭代大量的删除墓碑, 可能导致osd 心跳超时
逻辑比较简单, 只是迭代过程中, 看到一定数量的墓碑, 然后设置了强制压缩条件, 则触发压缩.
这个图片是DO提供的, 看延时应该是个非常大压力的hdd集群, 60s 延时
只修改memtable size 会导致性能下降
但是如果配合同时修改level 0/1等的size大小到恰当好处, 即从memtable->level0->level1控制的很好, 效果应该会很好, 但是这个场景很复杂. 我们后续可以考虑.
确实有帮助, 值得做.
]]>背景是通过perf捕获ceph-osd的cpu消耗, 发现在下述环节极高.
1 | --10.64%--PG::_prepare_write_info |
由于找不到现场版本的debuginfo
了, 因此姑且先推导一下.
interset的==
, 对应是map的==
, _Rb_tree_increment
初步定位应该是 map的迭代器调用的?
1 | bool operator==(const interval_set& other) const { |
1 | _Self& |
根据上述Map的内部实现来看, 基本就是他了, 比较map, 根据下述stl实现来看, 确实是operator==
产生的大量迭代
1 | //迭代器基类 |
ceph的快照删除官方文档如下, 因此purged_snaps
高在业务场景很正常.
SNAP REMOVAL To remove a snapshot, a request is made to the Monitor cluster to add the snapshot id to the list of purged snaps (or to remove it from the set of pool snaps in the case of pool snaps). In either case, the PG adds the snap to its snap_trimq for trimming.
A clone can be removed when all of its snaps have been removed. In order to determine which clones might need to be removed upon snap removal, we maintain a mapping from snap to hobject_t using the SnapMapper.
See PrimaryLogPG::SnapTrimmer, SnapMapper
This trimming is performed asynchronously by the snap_trim_wq while the pg is clean and not scrubbing.
The next snap in PG::snap_trimq is selected for trimming
We determine the next object for trimming out of PG::snap_mapper. For each object, we create a log entry and repop updating the object info and the snap set (including adjusting the overlaps). If the object is a clone which no longer belongs to any live snapshots, it is removed here. (See PrimaryLogPG::trim_object() when new_snaps is empty.)
We also locally update our SnapMapper instance with the object’s new snaps.
The log entry containing the modification of the object also contains the new set of snaps, which the replica uses to update its own SnapMapper instance.
The primary shares the info with the replica, which persists the new set of purged_snaps along with the rest of the info.
RECOVERY Because the trim operations are implemented using repops and log entries, normal pg peering and recovery maintain the snap trimmer operations with the caveat that push and removal operations need to update the local SnapMapper instance. If the purged_snaps update is lost, we merely retrim a now empty snap.
目前可以通过ceph pg 2.892 query 查到对应pg的purged_snaps, 足足有2412条, 确实数量很大, 对应的迭代高也看来合理.
~~那剩下就是为啥有的osd 有这个的情况下cpu消耗不高了? ~~该问题可忽略, 后定位节点间cpu型号性能有差异, osd cpu消耗低的节点cpu性能确实更好.
_prepare_write_info
的 call trace的触发逻辑?主要是pglog
的正常IO处理流程
1 | removed_snaps_queue [8f1~1,8f4~1] |
查看16版本环境, 发现好像是改成了removed_snaps_queue, 且purged_snaps在pg query
里也看不到了.
初步看, 虽然pg_info还比较purged_snaps
, 但是这项大部分时间为空了. 即不存在该版本的问题了
根据这条PRmon,osd,osdc: refactor snap trimming (phase 1) by liewegas · Pull Request #18276 · ceph/ceph, 提到曾经设计在这里提到过 Ceph Etherpad 时间轴 (注意, 只有4697版本可以看, 之后2020年被人用了机翻.)
在19年, 15版本分支中做了73条commit修改, osd,mon: remove pg_pool_t::removed_snaps by liewegas · Pull Request #28330 · ceph/ceph (github.com)
看Phase 2和3: remove SnapSet::snaps
, 好像在17版本已经准备去除了?
snaps are per-pool, so we should annouce deletion via OSDMap
successful purge is the intersection of all pool PGs purged_snaps
once a pool has purged, we can remove it from removed_snaps AND purged_snaps.
- this should also be announced via the OSDMap
mon and osd need the full removed set, but it can be global (and mostly read-only) once each pool has purged?
主要差异总结:
-- | planD | planC | planB | planA |
---|---|---|---|---|
osdmap相关 | 仅维护当前正在删除和删除完正在purged的snap | pg_pool_t增加recent_removed_snaps_lb_epoch, 维护该epoch后删除的snap列表 | pg_pool_t增加代表最早的deletion操作的removed_snaps_lb_epoch维护, new_purged_snaps维护在该epoch之后的 | 只维护deleted_snaps |
请求 | - | 请求参数中增加removed_snaps和purged_snaps的snapc | 请求参数中增加removed_snaps和purged_snaps的snapc | 维护一个序号, 仅当存在比这个序号低的snap信息, 才去访问old_purged_snaps, 否则忽略 |
pginfo | pginfo中维护removed_snaps和purged_snaps | - | - | |
mon | 维护删除操作的epoch | 维护删除操作的epoch, 并负责根据定义的窗口大小更新osdmap的recent_removed_snaps | 维护删除操作的epoch, 定义一个时间周期参数, 满足该周期, 聚拢最早的删除的快照, 从而更新removed_snaps_lb_epoch | 每个周期(如100个osdmap)更新purged_snaps的快照interval_set |
1 | 本次pr之前已增加了13版本的逻辑, 保留了12版本功能 |
TODO: 这里的15版本之前的兼容性是为了什么服务的呢?
osd/PG: use new mimic osdmap structures for removed, pruned snaps · ceph/ceph@6e1b7c4
在开始pr前, 通过该修改调整了osdmap等的格式?
1 | --- |
1 | --- |
osdmap更新
removed_snaps 归档
removed_snap_queue中
新增的newly_removed_snaps和newly_purged_snaps的维护
PGPool::update(CephContext *cct, OSDMapRef map)
PG::activate(
PG::publish_stats_to_osd()
PG::RecoveryState::Active::react(const AdvMap& advmap)
PGPool 初始化
pg_info_t中的removed_snaps清理
pg_stat_t中的removed_snaps维护
涉及pg激活流程? 以及快照删除状态机的流程
bool operator==(const pg_info_t& l, const pg_info_t& r)
PG::split_into(pg_t child_pgid, PG *child, unsigned split_bits)
PG::_prepare_write_info
PG::_scan_snaps(ScrubMap &smap)
着重观察 升级后 * mgr与osd通信的pg_stat_t新增对象的解析 * mon与osd的历史gap_removed_snaps的处理 * 查询与新增写入 * pg_info_t转到pg_stat_t * osdmap/pg_stat_t/pg_info_t/MOSDMap的编解码
稳定性 1. mgr升级后 1. 与低版本通信. 2. 全服务重启 3. 正常业务测试 2. mgr/mon升级后, 1. 与低版本通信 2. 全服务重启 3. 正常业务性能/功能接口测试 3. mgr/mon/osd局部升级后 1. 与低版本通信 2. 全服务重启 3. 正常业务性能/功能接口测试 4. mgr/mon/osd全部升级后 1. 与低版本通信 2. 全服务重启 3. 正常业务性能/功能接口测试 5. qa/unittest中, 是否存在相关测试集? 1. test_snap_mapper.cc 1. snaps 2. testPGLog.cc 1. pg_info_t中的purged_snaps
bool operator==(const pg_info_t& l, const pg_info_t& r)
PG::split_into(pg_t child_pgid, PG *child, unsigned split_bits)
PG::_prepare_write_info
PG::_scan_snaps(ScrubMap &smap)
1 | --- |
1 | /** |
If the head is deleted while there are still clones, a snapdir object is created instead to house the SnapSet.
好像是mon响应慢了?
1 |
|
CEPH_SNAPDIR 说是通过clone保留了原始快照造出来的.
1 | rbd::Shell::execute() --> rbd::action::snap::execute_remove() --> rbd::action::snap::do_remove_snap() --> librbd::Image::snap_remove2() --> librbd::snap_remove() --> librbd::Operations<librbd::ImageCtx>::snap_remove() --> Operations<I>::snap_remove() --> librbd::Operations<librbd::ImageCtx>::execute_snap_remove() --> librbd::operation::SnapshotRemoveRequest::send() --> cls_client::snapshot_remove() --> ... --> 发送op给rbd_header对象所在的Primary OSD |
这里应该是通过事务生成pglog里用的update_snaps设置snaps,然后正常pg逻辑里从pglog merge的? merge是直接merge的purged_snaps, 所以这里update的snaps是怎么更新成purged_snaps呢? 应该不是
1 | } else if (r == -ENOENT) { |
1 | snap_trimmer_machine |
PGPool::update(CephContext *cct, OSDMapRef map)
Objecter::_prune_snapc(
operator==(const pg_stat_t& l, const pg_stat_t& r)
operator==(const pg_info_t& l, const pg_info_t& r)
PG::_prepare_write_info
Objecter::_scan_requests(
OSDMonitor::prepare_remove_snaps
MRemoveSnaps
void pg_pool_t::remove_snap(snapid_t s) { assert(snaps.count(s)); snaps.erase(s); snap_seq = snap_seq + 1; }
case POOL_OP_DELETE_SNAP: { snapid_t s = pp.snap_exists(m->name.c_str()); if (s) { pp.remove_snap(s); pending_inc.new_removed_snaps[m->pool].insert(s); changed = true; } } break;
Objecter::delete_pool_snap
PrimaryLogPG::kick_snap_trim()
snap_trimq应该是干活的, 这块状态机基本上没咋改应该.
忽略, 看错了. * osdmap的decode, 需要feature识别...但是有差别, 在快照前应该还有个v功能. 初步理解, 还是需要引入mimic这个版本的feature标记. 才能区分是老osd? 能不能不引入mimic, 直接引入feature识别?
SERVER_M 这个flag已经在了
12这个patch需要转换下. 把SERVER_MIMIC换成 SERVER_M
20这个patch需要查下pg_stat_t, 12版本是22, patch是23->24 涉及PR, 2条commit https://hub.nuaa.cf/ceph/ceph/pull/18058/commits
a25221e
结果这个修改, 立马就被重构掉了 osd_types.cc: don't store 32 least siognificant bits of state twice · ceph/ceph@0230fe6 · GitHub
也是2条commit的PR osd_types.cc: reorder fields in serialized pg_stat_t by branch-predictor · Pull Request #19965 · ceph/ceph · GitHub
ae7472f 0230fe6
目前初步看到的障碍是 , 12版本 snaptrimq_len还在invalid后面, 13-14也做了一此reorder, 其实也兼容
13版本是033d246 12版本是ca4413d
所以为了满足12版本的兼容性?
这里那12升13怎么保证兼容性?
根据ceph/osd_types.cc at nautilus · ceph/ceph · GitHub 可以知道14和12是保证了兼容性的.
pg_stat_t::decode(bufferlist::iterator &bl)
需要先合入osdmap增加的
27d6f43
中间0020左右, 手动把新的recovery_unfound给合入了, 暂时不知道为啥会合入这段, 手动合入了.osd_types.h的#define那段
然后0028, 在pg_pool_t::encode增加了v改到27 0030, PGMapDigest& get_digest 这个的修改依赖高版本将mon里的部分函数重构到mgr中的修改, 所以尝试性修改是维持不变. 根据12版本现状做处理
编译通过, 实际运行 2023-04-12 14:43:11.981804 7f44f6d7d700 0 log_channel(cluster) log [DBG] : pgmap v10: 192 pgs: 165 active+clean, 18 peering, 9 unknown; 180GiB data, 415GiB used, 1.20TiB / 1.61TiB avail 2023-04-12 14:43:12.871023 7f44fdd8b700 -1 failed to decode message of type 87 v1: buffer::malformed_input: void object_stat_collection_t::decode(ceph::buffer::list::iterator&) no longer understand old encoding version 2 < 122 2023-04-12 14:43:13.866212 7f4500d91700 -1 failed to decode message of type 87 v1: buffer::malformed_input: void object_stat_collection_t::decode(ceph::buffer::list::iterator&) no longer understand old encoding version 2 < 122 2023-04-12 14:43:13.982740 7f44f6d7d700 4 OSD 0 is up, but has no stats 2023-04-12 14:43:13.982742 7f44f6d7d700 2 wc osd 3avail: 71955388378 2023-04-12 14:43:13.982744 7f44f6d7d700 2 wc osd 4avail: 86134315413 2023-04-12 14:43:13.982744 7f44f6d7d700 2 wc osd 5avail: 61272017967 2023-04-12 14:43:13.982748 7f44f6d7d700 4 OSD 0 is up, but has no stats 2023-04-12 14:43:13.982749 7f44f6d7d700 2 wc osd 1avail: 827202140267
mgr无法处理osd发来的消息, 同理, osd上也有
搜到个scrub的测试脚本?
推荐的升级流程
The upgrade order starts with managers, monitors, then other daemons.
Each daemon is restarted only after Ceph indicates that the cluster will remain available.
compatv确实是2, 但是小于25, 那个25哪来的? 还有header.version的6 哪来的?
终于想起来了, mgr应该是没重新编译, mon也是
编译mgr没问题 ,mon需要 把PGMonitor.cc这里的int32_t改成 uint64_t, 因为pg数据结构改了 mempool::pgmap::unordered_map<uint64_t,int32_t> num_pg_by_state;
替换之后 , 崩在了
1 | ceph version 12.2.12 (1436006594665279fe734b4c15d7e08c13ebd777) luminous (stable) |
找到问题了,pg_stat_t里 之前合并purged_snaps合并错了, 应该是环境里的osd被我之前误启动已经污染了, 暂时跳过升级的验证项
先单纯验证功能
osdmap里 removed_snaps_queue 和pg query看不到pg_info里有purged_snaps.
好像是我新增的代码都没生效? 是要让他满足HAVE_FEATURES选项, 在OSDMap.cc里获取过程uint64_t OSDMap::get_encoding_features() const
if (require_osd_release < CEPH_RELEASE_MIMIC) { f &= ~CEPH_FEATURE_SERVER_MIMIC; }
monCommands.h里 require-osd-release需要增加 | mimic | | ----- | | 增加这段, 让她可以通过mimic
if (rel == osdmap.require_osd_release) { // idempotent err = 0; goto reply;}assert(osdmap.require_osd_release >= CEPH_RELEASE_LUMINOUS);if (rel == CEPH_RELEASE_MIMIC) { if (!osdmap.get_num_up_osds() && sure != "--yes-i-really-mean-it") { ss << "Not advisable to continue since no OSDs are up. Pass " << "--yes-i-really-mean-it if you really wish to continue."; err = -EPERM; goto reply; } if ((!HAVE_FEATURE(osdmap.get_up_osd_features(), SERVER_MIMIC)) && sure != "--yes-i-really-mean-it") {ss << "not all up OSDs have CEPH_FEATURE_SERVER_MIMIC feature";err = -EPERM;goto reply; }} else { ss << "not supported for this release yet"; err = -EPERM; goto reply;}
CEPH_FEATURES_ALL 增加MIMIC, 否则osd被判定为低版本的
合入了d9cd2d7 mon feature mimic的这个commit
ceph mon feature set mimic --yes-i-really-mean-it
目前没看到起到什么作用.
ceph features可以忽略, 即便是16/17, 打出来都是叫luminous
SIGNIFICANT_FEATURES 好像默认osdmap用的这个feature, 这里没加上MIMIC
启动后, pg_info里确实没了, pg_stat_t里确实也没看到,
] osd_snap / removed_epoch_1_00000f08 osd_snap / removed_epoch_39_00000f08 osd_snap / removed_epoch_39_00000f0b osd_snap / removed_epoch_39_00000f0e osd_snap / removed_epoch_39_00000f11 osd_snap / removed_epoch_39_00000f14 osd_snap / removed_epoch_39_00000f17 osd_snap / removed_epoch_39_00000f1a osd_snap / removed_epoch_39_00000f1d osd_snap / removed_epoch_39_00000f20 osd_snap / removed_epoch_39_00000f23 osd_snap / removed_epoch_39_00000f26 osd_snap / removed_epoch_39_00000f29 osd_snap / removed_epoch_39_00000f2c osd_snap / removed_epoch_39_00000f2f osd_snap / removed_epoch_39_00000f32 osd_snap / removed_epoch_39_00000f35 osd_snap / removed_epoch_39_00000f38 osd_snap / removed_epoch_39_00000f3b osd_snap / removed_epoch_39_00000f3e osd_snap / removed_epoch_39_00000f41 osd_snap / removed_epoch_39_00000f44 osd_snap / removed_epoch_39_00000f47 osd_snap / removed_epoch_39_00000f4a osd_snap / removed_epoch_39_00000f4d osd_snap / removed_epoch_39_00000f50 osd_snap / removed_epoch_39_00000f53 osd_snap / removed_epoch_39_00000f56 osd_snap / removed_epoch_39_00000f59 osd_snap / removed_epoch_39_00000f5c osd_snap / removed_epoch_39_00000f5f osd_snap / removed_epoch_39_00000f62 osd_snap / removed_epoch_39_00000f65 osd_snap / removed_epoch_39_00000f68 osd_snap / removed_epoch_39_00000f6b osd_snap / removed_epoch_39_00000f6e osd_snap / removed_epoch_39_00000f71 osd_snap / removed_epoch_39_00000f74 osd_snap / removed_epoch_39_00000f77 osd_snap / removed_epoch_39_00000f7a osd_snap / removed_epoch_39_00000f7d osd_snap / removed_epoch_39_00000f80 osd_snap / removed_epoch_39_00000f83 osd_snap / removed_epoch_39_00000f86 osd_snap / removed_epoch_39_00000f89 osd_snap / removed_epoch_39_00000f8c osd_snap / removed_epoch_39_00000f8f osd_snap / removed_epoch_39_00000f92 osd_snap / removed_epoch_39_00000f95 osd_snap / removed_epoch_39_00000f98 osd_snap / removed_epoch_39_00000f9b
1 |
|
修改action 1
2
git push origin --tags
或者直接找相对路径
Workflow syntax for GitHub Actions - GitHub Docs
处理成功之后, 相对还是比较简单的?
1 |
|
以前引入了这些组件, 待会升级时需要处理
1 | npm install -g npm-upgrade |
_config.yml
更新external_link
deprecated更改格式为以下
1 |
|
比如indigo
更换scarqin/hexo-theme-indigo: 维护 hexo-theme-indigo 自用
1 | ERROR Asset render failed: css/style.css |
发现style.css是空的
说明less生成失败了
折腾了半天, Valine.less文件有问题, 更新了上面repo里的文件就恢复正常了
.io
前缀的根据该issue可知Package hexo-asset-image doesn't return correct image path · Issue #4930 · hexojs/hexo, 是用了过时的hexo-asset-image
引起.卸载即可.
通过Hexo: wrong address for meta and img tags - Stack Overflow 更换了另一个插件cocowool/hexo-image-link: 当MD中引用本地文件时,处理生成的html中的图片链接。解决的.
]]>写本文的背景, 主要来源于项目开发过程中的一些问题, 属于是十分影响进度的那类.
这点, 个人理解只能是期望个人设计能力以及评委的用心程度了.
这里倒是有个需要注意的, 模块与设计应当是匹配的. 后续其实迭代模块, 这些设计也是应该保留下来的. 仅在项目流程中出现一次, 后续这个设计就不再维护迭代, 可能不太好.
一个新增业务功能, 需要平台提供新功能支持, 而后业务部门方可开始开发.
目前采用的方式是, 业务小组的该功能相关的负责人会参与平台部门提供的业务功能配置的填写. 需求方面, 参与度较少. 主要还是架构师与平台功能组澄清功能.
从这个角度, 业务小组在这个开发过程的参与, 一般出现在平台功能组输出了 规格文档, 平台功能组需要各业务组开始使用, 调试. 然后联调阶段.
然后在联调阶段, 业务团队会发现平台团队的功能/环境存在的问题, 此时平台方面开始处理修复. 然后继续联调. 按照缺陷单和最终的输出上肯定是一样的. 但是是否可能通过分批上车, 让平台功能的稳定性也能通过完整的闭环流程予以通过呢?
这种情况下, 业务部门此时是否更适合作为需求方? 而不是单纯的平台部门的联调方?
从上面的描述来看, 其实一开始就参与也可以. 但是, 实际执行过程中, 业务小组实际上在一开始阶段没有怎么投入人力, 但是基于项目管理的统计时, 由于需求并不区分平台功能, 还是业务功能, 此类需求最终统计人力资源消耗时其实也是将业务团队在一开始纳入了考量的. 而实际, 这个功能是初期平台功能组投入, 后期各业务组投入. 所以这部分, 可能按照[^## 5. 多任务同时派发 与 分批次派发]拆解成分批次的需求, 可能更为恰当?
TODO: 是否平台功能与业务功能, 是需要拆分的? 需求方面也应该区分对待? 简单搜了下, 没搜到此类的区分. 在多团队组织中, 是否确实是有必要区分处理?
ceph产品的cluster->osd->pool->rbd|cephfs|rgw->iSCSi|nfs|samba
的部署就是一个链式关系, 这部分角度上来说, 关键是开源组件之间的接口依赖关系的联调测试的问题.
根据ceph-ansible的情况来看Test Scenarios — ceph-ansible documentation, 是通过vagrant搭建虚拟机, 然后拉起不同规格虚拟机来测试集群部署.
vagrant根据测试功能所需配置拉起对应盘的虚拟机, 然后运行.
1 | NOSDS = settings['osd_vms'] |
根据ceph-ansible和openEuler的做法来看, 部署类代码均藉由对虚拟机的控制的CICD来完成质量保障.
这些操作, 关键是属于资源的调度, 如果撇去虚拟机提供的资源, 实际上就无法测试到其对虚拟机内的硬盘等资源的实际分配了.
如果是分配逻辑贴近业务, 则可以比如mock不同的配置进行单元测试, 输出的也是一种配置形态. 但是如果分配逻辑关键就是对虚拟机操作系统提供的基础API的兼容性确认, 则就只能是藉由CICD完成质量闭环了.
SAN/NAS对ceph存在依赖关系, 而ceph如果做大规模升级, 经常会出现不兼容的问题, 此时SAN/NAS基本上就很难在ceph适配开发的同时, 同时使用新版本进行开发.
开发阶段, 手动搭建ceph环境即可. 联调/集成阶段, 理论上下层接口如ceph自身的单元测试完成了一定程度的保障, 剩下产品层面的完整功能集成这种确实只能依赖CICD.
当前我们的web后端, 是直接依赖底层的具体响应, 属于协议转换层. 而不是存在自身数据库设计的一个完整的管理平台后端, 因此个人来看确实无法脱离底层接口与操作系统, 自身进行这个web后端的业务逻辑的单元测试. 此时的话, 确实只能在底层开发完毕后, 通过CICD来闭环自身的质量.
如果是一个独立的后端, 底层只是作为一层数据来源, 以及具体实现层. 底层异常时, 其实不影响页面的数据展示与交互. 只是下发的请求会超时/失败. 但是不影响查询和展示. 这种层面的模块可能才能完成自身的单元测试吧.
前端模块主要就是依赖后端提供的协议设计了, 本身就具有mock能力. 协议设计变了, mock也要跟着变.
可能存在一方面因素就是, 由于目前CICD不完善, 后端在开发过程中极度依赖底层的接口, 底层接口的设计变更导致链路的变更. 从而工作量加大. 本身如果做好了解耦, 可能前端,后端,底层三层之间不至于耦合这么紧密.
按照我现在的理解, 一般情况下感觉因为有版本的功能需求, 比如支持集群规模, 性能等存在了更高的要求, 所以此时可以抛弃一大波历史包袱, 从而进行代码重构.
但是客观上, 实际进行的过程中也确实遇到了很严重的问题.
代码的重构开发, 实际上需要的时间要比预估的多不少.
重构是在项目整个过程当中的一种习惯,并非是其中的一个阶段。
不要定下一两周时间,说这个工作是重构。应该在整个项目过程当中,随时开始,随时停止。每一次重构都是很细微。 [^在感觉项目代码的构架不行的时候,你们会怎么办? - 知乎](https://www.zhihu.com/question/47283785)
按照上文的表达, 其实一些工作, 可能不能在新项目上进行. 而应该提前或者在日常过程中, 就开展了. 新项目只做一个局部适配或者切换版本的工作, 这样可以相当程度上降低需要的时间代价.
其实还是任务并行的问题, 如果是单纯的开发需求, 那实际上开发时已有初稿设计, 如果大家都提前定义后, 那确实后面开发的时间就无所谓, 互相之间没有依赖.
但是对于一些新定义的平台需求, 他基础平台开发完->自测通过->联调通过, 这个过程中其实是存在需要拉其他业务团队来设计, 沟通, 联调的. 而此时, 这就对其他团队的时间做出了限制, 这个阶段的时间由于要配合平台需求的落地, 就只得拆解部分时间. 这方面是否对于业务团队, 是否有必要那么快开始? 这个其实我现在来看, 还是个疑问.
考虑的可能的解决方式
TODO: 这款其实是可以参考下开源社区如何处理这部分的? ceph的话, 这部分其实是通过qa集成测试完成的?
TODO: 持续集成/基线开发, 这部分应该跟测试所需的代码之间的解耦无关吧?
TODO:ceph-ansible的测试是怎么跑? 是否要做隔离?
这里说的集成, 跟集成测试应该关联并不大. 跟ERP系统也有些关联.
指的是企业内的组件功能之间的集成好像.
类似大中台的那种理解? 由中心代理服务来管理各组件之间的通信.
更进一步就是
基于消息的企业服务总线, 将组件之间关联通过一条总线完成管理
当然这种管理机制, 如果总线不出问题的情况下, 延时可接受的情况下问题不大. 猪油最新的那种RDMA等技术, 对延时有要求时, 则物理总线会进行处理.
ESB 的定位,是一项采用开放标准的服务。这样就不需要为每个应用编写唯一的接口了。 只需对应用进行最小幅度的更改,就能部署集成服务。 ESB 依靠符合行业标准的开放协议和接口,简化新的部署。
[^【网站架构】中大型网站如何防止项目延期?敏捷开发在实际项目如何应用?\_停止重构的博客\-CSDN博客](https://blog.csdn.net/Daniel_Leung/article/details/122347803)
TODO:beta由qa团队组织, 是否会保存类似系统测试用例设计呢? 这种是否有资料可以借鉴?
基本总结可以理解为, 是基于mugen: Test framework and test suites框架, 对软件包提供的CLI
接口进行测试验证, 并完成安装卸载的. 有无痕要求.
markdown
是有对应的标准的. 似乎这篇可以参考?
下面todo中提到的一些list
的识别场景, 其实这篇spec
有做一定的边界澄清?
疑似是1.0.25这次这个增加自定义引起的这个问题
似乎是1.0.25去掉了memoize引起的.
右键了这个弹出来的comments的hide comments
了, 不知道会怎么样. 无效 似乎 跟之前设置的Panel: Opens Maximized 设置有关
如何区分切换文件和文件内编辑? * onDidChangeActiveTextEditor * 其实切换文件也不重要, 关键是感知到文件是第一次被打开就行?
似乎变量是持久化渲染的, 所以理论上是可以做计数器的?
插件的变量是全局的, 所以需要维护一张表, 打开的map, 占用这个内存? 而不是每次切换就打开一次?
TODO: 这个可以作为功能处理吧? 如果需要频繁切换, 减少图片渲染的代价则处理. 纳入计划中.
即使用内存来减少这部分切换
判定是文件刚切换带来的行切换事件,从而绕过这种渲染?
TODO:指针判定里增加哪些位置未渲染的逻辑吧, 这样仅当指针出现在存在未渲染的页面时,才会对那些位置进行渲染. 其他情况下指针的迁移, 不触发渲染.
Error setting up request: SyntaxError: Invalid regular expression: /*.md/: Nothing to repeat
, 忽然就无法渲染了好像就是在那几行写新的对象, 那个comment并没有自动被换行符往后推.
好像就是在图片渲染那行上面, 敲回车, 会逐渐走到图片下面去. 而不是让图片切换行? 这个所以应该是我这个插件处理的问题吧?
可能只要不点到那里, 就不触发图片重新渲染更加合适? 但是如果是批量位置修改呢? 被选中的时候再重新渲染?
主要涉及需要重新渲染的时候, 应该是用户点击那行的时候. 但是每次切换的时候, 实际上会触发一次
搞个索引存储吧, 每行尽量只渲染一次吧. 渲染完, 如果没有选中, 就不再触发?
state.activeEditor.document.lineAt
来存储, 好像不太行? 怎么把一行里所有的内容都存储进去呢? 以range为范围? 然后通过一张map记录? 为了减少时间复杂度?
如果open
触发的不是onDidChangeActiveTextEditor
这个的话, 其实把这个关闭就可以了?
切换文件的时候, onDidChangeTextEditorVisibleRanges
这个也被触发了. 关键问题是, 图片的渲染应该是我在切换页面之后发生的, 所以导致页面上滑了...
应该是其中初始化置空那次的问题.
但是onDidChangeActiveTextEditor
感知不到每个文件是第一次打开, 所以导致其实切换
ImageComment
的高度固化有可能不
到底是因为切换走了, 导致渲染全部没了, 还是说只是代码里的因素呢?Comment
记错了, 不是以文本为单位的. 还是以进程为单位的, 也就代表确实需要一个线程处理所有的markdown文件渲染了...这种情况下, 也就是最好uri也保存一下map...
TODO: 图片是在线程里, 开了一张map. createCommentThread
确实是离开的时候, 触发了dispose图片引起的?
好像不清理线程, 就会出现图片重复被塞进渲染线程里的情况?
好像每次都会被push进去的原因是Range对象是不同的? 在string化的时候, 也是可变的. 是不是因为这个导致不同? 不对啊, 我维持了一张rangeMap
, 那不应该出现那个不匹配的问题才对啊. 临时变量的地址应该是不同的, 所以最好转换一下成string?
奇怪, 怎么每次每个对象都触发了2次呢?
另外, 这个列表应该会触发几次, 理论上这张map里应该有3个或者以下的对象才对.
试了下, 官方的comment-sample
也会触发这个跳变的情况. 所以这里其实要解决的历史已经渲染的, 实时渲染的问题?
webp
格式, 也可能是本地file
协议不支持? (官方已合入支持)MarkdownString cannot render webp image · Issue #144188 · microsoft/vscode
但是markdown all in one
的preview
是支持的, 所以可能我的用法不太对?
看了下这个库里只是调用了官方的markdown.showPreview
command, 所以可能得看官方的里是怎么渲染webp的
<img src="../imgs/leveldb3.webp" alt="" class="loading" id="image-hash--205375520" data-src="../imgs/leveldb3.webp">
addImageRenderer
, 官方使用的MarkdownIt
触发的image渲染应该.
那问题就是这个,MarkdownString
并不是用的MarkdownIt
, 走的两套代码方案实现的? 看官方怎么回复吧.
yes~就是一开始发现的那个private readonly validExtensions = new Set(['.svg', '.png', '.jpg', '.jpeg', '.gif', '.bmp']);
这里加上就支持了.
TypeError: Cannot read property 'start' of undefined
at vscode-file://vscode-app/Users/sean10/.vscode/extensions/sean10.markless-sean10-1.0.24/out/main.js:53 at Generator.next (
那个租房的文章里的.
TODO:有没有能够精确的profile我的插件的实际cpu占用时间呢? 有点怀疑是我引入的那个log库占用的资源.
Panel: Opens Maximized
createCommentThread
这个接口操作的.
panel
应该属于comment
的父类的感觉.
collapsibleState
目前已经设置了Expand
了. 所以问题是, expand
的panel
的默认加载模式?
所以, 其实需要知道的是, 什么时候他才读取Panel: Opens Maximized
里的变量.
private panelOpensMaximized() {
似乎是在这里控制?
toggleMaximizedPanel
这个函数为什么默认触发的始终是temrinal
的呢?
TODO:这个变量哪次提交引入的呢PANEL_OPENS_MAXIMIZED
, 是不是我用错了?
Expanded
依旧是部分图片状态.所以关键还是在这个手动折叠展开, 用的哪个函数了.
A collection of comments representing a conversation at a particular range in a document.
Comment
这个类型似乎还有edit
和preview
模式. 不过初步看与这个问题的解决方案无关, 可能如果官方不提供api, 那就得更换comment
以外的方案?
<br/>
问题代码里用的remark-gfm
的渲染, 理论上不应该存在这个问题才对?.
试了下demo, 似乎是remark-gfm
的问题? 难道是<br/>
不符合gfm标准?
1 | async function main() { |
根据HTML blocks inside table cells not treated as blocks · Issue #10 · remarkjs/remark-gfm找到设置允许危险的方式, 可以渲染html
看来还是那个XSS
的风险, 所以导致默认关闭了. 并不是remarkGfm
的问题.
所以我也可以增加这个属性, 默认关闭即可.
这里作者用的不是remark
系列的了, mdast-util-to-hast
和hast-util-to-html
应该是remark-rehype
的前身.
在toHast
和toHtml
处, 都打开这个{allowDangerousHtml: true}
就可以了. 但是的确危险系数还是比较高的.
之后考虑直接更换为remark-rehype
.
应该是svg
没有渲染出来把?
开了debug之后, 可以看到确实html的AST是有的, 应该就是svg
渲染的问题了.
确实, 是<br/>
在被转换后变成了<br>
, 导致不符合svg
语法了. 符合语法之后就正常了. 我把这个作为一个选项开启吧.
在打开了这个allowDangerousHtml
之后, 也变得可以实现了
现在遇到的问题是, 理论上<h1>
的样例, 应该能显示出来才对, 但是没进入到html
的node解析逻辑里...
看起来是remark
这个解析库拦截掉了? 并没有, 单独打印出AST
还是有的, 但是的确进不到那个内部逻辑了现在.
带结局
疑似是我行的长度过长之后的渲染就会比较卡顿? 至少滚动就不流畅了.
应该是blockquote
这段的.
但是卡顿这个我怎么排查是哪的呢? 好像是css切换那块的. 我把filter
换掉能不能解决呢?
去掉filter
的渲染之后就正常了.
state.offset
用途?
ListItems
是哪里得到的type
?
好像某个第三方库的, 不是这套代码里的.
搜一下.
疑似是来自这段的.
1 | const parser = require('unified')() |
嗯, 没错, 应该是remark
这个markdown
解析库里的listitem
.
怎么区分有序列表和无序列表呢?
根据下面这段, 一开始type
为list
, 具有ordered
属性, children
中才是具体的列表内容.
所以如果要改, 预计需要在判断listitem
前就进行区分处理. 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110{
"type": "list",
"ordered": true,
"start": 1,
"spread": true,
"children": [
{
"type": "listItem",
"spread": false,
"checked": null,
"children": [
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "First",
"position": {
"start": {
"line": 71,
"column": 4,
"offset": 437
},
"end": {
"line": 71,
"column": 9,
"offset": 442
}
}
}
],
"position": {
"start": {
"line": 71,
"column": 4,
"offset": 437
},
"end": {
"line": 71,
"column": 9,
"offset": 442
}
}
}
],
"position": {
"start": {
"line": 71,
"column": 1,
"offset": 434
},
"end": {
"line": 71,
"column": 9,
"offset": 442
}
}
},
{
"type": "listItem",
"spread": false,
"checked": null,
"children": [
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "Second",
"position": {
"start": {
"line": 73,
"column": 4,
"offset": 447
},
"end": {
"line": 73,
"column": 10,
"offset": 453
}
}
}
],
"position": {
"start": {
"line": 73,
"column": 4,
"offset": 447
},
"end": {
"line": 73,
"column": 10,
"offset": 453
}
}
}
],
"position": {
"start": {
"line": 73,
"column": 1,
"offset": 444
},
"end": {
"line": 73,
"column": 10,
"offset": 453
}
}
},
todo: extension.js
里start
和end
和node
这几个参数哪里传进来的呢?
现阶段传入的每个node
, ordered属性并不在单个上面.
预计需要改成三层
无序的则是 * listItem * chilren * paragraph
或者 * listItem * children * paragraph * list * children * listItem * children * paragraph
存在多层关联的这种.不能直接以node的type来区分了.
这个节点的遍历关系? 是DFS还是BFS?
目前并没有找到
只是单纯的增加http
的schema
的透传就可以, 剩下的交给vscode
自身支持
似乎支持了?1.62
里?
MarkdownString.supportHtml The new supportHtml property on MarkdownString enables rendering of a safe subset of raw HTML that appears inside the Markdown text.
The supportHtml property defaults to false. When disabled, VS Code will strip out any raw HTML tags that appear in the Markdown text.
latex
显示的渲染结果太小的问题用Elements
定位了下, 实际上是svg
渲染的图片的渲染尺寸不对.
大致是通过 html/svg+xml base64 渲染 svg.
通过在ebfore元素里增加url
的content
.
把SVG代码直接内联在CSS的url()方法中
这个逻辑里可能之所以会根据行的数量来决定要渲染的长度, 应该是为了确保不会点到后面的空行, 结果触发的是这行的内容?
这个的做法是不是弄成自动收缩这几行更好?
mermaid的图也有同样的问题.
首先, 我的配置中默认lineHeight
是设置为0
的, 所以走的下面这个逻辑
Use 0 to automatically compute the line height from the font size. Values between 0 and 8 will be used as a multiplier with the font size. Values greater than or equal to 8 will be used as effective values.
根据这个, 所以跟font size
应该是成成比例的.
1 | const lineHeight = vscode.workspace.getConfiguration("editor").get("lineHeight", 0); |
的确, 发现这段代码这里lineHeight
只得到了2, 大概是这里写错了. 可能作者不是darwin
平台的, 所以感知不到.
latex
除了按行数整比放大代码之外, 是否有正常的方式折叠到那几行?collapse
folding
FoldingRange
registerFoldingRangeProvider
Todo:怎么找不到javascript
如何使用FoldingRangeProvider
的呢...
使用方式类似下面这样: 1
2
3
4
5
6
7
8
9context.subscriptions.push(vscode.languages.registerFoldingRangeProvider('markdown', {
provideFoldingRanges: (document, context, _) => {
console.log(document.languageId);
console.log("try to folding range");
let ranges = []
const temp = new vscode.FoldingRange(1, 50, vscode.FoldingRangeKind.Comment);
ranges.push(temp);
return ranges
}}));
context.subscriptions.push
如何理解?
是不是这个只是用在当我即将触发folding
的时候, 才会进入这个函数逻辑? 确实, 比如我设置kind
为Comment
的时候, 触发Fold all comments
就能够把我标记的范围给圈起来.
所以自动圈, 应该不是这个函数
目前根据这个[folding] API for programmatically folding lines · Issue #37682 · microsoft/vscode来看似乎是不存在的.
根据Built-in Commands | Visual Studio Code Extension API这篇里的editor.fold
发现还是文档里没写清楚参数的数据结构, 搜代码样例从这里找到的autofold/extension.ts at c721aa8b616060624b107cbdd44c44ec4827adec · TigerGold59/autofold
实际使用方式是类似这种 1
vscode.commands.executeCommand('editor.fold', {"selectionLines": [1]});
比如quote
的特殊符号仅第一行有.
2组rangeBehavior目前没有办法合并.
当处在中间时, 没有办法让左侧的识别到右侧的参与.
左侧的隐藏, 右侧的需要放大
cursor
离开该行heading
期间, 依旧不显示#
的范围, 才取消隐藏.考虑咨询下这个的效果兄弟萌,让我们在 Vscode 里放烟花吧 - 51CTO.COM
似乎他对这个range使用比较熟练.
如果我这个后加, 应该就有效了把?
A TextEditorDecorationType is a disposable which can be removed/releaded by calling its dispose-method 好像有清理的逻辑?
我先试试现在这套代码的pop逻辑?pop 不行, 我需要的是清理掉对应的decoration. 而不是现在的这种.
可以考虑增加一个清理掉所有的css的装饰, 在鼠标在上面时触发, 离开时, 这个效果被清理掉.
如果用dispose, 那所有的装饰器的变量也释放了, 如果要用这个, 那就必须要把每个heading 维护一个单独的装饰器, 否则就一起被释放了.
所以最好是有一个disable的方法, 比如从队列里去掉这个. 可能维护一张map, 或者这个列表里就自带了行号的属性, 这样就能够比较当前行是不是其中一个了.
官方的2个方法:Remove decoration type · Issue #22 · microsoft/vscode-extension-samples > option1 to nuke all uses of a certain decoration type across all editors is decType.dispose() > option2 to nuke all uses of a certain decoration type in a single editor instance is editor.setDecorations(decType, [])
好像memoize
这个函数还做了缓存加速.
好像代码里用的是offset, 而不是Range. 这样的话的确没有line
属性了.
这是原生的decorationRanges
的限制吗? 之前好像没看到这种要求. Range
噢, 2种构造函数而已.
todo:成功了. 不过好像enlarge
那个, 因为指针一移动, 又加回去了的样子? 这个明天再修一下.
所以并不是我一开始理解的字体大小默认设置14, 这个行就只能容纳14的情况.
是通过反查? 还是通过map出来之后, 检查?
block quote
存在2个渲染问题#
来的可能需要梳理一下, 这种怎么让多种模式能够复用一套清理逻辑?
怎么获取到对应的字的宽度呢? 毕竟不是以类型来显示node
, 而是反过来拆解的.
是否可以引入关联性呢? 毕竟本身是存在children
的概念的, 只是在子节点的时候, 不知道怎么访问到父亲节点的内容.
如果增加父亲节点的访问链接, 那就可以迭代父节点, 是否存在其他属性需要渲染了 ? 这样的话, 就可以按顺序一个个处理了? 这个好像其实就是迭代顺序? 只是后接收到的处理的数据依旧是原始数据, 而不是上级已经处理过后的数据?
目前是按照visit
的顺序. 如果让每次visit之后, 使用的数据也进行更新呢? 这样的话, 其实会在原始的数据里增加好几个链接?
listItem
的思路, 直接在visite的时候, 把上层的depth
赋给子节点. #### 暂时沿用之前那个修改listItem
的思路, 直接在visite的时候, 把上层的depth
赋给子节点.目前存在的问题: 位置是移动了, 但是好像还有一层外圈没跟着移动? 怀疑不是inlineCode
的装饰. 的确, 注释掉这个处理之后, 还存在. 怀疑是其他插件的. 确实, markdown_all_in_one
里的...
那如果我解决了这个问题, 可能还需要考虑那边如何关闭? 毕竟挺多快捷键依赖那个的...
先试试吧.
另外, 这里如果成功了, 还需要考虑inlineCode
在进入该行时, 自动被消除的操作.
另外, 似乎大小也并没有跟着变...
优先尝试解决border的size
的问题吧?
Border do not match correctly if the font size if too big or small · Issue #33 · leodevbro/vscode-blockman 根据这个来看, 好像是根据lineHeight
来的? 虽然fontSize
大的时候好像把lineHeight
也撑大了, 但是看起来border
没有变. 根据这个的意思, 好像可以把fontSize
调大, 然后影响border. 不过这块有点不理解, 为啥目前fontSize
已经大了, 并没有正常呢?
试了下, 直接在元素上加border
属性是能够按照字体大小扩大border
的. 所以问题就在于现在背景上显示的border
到底绑定在哪个元素上.
奇怪, 为什么全局搜索, 在html
的这个总元素上搜到的呢? 而且调整一点用都没有...
todo:现在的核心痛点就是怎么找到这个border
的css
设置. 不在默认点击找到的div
属性里, 因为删掉该行依旧存在. 好像确实很难找到. 不知道chrome为啥有的css就找不到呢? 之后再看看
view-overlays
?
终于一个个找找到的<div class="cdr ced-1-TextEditorDecorationType10-0" style="left:0px;width:36px;height:18px;"></div>
.monaco-editor .ced-1-TextEditorDecorationType10-0
ced-1-TextEditorDecorationType10-0 奇怪, 这个class的css
在哪?
手动逐个删除二分定位是找到了
但是在哪呢?
vscode createTextEditorDecorationType view-overlays
什么情况下, 装饰器会被添加到一个独立的div里呢? 为啥不复用文本的那部分呢?
export class ViewOverlays extends ViewPart implements IVisibleLinesHost<ViewOverlayLine> {
-> ContentViewOverlays
const contentViewOverlays = new ContentViewOverlays(this._context);
export class View extends ViewEventHandler {
这里构造的时候就能看到了.
contentViewOverlays.addDynamicOverlay(new CurrentLineHighlightOverlay(this._context)); contentViewOverlays.addDynamicOverlay(new SelectionsOverlay(this._context)); contentViewOverlays.addDynamicOverlay(new IndentGuidesOverlay(this._context)); contentViewOverlays.addDynamicOverlay(new DecorationsOverlay(this._context));
怀疑进到不同的渲染里了?
好像view-overlays是属于标准的?不对
一个是view-lines
是我之前那几个元素的. view-overlays
是那几个Css渲染的...基本上目前只看到border
会进这里.
Todo: 忽然想到一个猜测点. 由于html的正文始终只有一份, 会不会是如果对一个range存在多个css的装饰器, 则会把新增的装饰器添加到view-overlays
里.
todo: 但是理论上其实是可以做到识别用户插入的多个装饰器, 然后对Range进行拆分的吧? 拆分到比如以1个字符为单位, 然后重叠装饰器.
可以验证下, 是哪种. 这块不知道官方文档里有没有提到.
emm, *
和**
并没有进入到view-overlays, 依旧在view-lines
TODO: 还是说仅限于border
这种参数才会被放到overlays?
尝试了, outline
也被加到overlays
里了, 而top
虽然是同一个装饰器塞入的, 这个操作被放到了view-lines
里. 所以初步怀疑是根据属性划分? 明明可以把border
属性直接绑在对应的位置, 为啥非得拆到overlays
里呢?
应该是了, 这行单纯只有border
属性,同样也是被加在overlays
范畴.
目前, 位置对上了. 但是因为和字体大小不匹配, 还是会存在丢失的情况.
TODO:把判定当前行进入修改的逻辑封装为函数比较好.
在View
构造的时候, 会构造ViewLines
和ViewOverlays
好像是在registerThemingParticipant
这里触发的?
.monaco-editor .ced-1-TextEditorDecorationType10-0
, 注册时应该是.ced-
这个关键词
走的这个CSSNameHelper
...这样的话, 就没
cdr
呢? 具体的div
上有这个class
在DecorationsOverlay
的_renderNormalDecoration
是这条contentViewOverlays.addDynamicOverlay(new DecorationsOverlay(this._context));
ViewOverlays
->prepareRender
viewPart.prepareRender(renderingContext);
好像接下来就是ViewImpl
里的触发渲染条件调用的了.
那现在问题就是那个border
参数是什么时候被传递进来的了? 我应该使用的是createTextEditorDecorationType
的state.activeEditor.setDecorations
一时这个好像没找到到底做了什么
从这里添加DecorationsOverlay
啥都没找到, 直接用的const className = d.options.className!;
let decorations: ViewModelDecoration[] = [], decorationsLen = 0;
const _decorations = ctx.getDecorationsInViewport();
TODO:好像走到了这段_getDecorationsViewportData
, 这里是不是就是我需要知道的区分border
和textDecoration
的地方?
const renderingContext = new RenderingContext(this._context.viewLayout, viewportData, this._viewLines);
毕竟完全是存在特效重叠的情况的.
翻了下文档, border
好像没有size
的调整属性. 那是否这个就是限制呢? 感觉不至于. 那猜测, 可能是因为decoration
没有命中同一个区域. 所以没能采用到那个较大的size?
根据Get editor line height (px) and character space-width (px) by VSCode API · Issue #125341 · microsoft/vscode这个可以知道目前为止, 这个feature
官方暂时不做.
本来像统一用
1 | const delDecorationIfCurrentLine = (node, decoration, start=0, end=0, ...others) => { |
但是遇到一个问题, 居然有的node
没有position
属性...
不对, 1.0.17
版本正常, 之后的版本和现在开发的版本都有问题.
1.0.18
版本是否正常, 代表了是否我这个修改的思路是正确的...
奇怪, 好像是文件的问题
我这个文件的, node, 行号始终在400行以内显示. 而实际上我行已经到2000行了.
用remark跑了下, 是能到2000行的呀...为啥呢?
1 | node: 221 |
实际上是1569行, 为啥显示222行?
另外, 似乎此时的渲染速度慢了很多, 是不是因为那个遍历的操作? 是不是得内容优化
目前来看, 这个遍历的资源开销特别大, 达到用户会感知的层级了...
这块先优化成map再看吧.
emm, map里最后存的不是地址, 而是这个range对象, 这样的话可能不太行. 除非我把所有的Range对象都做了单例模式?
奇怪, 我做了单例, 为啥还找不到呢? 好像一定情况下照不出来是正常的. 因为如果同一行有多个渲染, 那就找不到.
是不是因为decoration
这个不是单例, 有好几次会创建新的?
这样的话, 就会导致我以为是同一个装饰器, 但是实际上这个装饰器是新的, 里面没有之前的
其实这样也没太大问题. 至少解释不了为什么第二次进这个函数, 底下就变成undefined了. 还真是一个全新的.
基于这个遍历的方案成功是成功了, 但是cpu占用太高.
而且试验了下, onDidChangeTextEditorSelection
是可以捕获到当前的行的. 至于更进一步的思路再想想.
考虑只处理指定行? 选中范围内会自动触发, 在临界范围内, range取消的能力. 这就不存在资源消耗了.
所以可能得放弃根据行来判断的逻辑.
好像大佬在setDecorations
这里做了实现?
1 | if (state.config.cursorDisables) { |
好像是利用这里更新range, 直接去除存在范围内的. 这样的话, 我只需要把行添加到这里, 也能起到一样的效果.
确实, 实现了.
关键是因为setDecoration
这里做了一次循环遍历了, 所以导致我在那侧做的实现, 完全重复了. 出现了2重循环引起的.
但是那个##
连带着这个一起放大的问题还是存在, 那就跟性能没关系了.
如何实现呢? 一种就是让由于存在被隐藏了空格的部分出现. 但是由于heading
的特性是在于表头, 因此无法复现.
hide
异常398行为什么在node
中显示265?
为什么content
打印出了list
的项?
奇怪, 为什么日志里记录的行号, 并不是我当前看的这几行呢? 所以是渲染的范围有问题?
1 | const ms = new vscode.MarkdownString(latexElement); |
初步怀疑是这块构造部分的markdown
这里出的问题.
1 | range = new vscode.Range(Math.max(range.start.line - 200, 0), 0, range.end.line + 200, 0); |
好像是这里引起的, 所以按照这段的意思, 如果把我超过400行的json去掉.是不是就正确了?
好像还是不对. 还是怀疑这里的range存在问题.
1 | {type: "heading", depth: 3, children: Array(1), position: {…}} |
噢噢. 忽然意识到问题点了. 这里construct
传入的是个相对路径, 但是我却去当做真实路径计算引起的问题.
奇怪, 为什么我必须用node
, 而不是直接用
heading
的逻辑里会出现普通节点的行...目前看到的实现, 是在vscode
进入view-line
的渲染之前就修改了字体大小等才行. 如果等到view-line
建完之后, wrap已经结束了
TODO: 所以需要考虑是不是有什么API可以重新指定wrap. 或者重新根据我的渲染结果重新触发render
的view-line
. 可能也得看下wrap的实现代码有没有对应的接口.
#
没被隐藏的问题. 1.0.22版本.依旧存在'#'的复现条件, 似乎是切换Editor和切换其他文件的时候发生的.
line
奇怪, 不是只要是整型就行了吗? 1
2
3
4
5
6
7mainThreadExtensionService.ts:64 Error: Illegal value for `line`
at l._lineAt (vscode-file://vscode-app/Applications/Visual Studio Code.app/Contents/Resources/app/out/vs/workbench/services/extensions/node/extensionHostProcess.js:88)
at Object.lineAt (vscode-file://vscode-app/Applications/Visual Studio Code.app/Contents/Resources/app/out/vs/workbench/services/extensions/node/extensionHostProcess.js:88)
at updateSelectionToLine (vscode-file://vscode-app/Users/sean10/Code/markless/out/main.js:55935)
at setDecorations (vscode-file://vscode-app/Users/sean10/Code/markless/out/main.js:55944)
at runNextTicks (internal/process/task_queues.js:58)
at processTimers (internal/timers.js:494)
最后定位到, 实际上还是editor
切换的时候, 全局变量没有重新更新的问题,用了错误的editor
了. 这样行号肯定是对不上的.
heading
处的最后发现之前加的const editor = vscode.window.activeTextEditor;
在切换editor
时没能切换, 所以访问错文件的line了.
我改完, 再切换回窗口, 我看到触发的还是原来的配置.
config好像还有active
那几个mermaid
的逻辑要用到.
这里的俩registerWebviewViewProvider
这个就不注册了把
clearDecotaions
里的对象找不到setDecoration
,初步怀疑是切换到非markdown的文件上, 出现的问题. 没能把函数取消掉.
listItem
的无序列表的那个符号怎么好像没被添加上去? 隐藏了?初步实验, 好像关键点是这行含不含有中文?
奇怪, 看起来decoration
还是生成了的.
在addDecoration
内部失败的, 目前初步怀疑是vscode
版本升级引入的问题?
TODO:可以通过MarkdownString
来完成彻底的渲染html. 自带了这个能力
todo: 第一级有序列表无法被verified
库解析出ordered
的list
属性. 所以这种得翻一下unified
的API, 他的判断逻辑里, 如果只有一层, 那还具有列表属性吗? listItem应该也有区分前缀才对?
todo: 遍历一层下述节点时, 第一级的node
中包含了其内的子节点的属性. 然后会再次遍历其中的子节点. 所以这里会存在一个覆盖的效果. 目前来看只有text
层级的position
属性可以作为一个表来判定节点之间的关联.
todo: 下属节点的遍历能力是否要进行区分? 可能没办法区分, 因为只有遍历进paragraph
层之后才能知道其children是否是list
还是listItem
.
ordered
与否, 那就不用考虑状态机转移了. emm, 似乎根据现有代码, 可以直接利用正则手动处理. 虽然性能低一点.value
里只有去掉了列表之后的内容, 现在导致我无法正则判断了. 怎么才能从解析后的node
反得到对应的完整行呢? 虽然有range
可以手动找到.range
提取到值根据上面的spec
可知有序列表的开始数字由第一个列表项的数字决定,而不考虑 后面的列表项。
所以理论上remark
应该直接解析出list
的ordered
属性才对?
remark-parse
主要用的syntax-tree/mdast: Markdown Abstract Syntax Tree format这个库来进行的解析.
mdast
这个库里只是文档? 那Remark
这么多repo
之间的关系到底是什么?
unified
代码里没搜到listItem
,
重新推一下
1 |
|
所以应该在remark-parse
或者remark-gfm
里. emm, remark-gfm
下载下来和mdast
一样是空的...
unified.js
和remark.js
是什么关联? 如果单纯只是API聚合的话, 应该不值得赞助吧?
噢, 根据这篇An Introduction to Unified and Remark - Braincoke | Security Blog, remark
看起来更像是unifed
的下属子集能力. 1
import {fromMarkdown} from 'mdast-util-from-markdown'
micromark
, 所以说明这个数据来源是这个库.
的确在这个库的代码里搜到了listItem
.
根据micromark
里的描述
remark is the most popular markdown parser. It’s built on top of micromark and boasts syntax trees. For an analogy, it’s like if Babel, ESLint, and more, were one project.
不过似乎语法树的部分解析可能还是在其他的代码里的.
乍看, 核心逻辑都在对应的compile
里的handler
那里.
micromark
输出的是html
, 那之前那些节点信息, 是哪个插件的呢?
奇怪, 用remark-parse
输出的节点结构基本对应上了
1 | { |
好像跟html
的结构一样, 先是外部结构, ul
还是ol
, 然后才是内部的item渲染.
这样的话, 也得按照html
的逻辑去渲染, 才能区分有序列表和无序列表了把.
现阶段的实现上, 是按照node
迭代去渲染的, 所以这块当进入子节点的时候就没办法感知了.
这样的话, 最好是找到内部节点, 然后往上去遍历节点是什么结构, 然后再渲染?
那这样的话, 最好渲染一次就不是渲染一个节点, 而是一组节点的. 这样就可以在ordered
变量存在的那层处理了.
当节点是listItem
类型的时候, 这里存一下父节点的信息, 是不是list
,然后type
为list
的ordered
参数作为渲染用参数.
最好是在得到这个node
信息的时候, 就遍历一下, 然后把这个属性给加到listItem
那项里.
接下来就是在什么遍历的时候增加这个属性了.
应该哪种都行. 反正只要type匹配即可. 增加在Visit那里了
1 | // context.subscriptions.push(vscode.languages.registerFoldingRangeProvider('markdown', { |
markless
的实时渲染有关. 好像禁用了markless还是卡, 开了进程监控, 也没看到是哪个进程的cpu显著的高, 除了main窗口.markless
是如何隐藏一部分文字的? 但是实际好像又能被搜索到? 透明色?在每个大节点的访问之后, 会再进到children内进行子节点的访问.
1 | { |
1 | { |
1 | { |
1 | { |
Ceph.io — v17.2.0 Quincy released * telementry report优化, 是否可以满足性能问题定位?
根据History for src/crimson -quincy ceph/ceph来看, 好像是官方不准备在这个版本上这个功能了.
相比History for src/crimson -master ceph/ceph已经差了大概2个月的更新了.
These are the topics discussed in today's meeting:
Change in the release process Patrick suggesting version bump PRs vs current commit push approach Commits are not signed Avoids freezing the branch during hotfixes Both for hotfixes and regular dot releases Needs further discussion https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow <-- more closely matches current model + proposed changes re: PRs, with the addition of a development branch https://www.atlassian.com/continuous-delivery/continuous-integration/trunk-based-development ceph-Jenkins account needs admin privs to ceph.git in order to push directly to branches doesn't apply to PR version bump publishing (Windows) binaries signed by cloudbase? Issues with Linux/Ceph Foundation RH might provide the signed binaries Probably don't publish binaries signed by Cloudbase b/c we wouldn't get telemetry data back from crashes etc. master-main rename rename completed there are still issues with some Jenkins jobs quincy blogs 17.2.1 readiness 3 PRs left release candidate by Jun 1st week 16.2.8 issue retrospective: https://docs.google.com/presentation/d/1hbwo_GW48O4nnM78US2ghVXglxZw5TRwmWUOy_oxam8/ scale testing stricter backport policy lack of reviews in the backport mgr component is orphaned (RADOS, lack of experience in other teams) conflict solving was not properly documented mgr has become too critical (due to cephadm it is now key) don't merge with 1 approval reviewers on sensitive PRs/files should acknowledge they understand the code changes different standards across teams try out requiring reviews from CODEOWNERS on the pacific branch
seastore去年变化很多, 现在有了一批监控seastore的metrics能力, 有助于未来的优化
quincy的crimson, 应该支持在没有快照的情况下在crimson测试rbd负载, 用单个reactor配置下基于cyanstore或者seastore后端, 用rook或者cephadm部署.
后续规划里, crimson比较巨大的事情是
快照是Baton负责开发的一个相当核心的功能, 已经填好了rbd测试用例, 后续多核会比较严峻.
保持debug_*配置选项
pg log messages格式保持和传统一致
vstart, teuthology 集群日志到和传统一样的文件里
尽量使用debug common选项, 快速收集到一些问题原因. 尽量确保所有人在调试的同一个页面上. 包括vstart和teuthology的日志, 这块与传统osd不太一样.
后续计划增加一个teuthology套件测试所有的pr.
如果有人愿意做审计, 会非常好
基础日志框架上, 有一个差异是, 20等级在传统上已经是debug, 而crimson不止.
qlc, zns讨论 qlc比tlc更密集
我的策略是, 要么足够快, 可以被视为随机(block)块管理器, 或者足够慢, 被视为(segment)段管理器, 被落入到与zns相同的存储桶汇总.
RedHat 分享性能基准, 如果期望早点测试rbd
今年不会太稳定, 但是几年会有所改善,
行业正在转向accuracy,而不是tlc. TODO: 这里有疑问, 这里的accuracy指的是什么新介质还是什么?
看到了该领域的兴趣, 看看crimson如何帮助从tlc过度
seastore目前进度落后于crimson, 所以成熟会有点困难 不过应该可以工作了.
最新版本通过用于人们的测试
但是Crimson, 不期望人们在quincy测试
也许每月一次或者每周一次, 某种自动构建的快照.
我们不做任何测试, 也不提供后端端口.
只是提供下载和运行的时间点快照, 给人们提供一些更频繁地测试crimson的方法比每年的发布.
如果有一些图像和说明, 会更好.
从bluestore转到crimson, 这是新贡献者可以做的另一个地方.
这个也可以作为一个强制功能来改进发布pipeline.
crimson可以在quincy一起使用, 但是没有快照, 所以还需要继续改进rbd, 以及得到rgw, cephfs的支持.
rbd一直是我们的主要目标, 因为从a cpu per io的角度, rbd大多数情况下最苛刻, 所以一直是我们的重点.
SeaStore Generational Segment Cleaning - Google Docs 接下来讨论的链接是这个
这块gc没细看, 跳过...详见CDS Reef: Crimson - YouTube > seastar把Extent存储到磁盘. 每个extent具有标记热的character, 意味着root extent或者logical lba extent, 或者未知的, 元数据. > > 可以是想对象数据块或者omap leave的代码 > > 每个extent有一个Age, 具有lfs的设计. 将相同字符和相似age的分组. > > 根据论文, 有一个成本效益的垃圾收集策略. 如果实现将相似的extent合并到同一个segment中进行回收会更有效.
device tier
这个方法, 划分多层. 每一层设备比另一层设备更强, 感觉这个思路不错.
这个应该是下一个ceph版本的规划讨论?
rocksdb 列族 pg log 优化?
rocksdb增加merge pg log.
recycling pidgey lobe
a mapping from the real pg info to the pg log key 导致性能下降.
more or less working implementation now poc at the moment and the idea is
yeah to replace roxdb write ahead log with 用外部日志替换 roxdb 预写日志,
external one residing to store 以便
implementation for write the hedge log 实现附近写入对冲日志
bluestore 性能提升.
you prototyped moving the pg log out of rocks db completely and you stop because
it increased our iops by 20
yeah the first one is about you write a headlock and the second one is about making well removing uh pg lock implementation at all along with all this replication stuff
iops增长20%
igor
早期
just write out um pg log updates to 64k allocations in bluefest and um
她没有看到任何好处,所以她很快就放弃
it sounds like igor you're you're having much better success with it with your i
with roxdb iterator boundaries
corey ?
rgw: add DAOS SAL implementation by zalsader · Pull Request #45888 · ceph/ceph
closed: https://github.com/ceph/ceph/pull/46095 (kv/RocksDBStore: Remove ability to bound WholeSpaceIterator, aclamk) <-- merged to master by yuriw https://github.com/ceph/ceph/pull/45993 (crimson/osd: fix argument parsing after seastar changes, markhpc) <-- merged to master by markhpc
updated: https://github.com/ceph/ceph/pull/46062 (crimson: Enable tcmalloc when using seastar, markhpc) <-- Discussion, updates https://github.com/ceph/ceph/pull/45771 (os/bluestore: Switch to time-based adaptive near-fit alogrithm, markhpc) <-- disccusion https://github.com/ceph/ceph/pull/45888 (rgw: add DAOS SAL implementation, zalsader) <-- discussion, review, needs rebase
igor的bluestore实现, 下周分享.
视频未上传,
1 | - CURRENT STATUS OF PULL REQUESTS (since 2021-04-07): |
Ceph Performance Meeting 2022-04-07 - YouTube
1 | - CURRENT STATUS OF PULL REQUESTS (since 2021-03-31): |
gabby's talking 关于fast nvme test nodes, 在基线上得到了较好的结果? 70K-80K的小对象随机iops
但是现在master分支只有20-30K.
mako notes and still saw high
performance
本周2个新pr与avl分配器有关
we
determine when to go into
best fit mode in the avl allocator替换去年夏天的fit mode?
在三星硬盘上, 大块顺序写性能大量的slow down. 看起来是动态调整分配模式 ,而不是线性分配.
写64K这种io模型似乎不太适配三星硬盘. 后续尝试增加4x或者8x的参数来让他适配? 这个有帮助, 不过并不能解决问题.
可能主要体现在搜索空间的耗时上. 所以写了这个基于cycles和字节来选择最优拟合. 超过1ms, 就切换到快速模式? 在pacific 16.2.7版本似乎更好. 将64K划分64个块. 可能是这些硬盘的模式针对我们的修改, 并不喜欢?导致很容易进入性能下降. 不管是其他nvme, 包括基于intel p3700的硬盘的性能?差异都不大, 但在三星上, 差异就大.
这张图里好像有性能测试的图? 从而决定要关闭或采取上面的优化方案的?
david galloway ?
workload test相关
dp compaction . avl分配器在4K情况下, 看到了70-700ms的情况.
stupid和hybrid分配器?
主要还是64K的申请上? 当分配器花2ms找2个连续的块的时候, 会锁住其他在相同块上的操作的处理. 导致没买哦只有500的分配能力?当我申请500K的chunk时, 实际分配单元是64K, 但是空间本身可能会用16K的块碎片.
目前修改后的stupid分配器没有原版的(没合入这个patch)的性能更好
...
后面都是针对这个分配逻辑的讨论了, 未了解, 后续了解后再翻译.
ashfinal/awesome-hammerspoon: awesome configuration for Hammerspoon. 开源,有点厉害,初步看着像是提供了整套操纵mac桌面的Lua API. 基本上是等同于alfred的效果了吧?
参照ashfinal/awesome-hammerspoon: awesome configuration for Hammerspoon.部署, 然后修改~/.hammerspoon/init.lua
即可
Hammerspoon docs: TextClipboardHistory
注册快捷键"alt+"v"
1 | hs.loadSpoon('TextClipboardHistory') |
Hammerspoon docs: TextClipboardHistory
实现类似alfred的自定义能力.
Hammerspoon “Freezes” After a While · Issue #2219 · Hammerspoon/hammerspoon 但是hammerspoon的Choooser好像实在是太卡了, hidden和显示都会转圈圈.
提了个issue,看看官方大佬有没有人能够解决, 我试了下我编译老是报错,感觉像是objective-c里还有什么依赖不在那个cocoa里的.
TODO:后来没怎么用这个功能, 后续遇到再定位看看
实现将当前转换快速修改窗口大小以及在桌面的平铺位置. 默认用的awesome-hammerspoon
里的
Option+R
进入hammerspoon模式, 通过方向键右切换到左右屏幕, 然后通过F来进行全屏化.
找的大佬的脚本1 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152-- Window management
-- Defines for window maximize toggler
local frameCache = {}
local logger = hs.logger.new("windows")
-- Resize current window
function winresize(how)
local win = hs.window.focusedWindow()
local app = win:application():name()
local windowLayout
local newrect
if how == "left" then
newrect = hs.layout.left50
elseif how == "right" then
newrect = hs.layout.right50
elseif how == "up" then
newrect = {0,0,1,0.5}
elseif how == "down" then
newrect = {0,0.5,1,0.5}
elseif how == "max" then
newrect = hs.layout.maximized
elseif how == "left_third" or how == "hthird-0" then
newrect = {0,0,1/3,1}
elseif how == "middle_third_h" or how == "hthird-1" then
newrect = {1/3,0,1/3,1}
elseif how == "right_third" or how == "hthird-2" then
newrect = {2/3,0,1/3,1}
elseif how == "top_third" or how == "vthird-0" then
newrect = {0,0,1,1/3}
elseif how == "middle_third_v" or how == "vthird-1" then
newrect = {0,1/3,1,1/3}
elseif how == "bottom_third" or how == "vthird-2" then
newrect = {0,2/3,1,1/3}
end
win:move(newrect)
end
function winmovescreen(how)
local win = hs.window.focusedWindow()
if how == "left" then
win:moveOneScreenWest()
elseif how == "right" then
win:moveOneScreenEast()
end
end
-- Toggle a window between its normal size, and being maximized
function toggle_window_maximized()
local win = hs.window.focusedWindow()
if frameCache[win:id()] then
win:setFrame(frameCache[win:id()])
frameCache[win:id()] = nil
else
frameCache[win:id()] = win:frame()
win:maximize()
end
end
-- Move between thirds of the screen
function get_horizontal_third(win)
local frame=win:frame()
local screenframe=win:screen():frame()
local relframe=hs.geometry(frame.x-screenframe.x, frame.y-screenframe.y, frame.w, frame.h)
local third = math.floor(3.01*relframe.x/screenframe.w)
logger.df("Screen frame: %s", screenframe)
logger.df("Window frame: %s, relframe %s is in horizontal third #%d", frame, relframe, third)
return third
end
function get_vertical_third(win)
local frame=win:frame()
local screenframe=win:screen():frame()
local relframe=hs.geometry(frame.x-screenframe.x, frame.y-screenframe.y, frame.w, frame.h)
local third = math.floor(3.01*relframe.y/screenframe.h)
logger.df("Screen frame: %s", screenframe)
logger.df("Window frame: %s, relframe %s is in vertical third #%d", frame, relframe, third)
return third
end
function left_third()
local win = hs.window.focusedWindow()
local third = get_horizontal_third(win)
if third == 0 then
winresize("hthird-0")
else
winresize("hthird-" .. (third-1))
end
end
function right_third()
local win = hs.window.focusedWindow()
local third = get_horizontal_third(win)
if third == 2 then
winresize("hthird-2")
else
winresize("hthird-" .. (third+1))
end
end
function up_third()
local win = hs.window.focusedWindow()
local third = get_vertical_third(win)
if third == 0 then
winresize("vthird-0")
else
winresize("vthird-" .. (third-1))
end
end
function down_third()
local win = hs.window.focusedWindow()
local third = get_vertical_third(win)
if third == 2 then
winresize("vthird-2")
else
winresize("vthird-" .. (third+1))
end
end
function center()
local win = hs.window.focusedWindow()
win:centerOnScreen()
end
-------- Key bindings
-- Halves of the screen
hs.hotkey.bind({"ctrl","cmd"}, "Left", hs.fnutils.partial(winresize, "left"))
hs.hotkey.bind({"ctrl","cmd"}, "Right", hs.fnutils.partial(winresize, "right"))
hs.hotkey.bind({"ctrl","cmd"}, "Up", hs.fnutils.partial(winresize, "up"))
hs.hotkey.bind({"ctrl","cmd"}, "Down", hs.fnutils.partial(winresize, "down"))
-- Center of the screen
hs.hotkey.bind({"ctrl", "cmd"}, "C", center)
-- Thirds of the screen
hs.hotkey.bind({"ctrl", "alt"}, "Left", left_third)
hs.hotkey.bind({"ctrl", "alt"}, "Right", right_third)
hs.hotkey.bind({"ctrl", "alt"}, "Up", up_third)
hs.hotkey.bind({"ctrl", "alt"}, "Down", down_third)
-- Maximized
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "F", hs.fnutils.partial(winresize, "max"))
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "Up", hs.fnutils.partial(winresize, "max"))
-- Move between screens
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "Left", hs.fnutils.partial(winmovescreen, "left"))
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "Right", hs.fnutils.partial(winmovescreen, "right"))
实现根据前缀快速切换到指定的窗口.
这里显示的图标位置按照窗口的左上角索引位置排列, 所以只要能记住窗口的位置, 就选择对应的前缀输入就可以完成切换了2
根据WindowHints Performance · Issue #233 · Hammerspoon/hammerspoon这篇来看, 这个问题早早已经存在, 且主要受限于各自电脑装上的某些会阻塞的程序.
根据下面的代码, 可以发现关键点是windows = windows or window.allWindows()
这段的用时长.
1 | --- hs.hints.windowHints([windows, callback, allowNonStandard]) |
在opt+z
呼出的窗口中输入, 获取到自己环境中导致耗时长的程序
1 | > hs.window._timed_allWindows() |
根据下文这段里的, 可以发现allWindows默认已经通过SKIP_APPS
跳过了一些程序.
1 | local SKIP_APPS={ |
理论上使用hs.window.filter
可能有帮助?
最后短时间没搞明白filter
的注入到window.allWindows
里的防范, 先手动在/Applications/Hammerspoon.app/Contents/Resources/extensions/hs/window.lua
底下的SKIP_APPS
直接增加了自己环境里耗时多的程序, 跳过后基本呼出alt+tab
的延时正常了.34
根据这句来判断, hammerspoon在关闭SIP之后还是会存在这个问题的.
1 | local SKIP_APPS={ |
1 | hs.hotkey.bind("alt", "o", function() |
1 | > os.execute("open \"~/Desktop/temp.sh\"") |
增加了shift+F11
的快速切换信号源的方案.
1 | hs.hotkey.bind("shift", "f11", function() |
How to debug Hammerspoon scripts? · Issue #989 · Hammerspoon/hammerspoon
[pkulchenko/MobDebug: Remote debugger for Lua.](https://github.com/pkulchenko/MobDebug
根据上述链接, 应该可以用lua的调试方式来操作, mobdebug.lua
1 | logger = hs.logger.new('preview snap') |
Switching between windows with Alt + Tab · Issue #856 · Hammerspoon/hammerspoon↩︎
Hammerspoon get sluggish with vscode running · Issue #2289 · Hammerspoon/hammerspoon↩︎
windowHints are slow · Issue #2970 · Hammerspoon/hammerspoon > Yabai is capable of doing what you want using only the window id, but that solution requires disabling SIP and injecting code into Dock.app.↩︎
目前已经为唯一的非自研的开源对接ceph的, 已将grafana嵌入.
The original Ceph Dashboard that was shipped with Ceph Luminous started out as a simple read-only view into run-time information and performance data of Ceph clusters. It used a very simple architecture to achieve the original goal. However, there was growing demand for richer web-based management capabilities, to make it easier to administer Ceph for users that prefer a WebUI over the CLI.
12版本的定位还是单纯的只读. 后面版本引入了OpenATTIC
的组件.
On 9 November 2016, SUSE announced the acquisition of assets relating to the OpenAttic storage management assets from the German IT firm it-novum.[16] OpenAttic was integrated into SUSE Enterprise Storage as a graphical tool to manage and monitor Ceph-based storage clusters. --SUSE - Wikipedia
Suse以前提供Calamari
的管理页面, 在Luminos
发布前一年收购了OpenATTIC
(当时就是提供ceph支持的web UI)替换Calamari
, 在Luminous
在2017-08-01发布时, 社区自己也开发了一个只读的dashboard_v1
.
在Nautilus
发布时2019-03-19, The openATTIC project enters maintenance mode | openATTIC在2019-08-22正式宣布,openATTIC
只做维护, 所有开发直接在上游ceph进行. 实际上, 在Mimic
版本就开始合入提供这个新的dashboard
支持了, 只是在Nautilus
提供了第一个稳定的支持多功能的dashboard_v2
. mgr/dashboard_v2: Initial submission of a web-based management UI (replacement for the existing dashboard) by LenzGr · Pull Request #20103 · ceph/ceph
2020年末suse在渠道中SUSE POC - Dead in the water - ceph-users - lists.ceph.io宣布最后一个基于ceph的企业存储版本, 同期收购了Rancher Labs
, 后续将投入Longhorn
TODO: 所以是否suse还在提供dashboard的维护支持?
dashboard模块负责人 Ernesto Puerta
, RedHat等人依旧在维护.
Tatjana Dehler
和Kiefer Chang
等SUSE成员自20年12月已停止继续提交到ceph中.
另外dashboard/的许可证是否确实是LGPL? 或者AGPL, 导致无法使用?
Free software (LGPL 2.1).
所以修改是必须遵守的, 要拆分Python和js框架, 不太确认.
grafana在21年修改为AGPL3.0, Grafana 8版本以前还是Apache 2.0
可以使用
初步来看, 是支持cephadm
提供的所有能力.
The dashboard module’s backend code uses the CherryPy framework and implements a custom REST API.
TODO: 所以是配合mgr提供的API能力, 还是自己重新写的调用呢? 内部走的mgr的rados接口, 只是封装成OpenAPI3.0的协议. src/pybind/mgr/dashboard/controllers/crush_rule.py
比如这里就可以看到
一个计划做的: crush map的自定义能力. 目前依旧不支持自定义能力.
osd的实施界面, 计划仿2004
Storage Devices and OSDs Management Workflows — Ceph Documentation 这里的设计也是类2004那种, 需要手动指定db,wal的. 2020年9月时的设计, 目前未更新.
目前的, 看到的主要是将cephadm集成入dashboard
根据service来看, 是将所有进程同等管理?
SUSE的文档基本足够了About the Ceph Dashboard | Administration and Operations Guide | SUSE Enterprise Storage 7 RH也是这个界面的文档Storage Devices and OSDs Management Workflows — Ceph Documentation ceph-ansible似乎也对接了cephadm. 具体还没看
1 | MON=1 MGR=1 OSD=3 MDS=1 ../src/vstart.sh -d -n -x --cephadm -i 0.0.0.0 |
以下为旧文, 当时针对Luminous
版本分析
grafana
的图[^2]Additionally, the Ceph Dashboard’s “Block” tab now includes a new “Overall Performance” sub-tab which will display an embedded Grafana dashboard of high-level RBD metrics. This provides a quick at-a-glance view of the overall block workloads’ IOPS, throughput, and average latency. It also displays the top 10 images that are using the highest IOPS and throughput, and as well as the images with the highest request latency.[^1]
不知道有没有这个设计的理由, 因为看起来,像是grafana+prometheus
的历史数据折线图展示并没有出现在这里. 只提供实时信息展示有什么特别的理由吗?
设置各权限账户的增删改查权限.
这里好像monitors
,osd
级别都只能看, 从pool
开始才能做创建修改
Nautilus
的版本来看, 基本比较一般.高并发整体可用性:一文详解降级、限流和熔断_Hollis的技术博客_51CTO博客
完整的高可用方案目前理随着当前发展主要分为以下几类.
本文不讨论完整的灾备/多活方案, 主要只涉及本地的高可用, 即双机热备那种, 应用级灾备.
客户端直接填写多个服务端ip, 故障后自行切换. 比如ceph的客户端节点访问mon, 主要依赖客户端节点的配置中mon的地址. 根据mon地址去连接. 当一个mon故障时, 会根据其他mon地址进行连接.
指的就是基于选举自身实现高可用了. 比如ceph-mon之间选举leader,
如早期不支持集群的redis上层增加的Codis等等
或者就是存储端上层, 增加keepalived/nginx等进行处理, 比如云计算当节点存在异常时, 会疏散切换到另一个节点, 比如keepalive提供的vip切换节点的能力.
或是提供dns层级的
脑裂问题按目前理解, 算是高可用问题中存在风险较大的一个了
[^HA高可用集群中"脑裂"问题解决 - 运维总结 - 散尽浮华 - 博客园](https://www.cnblogs.com/kevingrace/p/7205846.html)
目前主要是以下场景
2节点场景下, 需要提供数据存储/计算资源/等的高可用能力(有些情况还是有这种需求) 3节点以上场景, 则基本都是正常的疏散逻辑能够处理的.
这里我们讨论的, 目前主要是存储的冗余能力.
回到关心的存储的冗余的场景里, 指的是2个节点下, 其中一个节点彻底离线, 或者是出现了2个节点之间的网络中断, 而期望对外的业务服务并不中断的场景.
P.S. 其实严格意义上来说, 存储冗余之外,上层业务也需要做上面的另外三种方案. 只是如虚拟机等方案比如存储外再通过本地存储备份拉起可用虚拟机做降级等, 可能在一般的冗余要求下 投入成本过高.
以具体的存储比如ceph举例, ceph如果是基于rbd/cephfs/rgw协议直接对接使用时, 是需要先基于rados协议与mon节点建立连接, 获取到当前最新的osdmap后, 根据osdmap计算出当前请求的目标osd, 然后客户端与osd直接建立连接的.
当2节点场景下, 按官方默认推荐方案仅实施1个mon/mgr时, 当这个mon所在节点离线的情况下, 就会存在无法查询到最新的osdmap, 也就无法与osd建立连接的情况.
发散一下思路
可能有. 但是去中心化, 参考区块链的定义, 其实也意味着数据要么在任意协议中都有保存, 要么本地曾经有过, 保存下来了.
那可能的实现思路就是基于本地历史的osdmap去计算目标源. 然后连上osd之后, osd再承接一部分osdmap分发的能力, 转派真实的osd地址. 这样技术上有一定的实现难度, 但看起来似乎也不是不行.
这里的方案, 其实就首先每个节点运行一个节点服务, 另外1个节点会运行一个额外的服务. 当出现节点异常时, 通过感知到对端离线, 本地去启动那个额外的服务, 确保本节点数量是满足要求的.
这里引出一个需求点: 这个额外的服务的数据库哪里来? 无论是ceph-mon还是zookeeper其实都具有一个特点, 就是数据强一致性, 即数据库是完全可重用的. 即当判断时, 临时复制出一个也不是不可以. 或是通过一个共享存储空间.
P.S. 这里其实共享存储空间是否可能并不是决定性的? 因为本身节点之间通过强一致算法去写数据, 这里数据库之间不一致的检查是为了处理跨节点因网络或各式因素引起的强一致性不可靠. 而对于单节点拉起2个程序, 这点上, 起到的冗余效果可能只是进程级别的? 基本可能没有太大作用? 只是为了实现2节点下高可用才进行的.
按理想的场景 确实对端彻底离线了, 那切换确实问题不大. 但是这里的核心问题点, 当出现网络隔离或脑裂时, 如果两端真的同时拉起新服务, 同时起集群. 那就会导致数据不一致了.
换言之, 这个方案也还是需要一个第三方在网络隔离时, 能够识别出哪个节点才是真实可服务的.
通过设置mon之间的权重优先级,是否有可能?
其实是有可能的, 关键问题其实是在于脑裂, 网络隔离时, 两边都无法感知到对端时, 如何不两边同时拉起 或停止服务. 即需要有一个第三方仲裁者来完成这件事.
如果除了ceph和keepalived的ip之外, 这两个节点还使用了如keepalived这类vip层级. 即2节点其实是共用一个vip的. 这时vip的共用引入了一个第三方, 网关(三层交换机/路由器).
此时利用路由器网关的能力, 通过vip ping一下网关, 确保一个子网里, 只有一个vip存活.
这种思路, 其实就是依赖了ip冲突的检测能力, 从而完成了一个仲裁.
如果是双控服务器, 则双控服务器接入了同一套expander, 可以从expander层面作为一个第三方, 物理磁盘是同一套, 不同控制器可以在磁盘上存储标记位, 检测磁盘锁. 这样确保一个控制器离线的情况下, 另一个节点能够从磁盘锁上判断对端是否存活, 是否在本节点拉起额外的服务.
依旧如果是普通节点的情况下, 划分管理网络,前端网络, 后端网络, 2节点场景
业务程序分为运行在本节点上, 用户基于BS/CS使用本节点上的业务, 和外部节点通过前端网络通信 两种
脑裂为主要场景, 所以这里主要讨论网络异常引起的那种. 不讨论节点正常离线那种.
此时计算需要进行冗余处理. 只需要计算资源能够确认哪个节点是继续提供服务节点即可. 即通过上面的高可用常见解决方案来完成
此时存储无异常.
一台设备内网线全异常了.
这个情况下, 这俩与外网依旧能通, 但是这俩节点之间的后端网络不通了.
此时计算无异常. 此时, 因为前端网络依旧是通的, 所以vip本身还是可以保障的, 对外的vip一般应该是不变的. 即用户访问计算资源, 还是可以正常连接上的.
此时存储需要冗余处理. 但是ceph-mon的程序无法工作, 导致计算资源无法再操作存储了.
如果需要解决, 即需要存储在这个情况下不脑裂继续提供服务. 其实是可以通过上面说的拉起3个/以及后端网络也存在vip来处理. 因为当断一个外部网络, 存储自身通过网关检测到哪个节点的后端网络先能使用这个ip, 则由该节点拉起足够数量的mon继续提供服务.
此时计算/存储均出问题.
此时计算, 可能会出现两端都拉起了计算程序. 因为彻底无第三方, 则可能计算节点自身同时启动.
存储如果也做了上面的拉起足够数量的mon继续提供服务, 则也会出现脑裂问题. 两端同时启动, 同时对本机上的计算资源提供服务.
此时如果业务会有周期操作, 如快照等. 则会出现节点内产生的数据不一致的写操作.
业务请求无法访问某一台了, 存储如果没做上面拉起足够数量mon的方案, 则在mon节点离线时无法响应.
如果做了, 则正常冗余切换到另一节点继续提供服务即可.
此时即存储内网, 会正常通过心跳降级掉无法连上的节点的osd, 继续提供服务.
如果采用了拉起足够数量mon方案, 此时两端会同时尝试提供降级服务.
但是由于业务节点不在存储端, 倒是不会产生数据不一致问题.
根据上面可以看到, 在有些场景下, 提高了可用性, 会引起数据不一致风险. C和A在此时互相冲突.
这里就不再是技术问题了, 而是主要看市场如何了. 是支持高可用带来的利润足以抵消几起数据不一致风险, 还是为了数据一致性, 牺牲部分场景的高可用支持能力了.
近年来, 在组织团队的任务跟进方面, 存在一部分欠缺, 存在进度滞后的现象. 进展存在阻塞, 但是又没有及时感知到, 申请杨州哥等协助, 导致问题长期阻塞的情况.
因此在这里整理了一下目前主要的几个诉求.
irdms
有工作量任务统计要求, 所以总体的时间是需要同步到这里的.irdms
记录任务长期计划hikke
拆解详细子任务, 建议子任务不超过16小时.统计开发工时, 可以通过irdms
查看统计的工作量和一些日常任务的分类占用时间.
这部分可以用于参考, 和考虑将一些占用时间多的赋能给其他部门进行.
白盒的前提的基础模板最好是有一个经过迭代验证过的计算模型, 否则白盒可能带来更大的不公正. 现阶段还是沿用其他组一样的黑盒模式吧.
然后重点就是让任务的进展都在把控中, 重点就是OKR
的理念, 每月同步进展, 更新绩效计划具体的任务和小目标. 然后这种情况下, 是否存在因个人原因脱钩, 才是需要考虑的点. 一切按计划进行, 则绩效就没那么重要了.1
业务贡献:包括需求把控,业务项目和业务创新。
技术贡献:包括设计重构、技术影响力、Code Review、创新提效和代码质量。
团队贡献:包括招聘、新人培养和团队氛围。
Scrum实践总结-团队绩效评估_hellion2的专栏-CSDN博客
目前一些耗时长, 产生质量等方面价值的事情, 个人成长性较少, 预计不太可行.
投票过程采用求大同、存小异、取平均的方式。计划会议过程采用:讲解需求->提问与解释->匿名出牌->讨论->重新出牌->达到近似一致->采取平均的流程,确保大家都理解功能点的业务需求、处理方式
绩效评估的统计及公布。每个sprint迭代结束时候的反思会公布统计排名结果。邮件发送给全体成员,并记录到项目的总的绩效统计中。在项目奖的分配上严格按照工作量的统计结果计算。
绩效排名的指导原则:
团队内提前对齐标准,形成正确预期,不能有惊喜 信息收集要全面,要体现多元价值观,避免单一标准 定性与定量结合,任何数据都只是参考,警惕虚假的精确性
字节跳动,到底是怎么管理11万员工绩效的? - 《财富管理》杂志社
质量这块,量化难度太大,但也不是完全没法量化,它有一部分是可以量化的,在我看来,质量可以分成三部分:代码质量:代码本身的质量决定了对后续开发的友好程度研发质量:研发阶段产生的bug运维质量:产品上线以后的故障情况和资源消耗情况其中,第一类,我认为没法量化,我的做法是不考核,只作为努力提高的方向和主观考核依据,因为它产生的成本可以通过自己后续的努力重构来弥补,原则上应该谁挖的坑谁填(这样还是会有问题,可能导致"透支",即坑越挖越大最后人跑了留下个烂摊子,但我没找到更好的办法了,摊手)。第二类,很难用bug数量来衡量研发质量,所以这块我比较倾向于用红线的形式来考核:冒烟bug、build break、低级bug和regression做记录,其它bug视为正常研发中的沟通。第三类我认为是可以量化的,通过线上服务器消耗、故障恢复时长等来做考核,这种考核的优点同样是投资人和老板看得见,不需要担心指定的KPI偏离了大方向。总之呢,技术管理这事其实挺复杂的,现在这个发展阶段,很多问题都没答案,我觉得我能给的最重要的建议是“不要瞎搞”,宁肯全用主观评价,也不要引入错误的量化指标。
作者:winter 链接:https://www.zhihu.com/question/19995922/answer/138897200 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
如何量化衡量一个程序员的工作量和工作效率? - 知乎 > 市场机制每个人的工作效率不同,如何量化工作效率也是一个问题。假定公司需要解决一个 bug,这个 bug 由普通程序员来解决,需要1个月,但天才只需要1天时间。那么给天才的报酬就应该比普通人高一些。代码除了数量和质量不同,难度很可能也不同。难度越高,相应的报酬也越高。继续假定公司需要解决一个 超级难搞的 bug,这个 bug 由普通程序员修复是不可能的,这个bug远远超过了任何普通程序员的能力,他们根本就无法定位问题。而天才可以修复这个 bug。这区区几行代码的难度是什么,需要评估。如何评估这些不同,是让所有人都头大的难题。但如果我们换一种方式思考,所有的问题就迎刃而解了。让所有程序员对同一个任务的难度打分,然后将任务分配给打分较低的程序员。 > > 作者:松柏 > 链接:https://www.zhihu.com/question/20422695/answer/15095960 > 来源:知乎 > 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 >
由理解这些的TL来在心里建立一些代价的评估标准. 对内遵循TL的设计, 对上级汇报组内评估后的结构, 代价由TL来承接即可.
Q: 你这个设计重构怎么量化? A: 这个很难用系统标准化,更多的还是要依赖TL的专业能力进行评分,但即使是这样,也比以前什么都没有完全黑盒要强。至少在传达一个信息,我们鼓励好的设计,鼓励不断地重构优化。
Q:我们现在的业务需求已经在堆积,一线同学根本没有时间去做重构优化。 A:这个问题开篇其实已经说过了,你是要不断地裹挟在业务需求和烂代码里面呢,还是花时间improve,然后更快地支持业务。这个权衡应该不难做,关键是要看决心和能力。对于很多业务,我没有看到推迟几天上线就会影响业务格局的业务场景,所以作为技术团队,我们就应该在User Story之外,加上我们的Technical Story,把完成业务需求和系统重构都当成我们的核心任务。
这篇同样指的是OKR
的动态跟进进展, 及时更新计划.
作者:知乎用户 链接:https://www.zhihu.com/question/19995922/answer/139929808 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我觉得量化管理是做不到的,更多的是「set expectation」和「manage expectation」,然后通过「calibration」是做到更大范围内的公平公正。什么是「set expectation」,就是在事情没有做之前就要先说好事情做成功应该是什么样子的,需要付出怎样的代价。例如说,「未来 3 个月优化用户注册流程,使得用户注册转化率提高 10%」。如果一个 E5 的工程师跟经理说,「我能在未来 3 个月提高用户注册转化率 10%,你觉得怎样?」那么经理有几种回应的方法:1.「这很好,你去做吧,做好了至少 E5 能够拿一个 meets all expectation。」2.「这太简单了,只能算 E4 的工作,你 E5 做出来了也只能拿 meets most expectation。」3.「这可能比你想象中的要复杂,能做出来的话你升 E6 没问题,但有一些可能存在的问题你先要想清楚。」这样「set expectation」做好了,好处就是双方不用猜测到底自己做成怎样然后对方会给什么结果。然而这样的事情不是做一次就结束的,因为工作过程中会获得新的信息,然后这些新的信息会带来新的抉择,就如同一个动态规划执行的过程一样,所以中间必须不停地「manage expectation」。可能需要沟通的状况例如:1.「我们一个月就把转化率提升了 8%,我们应该把三个月的目标重新设置为 20%。」2.「我们第一个月只提升了 2%,在研究同类产品的注册转化率后我们觉得提升 5% 就封顶了,目标应该调整为 5%。」
接下来的问题就是怎样保证公平公正。因为每个人会都会想把 expectation 往下调,那怎样说明你做了你这个级别应该做的工作呢?就只能通过「calibration」来横向对比了。如果其它在这个级别的人做出来的结果跟你相似,那你做的结果就是没问题的。这个横向对比不仅仅可以在不同人之间做,还可以在跨不同时间段做。
作者:知乎用户 链接:https://www.zhihu.com/question/19995922/answer/139929808 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
最搞笑的是有些答案提及 OKR 的 Objective 要能量化。OKR 中的 Objective 和 Key Results 分离就是为了让 Objective 不能量化只能指导方向,然后 Key Results 是否指向正确方向允许争论。如果 Objective 是「让用户更加享受我们的应用」,Key Results 可以是「增加用户使用时间到 X」或者是「App Store 评分增加到 Y」。但如果你拼命忽悠用户去 App Store 给你打高分,他们因此而不爽这个应用,那你就是满足了 Key Results 而违背了 Objective。OKR 存在的意义就是允许允许大家了解 Objective 从而盯着 Key Results 的实现方式,阻止违背 Objective 的行为出现。
作者:知乎用户 链接:https://www.zhihu.com/question/19995922/answer/139929808 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
也就是说把 OKR 打分和绩效打分不放一起,中间隔 1 到 2 个月,这是一个反思并改进的过程。绩效评定出来之后,管理者需要和员工做绩效面谈,它分为两种,一种是内部激励,第二种是外部激励,内部和外部激励是分两次去进行的。第一次是去跟员工讲 OKR 的结果,主要是和他讲清楚哪些目标没做到?接下来应该怎么做?方法是否有欠缺?或者员工需要哪些帮助?从这个角度和员工聊,不涉及到工资和奖金,员工比较容易听进区去,不会友太大的抵触心理。
创业公司是如何进行研发管理和绩效考核的?从豌豆荚说开去 | 人人都是产品经理
敏捷绩效管理三剑客:OKR 、KPI、CFR | CODING 洞见
会前充分准备:从成员们正在处理的事务入手,确定哪些是争议问题,哪些又是关键信息。 确定工作优先级:专注于促进 OKR 达成的事情。 状态确认:团队对于 OKR 达成的信心是上升还是下降了?大家是否愿意说出困难和风险? 激发员工敬业度:要利用 OKR 激发员工的创造性思考,同时避免挑战性目标带来的消极情绪。 从大局出发:OKR 是对公司战略的执行,我们要从整体的角度来评判 OKR 的进展或问题,以及对未来的影响。
评估目标; 在问题爆发前识别潜在风险; 在使用 OKR 之初,就严谨地把 OKR 管理方法融入到企业文化之中,确保团队持续聚焦;
接下来说最重要的部分:过程度量和review 目前来说,我们是一个月review一次,review的过程接受跟进实际情况的部分调整,但这里得拉大家一起讨论处理。另外,对于大型项目,review的过程也可以发现kpi目标的制定是否有可度量的方式,如果不行,那需要再次细化kpi,避免出现最后核算的时候,确认不了是否能完成。
作者:匿名用户 链接:https://www.zhihu.com/question/20190597/answer/111073368 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
可能就是OKR
的理念, 每月同步进展, 更新绩效计划具体的任务和小目标.
绩效考核中的PeerReview与结果分析 - 知乎 结论似乎就是没啥用目前
公平、无偏差和清晰的反馈。反馈应该基于先前设定的预期,期望值应该经过校准,并清楚地说明该员工是否达到、低于或高于该预期。 激励。我希望我们在评估结束后都很受激励。这如何做到?首先承认所有好的工作,然后讨论下一步要关注什么。这里有一个棘手的部分:如果员工的实际表现不支持我试图传达的信息怎么办(低于预期)? 建立信任。由于绩效评估是经理和员工之间信任的最大考验,我的目标是去扭转这种情况,并加强我们两人之间的信任。这至少要求评估是一次诚实的双向对话。
这篇里, 我觉得作者花了很多时间来处理这个, 花在具体的沟通上?
然后这里的说服的关键, 是要维持和下属的信任关系.
但是作为一个研发, 非完全的管理岗, 这样是否合适?
没有时间也经常被认为是一个不肯花心思的糟糕的借口。
这种如果是偏管理岗, 我觉得这样做无可厚非, 毕竟体现深度. 但是研发岗且组人数不那么多, 目前不觉得这种做法是效益最大化的.
1:1 会议记录。
工程师参加的项目,他们贡献了什么?
产生的输出:代码,文档,电子邮件。
他们收到的反馈:同行反馈,通过电子邮件或其他方式收到的感谢,以及我能找到的其他反馈。
他们给出的反馈:代码审查、计划文档审查、与他人的交互。
自我评估:他们从自己的工作中收集到了什么?我会把这一个放到最后,尽可能收集他们可能遗漏的东西。
- 根据OKR, 增加阶段性成果物, 然后增加例会, 汇报OKR进展.
- 针对会踩坑的那种任务, 个人内部心里绩效合格打底, 但是需要确认进展合理. 的确有产出.
管理方面要关注沟通, 避免被陷入细节
TODO: 将沟通这条进行方法论/系统化.
信息预设
的风险别人家的技术leader是如何建设团队、管理人员、沟通工作的? 这二十个问题,可能是你技术人生中已经或即将遭遇的痛点,怎么解?
需求是把微信多个对话里的语音全部保存下来, 保存到一个文件或者一份链接. 指向多个文件.
1 | mkdir ~/Downloads/voice |
1 | find . -name '*.silk' | xargs -L1 -I {} cp -af {} ~/Downloads/voice/ |
主要使用以上脚本
1 | sh converter.sh ~/Downloads/voice ~/Downloads/out mp3 |
ffmpeg
1 | brew install ffmpeg |
生成要拼接的文件的目录. 1
ls ./ | xargs -I {} realpath {} | xargs -I {} echo "file '""{}""'" >> ~/Downloads/filelist.txt
使用ffmpeg
进行拼接 1
ffmpeg -f concat -safe 0 -i filelist.txt -y output.mp3
mp3
长度和1 | python3 xx.py ${mp3_path} |
输出的结果和我上面拼接后的文件的长度一致, ok没问题
1 | #!/bin/python3 |
用ipad/iphone
登录同一个微信账号, 需要安卓机和ios
设备在同一个局域网(即同一Wifi
里), 然后进入设置 -> 聊天 -> 聊天记录备份与迁移
.
mac
/pc
上, 将ios设备进行整机备份高版本macOS
比如10.15
是在finder
里进行的备份, 低版本则在itunes
中.
参照apple
官方文档即可, 如何备份您的 iPhone、iPad 和 iPod touch - Apple 支持 (中国) 备份速度通过lightning转usb-c, 最快也只有40MB/s, how long should an initial backup take on… - Apple Community
ios
设备在mac/pc
里的备份数据库路径可能因为finder
高版本导致默认打开软件时没找到备份路径, 手动指定到~/Library/Application Support/MobileSync/Backup
即可. 就立刻会显示出微信的聊天记录了.
不过似乎存在一个缺憾, 不能全选导出. 只能一个个选择导出.
index.html
查看和微信一般的web
页面来看记录吧ceph-fuse是如何将系统调用注册成自己的程序的呢?
libcephfs 是ceph-fuse通信的内核模块
ceph-fuse来处理真实的逻辑
本文提到为了系统实现概念的统一性、设计的一致性以及层次抽象与共用,操作系统将iscsi协议对接到scsi中间层。这种实现对性能的损失有多大?如果我们在device-mapper之下实现又会怎么样
https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/cache-policies.html
磁盘Cache 是否是在硬盘驱动哪层
avgrq-sz这个东西应该有个上限, 好像是512K
The default maximum IO size set by Max_Sectors_KB restricts the largest IO size that the OS will issue to a block device.
Whether you will be issuing IO at this size will depend on the elevator (scheduler) being used, the driver, and the type of IO your applications are issuing. But large reads and writes are often at the maximum IO size.
The answer is to use UDEV.
ACTION==”add|change”, SUBSYSTEM==”block”, RUN+=”/bin/sh -c ‘/bin/echo 1024 > /sys%p/queue/max_sectors_kb’”[^18]
这个内核的队列到底有多大. 队列大小.
autocork
write返回成功数据落盘了吗? > Buffered IO:write返回数据仅仅是写入了PageCache,还没有落盘。 > > Direct IO:write返回数据仅仅是到了通用块层放入IO队列,依旧没有落盘。 > > 此时设备断电、宕机仍然会发生数据丢失。需要调用fsync或者fdatasync把数据刷到磁盘上,调用命令时,磁盘本身缓存(DiskCache)的内容也会持久化到磁盘上。 > 2、write系统调用是原子的吗? > write系统调用不是原子的,如果有多线程同时调用,数据可能会发生错乱。可以使用O_APPEND标志打开文件,只能追加写,这样多线程写入就不会发生数据错乱。 > 3、mmap相比read、write快在了哪里? > mmap直接把PageCache映射到用户态,少了一次系统调用,也少了一次数据在用户态和内核态的拷贝。 > > mmap通常和read搭配使用:写入使用write+sync,读取使用mmap。 > 4、为什么Direct IO需要数据对齐? > DIO跳过了PageCache,直接到通用块层,而通用块层的IO都必须是块大小对齐的,所以需要用户程序自行对齐offset、length。 > 5、Libaio的IO栈? > write()--->sys_write()--->vfs_write()--->通用块层--->IO调度层--->块设备驱动层--->块设备 6、为什么需要 by pass pagecache? > 当应用程序不满Linux内核的Cache策略,有更适合自己的Cache策略时可以使用Direct IO跳过PageCache。例如Mysql。 >
为什么需要 by pass kernel? > 当应用程序对延迟极度敏感时,由于Linux内核IO栈有7层,IO路径比较长,为了缩短IO路径,降低IO延迟,可以by pass kernel,直接使用用户态的块设备驱动程序。例如spdk的nvme,阿里云的ESSD。 >
为什么需要直接操作裸设备? > 当应用程序仅仅使用了基本的read、write,用不到文件系统的大而全的功能,此时文件系统的开销对于应用程序来说是一种累赘,此时需要跳过文件系统,接管裸设备,自己实现磁盘分配、缓存等功能,通常使用DIO+Libaio+裸设备。例如Ceph FileStore的Journal、Ceph BlueStore。
mmap和direct IO区别 > > 系统调用,write会触发用户态/内核态切换?是的。那有没有办法避免这些消耗。这时候该mmap出场了,mmap把page cache 地址空间映射到用户空间,应用程序像操作应用层内存一样,写文件。省去了系统调用开销。 > > 那如果继续刨根问底,如果想绕过page cache,直接把数据送到磁盘设备上怎么办。通过open文件带上O_DIRECT参数,这是write该文件。就是直接写到设备上。 > >
> > 如果继续较劲,直接写扇区有没有办法。这就是所谓的RAW设备写,绕开了文件系统,直接写扇区,想fdsik,dd,cpio之类的工具就是这一类操作。
LIO
Linux-IO Target在Linux内核中(linux 2.6.38后),用软件实现各种SCSI Target,其支持的SAN技术中所有流行的存储协议包括Fibre Channel(Qlogic,linux3.5)、FCoE(linux3.0)、iSCSI(linux 3.1)、iSER (Mellanox InfiniBand,linux3.10), SRP (Mellanox InfiniBand,linux3.3), USB等,同时还能为本机生成模拟的SCSI设备,以及为虚拟机提供基于virtio的SCSI设备。Linux-IO Target使用户能够使用相对廉价的Linux系统实现SCSI、SAN的各种功能,而不用购买昂贵的专业设备。
TCM targets 运行在内核态,TCMU(TCM in Userspace)是LIO 的用户态实现。
传统的网络层数据传输协议栈:
内核层:硬件中断--->取包分发至内核线程--->软件中断--->内核线程在协议栈中处理包--->处理完毕通知用户层
用户层:收包-->网络层--->逻辑层--->业务层
存储的接入模块包括协议层接入、Client 模块。协议层就是上面所说的 iSCSI、NFS 等协议,而 Client 模块则负责将上层请求转换成存储系统所能支持的读写请求发送到存储的核心 IO 模块,这些要做的以及可以做东西很多:故障域管理、IO 转发、IO 超时重试、IO 条带化、集群节点视图等,每一个点都很有搞头。接入模块的改造潜力是比较大的,如利用 SPDK 改造 vhost / iSCSI .
这里其实指的就是iSCSI的Target端与下层存储服务的对接了把
主节点负责管理元数据和 IO 调度,从节点则负责读写数据,乖乖的干活即可。在没有主从之分的 peer 存储系统中,可管理元数据并不多,并且多集中在接入模块的 Client 中,因为它的元数据多是计算出来的,而集中式存储系统多是查出来的,所以存储系统按照这种方式可以分为计算型和查表型。集中式存储系统的元数据多存于类似 LevelDB 或 RocksDB 的单机存储引擎中。
常见的 CSI 对接有块存储的 iSCSI、对象存储的 FUSE,还有一些其他比较小众的,比如 ceph 的 rbd、sheepdog 的 sbd 等,这些的原理就是通过注册内核相关的 block device 类型来做的。
https://runsisi.com/2018-12-26/ceph-iscsi-research
第一个字段:8,0 这个字段是设备号 major device ID和minor device ID。 第二个字段:3 表示CPU 第三个字段:11 序列号 第四个字段:0.009507758 Time Stamp是时间偏移 第五个字段:PID 本次IO对应的进程ID 第六个字段:Event,这个字段非常重要,反映了IO进行到了那一步 第七个字段:R表示 Read, W是Write,D表示block,B表示Barrier Operation 第八个字段:223490+56,表示的是起始block number 和 number of blocks,即我们常说的Offset 和 Size 第九个字段: 进程名
其中第六个字段非常有用:每一个字母都代表了IO请求所经历的某个阶段。
Q – 即将生成IO请求 | G – IO请求生成 | I – IO请求进入IO Scheduler队列 | D – IO请求进入driver | C – IO请求执行完毕
当用户调用了write、read等系统调用陷入内核之后,系统会首先针对关联的file对象找到对应的file system,此处针对后端是否有持久化存储可将众多的file system划分为两类,在此我们关心的是后端对应真实存储的file system,经过了file system层处理之后将所有的数据块划分为了一个个的bio,交给device mapper层处理,device mapper层主要完成逻辑设备到物理设备的映射工作,例如可以将bio按LBA地址分别映射到管理的多个设备上(linear,raid0),将bio映射到每个设备上(mirror, raid1),或者是提供快照和多路径的功能,再往下是具体的block层,此处会为每个设备提供一个queue,在queue之上使用各种io scheduler(noop/deadline/cfq)进行请求的合并,最后调用各种不同的设备驱动完成服务。SCSI子模块就是这众多驱动中的一种,其他的还有按照协议和总线划分还有USB,ATA等等。
系统调用文件系统的函数, 参数为文件描述符和文件偏移量.
VFS支持的文件系统主要有三种类型:
基于磁盘的文件系统:Ext系列、XFS等。 网络文件系统:NFS、CIFS等。 特殊文件系统:/proc、裸设备等。 VFS主要有四个对象类型(不同的文件系统都要实现):
superblock:整个文件系统的元信息。对应的操作结构体:struct super_operations。 inode:单个文件的元信息。对应的操作结构体:struct inode_operations。 dentry:目录项,一个文件目录对应一个dentry。对应的操作结构体:struct dentry_operations。 file:进程打开的一个文件。对应的操作结构体:struct file_operations
具体的文件系统实现
物理页与扇区的对应关系由文件系统定义,文件系统定义了一个内存页(4KB)与多少个块对应,对应关系在格式化磁盘时设定,运行时由buffer_head保存对应关系:[^23]
linux # cat /proc/slabinfo | grep buffer_head buffer_head 12253 12284 104 37 1 : tunables 120 60 8 : slabdata 332 332 0
Cache层在内存中缓存了磁盘上的部分数据。
Page Cache主要用来作为文件系统上的文件数据的缓存来用,尤其是针对当进程对文件有read/write操作的时候。
在Linux的实现中,文件Cache分为两个层面,一是Page Cache,另一个Buffer Cache,每一个Page Cache包含若干Buffer Cache。Page Cache主要用来作为文件系统上的文件数据的缓存来用,尤其是针对当进程对文件有read/write操作的时候。Buffer Cache则主要是设计用来在系统对块设备进行读写的时候,对块进行数据缓存的系统来使用。
磁盘Cache有两大功能:预读和回写。预读其实就是利用了局部性原理,具体过程是:对于每个文件的第一个读请求,系统读入所请求的页面并读入紧随其后的少数几个页面(通常是三个页面),这时的预读称为同步预读。对于第二次读请求,如果所读页面不在Cache中,即不在前次预读的页中,则表明文件访问不是顺序访问,系统继续采用同步预读;如果所读页面在Cache中,则表明前次预读命中,操作系统把预读页的大小扩大一倍,此时预读过程是异步的,应用程序可以不等预读完成即可返回,只要后台慢慢读页面即可,这时的预读称为异步预读。任何接下来的读请求都会处于两种情况之一:第一种情况是所请求的页面处于预读的页面中,这时继续进行异步预读;第二种情况是所请求的页面处于预读页面之外,这时系统就要进行同步预读。
回写是通过暂时将数据存在Cache里,然后统一异步写到磁盘中。通过这种异步的数据I/O模式解决了程序中的计算速度和数据存储速度不匹配的鸿沟,减少了访问底层存储介质的次数,使存储系统的性能大大提高。Linux 2.6.32内核之前,采用pdflush机制来将脏页真正写到磁盘中,
Linux 2.6.32内核之后,放弃了原有的pdflush机制,改成了bdi_writeback机制。bdi_writeback机制主要解决了原有fdflush机制存在的一个问题:在多磁盘的系统中,pdflush管理了所有磁盘的Cache,从而导致一定程度的I/O瓶颈。bdi_writeback机制为每个磁盘都创建了一个线程,专门负责这个磁盘的Page Cache的刷新工作,从而实现了每个磁盘的数据刷新在线程级的分离,提高了I/O性能。
回写机制存在的问题是回写不及时引发数据丢失(可由sync|fsync解决),回写期间读I/O性能很差。 ### 参数 sysctl -a | grep dirty > ls -l /proc/sys/vm/dirty_*
1 | # ls -l /proc/sys/vm/dirty_* |
1 | $ cat /proc/vmstat | egrep "dirty|writeback" |
通用块层的主要工作是:接收上层发出的磁盘请求,并最终发出I/O请求。该层隐藏了底层硬件块设备的特性,为块设备提供了一个通用的抽象视图。
对于VFS和具体的文件系统来说,块(Block)是基本的数据传输单元,当内核访问文件的数据时,它首先从磁盘上读取一个块。但是对于磁盘来说,扇区是最小的可寻址单元,块设备无法对比它还小的单元进行寻址和操作。由于扇区是磁盘的最小可寻址单元,所以块不能比扇区还小,只能整数倍于扇区大小,即一个块对应磁盘上的一个或多个扇区。一般来说,块大小是2的整数倍,而且由于Page Cache层的最小单元是页(Page),所以块大小不能超过一页的
多情况下,数据的传输通过DMA方式
通用块层的核心数据结构是一个称为BIO的描述符,它描述了块设备的IO操作。每个bio结构都包含一个磁盘存储区标识符(存储区中的起始扇区和扇区数目)和一个或多个描述符 与IO操作相关的内存区的段。
I/O调度层的功能是管理块设备的请求队列。即接收通用块层发出的I/O请求,缓存请求并试图合并相邻的请求。并根据设置好的调度算法,回调驱动层提供的请求处理函数,以处理具体的I/O请求。
如果简单地以内核产生请求的次序直接将请求发给块设备的话,那么块设备性能肯定让人难以接受,因为磁盘寻址是整个计算机中最慢的操作之一。为了优化寻址操作,内核不会一旦接收到I/O请求后,就按照请求的次序发起块I/O请求。为此Linux实现了几种I/O调度算法,算法基本思想就是通过合并和排序I/O请求队列中的请求,以此大大降低所需的磁盘寻道时间,从而提高整体I/O性能。
常见的I/O调度算法包括Noop调度算法(No Operation)、CFQ(完全公正排队I/O调度算法)、DeadLine(截止时间调度算法)、AS预测调度算法等。
在许多的开源框架如Kafka、HBase中,都通过追加写的方式来尽可能的将随机I/O转换为顺序I/O,以此来降低寻址时间和旋转延时,从而最大限度的提高IOPS。
驱动层中的驱动程序对应具体的物理块设备。它从上层中取出I/O请求,并根据该I/O请求中指定的信息,通过向具体块设备的设备控制器发送命令的方式,来操纵设备传输数据。
俗话说的SCSI协议栈,包括三层,一个是上层的协议驱动,指磁盘驱动,磁带驱动,如果有其他的设备,比如打印机之类的,SCSI打印机和扫描仪也有,很早的时候,这块就是驱动设备了。SCSI Middle 层就是管SCSI指令,下发下来都是在内存里的数据结构,每个OS都不一样,但是如果发到磁盘,发到外面的交换机,必须把它弄成标准化的,因为外面有很多厂商做硬件,你不标准,就没法做了这个硬件,你不能说为每个OS都做一个硬件。这层除了翻译成SCSI指令,这边有SCSI语义,还有管真正的SCSI的处理,比如超时了怎么办这些事情,这是中间层。
底下这层是HBA层,首先要有驱动,在HBA上有设备发现的这么一层库,因为传统的SCSI几十年前,那时候只有SCSI这么一种物理硬件,大家可能有人见过,很粗很笨的线缆,后来出现了FC,SAS这些,更快速的、高效的物理链路类型。SCSI协议,如果想跑在这些物理链路类型说,就需要有这么一块代码,这个网络里面,把你对端的SCSI设备发现上来,如果后端改成SAS,这块代码就会往SAS网络上发出一些广播的消息,将设备探测到,然后才生成设备符号。
硬盘缓存功能等就在硬件内了. 比如最近我们接触的希捷高速缓存.
采用追加写
从文件中读一些数据时将会需要更多的时间:需要倒序扫描,直到找到所需要的内容。当然在一些简单的场景下也能够保证读操作的性能:
数据是被整体访问,比如HDFS
HDFS建立在一次写多次读的模型之上。在HDFS中就是采用了追加写并且设计为高数据吞吐量;高吞吐量必然以高延迟为代价,所以HDFS并不适用于对数据访问要求低延迟的场景;由于采用是的追加写,也并不适用于任意修改文件的场景。HDFS设计为流式访问大文件,使用大数据块并且采用流式数据访问来保证数据被整体访问,同时最小化硬盘的寻址开销,只需要一次寻址即可,这时寻址时间相比于传输时延可忽略,从而也拥有良好的读性能。HDFS不适合存储小文件,原因之一是由于NameNode内存不足问题,还有就是因为访问大量小文件需要执行大量的寻址操作,并且需要不断的从一个datanode跳到另一个datanode,这样会大大降低数据访问性能。 知道文件明确的偏移量,比如Kafka
LSM p
小文件合并 小文件合并为大文件后,首先减少了大量元数据,提高了元数据的检索和查询效率,降低了文件读写的I/O操作延时。其次将可能连续访问的小文件一同合并存储,增加了文件之间的局部性,将原本小文件间的随机访问变为了顺序访问,大大提高了性能。同时,合并存储能够有效的减少小文件存储时所产生的磁盘碎片问题,提高了磁盘的利用率。最后,合并之后小文件的访问流程也有了很大的变化,由原来许多的open操作转变为了seek操作,定位到大文件具体的位置即可
文件合并和元数据优化 # 直接IO[^23] > 当我们以O_DIRECT标志调用open函数打开文件时,后续针对该文件的read、write操作都将以直接I/O(direct I/O)的方式完成;对于裸设备,I/O方式也为直接I/O。 > > 直接I/O跳过了文件系统这一层,但块层仍发挥作用,其将内存页与磁盘扇区对应上,这时不再是建立cache到DMA映射,而是进程的buffer映射到DMA。进行直接I/O时要求读写一个扇区(512bytes)的整数倍,否则对于非整数倍的部分,将以带cache的方式进行读写。 > > 使用直接I/O,写磁盘少了用户态到内核态的拷贝过程,这提升了写磁盘的效率,也是直接I/O的作用所在。而对于读操作,第一次直接I/O将比带cache的方式快,但因带cache方式后续再读时将从cache中读,因而后续的读将比直接I/O快。有些数据库使用直接I/O,同时实现了自己的cache方式。
队列深度决定了给块设备写I/O的最大并发数,对于Linux系统,默认值为128,一般情况下不建议用户修改此参数。用户可以使用cat命令查询当前块设备队列深度。 linux-ob3a:~ # cat /sys/block/sdc/queue/nr_requests
预读量的默认值为512扇区,即256KB。用户可以使用cat命令查询当前块设备预读量。
linux-ob3a:~ # cat /sys/block/sdc/queue/read_ahead_kb 512
vm.swappiness的值的大小对如何使用swap分区是有着很大的联系的。swappiness=0的时候表示最大限度使用物理内存,然后才是 swap空间,swappiness=100的时候表示积极的使用swap分区,并且把内存上的数据及时的搬运到swap空间里面。linux的基本默认设置为60,也就是说,你的内存在使用到100-60=40%的时候,就开始出现有交换分区的使用。大家知道,内存的速度会比磁盘快很多,这样子会加大系统io,同时造的成大量页的换进换出,严重影响系统的性能,所以我们在操作系统层面,要尽可能使用内存,对该参数进行调整,一般sysctl vm.swappiness=10。
[^26]
IO出了龙潭又入虎穴,对于基于SATA/SAS SSD的AFA来讲,SCSI层很难绕过,因为这个协议栈太过底层,SCSI指令集异常复杂,协议状态机、设备发现、错误恢复机制等哪一样都够受的,如果抛弃SCSI协议栈自己开发一个新的轻量级SCSI协议栈,那是不切实际的,你会发现倒头来不得不把那些重的代码加回来,因为SCSI体系本身的复杂性已经决定了协议栈实现上的复杂性。 然而,如果使用的是PCIE闪存卡或者2.5寸盘的话,那么就可以完全抛弃SCSI协议栈。有些PCIE闪存卡的驱动中包含了自定义的私有协议栈,其中包含了指令集、错误处理、监控等通用协议栈的大部分功能,其直接注册到块层;而NVMe协议栈迅速成了定海神针, [^27]
支持的日志等级
SyslogHandler
的syslog
协议部分主要遵循RFC5424
.
在安装了systemd
和rsyslog
的设备上, 如果启用了syslog
协议, 日志一般会先经由systemd-journald
管控的/dev/log
套接字, 由rsyslog
从systemd-journald
的socket
中读取日志, 而后根据rsyslog
的过滤器写入到/var/log/messages
中.
基于filter
实现的handler
和logger
, 支持等级控制
基于name
的logger
字典
仅支持多线程, 不支持多进程
- 日志紊乱:比如两个进程分别输出xxxx和yyyy两条日志,那么在文件中可能会得到类似xxyxyxyy这样的结果
- 日志丢失:虽然读写日志是使用O_APPEND模式,保证了写文件的一致性,但是由于buffer的存在(数据先写入buffer,再触发flush机制刷入磁盘),fwrite的操作并不是多进程安全的
- 日志丢失的另一种情况:使用RotatingFileHandler或者是TimerRotatingFileHandler的时候,在切换文件的时候会导致进程拿到的文件句柄不同,导致新文件被重复创建、数据写入旧文件
logging --- Python 的日志记录工具 — Python 3.9.6 文档
1 |
根据目标的需求, 自定义开发handler.
配置了一个CentOS 8
的容器, 执行下以下内容
1 | WITH_SEASTAR=true ./install-deps.sh |
用服务器跑很快就失败了...发现主要是少了这个一点. /opt/rh/gcc-toolset-9/enable
, 导致默认用的是gcc 8
好像找到个Dockerfile.in
, 这里好像匹配了jenkins
里面的操作, do_cmake.sh -DWITH_SEASTAR=ON
归档一下docker save sean10/centos_ceph_clion:pacific_v1.0.0 | gzip > centos_ceph_clion_pacific_v1.0.0.tar.gz
提交到[sean10/centos\_ceph\_clion Tags](https://hub.docker.com/repository/docker/sean10/centos_ceph_clion/tags?page=1&ordering=last_updated)
这里了.
编译内存大概占用了13个g? 4G内存+15G的swap, 用掉了内存+9个G
1 | docker history sean10/centos_ceph_clion:pacific_v1.1.1 |
看了下, 后面我所有ssh上去做的复制操作都没有, 我的代码因为需要clion上做同步然后Cmake, 直接把代码放进去并不能省去上传的操作, image容量在clion重新上传一次之后还是被会拉大, 因此暂时不考虑压缩了.
我看了下gcc
已经装了8.4版本, 那就是cmake
没能识别到gcc的环境变量?
嗯, 我没装gcc-c++
, 所以的确识别不到.
好像缺失的lz4
的库?
姑且试了下, 好像是/usr/lib64/pkgconfig/lz4.pc
不存在,但是实际上/usr/lib64/pkgconfig/liblz4.pc
存在, 我做了个软链, 过了第一个, 但是后面还是报了上面这条报错.
--debug-output
CMake Warning (dev) at /usr/share/cmake/Modules/FindPackageHandleStandardArgs.cmake:273 (message): The package name passed to find_package_handle_standard_args
(LZ4) does
not match the name of the calling package (lz4). This can lead to problems
in calling code that expects find_package
result variables (e.g.,_FOUND
) to follow a certain pattern.
Call Stack (most recent call first): cmake/modules/Findlz4.cmake:30 (find_package_handle_standard_args)
src/CMakeLists.txt:335 (_find_package) src/seastar/cmake/SeastarDependencies.cmake:98 (find_package)
src/seastar/CMakeLists.txt:330 (seastar_find_dependencies)
This warning is for project developers. Use -Wno-dev to suppress it.
好像是FindPackageHandleStandardArgs
这个函数是类似find_package
的.
好像是我理解错了, 这个就是单纯在找lz4-devel
这个库提供的内容. 而现在我卸载掉这个devel
库, 的确还是报相同的错误. 那就是这个库提供的内容没有在他们的搜索路径里导致的
好像FindLZ4.cmake
和Findlz4.cmake
本身是不一样的. 我试验了一下, 直接把这几个文件作为CMakeLists.txt
的时候, 后者那个小写文件是能够找到的, 而第一个则是找不到的...
但是我把第二个文件覆盖掉第一个问题, 则是不工作的.
1 | -- Checking for one of the modules 'liblz4' |
所以是不是有地方定义了大写的LZ4:LZ4
, 所以才导致必须有一个大写的导入.
奇怪, 为什么我的代码里这个目录不存在一个几乎同名的findlz4.cmake
, 但是在设备上,却在findLZ4.cmake
同目录里有一个findlz4.cmake
呢?
这个目录疑似是生成的, 因为我改了以后, 这里的findLZ4.cmake就不存在了.
了解一下cmake的工作机制.
但是按照Cmake的工作机制, 理论上这个位置就不是一个动态生成的路径啊?
这里一直提到的The package name passed to find_package_handle_standard_args (lz4) does not match the name of the calling package (LZ4). This can lead to problems in calling code that expects
find_packageresult variables (e.g.,
这里的calling package
到底是什么意思呢?
根据cmake
里这段代码
1 | if (DEFINED CMAKE_FIND_PACKAGE_NAME |
也就是调用find_package(LZ4)
了.
1 | src/seastar/cmake/SeastarDependencies.cmake:98 (find_package |
所以是来自这个文件, 传递了lz4
为要找的包, 但是实际上只找到了findLZ4.cmake
. 但是他隶属于seastar
目录, 在那个目录里, 有一个findlz4小写的. 所以我怀疑是嵌套的cmake
作用域之类的问题?
build目录下有一个Makefile.cmake
如何理解?
这个文件是从CMakeLists.txt
里生成出来的? 的确是生辰出来的...
Boost库还没下载下来, 所以直接编译seastar
是肯定成功不了的.
最后复制了seastar目录下的cmake/Findlz4.cmake
到了cmake/modules
目录下, 和FindLZ4.cmake
在一起, 就能过了.
不过我推测,应该还有个CMAKE_FIND_ROOT_PATH
这种东西可能在工作, 可以让seastar
目录在加载时, 自动使用当前目录的cmake
来进行find_package
操作, 不过目前没找到...
这次是编译报错. 看着比较像是c++支持语言版本不足, 看了下, 官方的jenkins
里是有安装了gcc-toolset-9
这个操作的, 所以按理来说应该是用gcc 9
版本编译才对.
果然, 在ceph.spec.in
里是有加载这个toolset
的操作的. 1
. /opt/rh/gcc-toolset-9/enable
这个ceph.spec.in
是怎么生成的暂时没找到, 不过有个make-dist
的脚本,这里有生成的操作, 这里触发应该有用吧
通过make -n
可以看到使用的c++
的路径不对, 是低版本的, 果然, 我用的Makefile
版本不对. 必须得更新一下. 删掉整个build
目录, 先执行. /opt/rh/gcc-toolset-9/enable
, 这次生成的Makefile
版本对了.
1 | # 找一下怎么直接触发 |
Feature #51156: qa: Teuthology Testing on Centos 9 Stream - Ceph - Ceph
1 | dnf copr enable -y ceph/el9 centos-stream-9 |
通过操作该项即可正常执行install-deps.sh
主要问题是从svn下载的最新版的luminous分支, 编译时报这个错误
1 | undefined reference to OSD::HeartbeatInfo::HEARTBEAT_MAX_CONN |
1 | cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=/usr/lib64 -DCMAKE_INSTALL_LIBEXECDIR=/usr/libexec -DCMAKE_INSTALL_LOCALSTATEDIR=/var -DCMAKE_INSTALL_SYSCONFDIR=/etc -DCMAKE_INSTALL_MANDIR=/usr/share/man -DCMAKE_INSTALL_DOCDIR=/usr/share/doc/ceph -DCMAKE_INSTALL_INCLUDEDIR=/usr/include -DCMAKE_INSTALL_SYSTEMD_SERVICEDIR=/usr/lib/systemd/system -DWITH_MANPAGE=ON -DWITH_PYTHON3=3.10 -DWITH_MGR_DASHBOARD_FRONTEND=OFF -DWITH_SELINUX=ON -DWITH_LTTNG=ON -DWITH_BABELTRACE=ON -DWITH_OCF=ON -DWITH_LIBRADOSSTRIPER=ON -DWITH_RADOSGW_AMQP_ENDPOINT=ON -DWITH_RADOSGW_KAFKA_ENDPOINT=ON -DBOOST_J=2 -DWITH_GRAFANA=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=on |
linux macros
定义位置/usr/lib/rpm/macros.d/macros.python
在使用12.2.1
版本时, 发现经常会出现需要10+秒才能返回ceph -s
的现象, 偶尔又1s
内就返回了.
首先, time ceph -s --debug_monc 40 --debug_ms 40
看下详细日志有没有记录在哪呢?
看了下, 应该是在跟mon通信过程, 但是好像不知道发生了什么.
咨询了一下大神给了个关键词, objecter
, 打开debug
后看到一部分内容了. 有resend_mon_ops的操作, 那就代表第一次picked
到的ip
, 有些没正常工作? ## 异常日志 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
212021-03-08 17:46:40.591805 7ff668ff9700 1 monclient(hunting): continuing hunt [47/3291]2021-03-08 17:46:40.591809 7ff668ff9700 10 monclient(hunting): _reopen_session rank -1
2021-03-08 17:46:40.592032 7ff668ff9700 10 monclient(hunting): picked mon.noname-b con 0x7ff640001210 addr 10.192.89.63:6789/0
2021-03-08 17:46:40.592112 7ff668ff9700 10 monclient(hunting): picked mon.noname-c con 0x7ff640005ad0 addr 10.192.89.61:6789/0
2021-03-08 17:46:40.592194 7ff668ff9700 10 monclient(hunting): _renew_subs
2021-03-08 17:46:40.593008 7ff669ffb700 10 client.?.objecter ms_handle_connect 0x7ff640005ad0
2021-03-08 17:46:40.593072 7ff669ffb700 10 client.?.objecter resend_mon_ops
2021-03-08 17:46:40.593083 7ff669ffb700 10 client.?.objecter ms_handle_connect 0x7ff640001210
2021-03-08 17:46:40.593104 7ff669ffb700 10 client.?.objecter resend_mon_ops
2021-03-08 17:46:42.592257 7ff668ff9700 10 monclient(hunting): tick
2021-03-08 17:46:42.592283 7ff668ff9700 1 monclient(hunting): continuing hunt
2021-03-08 17:46:42.592287 7ff668ff9700 10 monclient(hunting): _reopen_session rank -1
2021-03-08 17:46:42.592435 7ff668ff9700 10 monclient(hunting): picked mon.noname-a con 0x7ff64000d0b0 addr 10.192.89.60:6789/0
2021-03-08 17:46:42.592468 7ff668ff9700 10 monclient(hunting): picked mon.noname-c con 0x7ff6400106f0 addr 10.192.89.61:6789/0
2021-03-08 17:46:42.592536 7ff668ff9700 10 monclient(hunting): _renew_subs
2021-03-08 17:46:42.593106 7ff669ffb700 10 client.?.objecter ms_handle_connect 0x7ff64000d0b0
2021-03-08 17:46:42.593133 7ff669ffb700 10 client.?.objecter resend_mon_ops
2021-03-08 17:46:42.593140 7ff669ffb700 10 client.?.objecter ms_handle_connect 0x7ff6400106f0
2021-03-08 17:46:42.593142 7ff669ffb700 10 client.?.objecter resend_mon_ops
2021-03-08 17:46:42.593596 7ff669ffb700 10 monclient(hunting): handle_monmap mon_map magic: 0 v1
2021-03-08 17:46:42.593638 7ff669ffb700 10 monclient(hunting): got monmap 1, mon.noname-a is now rank -1
2021-03-08 17:46:42.593643 7ff669ffb700 10 monclient(hunting): dump:1
2
3
4
5
6
7
8
9
10
11
12
13
14d = 0
2021-03-08 18:50:37.595153 7fe83ffff700 30 Event(0x7fe8400e8650 nevent=5000 time_id=2).create_time_event id=1 trigger after 900000000us
2021-03-08 18:50:37.595151 7fe83f7fe700 20 -- 10.192.89.60:0/288270703 >> 10.192.89.60:6789/0 conn(0x7fe8403a2660 :-1 s=STATE_OPEN_MESSAGE_READ_FOOTER_AND_DISPATCH pgs=50114 cs=1 l=1).process got 397 + 0 + 0 byte message
2021-03-08 18:50:37.595158 7fe83ffff700 30 Event(0x7fe8400e8650 nevent=5000 time_id=2).dispatch_event_external 0x7fe84039c280 pending 1
2021-03-08 18:50:37.595160 7fe80affd700 10 client.?.objecter ms_handle_connect 0x7fe84039ef30
2021-03-08 18:50:37.595162 7fe80affd700 10 client.?.objecter resend_mon_ops
2021-03-08 18:50:37.595174 7fe83ffff700 20 -- 10.192.89.60:0/288270703 >> 10.192.89.63:6789/0 conn(0x7fe84039ef30 :-1 s=STATE_OPEN pgs=27784 cs=1 l=1).process prev state is STATE_CONNECTING_READY
2021-03-08 18:50:37.595181 7fe83ffff700 25 -- 10.192.89.60:0/288270703 >> 10.192.89.63:6789/0 conn(0x7fe84039ef30 :-1 s=STATE_OPEN pgs=27784 cs=1 l=1).read_until len is 1 state_offset is 0
2021-03-08 18:50:37.595184 7fe83f7fe700 10 -- 10.192.89.60:0/288270703 >> 10.192.89.60:6789/0 conn(0x7fe8403a2660 :-1 s=STATE_OPEN_MESSAGE_READ_FOOTER_AND_DISPATCH pgs=50114 cs=1 l=1).process no session security set
2021-03-08 18:50:37.595190 7fe83ffff700 25 -- 10.192.89.60:0/288270703 >> 10.192.89.63:6789/0 conn(0x7fe84039ef30 :-1 s=STATE_OPEN pgs=27784 cs=1 l=1).read_until read_bulk recv_end is 0 left is 1 got 0
2021-03-08 18:50:37.595196 7fe83ffff700 25 -- 10.192.89.60:0/288270703 >> 10.192.89.63:6789/0 conn(0x7fe84039ef30 :-1 s=STATE_OPEN pgs=27784 cs=1 l=1).read_until need len 1 remaining 1 bytes
2021-03-08 18:50:37.595193 7fe83f7fe700 5 -- 10.192.89.60:0/288270703 >> 10.192.89.60:6789/0 conn(0x7fe8403a2660 :-1 s=STATE_OPEN_MESSAGE_READ_FOOTER_AND_DISPATCH pgs=50114 cs=1 l=1). rx mon.0 seq
1 0x7fe83000b830 mon_map magic: 0 v1
2021-03-08 18:50:37.595203 7fe83ffff700 10 -- 10.192.89.60:0/288270703 >> 10.192.89.63:6789/0 conn(0x7fe84039ef30 :-1 s=STATE_OPEN pgs=27784 cs=1 l=1).handle_write
刚才忽然想到的, 会不会是因为有些时候先去访问了非主节点, 所以没响应, 直到去访问主节点为止.
mon为什么非主节点就不能提供信息呢?理论上应该是全节点都可以(PS. 后来试了下12.2.12
版本, 全节点都正常, 就这个版本不正常, 暂时没去查这个版本到12.2.12
改了什么)
所以monclient是怎么选择mon_host
的呢?
mon_client是怎么触发的呢?
现在问题就是init之外, ceph -s怎么调用的status了.
按理来说, 只是把status
发送给了mon, 来进行mon_command
但是现在mon_host, 他其实是不知道的. emm, 按这个来说, 应该再init阶段, 或者connect阶段知道的
的确是在connect阶段, 从build_initial中取得的. 1
2
3
4
5
6int MonMap::build_from_host_list(std::string hostlist, std::string prefix)
void add(const string &name, const entity_addr_t &addr) {
add(mon_info_t(name, addr));
struct mon_info_t {
接下来就逐渐进到这个pick阶段了
1 | ldout(cct, 10) << "picked mon." << monmap.get_name(rank) |
开始反查
1 | void MonClient::_add_conns(uint64_t global_id) |
这里的n如果一开始就是集群size, 好像即便shuffle也不会有问题了.
cat /etc/ceph/ceph.conf | grep mon_client_hunt_parallel
unsigned n = cct->_conf->mon_client_hunt_parallel;
的确找到了对应的,我把这个size改成mon_host
的size
, 一开始就一定能选中主节点, 就不卡了.
net/ceph/mon_clinet.c:pick_new_mon
中, 主要支持多个mon选择是在这条提交libceph: pick a different monitor when reconnecting · torvalds/linux@0e04dc2
对应的patch [4/9] libceph: pick a different monitor when reconnecting - Patchwork
]]>这个问题其实之前遇到的还挺多的, 简单来说, 就是某个A服务启动B服务, 但是B服务又必须等待另一个服务启动完毕(比如A服务), 产生了一定的死锁. 而在ps
的进程表中看到的就是systemd-tty-ask-password-agent
这样一个agent
进程卡住. 一般查一下当前卡住的service
和他依赖的after
,before
的几个服务, 就知道了, 一般就是循环死锁了.
具体理论这块还没去查源码, 暂时只能给个结论.有大佬看过的话, 希望也能分享下~
``` bash # new2.service [Unit]
Description=System Logging Service
Documentation=man:rsyslogd(8)
Documentation=https://www.rsyslog.com/doc/
[Service]
Type=forking
EnvironmentFile=-/etc/sysconfig/rsyslog
ExecStart=/root/new2.sh
[Install]
WantedBy=multi-user.target
#!/bin/bash echo "heelo" sleep 1000 & systemctl restart new1
[Unit]
Description=System Logging Service
Documentation=man:rsyslogd(8)
Documentation=https://www.rsyslog.com/doc/
After=new2.service
[Service]
Type=forking
EnvironmentFile=-/etc/sysconfig/rsyslog
ExecStart=/root/new1.sh
[Install]
WantedBy=multi-user.target
#!/bin/bash echo "heelo" sleep 1000 & ``` bash
]]>可能有人会有疑问:向量时钟到底有什么用呢?举一个常见的工程应用:数据冲突检测。分布式系统中数据一般存在多个副本,多个副本可能被同时更新,这会引起副本间数据不一致,此时冲突检测就非常重要。基于向量时钟我们可以获得任意两个事件的顺序关系,结果要么是有因果关系(先后顺序),要么是没有因果关系(同时发生)。通过向量时钟,我们能够识别到如果两个数据更新操作是同时发生的关系,那么说明出现了数据冲突。
向量时钟算法利用了向量这种数据结构将全局各个进程的逻辑时间戳广播给各个进程:每个进程发送事件时都会将当前进程已知的所有进程时间写入到一个向量中,附带在消息中
假设分布式系统中有 N 个进程,每个进程都有一个本地的向量时间戳 Ti,向量时钟算法实现如下:
对于进程 i 来说,Ti[i] 是进程 i 本地的逻辑时间 当进程 i 当有新的事件发生时,Ti[i] = Ti[i] + 1 当进程 i 发送消息时将它的向量时间戳(MT=Ti)附带在消息中。 接受消息的进程 j 更新本地的向量时间戳:Tj[k] = max(Tj[k], MT[k]) for k = 1 to N。(MT即消息中附带的向量时间戳)
假设有事件 a、b 分别在节点 P、Q 上发生,向量时钟分别为 Ta、Tb,如果 Tb[Q] > Ta[Q] 并且 Tb[P] >= Ta[P],则a发生于b之前,记作 a -> b,此时说明事件 a、b 有因果关系; 反之,如果 Tb[Q] > Ta[Q] 并且 Tb[P] < Ta[P],则认为a、b同时发生,记作 a <-> b。例如上图中节点 B 上的第 4 个事件 (A:2,B:4,C:1) 与节点 C 上的第 2 个事件 (B:3,C:2) 没有因果关系,属于同时发生事件。
根据这个, ceph里的last_epoch_started
版本号应该也是起这个效果的吧, 具体分析待阅读到那个部分的代码再展开.
在看到这篇论文之前, 我所知道的只是linux上自带的时间戳和ntp
协议, 以及内核里有一个每次开机启动时开始记录的monotonic clock
可以用来保障时间一定是连续的.
但是对于分布式设备之间怎么判断互相的时间先后性, 这块是未知的.
比如针对leader节点发送一次自己的认证失效时间, 假如自己的物理时间戳比那个时间还早, 但真实世界的时间戳已经超期了, 到底是按失效还是有效来进行呢? 不过这个问题似乎可以通过失效后统一发送失效请求, 只要收到了, 或者自身参与选举时的时间加一个超时时间来判断超时来做.
所以, 跟大佬们说的一样, 因此问题的关键点在于节点间的交互要在事件的发生顺序上达成一致,而不是对于时间达成一致。
在一个分布式系统中,你无法判断事件A是否发生在事件B之前,除非A和B存在某种依赖关系,即分布式系统中的事件仅仅是部分有序的。
基于Happended Before
偏序关系(因果一致性), 扩展出强一致性的全序关系.
根据[^1]可以得知下述概念.
尝试使用逻辑时钟来解决分布式锁问题。
分布式锁问题本质上是对于共享资源的抢占问题,我们先对问题进行定义:
已经获得资源授权的进程,必须在资源分配给其他进程之前释放掉它; 资源请求必须按照请求发生的顺序进行授权; 在获得资源授权的所有进程最终释放资源后,所有的资源请求必须都已经被授权了。 首先我们假设,对于任意的两个进程Pi和Pj,它们之间传递的消息是按照发送顺序被接收到的, 并且所有的消息最终都会被接收到。每个进程会维护一个它自己的对其他所有进程都不可见的请求队列。我们假设该请求队列初始时刻只有一个消息(T0:P0)资源请求,P0代表初始时刻获得资源授权的那个进程,T0小于任意时钟初始值
为请求该项资源,进程Pi发送一个(Tm:Pi)资源请求(请求锁)消息给其他所有进程,并将该消息放入自己的请求队列,在这里Tm代表了消息的时间戳 当进程Pj收到(Tm:Pi)资源请求消息后,将它放到自己的请求队列中,并发送一个带时间戳的确认消息给Pi。(注:如果Pj已经发送了一个时间戳大于Tm的消息,那就可以不发送) 释放该项资源(释放锁)时,进程Pi从自己的消息队列中删除所有的(Tm:Pi)资源请求,同时给其他所有进程发送一个带有时间戳的Pi资源释放消息 当进程Pj收到Pi资源释放消息后,它就从自己的消息队列中删除所有的(Tm:Pi)资源请求 当同时满足如下两个条件时,就将资源分配(锁占用)给进程Pi: 按照全序关系排序后,(Tm:Pi)资源请求排在它的请求队列的最前面 i已经从所有其他进程都收到了时间戳>Tm的消息
按照大佬说的, 这个其实算得上是分布式一致性算法的雏形. 每次请求都带上一个逻辑时间戳.
Balls into bins 问题讲的是如果有 M 个小球随机等概率地扔进 N 个垃圾桶, 那么装球最多的那个垃圾桶大概率会有多少个球?
\[O(\frac{logn}{loglogn})\]
结果并不是正态分布的.
上面那个公式计算的是 max load. 假设有 64 个 node, LB 随机分配流量, 那么 load 最大的 node, 负载将会是平均负载的 log 32 / log log 32 = 2.7 倍.
解决方案出奇的简单(起码理论上是这样的), 这就引出了 The Power of Two Random Choices. LB 在分流的时候, 随机比较两台(d = 2)后端服务器的 load, 选最小, 得到新 bound.
\[ O(loglogn/logd) \]
log d
是常数, 可忽略. \[ O(loglogn) \] log log 32 = 1.2, 所以只需要预留20%.
2 choices 可以解决无状态的 load balance 问题, 但这对存储不起作用. 因为 LB 没有办法按 load 分流, 比如 key A 存在服务器 S 上, S 跑满了, LB 就能把 query redirect 到别的机器上吗?
Small Cache, Big Effect 这篇 paper 很大程度上解决了这个问题. LB 与 cache layer 存在很强的互补关系. LB 讨厌 imbalance, 但 cache 喜欢. query 越 skew, cache 效率越高.
在一个多 node 的存储系统, cache 至少要多少才有足够的信心说这个系统没有热点呢? paper 给出的答案吓死人了! 居然只要 \(O(nlogn)\) !n 是 node 数量.
Introduction:存储系统的工作负载高度倾斜,过载节点成为系统性能瓶颈,SoCC11[1]一篇文章证明了,n个存储节点,无论query是怎样的分布,缓存最热的O(nlogn)个对象即可均衡负载。SwitchKV和NetCache都使用该机制均衡KV存储集群的负载。单个缓存可以完成集群内的负载均衡(比如一个机柜),但集群规模不断扩展,集群间会出现负载不均的问题,也即过载集群的工作负载远高于其他集群,超过了该集群cache提供的缓存能力,该集群成为大规模集群的性能瓶颈。将一个集群视为One Big Server,m个集群视为m个Server,按照一个cache的机制进行扩展,采用更大的cache提供缓存功能,不具有可扩展性,即伴随着系统工作负载增加,heavy hitter需要的吞吐量势必超过One Big Cache的吞吐能力。
One Big Cache性能不足,扩展机制从One-Cache到Multi-Cache扩展,形成一层Cache Layer。现在就有两层cache,第一层cache layer,每个cache在所属集群中均衡负载,第二层cache均衡集群间的负载。怎么缓存heavy hitter和怎么在两层cache间query item成为了新的问题。如果采用replication机制,将第一层中最热的item复制到第二层的每个cache中,可以提供很强的读性能,但是在写的情况下,会带来很大的缓存一致性开销。如果采用partition机制,过载的cache热点无法打散,依旧是性能瓶颈。如何在partition和replication之间做trade off,平衡读写性能,是本文的关键。
缓存热点时使用independent hash function,如图3,ABC三个热点hash到同一个cache中,在第二层hash时采用不同的hash函数,旨在将热点打散,如图中,ABC分别被打散到第二层的cache中了。查询时采用power-of-two-choices,如图3,query A时观察C1和C3的工作负载,查询工作负载小的cache节点。
作者给出的方案很简单, 就是双层 cache. 当前所有 cache server 的所有 KV 用一个新的 hash function 存在另一层 cache layer 里. 从数学和直觉上可以确保, 任意一层的 cache server 出现了热点, 那么这个热点的 KV 数据在另一个 cache layer 是分散的, 无热点的.
然后在哪一层 cache 找 key 的问题又可以转化为 The Power of Two Random Choices 问题, 选负载最低的.
系统搭建承接了SOSP17的NetCache[2]工作,要具体理解本文的系统,还需追溯到NetCache去
这篇NetCache
里提到的主要架构应该是利用交换机和路由器来进行缓存读请求, 通过实现一套协议, 让存储服务器和交换机之间完成这个缓存数据的交互(读:查询, 写:write-through).
可扩展弱一致性感染型进程组成员协议
进程组的成员关系协议
- 可伸缩性
- 弱一致性
- 感染性
用于分布式系统中进程的健康检测或者说错误检测
SWIM是通过Gossip实现的Membership保持协议,也就是维护分布式系统节点的状态
成员资格协议的可伸缩型和执行效率主要由以下几个方面确定:
完备性:是不是每个失效的进程最终都能够检测到?
失效节点的检测速度:一个节点失效到它被非失效节点检测到的平均时间间隔是多长?
准确性:实际上进程未失效但却被认为是失效的频度(即误判率)是多少?
信息量:每个节点生成的网络通信的信息量有多大,它是否也是分布式的?
理想情况下,我们需要这样的协议:它一定要完全100%准确,这就意味着可以检测到每一个失效进程,而且不存在任何误判。然而,像分布式系统里的其他协议一样,存在这样的事实:在异步网络上保证100%的完备和准确是不可能的。因此许多成员资格协议(包括SWIM)为了完备性就会降低准确性,同时尽最大可能降低误判率。
概念:
T’: 协议周期,2次探测的时间间隔 k: k个节点 该协议中,若Mi检测Mj状态,则先发一个ping包到Mj,若Mj未在超时时间内ack,则Mi随机选择k个节点,发送间接探测ping-req包,k个节点如Mh收到ping-req请求,将会ping Mj并将结果返回给Mi。若任意一个ping-req返回Mj存活,则Mj存活。
上述过程,包含3个round-trip的网络过程,Mi->Mj, Mi->Mh, Mh->Mj,所以T’ 要大于3个超时;如T’=1s, timeout=200ms
这里故障监测, 也存在一篇论文讲到[SRDS 2004] The Phi Accrual Failure Detector
讲到可以通过概率模型来判断都次未响应后到底该节点是否已经离线, 还是只是网络波动缘由导致. 这个还挺可靠的, 的确某个设备的链路存在波动问题时, 常常代表他的响应会是长期离线的.
- 假设 heartbeat message 符合某一概率模型。例如定期发送的 heartbeat message 在有网络延迟的情况下,接收到消息的 interval 符合正态分布。
- 利用接收到的历史数据(滑动窗口),对概率模型参数进行极大似然估计。
- 利用估计得出的参数带入模型,计算在当前时刻接收到 heartbeat message 的概率 区别于传统方法,此时不直接得出对端是否存活的判断,而是直接向上层应用返回这一概率,由上层应用自行进行解释,是这篇论文中提出的另一个“亮点"。
当Mi检测到Mj失败,会利用IP多播等方式,发送一条 failed(Mj) 的消息,收到消息的Node,会把Mj从本地Node表中踢除。
所以理论上, 基于这个的linux集群网络稳定性监控机制应该是有的吧.
1 | pacman -Syy ceph |
安装完发生了一个奇怪的问题, ceph -v
执行报缺rados
库, python3-rados装到了python3.9
的site-package
里, 但是我设备里pacman
目前只装了python3.8
, 这个的包里没有看到这个库
执行了pacman -Syy python
升级到3.9
, 可以用了, 升级过程中python3.9
的库里少了好多我之前安装图形界面用到的python
库. 我最后是把3.8
的site-package
里的包不重复的复制进去安装的.
arch
有一个好处, 大佬们多, 源里最新版的东西很多. 虽然pacman
里没有, 但是AUR
源里有.
yay -Syy cephadm
就安装上了.
cephadm bootstrap --mon-ip 192.168.115.130
发现官方文档里没提需要安装chrony
, docker
,配置hostname
, 配置/etc/hosts
.
都配置完之后. 很可惜还是出现了失败.
通过ceph log last cephadm
查到, 失败原因是ssh
连不上, 然后我发现我的arch
默认没装openssh
导致的. 安装之后就通过了. i 然后创建几块虚拟盘进行操作. 不过我这里用默认的自动对全盘进行创建的逻辑无效, ceph orch apply osd --all-available-devices --dry-run
扫了下,我的环境好像不符合条件. 我猜是盘的格式化问题(我没有用sgdisk初始化过, 所以应该还是一个不符合ceph默认用的分区表的盘)
不过通过单独指定ceph orch daemon add osd ceph01:/dev/sdb
, 还是可以成功的.
成功建出来了, 发现默认就有一个device_health_metrics
的名字的资源池.
sudo cephadm rm-cluster --fsid d9850914-866a-11eb-8eec-000c2934ee39 --force
我居然在没清理干净集群的情况下, 成功部署了一次?
源码版本是有vstart
的, 不知道生产版本有没有, emm, 没有.
大家最受影响的就是一半时间开发, 一半时间运维的这个问题. 利用一个成熟的配置文件, 快速部署起自己需要的环境, 而不是一步步通过页面控制点击, 这是一个很省力的事情. 将运维中与部署相关的事情通过一些成熟的模板化的配置文件来提供, 最好不过了.
建议遵循unix
思想, 一个工具只做该做的事情, ansible虽然提供了能力, 但是能由脚本来完成的事情, 没必要在ansible中进行处理 根据[^10]终于意识到我现在所处理的最别扭的地方在哪里了...这篇文章中说了下述几点.
从个人使用上来说,Ansible 还是很好用的,至少它无需 Agent,SSH 连接等特性,使用起来很友好。
但是我们也不应该过分使用 Playbook,编写 Playbook 解析 json 花费了不少的时间,远不如直接在被执行脚本中完成的成本低。
deploy.yml 用来部署集群。执行 deploy 操作会自动将配置文件、binary 等分发到对应的节点上;如果是已经存在的集群,执行时会对比配置文件、binary 等信息,如果有变更就会覆盖原来的文件并且将原来的文件备份到 backup(默认) 目录。
start.yml 用来启动集群。注意:这个操作只是 start 集群,不会对配置等信息做任何更改
stop.yml 用来停止集群。与 start 一样,不会对配置等做任何修改。
rolling_update.yml 用来逐个更新集群内的节点。此操作会按 PD、TiKV、TiDB 的顺序以 1 并发对集群内的节点逐个做 stop → deploy → start 操作,其中对 PD 和 TiKV 操作时会先迁移掉 leader,将对集群的影响降到最低。一般用来对集群做配置更新或者升级。
rolling_update_monitor.yml 用来逐个更新监控组件,与 rolling_update.yml 功能一样,面向的组件有区别。
unsafe_cleanup.yml 用来清掉整个集群。这个操作会先将整个集群停掉服务,然后删除掉相关的目录,操作不可逆,需要谨慎。
unsafe_cleanup_data.yml 用来清理掉 PD、TiKV、TiDB 的数据。执行时会先将对应服务停止,然后再清理数据,操作不可逆,需要谨慎。这个操作不涉及监控。
作业编排:可以自由选择相应的原子化操作,编排和发布新的作业流程
可视化: 通过拖拽鼠标编排作业流程和参数表单,作业运行与任务的状态可视化
并行调度: 同一个作业中的任务支持并行执行,在很多场景下能极大的缩短执行时间
实时监控: 任务的运行状态在页面上实时更新
断点执行: 执行错误的任务在修复错误后可以继续执行,不需要重新执行整个作业
任务回滚: 遇到错误,可以支持回滚作业中已经执行过的任务,恢复执行环境
作业模板: 可定制作业模板,用模板创建作业实例,避免重复工作
任务组: 在作业模板中把任务分组,在创建任务实例时候可以动态的复制任务组,相似的作业使用同样的模板,但又不限制于模板
计算表达式: 使用表达式动态的计算任务的参数值,能够极大的简化重复参数的输入以及复杂参数的拼接
动态参数:前面任务的输出作为后续任务的输入参数
--start-at-task
这里采用的方案是自定义一个callback
的插件, 将failed
的task
记录下来,见callback, 然后封装一个CLI
, 在下次执行的时候增加--start-at-task {task_name}
来进行调度. ### 优化方案 和一开始调研时的目的有点不一样, 现在发现的主要问题在于ansible做的预处理的逻辑太多了, 而且主要框架严重依赖单独的ssd和shell, 这样带来的问题你就是部署的性能较差.
需要找一些优化的方案
主要目的是支持可以解析出当前失败的任务, 然后给另一个进行重试的接口进行使用.
好像应该叫做rolling update模型, 哦,滚动升级
哦, 忽然明白为什么是loop结合delegate_to了, 因为要的不是并发,而是滚动操作.
这俩函数有什么区别呢? 目前看起来v2版本并没有加强多少? 那就随意使用了先.
感觉[^3]这种插件的阅读方式能提供一些参考.
python3.8 调用ansible 2.9.4 api | 经验分享&服务器代维
根据GPL license implies that my plugins are also GPL (MPD's note: it does not) · Issue #8864 · ansible/ansible可知, 虽然ansible
是GPL 3.0
, 但是由于我们编写的部署脚本是被ansible
读取执行, 并非链接. 因此可自行决定使用的许可证.
-vvv
, 但是实际上代码里有一部分写了vvvvvv
的函数.Debugging modules — Ansible Documentation
chusiang/ansible-jupyter.dockerfile: Building the Docker image with Ansible and Jupyter.
Ansible 用 Docker Compose 练习 Ansible_w3cschool
通过playbook
调度role
的task
, 来进行批量任务执行. ### workflow操作多个playbook是否有开源替代方案? 还是直接写playbook, 通过role控制. 目前来看, 不算特别关键.
1 | ansible-playbook -i {inventory host} add-osd.yml |
这里为什么看到和loop
一起使用? delegate_to
不支持直接指定组嘛?为什么要loop
这个的主要用途难道不是到一个只需要一个组内的一个节点执行的时候, 直接选中某一个吗?
ansible解析参数的过程, 和delegate_to这种指定某个节点只执行一次的逻辑, 是哪个优先呢?
如果是后者, 那是否这个的目的是用来让这个组内只在第一个节点执行一次?
但是感觉有点像是后者
1 | delegate_to: "{{ item }}" |
当我不需要指定哪个节点来执行一个集群任务的时候, 直接用run_once就可以ile,他会直接指定第一台
不可以, 只能是当前执行hosts所在的目录的相对路径
可以修改ansible.cfg
内的配置
1 | name: xxx |
自定义模块, 内部包含具体的task
在 playbook 中存在多个 roles,且其中有相互依赖关系时,合理使用 meta 配置,填写其所依赖的 roles。注意,被依赖的 roles 会优先执行
在role内使用include_task
无法使用start-at-task
直接跳转, 而import_task
可以
1 |
|
Ansible include_tasks will not run when tags are specified - Stack Overflow
全节点临时执行的命令, 这个对于排查问题全节点查日志的时候还是用的比较多的.
1 | ansible 主机或组 -m command -a '模块参数' ansible参数 |
[^2]里稍微解释了一部分.
根据目前的调研结果来看, 存在两个问题 * osd层无法支持用于db,wal分区之后剩余空间创建osd * crush rule无法自由指定 * 不支持tier创建 * 不支持针对ec模式下的资源池,通过创建副本的元数据资源池进行创建rbd.
姑且按照[^5]先启动看看.`
osism/ceph-ansible Tags - Docker Hub
vagrant 启动失败了, 似乎我用的centos景象不太一样, 找个docker hub里的试试
所以library其实完成的任务就是拼接command?
AnsibleAPI 开发 - 简书 参考上面这篇文章基本可以说明怎么使用ansible提供的库
对应的是不同的Task, 因此的确会冲突
一个是启动shell
, 一个是启动sh
默认ansible使用的module 是 command,这个模块并不支持 shell 变量和管道等,若想使用shell 来执行模块,请使用-m 参数指定 shell 模块,但是值得注意的是普通的命令执行模块是通过python的ssh执行。
的确, 用来标记这个任务是否对环境产生了修改?
属于ansible自身的保留字段
是否是因为使用的是ssh等协议, 所以用Rados其实并不太方便? 并没有设计这个connection?如果要开发, 其实是开发一个connection机制吧?
主要用途是部署, 而在部署阶段, rados得在mon部署完毕后才能工作, 因此如果要切换connection, 还必须分阶段. 还不如直接ssh全套
1 | - set_fact: |
set_fact 是注册到hostvars , 即注册到不同的host里变量.
因为filter是jinja2语法, 外面需要先带上双引号. * You are using the debug: module with the option var: and this expects a variable, not a jinja2 template.
对task进行检查模式 ### ansible的模块里打印大量内容, 即便他不显示,也会记录到warning里? 有垃圾产生?
通过logging直接往其他文件, 或者通过Syslog协议等输出是一种方案.
或者可以记录到stderr中, ansible只检查stdout是否被污染, 然后报错.
不推荐用这个library, 将来自改造都不方便.
这个还需要找一下
{}
会把字符串转换成object, 导致即便to_json了, 双引号也没了, 其他程序无法识别了.没有提示的时候, 实际上问题就是在于你的模块里用了print...
ansible 是只检测stdout是否被污染, 所以如果我把信息写到Stderr里是没问题的.
我们目前用的ansible 2.6
, 我指望使用python3来进行操作, 避免一些问题.
按照[^13]里提到的来说, 理论上会按照从哪个python版本安装的ansible
, 就从哪个版本启动的功能.
根据[^11]发现的问题就是, 我用的2.6
通过ansible.cfg
中的设置, 并没能生效, 而通过-e
传入的参数倒是有效. 暂时通过上层传入来覆盖下层使用的解释器
只会在执行role的时候去读取对应的group_vars
中的变量
以上是错误的, 根据Ansible Configuration Settings — Ansible Documentation通过ansible.cfg
里增加debug = True
, 发现group_vars的load过程, 在plugins/vars/host_group_vars.py
代码中, 实际上在初始化阶段, 就根据每个执行任务的节点在哪些主机组里, 把对应的group_vars目录下的主机组的配置给load了. 并不是运行阶段才进行...
facts
配置导出呢template - Templates a file out to a remote server
1 | - hosts: all |
并不能自动识别缩进. 需要自己来控制order, 使用Jinja2
的filter
可以完成这个步骤.
1 | pools: |
to_nice_yaml output not as expected · Issue #16707 · ansible/ansible
如果我把这个封装成一个block, 然后每个block只处理其中一个任务的时候, 这个每个循环的变量怎么穿进去呢?
比如register
和with_items
混用时, 是什么效果呢? 拿到的好像就是每条任务注册结果的列表.
when
和with_items
一起使用时, 是每次都执行一次when
好像可以利用changed这个
import_playbook不支持传入指定参数.
强制重新读取inventory
1 | - name: refresh |
1 | - name: set fact on swarm nodes |
根据这个(1) meta: refresh_inventory does not include previously absent hosts in task execution : ansible
和我的实际结果, 当我在多个Task中间加了一个meta: refresh_inventory
之后, 后面的任务就一个都不执行了. 如果import_playbook
了, playbook倒是还会执行.
目前还没怎么看到代码, 为什么会失败.
解决方案目前是知道了, 写两个host, 上一个playbook的最后一步就是refresh_inventory
, 下一个playbook再使用这个group
结论, 不支持远程节点. 所以只能自己先把文件复制到本节点, 再进行导入
Controlling where tasks run: delegation and local actions — Ansible Documentation
好像有remote_src
选项
To assign included variables to a different host than inventory_hostname, use delegate_to and set delegate_facts=yes.
include_vars is unable to load files from remote · Issue #58169 · ansible/ansible
根据上面这篇issue
可以知道, remote_src
是不准备支持的.
The error message is on a generic function, was added for users that were using actions that did allow remote_src, though clearly when added they missed the confusion possible for other actions. We should fix the error message, but i would not try to personalize it too much or we will end up with similar ticket for other actions.
1 | tasks: |
ansible skipped task still register value
可以用以下方式绕过, 或者register不同名字变量, set_fact到同一个上(set_fact支持when) 1
2register: "{{ 'real_variable' if some_condition else 'cool_story_bro' }}"
when: some_condition
我遇到了修改了系统的hostname, 不过在有些节点, fact结果还被缓存了, 没有重新收集的问题
在运行中重新收集可以用以下命令 1
2- name: do facts module to get latest information
setup:
我试了下, 重新创建playbook
即便是同主机组好像也清理不干净, 我直接在原来的playbook里加了下述任务, 然后再注释掉重新执行就可以了.
1 | tasks: |
目前是遇到这样一个问题, 直接导致后续的任务执行时节点缺失.
看概念就只是:
A boolean indicating if the task had to make changes to the target or delegated host.
通过hostvars['localhost']['xxx']
可以实现
web editor
嵌入到vscode
中打开, 这样的话也算是实现了一个所见即所得的编辑器, 不过不是基于Vscode的倒是了. 不过这种情况下有些vscode配置的快捷键应该还是可以用的.markless
自己维护了一个markless_sean10
. 暂时用起来还是不错的.fzf
的search.showLineNumbers
打开这个选项即可, 默认未开priority
folding根据Support folding things other than subtasks, like folding by tag · Issue #85 · fabiospampinato/vscode-todo-plus, 目前还是缺少API的.
search editor: Default number of context lines
然后触发Open in Editor就可以了.
open new search editor to the side
CMD
和Ctrl
的快捷键可以通过 File -> Preference -> Settings中 vim.useCtrlKeys 选项设置为 false
打开设置中的vim的Use System Clipboard
ctrl + M 使用tab键切换焦点模式tab move foucs ctrl + ~ 聚焦到TERMINAL(展开/收起TERMINAL) ctrl + 聚焦到editor Ctrl + Shift + E 资源视图和编辑视图的焦点切换 Ctrl + Shift + V 预览Markdown文件【编译后】 Ctrl + K v 在边栏打开渲染后的视图【新建】 cmd+1 主窗口 cmd+2 terminal
visual studio code - vscode key binding for "goto next search result on the search results pane"? - Stack Overflow ## vscode将cursor从左侧搜索窗口切换到主窗口的快捷键是啥? cmt+1
cmd+2
据说最多人用的vim
插件冲突太多, 但是neovim
就会好转很多.
更换之后, 至少Ctrl+c
是没有问题的, 在insert
模式下
1 | " 添加vim开启默认insert |
Setting to open editors in insert mode by default · Issue #613 · asvetliakov/vscode-neovim
1 | Command->toggleVim |
Help->Process Explorer
, 这里会显示出每个窗口的CPU
和内存的占用.编辑器不会跟随同步删除.
根据[^2]来看,似乎是electron
和chromium
的问题. 但是我用的1.51
的vscode
还是存在这样的问题呀?为什么关闭了呢?
似乎我升级了下搜狗输入法就好了, 至少5.9.0.11685目前没什么问题.
哇, 今天偶然看到的这个插件, 太厉害了, 完全兼容我其他的插件的快捷键, 不需要学习成本, 直接就能享受typora
的效果.
开发修改
在Suggestions
里的Accept Suggestion On Enter
. 修改为Off
即可
快速切换tab, Option+Cmd+左右方向键
通过Cmd+1
等切换主窗口cursor
.
则可以通过F4
和shift+F4
切换结果
可以通过cmd+g
控制
因为最近写文字记录比较多, 有时候词写错了, 想快点删的时候只能一个个删, 不像因为单词可以直接删, 就忽然意识到. 现在分词做的这么好了, 理论上这种插件应该已经有了.
果然, 搜到了这个CJK Word Handler - Visual Studio Marketplace, 试了下, 还挺好用.
Cmd+B
覆盖了vscode
的outline
打开和关闭.remote ssh配置环境变量的时候
需要配置一下~/.profile
, 因为~/.bashrc
在非交互式窗口不会被初始化.
一开始用的pylance
和jedi
. 这次看pyx
代码的时候这俩都不支持, 就换回了microsoft python
.
1 | cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=True .. |
会生成compile_commands.json
, 放到加载的根目录即可.
vscode使用compile_commands.json - TruthHell - 博客园 #### 当在build目录下生成的文件时, 可以通过--compile-commands-dir=/mnt/qemu/bin/debug/native
指定让clangd搜索该目录下的数据库
rizsotto/Bear: Bear is a tool that generates a compilation database for clang tooling.
通过这个工具可以转换出一部分.
1 | Consolidate compiler generated dependencies of target ear |
在settings.json
中增加下述 1
2
3
4
5
6
7"files.exclude": {
"**/.xxxx": true
},
"search.exclude": {
"**/.xxxx": true
},
而不用在c_cpp_properties.json
中增加任何处理.
但是最好创建个这个文件, 好像就能够索引了.
安装clangd就能够支持call Hierarchy了. 放弃cpptools就行. 不过似乎需要时linux.
不知道为啥, 其他几个目录的代码这里都不报这个错的.
ccls 和 vscode 一种在vscode以外的c++方案 - 知乎 > ccls的优势一方面是跳转到定义和代码重命名,这个可以完全放心的交给ccls,clangd经常就无法跨编译单元做这些事情,尤其是在使用clang-tidy修复名字的时候,我曾经失手修改了一个工程根命名空间的名字,体验了ccls的绝对强大(笑;另一方面是基于语义的高亮,高亮种类齐全,基本上有高亮就不需要额外的精力判断词义了,clangd这方面的功能不知道是不是我的问题,始终没有什么作用。 > > ccls的劣势主要在补全速度上,这方面比起clangd差了至少一个数量级,工程越大越明显(clangd的自动头文件补全真的牛逼)另一方面加载cache文件,clangd也要快很多,clangd在分析编译的时候似乎会智能判断当前状况。不过ccls可以纠正.和->误用的情况,这里是比clangd强的。clangd集成clang-tidy以及提供代码fix也是ccls没有的功能,不过我认为跳转定义和改名是更加重要的功能 > > 总之clangd性能好,ccls功能强。 > > 另外,ccls可以指定编译额外的编译参数,这个在mac下非常好用,你可以开启额外的编译检查,或者修复头文件的缺失,clangd就需要手动了,尤其是使用compile_commands.json的时候,ccls可以不管里面的小问题,自己加两个参数,clangd就傻眼了,必须额外指定一次cmake生成compile database
ccls 和 vscode 一种在vscode以外的c++方案 - 知乎
shift+f8
高亮, 应该类似SourceInsight
的效果.
这块需要看下markdown渲染时, 到底是插件还是Vscode自身做的highlight, 可以找下, 换个.
试验了下, 切换不同Color Theme就可以切换出不同的渲染效果, 所以应该是在各自的color theme里控制的?
结论: 最后找了个Eva Theme装了下, 高亮的contrast还挺足.
通过执行Ctrl+Shift+P -> Developer: Inspect Editor Tokens and Scopes
可以看到下图
Markdown code block highlight consistency - what is "s" alias - Stack Overflow
vscode/markdown.tmLanguage.json at 1a9016d0a4aa0eb98fd6dce3baf1678a3ccc35b9 · microsoft/vscode
Developing inside a Container using Visual Studio Code Remote Development
Get started with development Containers in Visual Studio Code
visual studio code - VSCode Remote Container - extensions not installing on dev container using docker-compose - Stack Overflow mac上的Docker是跑在虚拟机里的, 所以即便设置--net=host
, 实际上还是和宿主机差一级, 所以这里指定宿主机的内网ip的话, 其实是可以通讯了的.
在devcontainer.json
里增加下述代理, 让容器里要下的插件可以通过代理下载. 1
2
3
4"containerEnv": {
"http_proxy": "http://192.168.31.36:7890",
"https_proxy": "http://192.168.31.36:7890"
},
process Explorer
中显示extension Host
cpu 100%+ . performance调试根据这个Performance Issues · microsoft/vscode Wiki定位方式, 打开Developer: Show Running Extensions
, 查到profiling time
中todo tree
插件占用了4s多, 可能是因为这个文件中内容太多, 导致慢了. 把这个插件禁用, 就不卡顿了.
捕捉cpu profile后, 去掉.txt后缀然后用vscode打开即可看到下图
目前单从这张图看不出啥...
code --status
排查根据这里How to build and publish a vscode extension pack - DEV Community这篇, 在package.json
中添加publisher
之后就可以vsce publish
了.
不过这个单纯只是存了元数据...我还需要他能够离线化保存.
看起来这个不是extension pack
官方预计提供的能力. 有没有办法recursively下载的方式呢?
1 | code --list-extensions |
如果开启git codelens
, 会出现...
一会显示, 一会不显示, 导致我当前行所在的焦点位置一会在屏幕中间, 一会跳到别的位置的情况.
https://d.shikey.com/jike/已完结的课程/34 玩转VS Code/35讲插件开发(四):Decorations装饰器.html # layout 好像1.64 支持了右侧, 可以拖拽窗口显示在那里的能力?
需要"editor.detectIndentation": false
设置了这个之后, Tab-Size
这个设置才能生效.
1 | rsync -rlptzv --progress --delete --exclude=.git "user@hostname:/remote/source/code/path" . |
Visual Studio Code Remote Development Troubleshooting Tips and Tricks
通过使用insider版本, 这样一个环境里同时有一个未装外部插件的开发调试用, 一个日常用.
好像说可以通过容器装vscode?
engines
is mandatory and must be of type string
with non-empty value需要package.json是在这次打开的根目录里, 即可.
outline-map.follow
设置为viewport
cmd+c
和cmd+v
在比如find或者ctrl+p
的窗口无法粘贴.toggle maximum
example
肯定得提供吧?openAPI
的文档, 但是这些测试框架中需要用的参数数据是另外填写的, 为什么openAPI
不集成在里面呢?OpenAPI是一种机器可读的接口文件的规范.
基于这个规范的文档可以提供的信息, 将原本需要开源人员处理的工作转移到工具上.
参照下面这篇文章, 其实就可以做到我们想要的效果. 使用 Postman 测试你的 API_json
使用Jmeter和Jenkins自动化测试OpenAPI – 小工蚁
ET应该可以做到API 自动化测试:零代码自动化、数据驱动测试、自动生成报告 - Eolinker API 全生命周期管理这个的程度才对.
非常适合应用单元测试、应用代码覆盖以及做到daily build/tes
万智牌看着是不是跟D&D
有点关系?, 旅法师之类的. 看起来是集卡式游戏的始祖级别? 看大家的评论中, 这个的设计师考虑的比较好, 平衡性等都考虑的不错.
游戏王嘛, 动画, 漫画, 实体卡的宣传都非常广, 只不过实体卡等那部分不是官方, 而是盗版, 在国内的市场里占了很大一部分, 相当部分的人的童年基本上都看过游戏王, 至少我是这样的. 而万智牌
我就到了大学才看到同学在玩, 才知道这个名词.
游戏王虽然宣传范畴上知道的人很多, 不过最大的诟病就是娱乐性较强, 平衡性较差吧, 新卡基本都非常强, 似乎是说每个时代的上位卡组基本都是一致的, 主流用户都玩同一套卡组, 只有玩娱乐场的时候大家能发散一点?
不过上面这都是大佬们为了整个游戏氛围的玩耍而考虑的. 对于我这种菜鸟来说, 光是很容易进行线上入门的ygo
的整个社区就已经很好了. 暂时先从主流卡组入手之类的.
记得大学的时候搜到过, 当时好像应该移动端还没怎么有,当时至少还是ygopro
一代的时代. 当时玩了两把, 连机器人都打不过(虽然现在也是). 在现在偏官方的网易的<决斗链接>已经发布的现在, 至少我的个人感受是依旧是玩的远比那种闯关式要舒服的多的.
emm. 似乎也没有线上服务, 也是需要收集实体卡的, 这样的话对于我这种就不太友好了. 暂时先不入门了.
ygocore
等的在线任选卡的游戏, 相比其他的要好多的了. 基本上全平台都有ygopro2
的游戏了, 安卓用ygomobile
即可.需要肝才能收集到想用的卡, 这样还是比较耗时间的.
真红眼融合
把龙骑士招出来之后, 发动, 把特召给禁了.时械神
, 既不让破坏, 又不受伤害, 就跟我的在那边进入死循环了.我的主要需求, 只是当我需要写引用时, 排序能够自动调整,而不是我手动去填写数字. 如果能够让最后的参考文献也能够像用Latex
时一样自动生成就更好不过了.
初步的一个思路是, 因为我主要需要的是纯文本, 然后在使用hexo-render-pandoc
生成为网页时, 能够把索引给使用上. 预计是在使用pandoc
的生成过程中, 将markdown
文件中的引用的key
给转换成对应的索引.
目前根据Sci.Fun | 如何使用markdown撰写论文? - 知乎这些来看, 跟使用pandoc
时导入bib
的library
基本一致.
搜集一下有没有现成的.
暂时好像是没找到这个结论.
在引用中使用3, 在参考文献位置则是即可完成链接的功能.
1 | [^1]:xxx |
更进阶的方案应该需要看使用的render
, 比如我的是pandoc
,就看pandoc
的引用相关的文档即可.
Pandoc - Pandoc User’s Guide Citation rendering
所以脚注内的内容, 最好不用编号, 而是实际的指代. 且将参考文献直接放在该段后. 通过渲染自动挪到文章/页最后.
苹果官方直接就有给出解决方案,关闭限速即可
1 sudo sysctl debug.lowpri_throttle_enabled=0
如果想要再开启,输入以下命令即可
1 sudo sysctl debug.lowpri_throttle_enabled=1
不能直接删除那个硬盘里的目录, 必须得tmutils
来清理, 预计可能是增量备份. 我当时删的时候没意识到这点, 当时手动删的几个目录完全没被识别出已经被释放, 从而腾出空间.
1 | sudo less +F "/Volumes/MacBackup/Backups.backupdb/MacBook Pro/2020-08-05-163227.inProgress/.Backup.618330747.626060.log" |
Mac 根目录下有以下几个文件夹: /System 文件夹,系统文件夹。与Windows 之中的 C: 等文件夹类似。 Library 系统资料库,其中的 Caches 可以删除。 iOSSupport 提供了系统的 iOS 支持。 /Applications GUI软件文件夹,共享的所有软件包都存放在此。 /Library 应用资料库,包括了大部分非核心的系统组件。Caches 可删除。 /Users 文件夹,与 Linux 之中的 /home 文件夹功能类似。而mac 之中的 /home 只是为了与 Linux 兼容,一般不放任何东西。 /Network 和 /net 网络相关,空的。 /Volumes 与 /mnt 类似,其中挂载了全部硬盘、网络硬盘等。 /sbin,/bin,/usr /dev文件夹,与 Linux 基本一致。与 Linux 兼容。 /etc, /var /tmp 文件夹,是位于 /private 之中对应文件夹的软连接。存放系统配置、数据库、缓存等。用于与 Linux 文件结构兼容。 注意,/root, /procfs, /boot, /sysfs 等非必须文件夹均不存在。
遵照freeBSD
的/bin,/etc,/lib
目录都是不建议修改的, 所以所有程序都是装到/usr/local
目录下
lldb -c /cores/core.99415
这样就可以调试了,不需要指定可执行文件看起来
1 | #更新brew到最新版本 |
Unsupported special dependency :maximum_macos · Issue #38604 · Homebrew/homebrew-core
macOS 使用 Homebrew 的经验分享 | HelloDog
没在文档里找到如何下显示brew update的进度,不然老是以为阻塞了,还好我想起来一般开源软件都用verbose,就试了一下,看了brew的写法也是相当不错的。
不过就是也只能显示到fetching的进度了,fetching快不快应该只能靠监控流量了。
brew tap homebrew/cask-fonts
brew cask install font-fira-code
mkdir -p $(brew --repo homebrew/core)
git -C "\((brew --repo)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git git -C "\)(brew --repo homebrew/core)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git git -C "\((brew --repo homebrew/cask)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-cask.git git -C "\)(brew --repo homebrew/cask-fonts)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-cask-fonts.git git -C "$(brew --repo homebrew/cask-drivers)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-cask-drivers.git
brew底层用的还是Git,git现在通过改host还是不怎么能提速,虽然现在用不了proxychains4,但是似乎可以直接用git config --global http.https://github.com.proxy socks5://127.0.0.1:1086
直接用代理,唉,早知道这个就没那么多事情了。
奇怪好像找到我的cask和homebrew不正常的原因了, 不知道为什么, 我执行上面的这堆命令的结果, git和目录的对应都是错的. cask的目录里的git却是什么Font的.
/usr/local/Homebrew/Library/Taps/homebrew
奇怪, 怎么好像这个目录下属的子目录里, 还是在brew那个仓库里呢... 是因为装的太早, 不是通过tap安装的?
看起来的确是这个问题, 新装的这个version目录没问题
brew tap homebrew/core brew tap homebrew/cask brew tap homebrew/cask-fonts brew tap homebrew/cask-drivers brew tap homebrew/cask-versions
暂时不配置源了, 用代理下载也挺快. 安装HomeBrew 失败的解决方案(Error: Fetching /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core failed!) - 子钦加油 - 博客园
实际上不存在这个了, 已经改名成homebrew/homebrew-cask了.
macos - Error: caskroom/cask was moved. Tap homebrew/cask-cask instead - Stack Overflow # 降频
机时以 sudo 运行 Turbo Boost Switcher,就不用来回输密码切换了。
如命令行输入:sudo /Applications/Turbo Boost Switcher.app/Contents/MacOS/Turbo Boost Switcher
Turbo Boost Switcher 真香,试用了一天立刻买 Pro 了 - V2EX
Spotlight打开之后,输入单词,按command+b可以直接用浏览器搜索,用command+L可以直接跳到字典项进行查询。
从官网买的时候, 听说官网不像渠道那边, 以开机联网开始激活, 而是以发货时间开始,如果发货到你收货就过了3天, 那就相当于你的保修期已经过了3天, 对于我想从淘宝加3天内的apple care+
的需求来说, 这就导致我需要提前查询到序列号.
还好, 虽然大部分地方没提到,但是实际上只要进入发货阶段, 当你邮箱里收到发货信息之后, 序列号已经有了, 可以直接联系客服, 通过一些信息直接询问序列号. 根据客服当时说, 根据正常流程, 发票实际上会自动发出, 只是可能相比你直接去问要晚一点发送. 我问到序列号, 办完apple care+
后的一天收到了发票.
如何通过launchctl控制进程呢?比如App
其实好像也只有pkill一条路.
一开始我以为必须按照他的提示来使用以前的那些功能按钮.
偶然按了fn的情况下去按touch bar上显示的f12, 发现也能成功调整音量. 所以如果知道原来这个按钮上对应的功能键, 其实还是可以用的.
- Paste this command in the Terminal window, then press Enter to execute it:
sudo softwareupdate --ignore "macOS Catalina"
- Next, paste this in Terminal and press the Enter key to run the command:
defaults delete com.apple.preferences.softwareupdate
- Lastly, execute the following command in Terminal:
softwareupdate --list
CPU会被暂停, 所以可能一些程序如果没做IO超时处理,就会直接触发异常中断了.
的确很对, 像是一些视频网站打开后休眠再次打开, 缓存的连接都失效了, 一般需要刷新再操作了.
我习惯用搜狗输入法了, 高级->动态组词
, 勾选上之后, 单纯输英文时, 一般都不会显示出中文的候选词了, 直接空格就能够输入了.
Ctrl + s
弹出的save
窗口里, shift切换中英文失效[^7]据说是微信的客户端引起, 重启设备似乎就好了.
我的确打开过微信, 然后没重启过.
试试.
已给搜狗和百度输入法提供了反馈, 留了邮箱, 不知道有没有反馈了.
类似下面这俩微软输入法的问题一样.
跟这个表现很像. 卡顿的时候window server占用也高.
当前是Monterey 12.6版本.
看到这个愈发感觉还是搞个纯linux笔记本省事...
其原因似乎是 Google Chrome 浏览器自带的一个更新组件 Keystone 触发了 macOS 内部的某种 bug。有很多其他用户也都发现了这两者间的关联。触发这个问题并不要求 Chrome 正在运行,部分用户仅仅是安装 Chrome 就可轻易重现。
降低 WindowServer 的 CPU 占用 - My Nook
确实, 关了chrome, 立马表现就正常了...
开了chrome之后又复现了...
问题单仍旧在这里.
临时先切换成Edge了. 虽然vscode有我那个markless问题引起的卡顿, 但是应该没延迟到那个程度.
10.15
版本, 我点开wifi管理, 发现居然直接就有一个Automatically join this network
的选项,而我没有勾选这个.
之前我用10.12
版本的时候, 我记得网络设置那里并没有这样这个选项, 导致每次都需要我手动点击. 现在居然完美解决了这个问题...
目前遇到的主要问题是, 我开的linux虚拟机, 用来起Docker
的虚拟机, 老是在我电脑合盖之后, 就自动休眠了.
找了下, 搜关键词stop vmware from hibernating linux
搜到了.
在虚拟机的vmx
文件中添加suspend.disabled = "TRUE"
emm. 似乎无效.
1 | pmset -g log | grep Wake | less |
主要用的这个[GitHub \- blind\-oracle/transmission\-trackers: Script to automatically add trackers from a list to all torrents in Transmission](https://github.com/blind-oracle/transmission-trackers)
这个是mac的图形界面展示的进程, 因为是集成显卡, 所以集显能力不足的时候, cpu占用会偏高.
有人说是降低透明度就可以不卡顿, 不知道是否有效.
在System Preferences > Keyboard中, 将Key Repeat跟Delay Until Repeat往左边设置:
有人说这样设计也能好转. 姑且看看吧.[^11]
Turbo Boost Switcher
暂时关闭睿频, 姑且在cpu满载100%的时候, 风扇不会那么容易转了opsengine/cpulimit: CPU usage limiter for Linux
有没有办法限制某个程序进程的 CPU 占用率呢? - V2EX
1 | for i in `ps -ef | grep ceph-osd | grep -v grep | awk '{print $2}' `; do nohup cpulimit -l 500 -p $i &>/dev/null & done |
cpulimit works by continuously sending SIGSTOP and SIGCONT to the target process to limit it's cpu time. So the program in this case doesn't hang, it's doing it's job.
需要注意cpulimit进程退出时, 需要及时发送SIGCONT信号恢复
linux - cpulimit not working correctly - Stack Overflow
通过appPolice
或者cputhrottle
等限制指定进程的cpu cputhrottle
好像不能用, 源码编译后, sudo
运行还是报这个
1 | cputhrottle libc++abi.dylib: terminating with uncaught exception of type Process::ManipulatorException: Error on task_for_pid |
简单看bash - Iterate over pgrep results - Stack Overflow 好像是API
在高版本改了? 不能用这个了?
资源占用过高
)
参考在 Finder 的右键菜单中添加「Open in VSCode」 | 始终
在automator
中可以增加比如open in vscode
的能力~
[(1 封私信 / 20 条消息) 如何将android手机屏幕投影至Mac? - 知乎](https://www.zhihu.com/question/38722634
Macast
barry-ran/QtScrcpy: Android real-time display control software ### 步骤 1
2
3
4
5
6
7
8
9
10无线连接步骤(保证手机和电脑在同一个局域网):
安卓手机端在开发者选项中打开usb调试
通过usb连接安卓手机到电脑
点击刷新设备,会看到有设备号更新出来
点击获取设备IP
点击启动adbd
无线连接
再次点击刷新设备,发现多出了一个IP地址开头的设备,选择这个设备
启动服务
备注:启动adbd以后不用再连着usb线了,以后连接断开都不再需要,除非安卓adbd停了需要重新启动
MIUI注意除了USB调试还需要开启USB调试(安全设置)
mac, 应该可以做到, 针对不同的wifi, 切换不同的dns服务器
Mac下面的根据场景切换网络配置 我们只需要在连接一个新网络时,添加一个位置描述,然后跟之前一样设置各连接参数,然后应用。
之后在场景发生变化后,如果想切换不同位置的网络配置时,在位置处选择你之前添加好的场景,然后应用就可以了
macOS 自定义场景以快速切换不同的网络连接参数_weixin_33755847的博客-CSDN博客
wifi profile switcher
Mac OS 自动根据 WI-FI 名字改变网络位置 - Razeen`s Blog
参照hammerspoon扩展方式配置.
疑似某个软件引起的? 但是在家里又没有这个问题.
会不会是chrome开几百个窗口, 某个窗口引起的?
重启设备后恢复, 看起来还是跟软件有关.
配置proxy-groups设置url-test
就自动选最快的了, 就不需要手动更改了. 不过注意需要把直接连接
的这个代理从选择的这里去掉, 不然延时一般都是最低 1
2
3
4
5
6
7
8
9
10proxy-groups:
-
name: 🔰国外流量
type: url-test
url: 'http://www.gstatic.com/generate_204'
interval: 300
proxies:
- '广东移动转台湾HiNet[M][倍率:1]'
- '广东移动转新加坡Azure[倍率:0.9]'
- '广东移动转日本NTT[倍率:0.8]'
我看我的switchOmega
已经停用很久了, 这次想考虑easyconnect和clash并存, 好像就要改浏览器的路由.
看到配置的地方了, 是在Wifi
的高级选项里设置了的proxies, 这里增加了公司的url bypass之后能共存了.
按option显示成2304x1296 感觉字大小刚好, 又能显示多个窗口了.
mac mail邮箱登录密码必须手动生成授权码
手动输入 且 敲回车
不能复制粘贴, 也不能直接点击验证/ok
(1 封私信 / 73 条消息) mac邮件添加邮箱无法验证,求大神帮助? - 知乎
1 | brew install coreutils |
1 |
|
这条疑似有效?
(1 封私信 / 80 条消息) mac os 苹果系统如何关闭鼠标滚轮加速? - 知乎
对于不需要的, 直接cmd
+拖拽下来就能删除该图标
比如搜狗输入法的图标就被我删了.
根据下面大佬安利的, 采用bartender 类似windows的小图标隐藏的效果一样, 可以折叠掉小图标.
这里来看, 确实似乎不够人性化, 程序的应用菜单, 确实会占用不少屏幕, 这样任务栏对于需要大量插件的用户来说, 确实会产生冲突, 这种情况下应该提供一种折叠之类的方案来提供支持.
macOS 顶上菜单栏空间不够,右侧小图标满了放不下了,一些图标直接显示不出来直接隐藏了,这种情况怎么解决呢? - V2EX
Mac 選單列空間不夠嗎?Bartender 讓你擁有第二選單,隱藏不需要的圖示 - Rockyhsu
1 |
|
像这里看到的, 其实升级的时候, 旧版本也保留了.
但是发现/usr/bin/python3
居然是个python3.8版本, 这里就需要确认下这个环境里之前是咋装进来的了.
1 | ➜ sean10.github.io git:(hexo) ✗ pip3 --version |
根据这段判断, 比较像是CLT装进来的...但是时间戳和我今天装的好像对不上...怎么确认CLT不同版本里对应的python版本呢?
From the Xcode 11 release notes...
”In future versions of macOS, scripting language runtimes won’t be available by default, and may require you to install an additional package. If your software depends on scripting languages, it’s recommended that you bundle the runtime within the app.”
It might be available in the short term, but not the long term.
根据上面这句可以得知, CLT不再安装python3, 即可以通过brew或者pyenv管理了.
暂时先不管, 但是如果需要清理
实况文本
, 用mac原生软件预览图片时, 鼠标放过去就自动OCR了.睡眠后, 尝试激活, 发现无信号了.
下一步尝试type-c转hdmi线, 公司有个, 主要需要出去拿.
AirPods Pro won't autoconnect to Anroid Phone : airpods
AirPods Pro won't autoconnect to Anroid Phone : airpods
wifi连接的延时稍高一点, 还是usb连接比较稳定.
点击安装sndcpy
, 会报AudioOutput::" "/bin/bash: sndcpy.sh: No such file or directory\n"
参考这个"AudioOutput::" "/bin/bash: sndcpy.sh: No such file or directory\n" · Issue #643 · barry-ran/QtScrcpy, 接着usb线的情况下, cd /Applications/QtScrcpy.app/Contents/MacOS && /bin/bash sndcpy.sh
即可成功安装. 然后就点击开始音频即可.
[[Pasted image 20230218225149.png]]
根据这个来看, design好像是5000, 差的有点多.
自从升级了Dropbox到了CloudStorage之后, 目录迁移到/Users/sean10/Library/CloudStorage/Dropbox
之后, 出现了老是在下载云端文件的情况.
我期望的使用方式是跟之前版本一样, 没有变化时就是操作本地文件, 比如我外部打开整个目录, 直接触发全目录搜索就可以了, 不需要做什么多余的事情.
但是目前的体验就是会出现一直在转圈, 等待从云端同步. 如果真的要做这种逻辑, 那这种在云端的盘还不如hdd呢...
No longer have offline access automatically? - Page 5 - Dropbox Community
这里看到的讨论是都遇到了相同的问题.
在项目之外, 技术方面. 在年初时, 主要针对全链路追踪做了调研和demo的实现. 这个全链路追踪的思想在社区里查看, 针对java的果然还是比较成熟, 翻了翻综合安防平台的报告, 实际上他们的微服务的业务方面的开发框架, 基本上是集成了这部分功能的. 而针对嵌入式相关的编译时语言, 如果原先不是基于某套开发框架开发的背景下, 比如我们的代码大部分都是直接调用的syscall
,这种情况下, 我们如果想做无感知的埋点, 就只能利用gcc
或者clang
方面编译器提供的pre
的hook
函数的支持--finstrument-functions
来设置每个函数执行前后的回调之类的. 不过这个的性能代价相对来说较大, 因为源码文件中所有的函数均会被埋点. 当每个埋点都存在一个rpc通信时, 代价较大.
后续接触到ceph时, 对于pool,crush,pg,rados迅速有了一定的了解. 在这个过程中, 发现ceph基本上是应用了主流大部分的分布式技术方案. 然后, 逐渐意识到, 在分布式领域, 其实针对不同场景下的方案取舍, 存在很多种代价的组合. 在这其中发现虽然是称为分布式, 但是在程度上其实还是有差异的. 就目前的理解来说, mon等元数据管理机制其实都只是单点提供服务, 更多的管理节点完成的只是高可用(避免脑裂等问题). 而mon直接仍然存在单点性能的上限问题, 当然这个性能在一定规模下是不会成为瓶颈的. 比如, 假如说当管理的osd节点数量爆炸的高时, mon节点的性能的确开始成为瓶颈时, 是否就可以开始应用传统的垂直分割技术, 类似数据库垂直分表一般, 将传统技术在分布式领域的某些场景下再次应用, 就又可以突破一点. 诸如此类等等, 像是使用crush算法, 实际上算法始终存在分布不均匀问题.各公司在进行自研时, 是采用元数据管理服务器来提供分布来提供容量和性能, 还是为了让元数据管理服务器不成为功能上的瓶颈, 将选择节点过程迁移到客户端来进行, 暂时通过其他手段来解决crush带来的不均匀问题. 看起来都是根据自身的考量来说. 目前就目前的crush算法的限制, 以及个人的实验结果来看, 企业级产品, 目前可能这个资源占用的代价更愿意放到元数据管理服务器, 因为这个资源消耗相比性能的代价来看, 代价不如价值大. 但是, 对于社区来说, 前沿有意思的技术更有意思, 就像最初的ceph论文, 差异点主打的就是分布式. 且指不定忽然就出现了算法突破(比如可以通过配置不同的算法达成不同的虚节点分布, 一定程度上能够保障均衡), ceph的劣势一下子就不再是短板, 基本就完全碾压了基于元数据管理服务架构下的组件.
回到目前源码的学习中, 一个比较入门的感触, 就是目前组内调试, 可能并没有充分利用起社区提供的工具?主要还是只是增加日志重新运行等方式, 像是比如flame
火焰图等排查分析函数调用工具, 甚至其中之前做的全链路追踪, ceph中就有使用用户态链路追踪的工具lttng
, 基于这个额外提供了zipkin
的埋点对接. ceph采用的方法是目前zipkin
社区主要做的, 针对静态编译式语言, 由开发者人工在需要封装的函数位置调用宏tracepoint
, 通过编译时宏来进行控制是否启动埋点检测, 预计对于帮助理解代码, 会有比较好的效果.感觉这个是接下来有必要做的事情, 将这种快速学习代码逻辑的工具充分利用起来.
总的来说, 今年在技术上发现了不少新的思路, 可扩展学习的方向希望早日能够打通ceph的整条路径中各节点的竞争方案
希望2021年能将ceph的rbd及下层的IO流程中角色打通~能够逐渐对设计IO路径中某个角色有所思考, 能糅合出较佳的一个方案
]]>1 | docker pull sebp/elk |
日志数据采集器
把elk-logstash
的证书[^6], 和这个证书对应的域名加到hosts里解析, 成功导入了宿主机里的日志. 不过这种不修改filter
的逻辑的日志, 只是把log的每行都记录成message了, 其实索引没利用起来的感觉. 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15output:
logstash:
enabled: true
hosts:
- elk:5044
timeout: 15
ssl:
certificate_authorities:
- /etc/certs/logstash-beats.crt
filebeat:
inputs:
paths:
- "/var/log/ceph/*.log"
1 | docker run --name filebeat --user=root -v /Users/sean10/Code/ceph/ceph-14.2.9/src/out:/var/log/ceph -v /Users/sean10/Code/Algorithm_code/elk/logstash-beats.crt:/etc/certs/logstash-beats.crt -v /Users/sean10/Code/Algorithm_code/elk/filebeat.yml:/usr/share/filebeat/filebeat.yml -v /Users/sean10/Code/Algorithm_code/elk/hosts:/etc/hosts elastic/filebeat:7.10.1 |
1 | input |
从ceph文档里找来的Filter, 不过好像匹配失败了, 可能跟版本有关系吧.版本太低? 用第一个,至少时间戳出来了.
1 | /opt/logstash/bin/logstash --path.data /tmp/logstash/data \ |
似乎要导入log, 默认的elk
的配置不行, 需要修改logstash
的配置, 增加一些属性.
在日志存储服务器上安装filebeat filebeat将log存储目录下的所有log全部读取,发送到kafka logstash从kafka上读取日志,格式化后发送到elasticsearch elasticsearch正确索引后,response给logstash logstash更新kafka的offset,读取新的记录 一直循环以上步骤,直到所有的log都被写入到elasticsearch当中
正确的处理大量历史数据的导入 不过很诧异,我很难在网络上搜到有人提过429的问题。仔细回想以下,一定是我的集群太low了。整个elasticsearch cluster的索引处理能力太弱鸡。。。所以,在你的集群比较弱鸡的情况下该怎么处理?
当然,最直接的方法就是减少索引的量,比如,你要导入10月1号之前的所有log,你直接把索引的名字命名为platform-2017-10-1,而不是platform-%{+YYYY.MM.dd}。就算是只有一个eleasticsearch node,2C4G,亦能在5分钟之内把1G的log全部索引完。等索引完这些历史数据之后,你再把logstash上的output规则改为platform-%{+YYYY.MM.dd}。
还是使用volume
挂载 1
2
3path:
data: /var/data/elasticsearch
logs: /var/log/elasticsearch
导入是导入成功了, 基本的搜索也有了, 但是显示出来的冗余内容有点多.
有点用腻了mbp的mac os系统的感觉,想换arch了.
因为总觉得mac系统里好乱, 好多感知不到的空间管理. 而且终究只是unix, 并不是linux.
但是嘛, 都用了一段时间之后回来想想, 最近我比较倾向于arch
的原因主要是最近工作所需一直在用linux
规范下的systemd
控制下的CentOS
, 用的比较熟悉了, 对目录结构比较清楚了, 而对于自己非常不熟悉的freeBSD
基础的mac, 有种不在自己控制内的感觉...
现在想了想, 这其实不就是舒适区的问题吗? 学习一下mac的目录结构和日志排查等, 其实也并不会有多费事, 也并不会增加我记忆unix
生态的混淆程度.
哎,但是看笔记本的品控上来说,可能还是mac os让人比较省心了. 至少售后给的新笔记本不会像xps那样经常出问题...
什么电流音之类的,决定还是不选这个了.
虽然好像XPS 7590 15寸或者13寸的直接支持hdmi 2.0接口,不过品控还是太让人担心了.
emm, 似乎还行,但是触控板的体验还是比不上mac的应该
好像散热不行, 键盘部分会烫?
听说bios不太行?
bootloader
一直没能正常使用参照[^3]这个带图片的安装过程终于安装成了. 发现我直接选择第三种systemd-boot
的引导,最后是看不到进入系统的东西的,可能兼容性上有问题? 最后我额外安装了Refind
这套引导,终于进入桌面了.
大小写老师被自动切换了
似乎是vmware 15.5这个版本的缺陷
Temporary solution for Ubuntu guest which worked for me was just disabling the Caps Lock key all together with this
setxkbmap -option caps:none setxkbmap -option caps:none # (disable the caps lock key)
xdotool key Caps_Lock # (toggle caps lock) Caps Lock Issues With Upgrade - VMware Technology Network VMTN
禁用这个按钮倒也是个方案, 反正我基本也不用.
不过就怕他也影响宿主机, 那就很讨厌了.
为啥我安装的虚拟机鼠标放在Linux界面再挪出去,大小写键盘总_虚拟机吧_百度贴吧 VMware虚拟机中大小写不停切换的问题_helen2977的博客-CSDN博客 好像无解
我之前应该是直接pacman -Syy open-vmware-tools
现在发现vmware-tools
这个服务没能启动.
好像搜到说是
[SOLVED] Automating installation of vmware-tools
实际上我装的是open-vm-tools
### open-vm-tools vmtoolsd
这个服务似乎可以工作
VMware中的Manjaro启用复制粘贴_darkula的博客-CSDN博客
Installation Install open-vm-tools. If the legacy vmhgfs shared folder module is desired, the open-vm-tools-dkmsAUR package must be installed (the new vmhgfs-fuse driver is included in open-vm-tools). Start and/or enable vmtoolsd.service and vmware-vmblock-fuse.service.
Try to install gtkmm3 manually if it does not work properly. To enable copy and paste between host and guest gtkmm3 is required.
VMware/Install Arch Linux as a guest - ArchWiki
不能复制粘贴的时候, 不知道为什么,执行下这个vmware-user
就能用了.
Arch Linux / Manjaro 配置 VMware copy/paste
vim内没开全局剪切板, 无法向外复制. cat处理吧.
1 | # 这里fdisk分区时, 及时将类型修改为EFI类型 |
按照这个终于过了. Arch Linux (UEFI with GPT) 安装 | 沈煜的博客
ArchLinux图形界面安装与美化:i3+polybar_盐焗咸鱼的博客-CSDN博客_archlinux安装i3桌面
Can we use this to enable hibernation? A: Nope as hibernation wants a persistent fs blocks and wants access to swap data directly from disk, this will not work on: swapfs
emm, 不能用这个来做休眠用的swap.
1 | fallocate -l 16G /swapfile |
Arch Linux 使用 Swap File 进行休眠 - xzOS ## 安装vmware
VMware (简体中文)/Installing Arch as a guest (简体中文) - ArchWiki
systemd-boot和EFISTUB - 知乎 ### boot manager可以看到有我新建的grub, 但是EFI似乎没法直接从硬盘启动?
/etc/locale.gen ### UEFI和 grub什么关系?
并不是EFI分区, 导致bootctl无法识别了.
bootctl status可以帮助判定
[SOLVED] EFI partition not detected / Installation / Arch Linux Forums
grub-install --target=i386-pc /dev/sda 用legacy就尅引导了
用uefi的反而麻烦, 必须分区时gpt符合efi的
创建出来之后, 从哪里复制内容过来, 好像这个文档里都没有提啊s
Partitioning (简体中文) - ArchWiki
/boot分区里的内容在哪里呢?
使用过grub-config来往里面生成内容?
mkinitcpio -P 似乎也能生成新的initramfs
grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=grub
grub-mkconfig -o /boot/grub/grub.cfg
将已经存在的archlinux迁移到lvm - 知乎 #### initramfs选择?
LVM (简体中文) - ArchWiki archlinux安裝手记(Win10+Arch、GPT+UEFI、lvm) - 停止使用的账户 - 博客园
1 | sudo pacman-mirrors -c China |
3.8
升级到3.9
, 看了下/usr/bin
下的确只有新的python3.9
了, 但是/usr/lib64/
下还存在3.8
的目录, 是不是之前pip装的东西没有被自动卸载或者移植?这是一个疑问句 > 不要用 pip 管理 /usr 下的包,它会和 pacman 打架。要么建 venv 在里边用 pip,要么不用 pip 只用 pacman。
根据这个pacman 安装的python包无法识别 / 应用程序与桌面环境 / Arch Linux 中文论坛, 是建议通过venv
来管理python依赖的.
从TTY2
执行了下i3-sensible-terminal
, 结果发现是psutils
这个库没了, 应该就是我刚才升级引起的了.
怎么让pacman来安装包呢? 1
2pacman -Syy python-pip
pacman -Syy python-sixpip
之后报没有six
, 安装了下就能使用了.
执行terminator
, 报You need to install gobject, gtk, pango to run Terminator
最后, 我也没找到怎么处理的办法, 就把python3.8里的库复制到了python3.9的里...
[Solved] Connection time-out on installation (curl / iPv6 problem) / Installation / Arch Linux Forums 捣鼓了半天, 最后是因为我在Arch里默认开了代理, 代理好像有问题了.
There was a kernel update, some element of your initramfs wasn't successfully built and copied to your bootloader. Generally the output of mkinitcpio shows errors, chroot in and make it again. 执行
(1) Stuck on Loading initial ramdisk : archlinux
1
2 mkinitcpio
# 就会报出initramfs的错误.
1 | vim /boot/grub/grub.cfg |
通过增加set debug=all
和set ignore_log_level=all
倒是生效了, 看到了一些代码的执行记录. 但是倒是没看到Error信息
General troubleshooting - ArchWiki
为其他不是当前正在运行的内核创建镜像,添加内核版本到命令行, 可以在/usr/lib/modules目录查看支持的内核版本.
捣鼓了2天, 换了个稳定版的内核就正常开机了. #### 进入reach target basic system之前的shell 在
1
2 # mkinitcpio -p linux
就会以当前安装的linux版本对/boot目录下的initramfs-linux.img进行再次生成了.linux16
那行追加rd.break=pre-trigger
在linux16
这行追加rd.debug
可以打出initrd
中的dracut
脚本的内容
1 | pacman -Sy linux-lts |
不知道为什么alt+f
和alt+b
没办法快速在单词之间跳
这个快捷键是emulator
的还是shell
的呢?
bash默认是启动了emacs
模式的.
哦, 好吧, 是Super
key覆盖掉了alt+f
这个操作, 所以把Super
key换成command也就可以了.
(1) Can I completely swap the Alt and Super keys? : i3wm
set $mod Mod4
Archlabs-i3wm/config at master · alexandrebobkov/Archlabs-i3wm
主要是我之前生成的配置文件里居然都是直接Mod1 + Enter
之类的, 而不是$mod + Enter
, 导致我直接set $mod
没有效果.
systemd-networkd 禁用ipv6 net.ipv6.conf.all.disable_ipv6 = 1
可以和i3结合?
bspwm ## 文件系统只读问题 一般都是fstab被改坏了之类的.
毫无疑问,我主要感兴趣的点就是能够尽量完全使用键盘来控制的方式.
$mod + Enter 启动虚拟终端 $mod + A 焦点转义到父窗口上
歧义性好严重...没有看到输入密码的框, 原来是因为默认就处在接收密码的状态下, verifing
和wrong
的状态就是密码验证的结果... ### 登录桌面[deprecated] 最后因为vmware
里似乎X11
的直接配置没能配置生效, 导致sddm始终分辨率都是800x600
, 我最后还是选择使用.xinitrc
来调用xrandr
修改分辨率
1 | pacman -S sddm |
rxvt-unicode 不知道这个好不好用
(1) Which terminal simulator do you use with i3, and why? : i3wm
urxvt 要支持hidpi, 还需要一些特殊配置. 先这样用吧. 已经挺好了.
Ctrl+alt+c/v
因为HiDPI
, 所以增加了pixelsize
的设置, 结果无法启动了.
看了下, 似乎是缺字体?
feh
feh --bg-scale new.jpg
看起来多屏高分屏支持有点费事, 暂时还是不捣鼓了.
xrandr --output eDP-1 --auto --output DP-1 --auto --scale 2x2
这个的scale 和那个~/.Xresources的Xft.dpi=192区别?
是不是xrandr修改了dpi之后, 修改指定程序的字体大小就行了?
xrandr --dpi
这个好像是xft.dpi
的更新的方案
这个设置了以后, 标题栏的确hidpi了, 但是应用程序并没有, 不管是Terminal还是chrome.
Xft.dpi: 141 xrdb ~/.Xresources xrdb -merge ~/.Xresources xrdb -query -all
最后其实还是按照wifi操作成功的.
我在 .xinitrc中添加xrandr --output Virtual-1 --mode 2560x1600 在.Xresources中添加 1
2
3
4Xft.dpi: 256
export GDK_SCALE=2
export GDK_DPI_SCALE=0.5
export QT_AUTO_SCREEN_SCALE_FACTOR=1
我看到的窗口和颜色不太一样啊,vnc
先用i3试试.
[^4]可以看. 不配置de
还挺舒服的.
和dmenu-manjaro
冲突
目前来看,虽然Wayland还是在逐渐开发, 不过Xorg的成熟度还是远比其成熟, Wayland存在的问题稍多.
等价于i3wm
的管理器,不过这个没能做到i3polybar
的程度, 而且也始终是存在部分问题的. 所以如果要用arch, 还是继续Xorg
下吧. ## 开关机 shutdown -h now
【openSUSE】软件源和软件搜索 --- 看了之后 受益匪浅 - 孙愚 - 博客园
最近难得的出差去了一次杭州, 到了高铁站才用高德打车, 结果因为不知道该从什么地方去找师傅, 第一个打车的师傅说找不到这个地址, 只好取消, 换个试试能不能遇到对路线比较熟悉的师傅. 后来,稍微问了问这个比较熟悉录下你的师傅一些火车站的技巧. 顺便把以前常走的火车站的一些也给记录一下.
ceph
时, 发生了libpython3.6m.a: could not read symbols: Bad value
的问题, 在这附近有一个疑似相关的具体符号表的提示.1 | /usr/binld: /usr/lib/python3.6/config-3.6m-x86_64-linux-gnu/libpython3.6m.a(abstract.o): relocation R_X86_64_32S against `_Py_NotImplementedStruct` can not be used when makeing a shared objectl recompile with -fPIC |
第一感觉看上去的意思是ceph编译时尝试去链接的静态库的符号表没有通过-fPIC
的方式生成,从而完成共享库代码段复用的功能.
但是回忆一下这个问题发生的背景, 如果我用的是同一套yum源安装的依赖, 理论上python出现小版本不兼容的可能性较低.
那么, 就存在一个可能性, 这个报错链接找到的库并不是yum源里安装的, 可能是同事通过源码编译安装的python的库.
通过rpm -qf /usr/lib/python3.6/config-3.6m-x86_64-linux-gnu/libpython3.6m.a
查到这个库并不是安装的, 验证了我的猜测. 而rpm -qf /usr/lib64/python3.6/config-3.6m-x86_64-linux-gnu
则发现这里的文件才是python36-libs
里提供的.
既然如此, 就将/usr/lib
下的这个版本错误的库挪走, 重新编译, 这次报的是-lpython3.6m
未找到.
查看/usr/lib64/libpython3.6m.so
发现这个文件并不存在, 只有/usr/lib64/libpython3.6m.so.1.0
存在,而软链并不存在.
通过rpm -ql python36-libs
发现的确这个包中并不提供指向的链接.
根据目前对包管理的理解, 怀疑这个缺失的指向真实文件的软链很有可能在python36-devel包中
, 安装后果然如此, 链接找到了. 执行编译, 毫无问题了~
因为目前出现的python3.6m.so这个库的疑问, python3-libs这个库里没有提供指向.so的链接, 而是在python3-devel包中存在
而python34则都是在python34-libs里.
但是python36应该是能够正常使用的吧?
那是不是其实可以作答哦这样一点, 如果编译时我指定了链接, 然后在符号表中, 他就能找到针对这个链接指向的真实路径 ,然后存起来.
到生产环境里, 如果这个路径中存在, 就不需要ldconfig的默认路径了呢? 会不会有这个选择呢?
还是说这个libpython3.6m.so这个库基本只会在编译C与python之间的库时使用,而且只编倾向于静态的? 然后这样编完到生产环境里就不需要了呢?
]]>需要将现成的CLI转换为web服务的HTTP接口.
这个我记得之前看到哪篇文章推荐过, 但是一时想不起来了
想了想关键词
所以根据上面的思路来看
dlysm
或者libeffi
来根据配置中填写的函数名, 去进行调度.最近公司里多项目并行的时候, 发现在以前组管理的感觉比较混乱的分支方式(多个项目并行的时候, 同一个组件的代码需要维护多套分支)居然其实算是一个比较好的实现了...
这里的分支反而更加混乱. 索性梳理一下分支到底应该怎么管理, 才是最佳实现.
下面这部分是我目前的理解.
据我目前接触到和理解的, 会有以下几种常见的场景. 考虑到这里主要的障碍在于分支管理, 暂时假设一个组件的维护人员就是一人, 暂时跳过多人维护一个组件情况下的merge障碍问题(其实这块也比较要求模块设计的解耦, 多人改同一段代码这种情况本就不好合并, 我的理解是从设计上尽量避免对同一行冲突的修改). 根据[^6]中可知, 良好的模块化基本不会遇到代码分支带来的相同行修改的隔离问题.
- 项目制发布模式: 项目制发布模式, 预先确定功能特性, 在所有功能开发完成后进行版本发布
- 发布火车模式: 大型套装分发类软件, 各部门之间互相依赖, 约定版本发布的时间
- 城际快线模式: 固定版本发布时间和质量维度, 时间较短
我理解, 项目制和发布火车模式有点相近. 都是约定一个集成时间, 开始各功能提交进行集成.
而城际快线就是高频集成的持续集成模式那种吧.敏捷开发?因为高频集成, 所以重构代码带来的代价也少, 这种比较贴合代码架构变更频繁的场景倒是.
当一个项目开发中时, 开启了另一个项目, 需要用到目前正在第一个项目的分支中开发的功能
当一个功能即将开发完毕时, 但是由于对接方面的人时间计划来不及处理了,他的功能没有上车,这样的话, 对于我们的代码应该怎么处理呢?
公司研发了一套svn插件, 导致通过git-svn来完成本地使用git这个逻辑不太可行.
代码合并的最大成本主要是在于工具吗?
从主分支里拉了一个新分支出来,在合并回主分支之前,必须持续地把主分支的更改尽早尽快合并到新分支。新分支永远保持 最新主干代码+新模块代码 的状态。
其实的确是对的, 如果基线修改了, 那么就应该尽快把基线的一些修改合并到你当前的分支中去, 避免将来无法合并
因为你merge代码的时候,用BC除非没有冲突的文件,只要有add和delete操作的文件你都要逐个逐行进行处理啊,更别提有冲突的文件了。用git做merge无冲突文件且版本号高于master分支就自动帮你解决了,你只需要解决冲突就行;
git合并分支容易的原因是,要经常和master分支merge,这样把冲突消灭在萌芽状态。
git可以合并commit到一个后来被修改过的文件>
Git可以修改和重构历史提交:使用Git本身的reset以及 rebase 命令可以修改或者重整/重构历史提交,非常灵活。使用强大的 stg 可以使得历史提交的重构更为简洁,如果您对 stg 或者 Hg/MQ 熟悉的话。
巧用svn create patch(打补丁)方案解决定制版需求_golden_lion的博客-CSDN博客
问题原因 * 如何找到你想要的信息 * 如何保证一个用户不会读取另一个用户的数据 * 如何得知哪些块是空闲的, 等等
文件是上面这些问题的抽象解决.
Haystack
通过合并多个小文件成一个大文件, 通过减少文件数量的方式解决TODO:JuiceFS 只需要专注元数据的管理,也大大降低了元数据服务的复杂度(GFS 和 MooseFS 的 master 要同时解决元数据的存储和数据块的健康管理)。
挂载完成后,将在/proc/self/{mounts,mountstats,mountinfo}这三个文件中写入挂载记录和相关的挂载信息,并会将/proc/self/mounts中的信息同步到/etc/mtab文件中,当然,如果挂载时加了-n参数,将不会同步到/etc/mtab。[^3]
inode
为索引的bitmap
, 修改过的文件被标记目前都是字节序列, 用使用者自己进行定位. 这样的话其实和操作裸块也有点像吧? 的确
不知道有没有采用树等结构化关系的, 感觉更适合更上层的应用自己抉择.
操作系统至少能识别自己的可执行文件. 所以文件系统和操作系统还是有一些绑定关系的?
按照一般的编译过程, 是可以在CentOS
或者Debian
系的环境里直接执行do_cmake.sh
然后自动安装install-deps.sh
, 然后关闭一堆功能触发cmake
的.
不过因为暂时没找到可以进行环境隔离比如通过在docker
内编译ceph(初步尝试, deps中出现了systemd, 而docker内使用使用的一个fake-systemd, 这俩产生了冲突, 不知道有没有专门针对docker做的一个fake版本), 从而绕过弄坏OSX
宿主机的依赖的风险的. 在这个安装依赖的脚本中是有针对freeBSD
的pkg
安装命令的, 但是和目前主流推荐的brew
还是不一样的, pkg
还是给服务端使用为主. 怕产生污染, 所以我暂时选择手动来处理这个依赖的问题.
当然, 如果有远程服务器, 可以利用remote develop
, 通过服务器编译也能把成功建立索引.
根据[^11]的image
的build
仓库来看, 这个docker也是可以来触发编译的?
make CEPH_DEVEL=true FLAVORS="luminous-12.2.13,centos,7" build 单看这个脚本, 看起来可以编出来的样子.
make CEPH_DEVEL=true FLAVORS="nautilus,centos,7" build 这里为什么这个版本的centos里装的是真实的systemd?, 我之前拿官方的centos的image就是
fake-systemd
呢... 搜了下, 用fakesystemd
是为了cgroup
与宿主机的隔离. 手动卸载再安装其实也是可以的.
不对, 这个镜像里是直接配置的yum install -y ceph-mon
等...不是编译出来的...
1 | docker run -itd --name centos-test centos:centos7 |
[^12], 这个是opensuse
的.
1 | #src.rpm 12.2.13 |
通过传给cmake
的CMAKE_BUILD_TYPE
来确认编译出来的是否携带debuginfo.[^13]
Release —— 不可以打断点调试,程序开发完成后发行使用的版本,占的体积小。 它对代码做了优化,因此速度会非常快, 在编译器中使用命令: -O3 -DNDEBUG 可选择此版本。 Debug ——调试的版本,体积大。 在编译器中使用命令: -g 可选择此版本。 MinSizeRel—— 最小体积版本 在编译器中使用命令:-Os -DNDEBUG可选择此版本。 RelWithDebInfo—— 既优化又能调试。 在编译器中使用命令:-O2 -g -DNDEBUG可选择此版本。
默认是RelWithDebInfo
, 我给他传了俩参数-DWITH_LTTNG=ON -DDWITH_BABELTRACE=ON
结论: 看上去是suse
的tumbleweed
现在的源里没python2
的包了[^14]? 而这个仓库里的master
分支如果编ceph
还没去除python2
的版本, 就会有问题.
根据这个来看Re: [opensuse-factory] Removal of Python2 from openSUSE Tumbleweed
高版本的suse把python2彻底抛弃了, 但是是不是也有办法加回来安装呢? 搜了一圈, 主流的思路是这种大版本迭代, 如果始终要考虑老版本兼容性, 始终去兼容python2
, 对于开发的迭代并不利, 不如让开源软件去遵循这个规则, 统一进行大版本升级. 这种在商业产品里, 做法已经比较成熟了, 都是软件去适配系统. 只是linux
系统在python
以前可能没依赖性这么强的类似基础框架组件性质的部分, 所以对于彻底不兼容的软件会相对少一点. hhh, 说到这个, 这种换到公司里的代码开发, 就是为了兼容性, 当单元测试少时, 就不重构, 变成垃圾代码. 而开源的, 就看开发者的能力了. 好像见到的一般都有大神在把控代码质量的~
所以理论上其实ceph应该不完全依赖python2了. 我应该通过其他方式去判断是否要编python2相关.
根据不同版本的ceph
的install-deps.sh
和ceph.spec
, 可知mimic
和nautilus
的版本还没有完全可以免去python2.7
, 而Octopus
里彻底去除了python2
的编译.
Factory 和 Tumbleweed 合并了, 所以直接使用 factory 的源就好. 不过大多数镜像只提供了 repo-oss 和 repo-non-oss 这两个源
https://download.opensuse.org/repositories/openSUSE:Factory/standard/openSUSE:Factory.repo 搜出来显示这个,但是这个路径实际上已经不存在了...不知道怎么给他们提
Install package devel:languages:python:Factory / python Install package devel:languages:python / python-virtualenv
openSUSE:Build Service 仓库详解 - openSUSE Wiki
看上去这上面源里的是src.rpm. 咋Search能看到, 就是提示转不上呢? 我先配置上面这个, 给装上去了.
Index of /pub/opensuse/tumbleweed/repo/oss/x86_64/
但是下面这个源里明明也是oss, 但是却有呢?
1 | export NAME=ceph-dev-nautilus |
这个倒是安装成功, 开始触发编译了.
不过中间dashboard
的frontend
在安装pip时报了点Input/Output Error
, 暂时不是重点, 在setup-ceph.sh
里设置-DWITH_MGR_DASHBOARD_FRONTEND=OFF
, 就可以通过编译了. 就是本地编译有点慢...
1 | cd /ceph/src |
[ 97%] Building CXX object src/mon/CMakeFiles/mon.dir/AuthMonitor.cc.o c++: internal compiler error: Killed (program cc1plus) Please submit a full bug report, with preprocessed source if appropriate. See https://bugs.opensuse.org/ for instructions. make[3]: *** [src/mon/CMakeFiles/mon.dir/build.make:351: src/mon/CMakeFiles/mon.dir/MonmapMonitor.cc.o] Error 4
看着跟OOM
的表现有点像.
根据linux - make -j 8 g++: internal compiler error: Killed (program cc1plus) - Stack Overflow这篇说的, 可能是我开的线程太多, 给他分配的内存又没那么多引起的? 我试试
降低以后的确编过了.
load dlopen(/ceph/build/lib/erasure-code/libec_jerasure.so): /ceph/build/lib/erasure-code/libec_jerasure.so: cannot open shared object file: No such file or directory
1 | ╭─root@ceph-dev-nautilus /ceph |
暂时没找到怎么只是make , 生成到build/lib/erasure-code
目录下的功能.
根据Support #23433: Ceph cluster doesn't start - ERROR: error creating empty object store in /data/ceph/build/dev/osd0: (22) Invalid argument - bluestore - Ceph这里的回复, 看起来这个是因为毕竟是虚拟化的, 所以指向的/dev/目录下的内容并不是块设备引起的吧?
采用官方给的关闭的大部分配置文件的方案[^1], 可以避过大部分坑, 至少Luminous
版本做到了.
根据[^7], 还有有参考价值的.
似乎这里主要运行test
和image
, 那个dev
的镜像看起来是给调试镜像版本用的, 并不是直接提供运行vstart.sh
来调试编译版本的.
1 | -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DCMAKE_EXE_LINKER_FLAGS="-L/usr/local/opt/llvm/lib" -DENABLE_GIT_VERSION=OFF -DSNAPPY_ROOT_DIR=/usr/local/Cellar/snappy/1.1.7_1 -DWITH_BABELTRACE=OFF -DWITH_BLUESTORE=OFF -DWITH_CCACHE=OFF -DWITH_CEPHFS=OFF -DWITH_KRBD=OFF -DWITH_LIBCEPHFS=OFF -DWITH_LTTNG=OFF -DWITH_LZ4=OFF -DWITH_MANPAGE=ON -DWITH_MGR=OFF -DWITH_MGR_DASHBOARD_FRONTEND=OFF -DWITH_RADOSGW=OFF -DWITH_RDMA=OFF -DWITH_SPDK=OFF -DWITH_SYSTEMD=OFF -DWITH_TESTS=OFF -DWITH_XFS=OFF -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DCMAKE_EXE_LINKER_FLAGS="-L/usr/local/opt/llvm/lib" -DENABLE_GIT_VERSION=OFF -DSNAPPY_ROOT_DIR=/usr/local/Cellar/snappy/1.1.7_1 -DWITH_BABELTRACE=OFF -DWITH_BLUESTORE=OFF -DWITH_CCACHE=OFF -DWITH_CEPHFS=OFF -DWITH_KRBD=OFF -DWITH_LIBCEPHFS=OFF -DWITH_LTTNG=OFF -DWITH_LZ4=OFF -DWITH_MANPAGE=ON -DWITH_MGR=OFF -DWITH_MGR_DASHBOARD_FRONTEND=OFF -DWITH_RADOSGW=OFF -DWITH_RDMA=OFF -DWITH_SPDK=OFF -DWITH_SYSTEMD=OFF -DWITH_TESTS=OFF -DWITH_XFS=OFF -DCMAKE_EXPORT_COMPILE_COMMANDS=on -DPYTHON_LIBRARY=$(python-config --prefix)/lib/libpython2.7.dylib -DPYTHON_INCLUDE_DIR=$(python-config --prefix)/include/python2.7 -DPYTHON3_LIBRARY=$(python3-config --prefix)/lib/libpython3.9.dylib -DPYTHON3_INCLUDE_DIR=$(python3-config --prefix)/include/python3.9 -DOPENSSL_ROOT_DIR=/usr/local/Cellar/openssl@1.1/1.1.1o/ |
1 | brew install llvm |
由于我的主要目的是在CLion
中使用, 所以我在Perference->Build,Execution,Deployment->Cmake->Cmake Options
中添加的是上面的配置的单行形式.
查看find_package(NSS)
中调用的其实是pkg-config
来查找nss.pc
, 使用pkg-config --cflags --libs nss
报不存在, 所以说明pkg-config
的扫描路径中没有上面这个pc
文件.
通过brew list nss
查找到nss.pc
文件所在, 通过pkg-config --variable pc_path pkg-config
找到pc
的默认搜索路径, 有两种方案 1. 做个软链到pkg-config
的默认搜索路径中 1
ln -s /usr/local/Cellar/nss/3.35/lib/pkgconfig/nss.pc /usr/local/lib/pkgconfig/nss.pc
pkg_config
使用的搜索环境变量PKG_CONFIG_PATH
中导入 1
export PKG_CONFIG_PATH=/usr/local/Cellar/nss/3.48/lib/pkgconfig:/usr/local/Cellar/
我采用的第一种方案. 同理,还会出现nspr
等需要这样处理的库.
在CMake选项中增加指定下述的python库和头文件路径 1
-DPYTHON_LIBRARY=$(python-config --prefix)/lib/libpython2.7.dylib -DPYTHON_INCLUDE_DIR=$(python-config --prefix)/include/python2.7
暂时看到的资料都是说需要看测试代码和修改cmake文件的样子, 我并不需要编译, 只是需要建立索引, 所以我暂时注释了这部分的检查[^4,5,6]
采用同样的方案, 暂时未去推进.
报了一个暂时没去尝试解决的错误, 识别不了clang
?
1 | export PKG_CONFIG_PATH=/usr/local/Cellar/nss/3.35/lib/pkgconfig:/usr/local/Cellar/openssl@1.1/1.1.1o/lib/pkgconfig |
把这里unknown compiler这段给屏蔽掉, 试试. 1
2
3
4
5
6
7
8
9if(CMAKE_CXX_COMPILER_ID STREQUAL GNU)
set(toolset gcc)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(toolset clang)
else()
# 换成这个
set(toolset clang)
# message(SEND_ERROR "unknown compiler: ${CMAKE_CXX_COMPILER_ID}")
endif()
1 | cmake -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DCMAKE_EXE_LINKER_FLAGS="-L/usr/local/opt/llvm/lib" -DENABLE_GIT_VERSION=OFF -DSNAPPY_ROOT_DIR=/usr/local/Cellar/snappy/1.1.7_1 -DWITH_BABELTRACE=OFF -DWITH_BLUESTORE=OFF -DWITH_CCACHE=OFF -DWITH_CEPHFS=OFF -DWITH_KRBD=OFF -DWITH_LIBCEPHFS=OFF -DWITH_LTTNG=OFF -DWITH_LZ4=OFF -DWITH_MANPAGE=ON -DWITH_MGR=OFF -DWITH_MGR_DASHBOARD_FRONTEND=OFF -DWITH_RADOSGW=OFF -DWITH_RDMA=OFF -DWITH_SPDK=OFF -DWITH_SYSTEMD=OFF -DWITH_TESTS=OFF -DWITH_XFS=OFF -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DCMAKE_EXE_LINKER_FLAGS="-L/usr/local/opt/llvm/lib" -DENABLE_GIT_VERSION=OFF -DSNAPPY_ROOT_DIR=/usr/local/Cellar/snappy/1.1.7_1 -DWITH_BABELTRACE=OFF -DWITH_BLUESTORE=OFF -DWITH_CCACHE=OFF -DWITH_CEPHFS=OFF -DWITH_KRBD=OFF -DWITH_LIBCEPHFS=OFF -DWITH_LTTNG=OFF -DWITH_LZ4=OFF -DWITH_MANPAGE=ON -DWITH_MGR=OFF -DWITH_MGR_DASHBOARD_FRONTEND=OFF -DWITH_RADOSGW=OFF -DWITH_RDMA=OFF -DWITH_SPDK=OFF -DWITH_SYSTEMD=OFF -DWITH_TESTS=OFF -DWITH_XFS=OFF -DCMAKE_EXPORT_COMPILE_COMMANDS=on -DPYTHON_LIBRARY=$(python-config --prefix)/lib/libpython2.7.dylib -DPYTHON_INCLUDE_DIR=$(python-config --prefix)/include/python2.7 -DPYTHON3_LIBRARY=$(python3-config --prefix)/lib/libpython3.9.dylib -DPYTHON3_INCLUDE_DIR=$(python3-config --prefix)/include/python3.9 -DOPENSSL_ROOT_DIR=/usr/local/Cellar/openssl@1.1/1.1.1o/ .. |
用来索引好像还是用的.
1 | ./configure CFLAGS='-g3 -O0' CXXFLAGS='-g3 -O0'. |
git里面有一些submodule, 这部分我没能拖回来, 导致这些目录是空的, 静态编译没能启动.
问题并不是真正的CMakeLists.txt. CLion解析了cmake中引用的所有源文件,以启用大多数功能(导航,code-completion,重构).根据我的经验,索引大型项目可以花费几分钟(十分钟).
减轻这个问题的一种方法是标记项目的“第三方”目录:右键单击您的common目录和Mark directory as... > Libraries.如果需要,您甚至可以将目录排除在项目之外.
还请注意,CLion索引的结果被缓存:在初始索引之后,即使在重新启动项目时,只应修复经修改的文件(注意,修改CMakeLists中的构建选项可能触发完整的reindex)
索引代码理论上只需要选择这些代码文件就可以, 但是实际上像ceph如果我cmake工程没能成功编译,他也是并没有把函数的调用关系给分析出来的. 哎, 静态分析功能还是得依赖clang.
diff
和merge
是一回事吗?实际上是两回事. 如果可以通过已知的规则, 比如同时保留双方不一致内容, 自动merge, 那属于diff
可以做到的内容. 但是如果想要手动merge
, 比如展示开来, 然后一行行比较人工决定使用哪份, 这个就不属于diff
提供的功能了, 需要一个展示diff
结果然后保存回原始文件中的工具, 这个就属于编辑器的范畴了. 像beyond compare
这种更像是一个集成了差异比较功能的编辑器了.
diff
和patch
和RCS
版本控制关系?unified输出的diff就是一个patch, 然后通过patch命令打入. 基于上面的开发出了Source Code Control System
和Revision Control System
(RCS).RCS新增了lock
的功能, 防止文件被其他人checkout之后修改了.
1 | ci a.txt |
执行上面的命令就能把文件纳入版本控制, 会在当前目录生成对应文件的管理.
中心化版本控制系统
都是通过保存diff
原型的changeset
来保存作为日志.
另外增加了branch
, trunk
这些概念.
分布式版本控制系统
基本操作单位从上面的changeset
变成了blob
(压缩保存的完整文件). 所以在查询diff
时是当前做的比较,而不是直接输出结果, git log --patch
.
patch
用的基本都是这个模式的样子.1 | diff -e a.txt b.txt > c.ed |
sdiff
和diff3
之类的支持merge
的操作.
diff
自身也支持一定的if-then-else
的merge
操作
1 | The Way that can be told of is not the eternal Way; |
1 | The Nameless is the origin of Heaven and Earth; |
C
语言的1 | diff -DTWO a.txt b.txt |
等价于下面这段,这段等后面的自定义掌握之后比较好看懂.
1 | --old-group-format='#ifndef name |
输出得到的是以第二个文件为基础的ifndef
之类的分支.
1 | #ifndef TWO |
基本上这里的区分主要就是多行匹配和单行匹配的区别了. 和LFMT
同时运行的时候, 匹配了单行之后, 多行匹配也会触发一次. 具体暂时不知道运行时是先按多行还是单行扫, 总之看到的结果是两项修改均会触发.
主要会有以下几种结果. * --old-group-format=format * --new-group-format=format * --changed-group-format=format * 目前来看,如果配置了这条, 则--new-group-format
基本上会被替换成这个. * 只有当这条没设置的时候, 可以让new
生效. * --unchanged-group-format=format
1 | diff \ |
上面这段的%(n=1?:s)
一开始一点都不理解是啥意思, 突然才反应过来是line
和lines
的区别...还以为这个N
是和前面的%dn
有关的...
‘%<’ stands for the lines from the first file, including the trailing newline. Each line is formatted according to the old line format (see Line Formats).
‘%>’ stands for the lines from the second file, including the trailing newline. Each line is formatted according to the new line format.
‘%=’ stands for the lines common to both files, including the trailing newline. Each line is formatted according to the unchanged line format.
上面这几个, 分别代表旧文件, 新文件, 两个文件中相同的内容.
‘Fn’ where F is a printf conversion specification and n is one of the following letters, stands for n’s value formatted with F.
‘e’ The line number of the line just before the group in the old file.
‘f’ The line number of the first line in the group in the old file; equals e + 1.
‘l’ The line number of the last line in the group in the old file.
‘m’ The line number of the line just after the group in the old file; equals l + 1.
‘n’ The number of lines in the group in the old file; equals l - f + 1.
‘E, F, L, M, N’ Likewise, for lines in the new file.
‘(A=B?T:E)’ If A equals B then T else E. A and B are each either a decimal constant or a single letter interpreted as above. This format spec is equivalent to T if A’s value equals B’s; otherwise it is equivalent to E.
For example, ‘%(N=0?no:%dN) line%(N=1?:s)’ is equivalent to ‘no lines’ if N (the number of lines in the group in the new file) is 0, to ‘1 line’ if N is 1, and to ‘%dN lines’ otherwise.
单纯格式符和行号这部分, 基本按照printf
的使用方式就可以了.
1 | \begin{old} |
主要做差异有这几种结果. * --old-line-format=format * --new-line-format=format * --unchanged-line-format=format
1 | diff \ |
使用这个的时候,发生这样一个问题, 只能看到触发了--old-group-format
和--changed-group-format
, 没变化的那种倒是没发现
1 | 1,2d0 |
In a line format, ordinary characters represent themselves; conversion specifications start with ‘%’ and have one of the following forms.
‘%l’ stands for the contents of the line, not counting its trailing newline (if any). This format ignores whether the line is incomplete; See Incomplete Lines.
‘%L’ stands for the contents of the line, including its trailing newline (if any). If a line is incomplete, this format preserves its incompleteness.
‘%%’ stands for ‘%’.
‘%c'C'’ where C is a single character, stands for C. C may not be a backslash or an apostrophe. For example, ‘%c':'’ stands for a colon.
‘%c''’ where O is a string of 1, 2, or 3 octal digits, stands for the character with octal code O. For example, ‘%c'\0'’ stands for a null character.
‘Fn’ where F is a printf conversion specification, stands for the line number formatted with F. For example, ‘%.5dn’ prints the line number using the printf format "%.5d". See Line Group Formats, for more about printf conversion specifications.
Conflict
文件的时候1 | diff --old-line-format=%L --new-line-format=%L a.txt b.txt |
直接双向合并就行了.
主要有整理笔记时, 有些内容需要注意引用的需求, 在给[^2]提交PR后很可惜作者一直没有上线, 就去提交给了markdown all in one, 在大佬们的讨论中, 发现这样一个场景, markdown的语法中对于引用只要求>符号即可, 但是在各渲染及自动补充的实现中, 追加空格是个优雅的习惯. 然后在[^3]的review中, 大佬发现针对是否增加空格, 针对多级列表, github的markdown渲染的表现还不一致. 这就导致这套实现存在局限性了, 轻易引入存在较大bug风险了.
所以这个插件暂时主要用于下述需求的场景, 对于需要对多级列表等进行引用的场景, 还没有一个比较好的方案.
自己先单独封装了一个来使用.
cursor
在某个位置, 未选中任何内容>
即可>
>
即可.1 | let editor = window.activeTextEditor; |
上面的接口似乎满足了第一需求
这个通过获取到start所在line的Start就可以了.
进行简化
场景一和场景二的最终动作都是在该行增加>
场景一和场景二? 增加一个循环, 一开始获取到selection
的时候就可以设置为循环, 最后处理为循环内每行遍历. 如果是场景一和场景二那就是处理当行
都是根据遍历后的状态进行处理.
首先一开始,场景一可以提取为场景二, 获取到指针所在行的全部内容, 场景三, 由于我们不关心尾部内容,因此其实也就是场景二的状态,只需要处理start
所在指针的内容.(PS, 后来发现如果不关心尾部内容,也是存在问题的, 插入内容的时候直接导致尾部的文字会被替换一部分)
最近要做痔疮手术, 术后有一段时间估计是还不能坐着的. 台式机就没有办法用上了, 但是笔记本的性能还是有点差,内存也不太够.
主要有以下以及更多的访问方式
xrdp
的工具一开始我直接用的自带的finder
里的vnc
访问的, 效果还可以.不过因为笔记本只有13寸,访问3840*2160
的台式机, 分辨率用系统配置的display
最低也只能设计成拟合1920*1080
的, 相比我一般设置成2560*1440
拟合1280*720
的笔记本的字实在是小太多了.
据我所知, windows
的rdp
是可以做到让渲染在客户端设备执行的逻辑的, 这样就无所谓我目标设备分辨率到底是多少了.
然而,很可惜, 经过调查, mac
端主要也就上面这几种工具. apple remote desktop
主要也只是基于vnc
进行的封装,而vnc的渲染主要是在服务端进行的, 客户端只是直接显示的效果. 对于基于这种协议的远程访问工具只能直接修改服务端设备的分辨率来适应客户端, linux
端至少看到的一般的解决方式都是这样的. 原生设置能够控制的分辨率还是有限, 最后我用了SwitchResX
来进行的分辨率修改, 修改成拟合1440*900
在客户端上用的.
偶然看到好像有人提到没有接显示器的mac mini
, 有人试着远程连接直接修改分辨率. 看到针对没有显示器连接的时候, 分辨率也能够修改. 我就试验了下SwitchResx
, 发现居然的确显示了一个Virtual Desktop
,可以修改的分辨率基本上都是客户端的分辨率. 基本上达到我的目的了.
1 | #displayplacer |
1 | #Example: |
1 | fb-rotate -l |
最后总结的方案主要就是下述两种了. 1. 基于SwitchResX
直接修改服务端的分辨率 2. 拔掉服务端连接的显示器,这个时候用SwitchResX
基本上就可以设置成客户端的分辨率了.
目前我主要用的就是第二套方案了.
Nautilus在OSD和MGR中合并了通用的度量收集框架,以提供内置的监视,并且在该框架之上构建了新的RBD性能监视工具,以将各个RADOS对象度量转换为针对IOPS,吞吐量和性能的聚合RBD镜像度量。这些指标都是在Ceph集群本身内部生成和处理的,因此无需访问客户端节点即可获取指标。
1 | rbd perf image iostat |
根据这里v14.2.9 Nautilus — Ceph Documentation
New rbd perf image iotop and rbd perf image iostat commands provide an iotop- and iostat-like IO monitor for all RBD images.
The ceph-mgr Prometheus exporter now optionally includes an IO monitor for all RBD images.
应该是在14.2.0的发布里完成的,那这次大版本包含了什么?小版本更新会包含pr和issue链接
14版本还增加了ceph config全局配置修改的功能。
ceph14支持pg数减小和pg数根据pool容量自动调整 ceph telemetry命令用ceph mgr module enable telemetry启用,但是对telemetry不了解也跳过了。 这个应该就是我要看到的openTelemetry
Ceph v12.2.13 Luminous released - Ceph
这个应该是12版本最后一次补丁升级吧
这个里不包含大版本开发的代码
就这个提交来看,大部分代码都在处理上下文的接口适配,增加选项,假如说这套代码里只是使用已经增加的接口,那么代码里就应该有调用的API
这个问题怎么才能验证?
src/toos/rbd/action/Perf.cc
这个新增文件,依赖的是rbd perf image stats。
然后rbd perf image stats
也是这次commit里增加的,是继承MgrModule使用API实现的,看看这里面用的哪些api.
应该就是这里拿的了
register_osd_perf_queries
好像有点像了。
这里使用了mgr_module.py提供的add_osd_perf_query
从这里拿到了osd层面的ops,write_ops, read_ops, bytes, write_bytes, latency这些内容
拿到osd的结果,怎么计算出rbd层的?
他这里只查appplication_metadata里有rbd这个的资源池,然后拿到资源池的osd_map
user_query变量里,QUERY_POOL_ID_MAP拿到了
在PerfHandler里进行的周期查询perf数据。
在这里周期获取数据时又调用了mgr-module的get_osd_perf_counters。emm,这个函数在12版本我们的代码里还没有,不过单论这部分呼出,其实我们可以。这个函数在14版本里,prometheus也调用了这个,那就有意思了,prometheus刚好在这个版本支持了rbd performance monitor, ceph-export输出了这部分数据。那是不是就是从这里拿到的呢?(osd_perf_query_support
在这里ceph-mgr里面的_ceph_get_osd_perf_counters的内容。这个函数就是Cython的封装了。
get_osd_perf_counters这个封装,好像还是在ceph-mgr里提供的,果然。
在ActivePyModules.cc里又封装了一层,这里应该是从DaemonServer.cc里封装的实际逻辑了。
果然,在ActivePyModules里实例化了DaemonServer这个对象,
这里调用osd_perf_metric_collector的get_counters。OSDPerfMetricCollector这个类,应该是12版本之后给mgr好好梳理时更新出来的。
在OSDPerfMetricSubKeyDescriptor这个struct里,封装了支持的类型
卧槽,好像这里全覆盖了,在下面这里做的扩充。从Client,牛皮
osd: support more dynamic perf query subkey types by trociny · Pull Request #25371 · ceph/ceph
那OSDPerfMetricCollector
呢,是在这个PR里增加的?
mgr: create shell OSD performance query class by trociny · Pull Request #24117 · ceph/ceph
在下面这里提到了,开启这个收集功能之后,这套功能会遇到rbd images数据过于庞大的问题,
osd: collect client perf stats when query is enabled by trociny · Pull Request #24265 · ceph/ceph
再在这个类下面继续探究就有点难的感觉了。下面就越来越细,估计需要实机环境验证了。
Prior to Red Hat Storage 4, Ceph storage administrators have not had access to built-in RBD performance monitoring and metrics gathering tools.
Ceph Storage 4 now incorporates a generic metrics gathering framework within the OSDs and MGRs to provide built-in monitoring, and new RBD performance monitoring tools are built on top of this framework to translate individual RADOS object metrics into aggregated RBD image metrics for Input/Output Operations per Second (IOPS), throughput, and latency.
好像在这篇文章里要解释到底是怎么计算的了。哦,没解释
eph ceph mgr module enable rbd_support
Noisy Neighbors and QoS看到好几次,。
到底这部分是怎么计算出来的呢?ß
Ceph Block Performance Monitoring
the OSD-based statistical stats are the end solution since that can never provide the latencies that the client is actually experiencing.
这篇里提到这个链接,有点意思?
Live Performance Probes - Ceph - Ceph
mgr, rbd: report rbd images perf stats to mgr by Yan-waller · Pull Request #16071 · ceph/ceph
(看这篇)
目前的怀疑点是在Nautilus的版本里在osd那层新增了不少埋点数据收集
这里提到了SLA(服务等级协议service level agreement) (提问题的人说到了想要这种客户端级别的IOPSjiankong )
As we know, one perfcounter metric was created in ImageCtx when we opened a rbd image , but these metrics data is scattered and reside in kinds of clients, furthermore, a rbd image could be opened simultaneously by more than one client. report these information (especially ops, bytes, latency ) to MGR may be useful.
Ultimately, I still expect that operational indicators that we send up to mgr will mostly get reported onwards to something else (nagios, zabbix, snmp, etc), so for O(clients) monitoring jobs we should consider skipping the middleman.
这个@jcsp大佬推荐是在mgr的插件层完成这个监控的操作,然后实际上好像也的确完成了。support-rbd啊,osd-perf-count啊,都是基于ceph-mgr的插件。
monitoring that gives them per-client throughput, latency and op type breakdown, I'm not sure how strong the push would be to implement the separate monitoring path to go get the client's view of the same set of stats.
在module.py里通过OSD_PERF_QUERY_COUNTERS_INDICES这个来把一个counter按照我们要的key导出数据。
extract_pool_key
是用来提取像pool1/image0
这样的spec字符串里的pool name的,
user_queries
到底是放了什么内容?
OSDPerfMetricCollectorListener
这个应该是才是真实被Ceph-mgr实例化的内容。、
1 | class OSDPerfMetricCollectorListener : |
在DaemonServer
初始化的时候
1 | osd_perf_metric_collector_listener(this), |
根据这里来看,果然是在Nautuil版本里,给ceph-mgr里增加了很多功能。 1
2
3
4
5
6
7
8
9 typedef std::map<OSDPerfMetricQueryID,
std::map<OSDPerfMetricKey, PerformanceCounters>> Counters;
int OSDPerfMetricCollector::get_counters(
OSDPerfMetricQueryID query_id,
std::map<OSDPerfMetricKey, PerformanceCounters> *c) {
int DaemonServer::get_osd_perf_counters(get_counter
.这个接口被封装进pybind里了。
这里面的数据都是从哪里手机的呢?这个collector只是作为一个接口层提供收集的函数,手机的内容是通过指针穿进去的。还是存在了DaemonServer这个类里。
这里面到底调用了什么接口来收集osd? OSDPerfMetricQueryID OSDPerfMetricCollector::add_query(
在这个函数里添加的到底是任务,还是收集的值呢?应该是任务吧?
1 | typedef std::map<OSDPerfMetricQuery, |
这个和queries有什么关系呢? 1
2
3
4
5
6
7
8
9
10 typedef std::optional<OSDPerfMetricLimit> OptionalLimit;
typedef std::map<OSDPerfMetricQuery,
std::map<OSDPerfMetricQueryID, OptionalLimit>> Queries;
typedef std::map<OSDPerfMetricQueryID,
std::map<OSDPerfMetricKey, PerformanceCounters>> Counters;
struct OSDPerfMetricLimit {
PerformanceCounterDescriptor order_by;
uint64_t max_count = 0;
在bool DaemonServer::handle_report(MMgrReport *m)
这个里 osd_perf_metric_collector.process_reports(m->osd_perf_metric_reports);
1
2
3
4
5
6
7
8
9class OSDPerfMetricCollectorListener :
public OSDPerfMetricCollector::Listener {
public:
OSDPerfMetricCollectorListener(DaemonServer *server)
: server(server) {
}
void handle_query_updated() override {
server->handle_osd_perf_metric_query_updated();
}
1 | void DaemonServer::handle_osd_perf_metric_query_updated() |
可能数据是从MgrClient里来的?嗯,忘了之前看过, 现在也才意识到mgr其实分成了Server和client,
osd和pg都看到了,但是其他的呢?如果只有这两个数据,那其他的rados那些是怎么算出来的?
set_perf_queries_cb(m->osd_perf_metric_queries);
这篇里主要讲的mgr的代码
以osd daemon为例,在启动过程中,会发送消息MMgrOpen,mgr收到后,会回复MMgrConfigure消息,主要是返回一个period时间,后续osd就根据设定的period, 定期将状态信息上报给mgr,即消息MMgrReport和MPGStats:
本以为这段代码里不涉及image的信息了
但是下面这段查询到的数据里显然带上了rbd的image信息 1
2
3
4
5
6res = self.module.get_osd_perf_counters(query_id)
for counter in res['counters']:
raw_image[0] = None
if not raw_image[0]:
raw_image[0] = [now_ts, [int(x[0]) for x in counter['c']]]
mgr: templatize metrics collection interface by vshankar · Pull Request #29214 · ceph/ceph
这里做了一层封装模板化
blame
Daemon里的这个函数get_osd_perf_counters是下面这条里加的
mgr: make dynamic osd perf counters accessible from modules · vshankar/ceph@b8362d9
get_counters这个实际工作的方法呢?
mgr: store osd perf counters received in osd reports · vshankar/ceph@438a3f7
又没头绪了
还是回到prometheus这里使用上看看阿布
mgr/prometheus: provide RBD stats via osd dynamic perf counters by trociny · Pull Request #25358 · ceph/ceph 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31# Per RBD image stats is collected by registering a dynamic osd perf
# stats query that tells OSDs to group stats for requests associated
# with RBD objects by pool, namespace, and image id, which are
# extracted from the request object names or other attributes.
# The RBD object names have the following prefixes:
# - rbd_data.{image_id}. (data stored in the same pool as metadata)
# - rbd_data.{pool_id}.{image_id}. (data stored in a dedicated data pool)
# - journal_data.{pool_id}.{image_id}. (journal if journaling is enabled)
# The pool_id in the object name is the id of the pool with the image
# metdata, and should be used in the image spec. If there is no pool_id
# in the object name, the image pool is the pool where the object is
# located.
if 'query_id' not in self.rbd_stats:
query = {
'key_descriptor': [
{'type': 'pool_id', 'regex': pool_id_regex},
{'type': 'namespace', 'regex': namespace_regex},
{'type': 'object_name',
'regex': '^(?:rbd|journal)_data\.(?:([0-9]+)\.)?([^.]+)\.'},
],
'performance_counter_descriptors': list(counters_info),
}
query_id = self.add_osd_perf_query(query)
if query_id is None:
self.log.error('failed to add query %s' % query)
return
self.rbd_stats['query'] = query
self.rbd_stats['query_id'] = query_id
res = self.get_osd_perf_counters(self.rbd_stats['query_id'])
所以其实还是这个借口的功能,类似mon_command,提供了根据传入的数据进行收集的操作。
这里也的确用上赋值逻辑
queryID到底是怎么期作用的?
来自add_osd_perf_query
OSDPerfMetricQueryID DaemonServer::add_osd_perf_query(
1 | query = { |
使用上
mgr: create shell OSD performance query class · vshankar/ceph@a6c3390
这个add_osd_perf_query
是在这里加的
add_query也是他家的。 1
2
3
4
5
6
7
8
9
10
11static PyObject*
ceph_add_osd_perf_query(BaseMgrModule *self, PyObject *args)
{
static const std::string NAME_KEY_DESCRIPTOR = "key_descriptor";
static const std::string NAME_COUNTERS_DESCRIPTORS =
"performance_counter_descriptors";
static const std::string NAME_LIMIT = "limit";
static const std::string NAME_SUB_KEY_TYPE = "type";
static const std::string NAME_SUB_KEY_REGEX = "regex";
static const std::string NAME_LIMIT_ORDER_BY = "order_by";
static const std::string NAME_LIMIT_MAX_COUNT = "max_count";1
auto query_id = self->py_modules->add_osd_perf_query(query, limit);
在_send_configure的时候,把limit里的任务传给了MgrClient 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26struct OSDPerfMetricLimit {
PerformanceCounterDescriptor order_by;
uint64_t max_count = 0;
OSDPerfMetricLimit() {
}
OSDPerfMetricLimit(const PerformanceCounterDescriptor &order_by,
uint64_t max_count)
: order_by(order_by), max_count(max_count) {
}
bool operator<(const OSDPerfMetricLimit &other) const {
if (order_by != other.order_by) {
return order_by < other.order_by;
}
return max_count < other.max_count;
}
DENC(OSDPerfMetricLimit, v, p) {
DENC_START(1, 1, p);
denc(v.order_by, p);
denc(v.max_count, p);
DENC_FINISH(p);
}
};OptionalLimit
吧,这个有啥用?
里面有个order_by
里申明是哪种type,是pg还是rados了好像
encode之后发送到mgrclient
看看怎么解码
std::function<void(const std::map<OSDPerfMetricQuery, OSDPerfMetricLimits> &)> set_perf_queries_cb;
在这个MgrClient.cc
里居然有2个set_perf_queries_cb
一个是封装的MgrClient对外暴露的函数,就是用来赋值的?
一个是private的值,与mgr的Daemon沟通时进行调用。
但是这里好像只看到了osd.cc触发啊
这个被osd.cc里调用了
1 | void OSD::set_perf_queries( |
怎么看上去osd.cc里不止负责普通的osd信息收集
在12版本里,这个OSD.cc里也有mgrc
在14版本里, std::list1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24private:
void set_perf_queries(
const std::map<OSDPerfMetricQuery, OSDPerfMetricLimits> &queries);
void get_perf_reports(
std::map<OSDPerfMetricQuery, OSDPerfMetricReport> *reports);
Mutex m_perf_queries_lock = {"OSD::m_perf_queries_lock"};
std::list<OSDPerfMetricQuery> m_perf_queries;
std::map<OSDPerfMetricQuery, OSDPerfMetricLimits> m_perf_limits;
void get_perf_reports(
std::map<OSDPerfMetricQuery, OSDPerfMetricReport> *reports);
mgrc.set_pgstats_cb([this](){ return collect_pg_stats(); });
mgrc.set_perf_metric_query_cb(
[this](const std::map<OSDPerfMetricQuery, OSDPerfMetricLimits> &queries) {
set_perf_queries(queries);
},
[this](std::map<OSDPerfMetricQuery, OSDPerfMetricReport> *reports) {
get_perf_reports(reports);
});
mgrc.init();
osd把收集pg信息的回调函数设置到mgrc里,接下来应该是由mgrc来调用了把
在void MgrClient::_send_report()
这里会触发调用.
理论上应该是
1 |
|
void add_to_reports(
下面这个数据在primary_log的里 1
2
3
4
5
6
7
8class PrimaryLogPG : public PG, public PGBackend::Listener {
DynamicPerfStats m_dynamic_perf_stats;
void PrimaryLogPG::get_dynamic_perf_stats(DynamicPerfStats *stats)
std::swap(m_dynamic_perf_stats, *stats);1
2
3void PrimaryLogPG::log_op_stats(const OpRequest& op,
m_dynamic_perf_stats.add(osd, info, op, inb, outb, latency);log_op_stats
函数里,没有记录perf信息的. 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 auto m = static_cast<const MOSDOp*>(op.get_req());
std::string match_string;
switch(d.type) {
case OSDPerfMetricSubKeyType::CLIENT_ID:
match_string = stringify(m->get_reqid().name);
break;
case OSDPerfMetricSubKeyType::CLIENT_ADDRESS:
match_string = stringify(m->get_connection()->get_peer_addr());
break;
case OSDPerfMetricSubKeyType::POOL_ID:
match_string = stringify(m->get_spg().pool());
break;
case OSDPerfMetricSubKeyType::NAMESPACE:
match_string = m->get_hobj().nspace;
break;
case OSDPerfMetricSubKeyType::OSD_ID:
match_string = stringify(osd->get_nodeid());
break;
case OSDPerfMetricSubKeyType::PG_ID:
match_string = stringify(pg_info.pgid);
break;
case OSDPerfMetricSubKeyType::OBJECT_NAME:
match_string = m->get_oid().name;
break;
case OSDPerfMetricSubKeyType::SNAP_ID:
match_string = stringify(m->get_snapid());
break;
default:
ceph_abort_msg("unknown counter type");
}m
是怎么拿到所谓的snapobject这些信息的呢? 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 auto m = static_cast<const MOSDOp*>(op.get_req());
Requests are MOSDOp messages. Replies are MOSDOpReply messages.
An object request is targeted at an hobject_t, which includes a pool,
hash value, object name, placement key (usually empty), and snapid.
The hash value is a 32-bit hash value, normally generated by hashing
the object name. The hobject_t can be arbitrarily constructed,
though, with any hash value and name. Note that in the MOSDOp these
components are spread across several fields and not logically
assembled in an actual hobject_t member (mainly historical reasons).
A request can also target a PG. In this case, the *ps* value matches
a specific PG, the object name is empty, and (hopefully) the ops in
the request are PG ops.
Either way, the request ultimately targets a PG, either by using the
explicit pgid or by folding the hash value onto the current number of
pgs in the pool. The client sends the request to the primary for the
associated PG.
Each request is assigned a unique tid.1
2
3
4
5class MOSDOp : public MessageInstance<MOSDOp, MOSDFastDispatchOp> {
std::map<OSDPerfMetricQuery,
std::map<OSDPerfMetricKey, PerformanceCounters>> data;
找到了,是在这个pr里增加的数据收集, 又是trociny
这个大佬提交的.
ceph tell mgr osd perf query add simple the mgr starts to receive perf report, writing to the log:
简单点说原理是在osd的最下层通过request的MOSDop这个类,查到这次request各层的名字,然后在对应的计数器数加一 1
2
3void PrimaryLogPG::log_op_stats(const OpRequest& op,
const uint64_t inb,
const uint64_t outb)
这个DynamicPerfStats.h的文件是在osd目录里.
dps.merge(pg_dps);
]]>TODO: rados对象相比一个文件, 其实差别就在于rados对象自带了offset位置的概念. 并不存在太多其他的元数据信息.
所以基本可以忽略rados对象的元数据信息? onode等可能不行?
rados层的聚合, 即在tier上的时候, 表现就是存在这个rados对象, 但是这个rados对象只被识别出几K实际写入的对象大小. 而如果是聚合拼接时, 则意味着, 上层其实访问的也是这个大对象. 但是只访问这个对象中的一部分的时候, 怎么办呢?
tier层访问的rados对象还是小的. 但是每次落下去会合并成大的. 那问题就来了. 读上来的时候读上来一批小对象, 第二次合并的时候的小对象必然不会是第一次那种组合了. 会是一个新的组合. 这个时候要落hdd, 如果直接合并, 那就是一批新的. 除非有一个journal的实现, 每次请求都是一种journal.
tier的删和读, 写, 都以4M为粒度最好?
这些索引数据, 预计是需要放在ssd上的.
除非是对象存储那种, 只写一次, 所以他的小对象聚合, 不用考虑修改的问题. 只有写和读.
都是写少读多. 那理论上写完, 读的时候涉及部分小对象的逻辑? 难道通过预读来减少大合并文件中只有几个小文件才是用户要读的文件的问题?
所以针对这种通用场景, 只有一种办法, 那就是日志层级. 将随机写转换为顺序写, 然后通过compaction等方式把日志转化成实际的对象?
适合写多读少场景.
通过拆分整块写, 可以避免读那个被写了部分的offset的问题.
改数据的时候没问题, 通过gc来处理, 每次都写新的大对象.
读的时候, 就通过预读, 调整预读的大小等来完成覆盖. 如果预读的效果不好, 那其实还不如直接就读这个4K对象, 如果能够分析出行为, 则可以试试预读512K, 1M等方式.
如果是以短时间的访问粒度, 拆解全随机和存在热数据比例?
即存取周期
无热数据, db,wal分离的hdd性能好.
有热数据, 开命中集tier好, 则tier能作为热数据访问的介质, 其他数据依旧访问hdd
待定
主要拆解为 * 目标小io IOPS高, 延时低. 尽量让任意组合的ssd和hdd, 均能用出ssd的能力?
ssd的容量大小, 其实取决于热数据到底有多少? 刚好卡在热数据范围最好.
如果完全没有热数据情况下, 即便都落ssd, 也价值不大?
缓存击穿时, 我感觉还是一种关键, 但是主要是需要硬盘不成为瓶颈的情况下, 这样按理论上就不会受到flush和evict影响, 剩下的就是writeback和writethrough的差异了
TODO: 单纯命令集的效果的话, 应该就是ssd承接部分命中的请求, 未命中的请求全落hdd.
这样ssd的使用率如果命中的请求打不满, 那就浪费很大. 所以可能需要配合小对象都落ssd的方案, 这样就可以提高ssd的利用率? 这个方案应该是主流方案吧?
所有rbd, 超出tier大小性能差, 随着越大, 性能越差, 直到下限
tier不满的情况下, 对象不存在的第一次写时, 缓存层应该能全部缓住. 但是启动hit_set时, 存在与刚才那句预期不符. promote应该只产生一次请求就可以了.
缓存击穿定义是?
G5那种, 每天预计访问1-2T数据, 而缓存池20T以上, 总数据100T+, 是否能保障使用?
写满
常态
击穿, 最差性能
tier的大小, 是应该跟着业务, 让他起到部分命中的效果.
根据上面的evict, 应该就需要针对业务, 提高命中率的方案, 在对象粒度不变的情况下, 可以提高性能.
开了命中集之后的方案来进行.
新写对象的性能
这批对象全部写满了, 小于tier大小, 然后全部不在tier上.
rbd_index分离, rbd_header一整个rbd也才读一次, 应该不至于有什么影响.
常态逻辑下需要支持的场景
加个指标, 命中的范围. 只是举例
0-1ms, 10%
1-10ms, 20%
10ms-20ms, 10%
小对象, 提高对象粒度
1 | # tier_flush\tier_promote等数据 |
下面的flush
和evict
动作主要根据热度命中集和当前空间占用的ratio
有关. * flush * evict * 主要zero, 因此主要是在ssd上的写. * 置零, 从op的角度应该也有什么东西能够观察到吧. * promote(感觉只和这个有关系了)
根据flush_mode
和evict_mode
状态判断触发下面的may_read
,can_proxy_write
等操作的成功与否.
下面的每一个操作好像都是may
或者can
的, 看上去像是有开关之类的.
tier_proxy_read这个数据是哪里统计增加的?是在finish_proxy_read
里增加的,那么同理
osd: tiering: add proxy read support · ceph/ceph@70d3d08
这次提交好像是个核心开发者的提交,当时不是走的github的pr?
看了下代码,这里好像没有计数器,inc
虽然是在ceph daemon osd.x perf dump里拿到的数据,但实际上还是pg层的,只是这个数据pg那边的接口暂时没找到能直接打印出来的方式.不对,这里虽然是PG类里的,但是数据记在了这个pg所属的osd上.
osd: add proxy write perf counter · ceph/ceph@b9ec7e6
tracker单里咋也啥都没有呢?那他们怎么决定的呢?
在这个提交里增加的计数器.这个是直接提交在master分支里的了...哎
int PrimaryLogPG::do_osd_ops(OpContext *ctx, vector
write这边是尝试写,或者跟tier有关的都会进行计数
而read这边则是读一些元数据信息会触发读计数
这样的话,write和read数据就没有那么准确了.
pg的counter好像不够实时, 有一个类似pg map
的同步周期的样子,待核对.
rmw_flags flags这个是判断到底要不要promote,和may_proxy_write的结果判断的
bool can_proxy_write = get_osdmap()->get_up_osd_features() & CEPH_FEATURE_OSD_PROXY_WRITE_FEATURES;
uint64_t OSDMap::get_up_osd_features() const
{ return cached_up_osd_features; } void OSDMap::_calc_up_osd_features() 在这个函数里做的赋值,
osd/OSDMap: cache get_up_osd_features · Mirantis/ceph@e0e765f
在这次提交里做的改名,改成cached_up_osd_features
,之前叫uint64_t OSDMap::get_up_osd_features() const
, 每次都是重新查.
osd/OSDMap: get_up_osd_features() · Mirantis/ceph@1d8429d
age Weil authored and Yan, Zheng committed on Dec 29, 2013
struct osd_xinfo_t {
变量在osdMap这个class里 mempool::osdmap::vector
dump时用的iops_wr和iops_rd
int64_t iops_wr = pos_delta
是从pg_sum_delta来的
1 | mempool::pgmap::list< pair<pool_stat_t, utime_t> > pg_sum_deltas; |
所以pool_stat数据的更新,基本上就是和这个pg_sum数据的更新一致的.
这里的stamp
周期到底是多长呢?
PGMap::apply_incremental
像是在这merge的数据?
在PGMonitor::tick里会更新这个delta的数据.
因为太多人不推荐这个方案了, 所以看看有没有推荐的.
所以理论上控制好, 还是有用的?
uint64_t evict_target = pool.info.cache_target_full_ratio_micro; uint64_t evict_slop = (float) evict_target * cct->_conf->osd_agent_slop;
这次新增了flush_high
> commit 8f6056aebbabcbe236d332f546d075e06a14c0ca > Author: MingXin Liu mingxin.liu@kylin-cloud.com > Date: Thu May 28 14:33:10 2015 +0800 > > Osd: revise agent_choose_mode() to track the flush mode >
> Signed-off-by: Mingxin Liu mingxinliu@ubuntukylin.com > Reviewed-by: Li Wang liwang@ubuntukylin.com > Suggested-by: Nick Fisk nick@fisk.me.uk
commit c9daf8e5ea401f5bc2aafd4025991fb4903ffcd4 Author: Sage Weil sage@inktank.com Date: Mon Jan 27 17:57:53 2014 -0800
osd/ReplicatedPG: add slop to agent mode selectionWe want to avoid a situation where the agent clicks on and off when thesystem hovers around a utilization threshold. Particularly for trim,the system can expend a lot of energy doing a minimal amount of work whenthe effort level is low. To avoid this, enable when we are some amountabove the threshold, and do not turn off until we are the same amount belowthe target.Signed-off-by: Sage Weil <sage@inktank.com>
该值需要根据缓存池的容量大小以及副本个数来设置,以三副本为例,target_max_bytes 不应该超过容量的 1/3,如果实际的负载使得存储池中的数据大小达到了容量的 1/3,后续的 IO 将被阻塞,所以需要设置别的参数来避免池中的数据到达该阈值。
为什么是1/3?
该类参数的设计目的: 作为刷回淘汰操作的触发条件,避免 OSD 被数据撑满。 为什么不直接使用存储池的容量作为该参数,是为了考虑另外一种场景,存在多个缓存池,使用相同的磁盘。
Agent will be always in idle state if target_max_bytes or target_max_objects not set on the pool irrespective of other tiering params set in the pool. dirty_micro and full_micro will not be calculated if those two params are zero which is by default I guess.
Now, flush will be activated if dirty_micro is > flush_target. My understanding is, once it is activated it will iterate through all the dirty objects and flush all the dirty objects which is > cache_min_flush_age. Am I right ?
triggered after crossing the dirty_threshold, right ? If dirty_threshold is not breached, the flush age param is never checked.The cache_min_flush_age will only be applicable if the flush is
missing anything ?I saw the cache_min_evict_age is not been used anywhere, am I
It's possible. The min params are a bit dangerous because they can potentially confuse the cache agent (e.g., what if all objects are under the min? How/when do we decide to ignore the min, or, how/when do we give up trying to find an older object?).
1 | uint64_t over = full_micro - evict_target; |
1 | full_micro = |
根据目前的理解, 当full_ratio达到1000000时, 根据上文, 也就是只有当达到target_max_bytes
的时候才会彻底阻塞, 在这之前都是不会停止写io的.
TODO: 目前看到的是, flush和evict的速度, 基本取决于osd的业务压力, 会让存储空间瓶颈停留在80%.
evict_effort
与实际速度的计算根据默认配置可知, osd_agent_min_evict_effort
默认值为0.1
,即默认的evict_effort
为0.1
.
osd_agent_quantize_effort
默认值同样为0.1
1 | uint64_t inc = cct->_conf->osd_agent_quantize_effort * 1000000; |
看起来这段只是在凑0.1-1
以0.1
为单元.
if (full_micro > evict_target), the mode is set as TierAgentState::EVICT_MODE_SOME. In this scenario evict_effort is calculated and based on hit_set and temp calculation some clean objects are evicted. My question is , can we quantify this value ? For ex, if the target_full_ratio = 50%, once the eviction is triggered, what %objects will be evicted ?
The effort is calculated based on how far between target_full and 100% we are. This is mapped onto the estimation of atime. We generate a histogram of age for the objects we have examined, so after the agent has run a bit we'll have a reasonable idea how the current object's age compares to others and can decide whether this is older or newer than the others; based on that we decide what to do.
see maybe_handle_cache(). That's not strictly agent behavior per se. Also, there is now a readforward mode that doesn't promote on read ever, based on our discussion about the performance of flash on read.
1 |
|
pow2_hist_t
: histogram of ages we've encountered.
应该是位运算. cbits, 64字节, 值为1, bin得到的就是1, 值为2, 则得到的是2, 因为要去除前导的0, 值为4, 得到3, 8得到4.
hist_t里存的啥? 好像low和up得到的就是bin前面的, 和加上bin之后的差值.
agent_estimate_temp
这里添加的热度?
迭代历史的热度, 增加热度统计 int last_n = pool.info.hit_set_search_last_n; grade_table
看以下代码, 历史的所有命中集的热度是统一对待的.
1 | void calc_grade_table() { |
如果历史几次命中集都有命中, 则热度偏高一点.
穿进去的热度v应该是1000000以上或者0. 这里应该是牺牲准确度, 但是减少空间消耗. 1000000应该会被归在20位, 即bin[20] = 1
然后算upper和lower的时候, 如果没有其他的命中过, 则是lower 0 , upper 1 total = 1 即lowwer 0, upper 1000000
如果前面有个热度不高的, 不考虑具体热度多少, 只考虑当前这个热度, 在那个阶段的比例?比如, 命中了比较多的, 那1000000有20次, 2000000有4次, 此次这个对象的热度是1500000 则lower 20 , upper 4 lower 20/44*1000000, upper 24/44*1000000
, 最后得到一个450000 和 550000这种比例.
TODO: 这里的hist是以什么为单位的? 热度统一控制的目的?
好像又回到evict_effort
了, 假设当前evict_effort是500000, 则这种因为upper较高, , 按照说的是统一正交化了的.
只有当前对象所处热度的那个位时较高时, 才不会被踢掉.
agent_state这个对象的粒度?
PrimaryLogPG
为单位的, 那就是pg为粒度. 这个pg每处理一个对象就Add一下.
每agent_work一次就加一次hist_age
, 默认参数1000, 也就是1000次读写, 就降级一次hist?... 所以这里关心的是pg内全局的对象热度, 是命中的比较多, 还是不命中的比较多.命中的比较多时, 为啥就会被evict?
osd_agent_min_evict_effort
这个默认是0.1, 即如果是0.81的数据量, 0.8为水线, 则取较大的, 即0.1, 而不是0.01/0.2=0.05的比例作为effort. 这里应该是为了取要踢多少对象.
超过full_ratio的情况, 需要均一化. 但是下面这段并没有完全做均一化, 默认只处理掉0.1的话, 完全存在超过1.1的比例的情况.
这里其实是把比例按100000为粒度合并, 避免太多差异值. 所以full_ratio那里有计算, 如果直接进入FULL状态, 就不判断热度了.
另外这里的这个按位运算方式, 肯定有个什么术语...热度梯度统计?
1 | // quantize effort to avoid too much reordering in the agent_queue. |
比如2000000对应21位, 如果这个命中热度的对象, 出现频次较高时,
TierAgentState
,
hitset的淘汰逻辑?
pg map
的同步周期?1 |
|
分位数近似计算 分位数近似算法有很多种,比如 HdrHistogram 算法、q-digest 算法、GK 算法、CKMS 算法、T-Digest 算法等,其中 HdrHistogram 算法和 T-Digest 算法在软件系统中使用的比较多,T-Digest 算法用于 ElasticSearch、Kylin 等系统中,HdrHistogram 的简化版用于 Prometheus 中。下面我们简单介绍一下这两种常用算法: 静态分桶 思想:将整个存储区域以规律性的区间划分为多个桶,整个规律性的区间可以是线性增长,也可以是指数增长。每个桶只记录落在该区间的采样数量,计算分位数时,会假设每个区间也是线性分布,从而计算出具体的百分位点的数值。这样通过牺牲小部分精度,达到减小空间占用,并且统计结果大致准确的结果。 典型的实现是:https://github.com/HdrHistogram/HdrHistogram。所以后续也称之为 Histogram 算法。 缺点:统计范围有限,需要预先确定,不能改变
两种数据增长算法:Linear 和 Log2,Linear 是线性增长,适合对百分位数精度要求比较高,而且数据范围比较小的场景。Log2 是指数增长,适合对百分位数精度要求相对低,而且总的数据范围跨度较大的场景。当然精度大小还依赖于坐标增长单元
Ceph RBD 性能及 IO 模型统计追踪功能设计与实现 原文链接: https://www.infoq.cn/article/2ojeh5jgo4s1xkxiztcg
[ceph-users] SSDs for journals vs SSDs for a cache tier, which is better?
\[\frac{num\_write_{cache}}{num\_write_{cache} + num\_write_{base}}\]
\[\frac{num\_read_{cache}}{num\_read_{cache} + num\_read_{base}}\]
实际还是pg的数据, 只是没有在pg那里直接对外暴露
1 | { |
1 | { |
根据上面这张图以及<ceph源码分析>
可知, 主要的最后执行任务场景如下
可知, promote
和proxy
并不是强相关的,只有实际的op
的read
和write
存在强相关性.
只是由于read类型和write类型,可知元数据的读写也被统计了. 不过在总体上来说, 每个对象相关的元数据是存在正比关系(理论上应该是), 所以这个比值应该也是可靠的.
num_read
计数OP类型1 | case CEPH_OSD_OP_CMPEXT: |
1 | case CEPH_OSD_OP_UNDIRTY: |
针对当cache
资源池满的时候,write
的请求会进入block_write_on_full_cache
, 等到不满以后,再调用requeue_ops
重新加入,因此对于num_write
和num_read
是体现不出这里的block
的过程的. 可能需要增加一个latency
的数据展示.
该统计方式, 需要分场景独立统计后使用
cache_target_dirty_ratio
下cache_target_dirty_high_ratio
下cache_target_full_ratio
下cache_target_full_ratio
时,主要只有延时需要关注了应该想起来之前用inoreader
订阅过一些频道, 现在把最近一段时间收藏的博客再给补充进去, 顺便把自己博客的atom
源也给打开.
PS. 扫了一遍,好多用自定义域名的博客都已经不再维护, 这些不怎么常见的域名像是被专门去买别人未续费的域名机构给收集了,跳转到了一些做SEO
. 还是github这种基本有几乎永久保障的二级域名好,.
1 | # npm install hexo-generator-feed --save |
在打开生成的atom.xml时输出这样一个错误PCDATA invalid Char value 8
, 最后发现是md
文件中多出了^H
这个符号, vs code
暂时不展示这种字符. 暂时也不知道是在什么情况下被添加进来的. 总之, 按照[^1]通过vim
找到并删除之后就可以了.
按照有些大佬说的,设置Render Whitespace
成all
可以看到一些字符了,但是这个里还是只显示space
和tab
,似乎并不能显示换行符及像上面这种^H
符号.
本来想着常开这个,至少能起到看我有没有在某些文件里错用tab,发现这个功能在markdown文件编写标题时,会出现和输入法重复的问题, 会出现下述这种效果.
在用异常的时候,会有这么几个疑问 * 异常是直接raise,让程序退出,还是捕获记录到日志中后继续运行 * 异常的代码影响到了代码的可读性, 有没有更合适的解决方式?
像是如果一个业务逻辑里要执行多种封装的接口调用
1 | try: |
这种应该是有些类似装饰器,阅读代码中
一直有一个疑问,究竟是捕获异常,然后按照约定的接口输出错误信息,还是直接抛出,让程序直接退出呢?
根据目前写的比较多的几种场景的程序来看 * 守护程序, 进程间通信运作 * 接口程序,执行完即退出
无论是上面哪种场景,按照接口交互的开发标准来说,已知的异常都应该按照拟定的接口格式,错误时输出约定的错误码及信息,而不应该直接将exception
直接抛出, 这样对其他调用解析你的接口的人来说,会产生未知错误.
只不过,这里的封装只在最上层封装, 然后在内部依旧可以使用raise
,便于直接从底层快速将问题抛到上层的try...except
处.(这里主要是相比return
, 能快速退出多层接口调用)
PS. 虽然直接把异常抛到上层是很便捷的做法,但是对于一个公有库来说,最好还是在自己的抽象层定义自己的异常, 一个层次只处理他调用的层次的异常,而不是直接向上暴露底层异常.
当然,这里封装的异常同样也是已知异常,对于未知异常,只能尽可能靠单元测试来发现并捕捉,不然就只能抛到最上层捕获记录到日志里,修缺陷时再进行补全捕获了.
而对于未知的错误, 肯定就是直接抛出了,但是这部分是需要在单元测试中尽可能覆盖完全来避免出现的.
那么,怎么才能把traceback
栈信息记录到日志里呢?
目前我用的比较多的是python3.6
, 这个版本的logging.log
提供了exc_info
的选项
1 | try: |
正常来说,我们提供接口时,会约定一套返回值及内容格式,以及这个接口执行出现问题时的反应,如抛出什么异常,来让调用者捕获.这是Python
的推荐做法.
但是我看到[^4]这样一篇文章,里面提到了将Exception
作为返回值,并且还存在一套针对这种用法的比较完备的库returns
. 就仿佛静态类型里的非0
值
单纯就这个做法来说,可能不是比较Pythonic
的,但是按照readme
来看,比较贴合目前python
对静态类型的青睐?因为可以完全利用上类型标注的检查效果. 对于遗漏的异常返回值检查,可以做到很好的效果. 而且这样,又不会丢失Exception
饱含的错误信息.
但是,又有一个问题,程序调用栈可能就会被破坏了,就像下面说的.
我调用一个库要一个int
类型的结果,结果这个库里的接口在失败时并不会直接抛出,打断运行,而是返回一个Exception
,我必须增加额外的判断检查,才能使用,否则一定会让调用者出错,这样其实也是违背了静态类型的接口设计的吧. 谁的问题应该由谁抛出更合适吧.
但是又看到一种说法[^5],有人说是由于python底层针对各种语句,存在各式各样的异常,基本不可能捕获,这个语言就是这么脆弱,捕获异常也只能做到非常有限的效果的话, 直接让程序出错直接退出就好了,如果这个场景没有考虑到的话.
但是按照我的理解, 如果底层的异常太多没处理好,其实就说明底层就开始有问题吧,应该一层层逐渐封装异常,最上层的使用不应该直接接触底层异常,而是下层逻辑的抽象异常才对. 这本就属于业务异常要考虑的逻辑.
这里python之父居然参与讨论了,回答了问题了.
Let me draw a line in the sand. The PEP will not support any form of exception checking. The only thing possibly under discussion here is whether there is some other use of stubs (maybe an IDE suggesting a raise or try/except) that might benefit from declaring exceptions. But so far everything brought up has just been about the relative advantages of checked exceptions, and on that issue is close. We won't do it.
The PEP doesn't mandate any particular behavior from a type checker, so I'm not prohibiting you from doing something you find useful. Whether it is actually useful may well depend on the codebase you are checking. I just don't want to have to put anything in the PEP that would seem to make checked exceptions part of the signature of a function. Maybe as a compromise we can just say in the PEP that a conformant type checker should not interpret the body of functions in stubs, and you can have a non-conformant option that interprets raise statements in stub function bodies.
PEP里是不是没有这样的讨论?
Never throw an exception of my own Always catch any possible exception that might be thrown by a library I’m using on the same line as it is thrown and deal with it immediately.
It's all well and good that exceptions are widely used in core Python constructs, but why is a different question.
以前python
编写接口时, 只能在用类型注释或者注释来告知使用的人,这套接口的入参.
现在python3.6
开始可以用typing
的类型标注功能了.
简单举个例子, 返回的是什么样的generator
都可以定义了,利用mypy
可以在IDE中编写时直接做检查了.虽然还是不能做到运行时强制检查. 1
2
3
4
5
def function(a: str, b: int) -> Generator[ptional[str, int]]:
c: list = [a, b]
for k in c:
yield k
对于愈发庞大的代码来说,静态类型声明带来的直观的类型阅读及清晰的调用逻辑,在后续维护时带来的好处非常大.
当然,即便原先没有这种类型标注,对于一套良好的动态类型代码,也是会说明清楚所有的类型的, 只是因为不像静态语言编译时会自动检查, 因为当时还没有类型标注这样的标准,开发自动检查的插件的难度也非常庞大, 这种情况下全靠开发者自觉.
对于一套大型代码, 最主要的是不能依赖开发者的个人素质, 要通过各式各样的检查工具,来提高开发者们的下限水平. 所以, 相比于依赖注释,这个只能靠认为review或不标准的检查工具的形式, 标准支持的类型标注的价值要大得多了.
再之后,对于类型检查,准备在防御式编程初探中讨论.
python
里属于防御性编程下属的两种风格就代码风格上差异来说,主要的差异点好像在于以下
针对不同类型的业务代码,其实是根据场景都适用的。
EAFP
的, 但是也不排斥利用静态语言的特性做一定程度的防御式编程Python 的动态类型(duck typing)决定了 EAFP,而 Java等强类型(strong typing)决定了 LBYL。语言之间的设计哲学差异,Java 对类型要求非常严格,要求明确,类/方法等,它假定你应该知道,任何时候你正在使用的对象类型,以及它能做什么。相反,Python 的鸭子类型意味着你不必明确的知道对象的显示类型是什么,你只需要关心你在使用时候它能有相应的反馈。在这种宽松的限制下,唯一明确的态度就是认为代码会工作,准备面对结果。 这无关语言的好坏,每一门语言都有自己的哲学与态度,正确的对待,理解。
上面这句话说的很对, 对于python来说, 他提供了动态/静态语言的能力, 剩下的取决于开发者自身的运用了.
类型 | 预计成功比例 | 业务逻辑可阅读性 | 存在潜在不可控问题时的处理逻辑 | 预检查成本 | 多线程race condtion 风险 |
---|---|---|---|---|---|
EAFP | 高 | 高(异常拆分恰当) | 省力. 通过父异常统一拦截 | 低 | 无 |
LBYL | 低 | 高(在python3.6, mypy之后, 参数校验可从业务逻辑中去除) | 费力. 需要一个个异常判定, 工作量大, 风险高 | 一般低, 偶尔高 | 有 |
if
是最适合编译器和处理器做分支预测优化的部分了*。race condition
的潜在问题type annotion
后, 通过typing
特性的类型标注后, LBYL的参数检查可以不用再显式isinstance
来判断了, 即解除了一个缺点: 参数检查会混杂在业务逻辑中
的劣势.Exception
的支持好像在mypy
里这个是在计划支持中, 具体进展暂未了解到. 但大致的趋势是2者皆可用. 并不绝对.
最近发现用外部OSS图床,在本地处理归档文件资源时,不是那么的方便。且自己用的腾讯云COS的上传插件没做对剪切板中图片上传的功能,导致每次粘贴图片都得多一步去获取文件的绝对路径或者选中文件。
然后发现vs code
有个Paste Image
的插件可以做到直接将剪切板的图片放到本地指定相对路径的功能。
但是对于发布到github page
上的文章呢,图片资源怎么才能使用相对路径呢?
就发现hexo 3
提供了一部分功能(即便现在4了,好像也暂时没有更进一步优化的计划,relative path没怎么搜到有人提PR了)。
按照hexo的意思是不使用标准markdown语法,而是使用下面这种提供的外部语法来处理……
1 | {% asset_path slug %} |
不太能接受,然后发现配合一个好多年前的插件hexo-asset-image
就可以做到对于用户可以无感知的使用相对路径的图片了。
post_assets_folder
设置为true1 | - var endPos = link.lastIndexOf('.'); |
原始的md
文件中的图片路径依旧可以用正常的相对路径{% asset_img "hexo结合PasteImage使用本地图片_2020-06-24-00-47-39.png" "" %}
, 在hexo g
时生成的文件中的图片资源路径会被这个插件自动转换成url后静态文件相对于静态文件根目录的绝对路径。
1 | { |
单测主要遇到需要mock
数据的场景目前遇到比较多的是两类 * HTTP接口 * 对底层业务逻辑API
HTTP接口相关的mock工具非常多了,暂时就不具体说了。这篇主要记录的是针对依赖的API的mock。
在python单元测试数据mock有两座山,一个是unittest.mock
,一个是pytest
的mockeypatch
。当然,还有一些easymock
之类的库,但是没去研究,暂时搁置。
很多文章里提到Test Double at XUnitPatterns.com,好像TDD开发中的概念,这里做了非常详细的划分。
不过,主体上其实对数据的模拟主要是2点。 * mock * stub
虽然上面的理论上是这样说,但是对于python的unittest
库里,并没有按照这两个来分开实现,统一都是叫做mock.patch
。
不过使用的方法上的确有差异 1
2
3
4
5
6# mock
mock_instance.assert_called_with
# stub
mock_instance.return_value = 'xxx'
mock_instance.side_effect = {'a': 'xxx'}mock
应该主要指的是检查函数的调用、异常的触发情况,而stub
主要就负责提供我们要的模拟数据
主要使用的是pytest
及pytest-mock
,pytest-mock
提供了一个mocker
的fixture
。
PS. fixture
是pytest的一个封装,将一个函数封装,作为全局参数使用,在被写入某些函数的参数时,这些被封装的函数会执行一遍(对于像patch
这类修改运行时的操作,能够保留)
1 |
a使用b模块中提供的接口,而单元测试脚本patch掉b模块,手动封装b模块会提供的数据
1 | # a.py |
上面要注意的地方,原因主要在于模块使用时的运行时上下文,在这个模块被导入前,这个模块是不存在于代码的运行时中,只有当这个模块被导入了,这个模块才存在于运行时,patch的操作才能生效。除非是在你的当前文件中,这样定义和导入默认都已经完成了,就不会遇到这个问题了。
下面所有的mock
都可以用上面提到的mocker
替换
1 | # mymodule.py |
1 | def my_side_effect(*args, **kwargs): |
1 | myMethod = Mock(side_effect=KeyError('whatever')) |
Python
常用调试方式1 | python3 -m pdb xxx.py |
1 | (Pdb) commands 1 |
1 | import pdb |
1 | while a > 0: |
1 | # b(reak) [([filename:]lineno | function) [, condition]] |
pdb原生不支持多线程调试,但是基于这个开发的不少第三方库支持, 可能rpdb?详见下述
稍后讲下pycharm
多线程调试
pycharm是开发了pydevd这个插件来完成的远程调试、多线程调试的功能
对于代码量一大,手动gdb肯定不方便,又不像二进制程序会出现coredump,所以gdb的backtrace
经常需要用来排查段错误。
日志才是最主要的排查手段,通过等级控制,打开debug日志复现一次,日志足够详细就能直接定位到问题行。如果不够详细,就需要补充了。
1 | import logging |
1 | { |
1 |
|
ceph -s
和ceph df
都看不到在这之后创建的资源池了。在大佬的带领下,得知了这部分数据来源于ceph-mgr
的pgmap
例行同步。时间出现变化之后,debug日志里也的确不再出现pgmap的同步日志了。从这个方向找到了入手点。
根据打开调试信息后, 看到的日志中大量的pgmap v137057: 565 pgs: 576 active+clean
搜索日志pgmap v
,是在DaemonServer.cc
中调用的send_report
MgrStandby和Mgr是组合关系,MgrStandby里实例化了一个active的Mgr
在触发一次tick之后,还会记录一个事件,好像让下一次触发timer.add_event_adter(g_conf->get_val<int64_t>("mgr_tick_period") new FunctionContext([this](int r))){ tick();}
的逻辑
对,在MgrStandby
启动时调用init
,触发第一次tick
。之后应该就是这个计时器在工作。
这个mgr_tick_period
的功能,看看咋工作的,好像只是std::chrono::seconds
的封装。
这个mgr_tick_period
应该只是个配置文件,记录多久同步的吧,果然,ceph daemon
里可以看到是2s一次。
timer.add_event_after
应该才是重头戏,这里居然是设置的根据时间戳来调用add_event_at
添加回调的任务……当timer_thread
执行到ceph_clock_now
,用gettimeofday
拿到系统时间,如果当前时间大于设置的时间,就是永远不会触发了?
所以这里存在一个潜在问题,是不是所有使用这个计时器的地方都存在会直接受到这个时钟往回调影响的问题。目前来看定时器应该只有这一个。
1 |
|
以ceph-mgr为例,调用顺序是这样的。
ceph_mgr.cc
里实例化MgrStandby
,MgrStandby
实例化SafeTimer
,然后ceph_mgr.cc
调用mgr.init
,里面调用SafeTimer
实例的init
,在这里
1 | thread = new SafeTimerThread(this); |
mgr
的定时器任务就开始周期执行了。根据safe_callbacks
这把锁的状态决定是否要申请到锁阻塞式执行任务,还是非阻塞式。
时间的触发基于cond.WaitUnitl(lock, schedule.begin()->first
这个的实现。这个中主要依赖的应该是pthread_cond_timedwait
这个超时等待接口。似乎有些库里的sleep
就是通过这个实现的。
pthread_cond_timedwait()
函数阻塞住调用该函数的线程,等待由cond指定的条件被触发(pthread_cond_broadcast() or pthread_cond_signal())
。如果超时了,就可以作为定时器使用。这里传入的是当前的abstime
。
但是这块我好像看到,这个函数其实是支持传入monotonic
时钟的,这个时钟开机开始计数,不受外部影响。
根据 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#man 3 pthread_condattr
DESCRIPTION
Condition attribute objects are used to specify parameters to the
pthread_cond_init(3) function. The pthread_condattr_init() function
initializes a condition attribute object with the default attributes and
the pthread_condattr_destroy() function destroys a condition attribute
object. The pthread_condattr_getclock() function shall obtain the value
of the clock attributes object referenced by attr. The
pthread_condattr_setclock() function sets the system clock to be used for
time comparisons to the one specified in clock. Valid clock values are
**CLOCK_MONOTONIC** and **CLOCK_REALTIME** (the default). The
pthread_condattr_getpshared() function shall obtain the value of the
process-shared attribute from the attributes object referenced by attr.
The pthread_condattr_setpshared() function shall set the process-shared
attribute in an initialized attributes object referenced by attr.
可见应该是支持的,但是这个时钟不知道在分布式环境,能不能保证多节点内及时被chrony或者ntp协调一致。根据这个[^5]来看,应该是能通过ntp服务来保障长时间稳定一致的。
根据[^6]的结果来说,mds
是应用上了这套,也向下移植到了Luminous
版本。
根据[^7],在bluestore
的perf
上也用上了这个时钟来进行latency
延时统计。在Nautilus
版本里增加的功能。
在Luminous
版本里,我也搜到了这个在[^7]里使用的coarse_mono_clock::now()
时钟。
根据[^8],在Mimic
版本里,好像mon
也迁移了部分。
根据[^10],在Mimic
版本里,rados bench
也开始使用这个时钟。
所以都只是部分组件已支持, 只是mgr
没有支持, 目前来看也没有统一迁移到支持的计划.
官方已提供common/Timer: use mono_clock for clock_t by tchaikov · Pull Request #39273 · ceph/ceph
1 | digraph G { |
vue-mark-display
这套功能,直接基于markdown生成slides,都不用去专门学习beamer那套,直接就能基于现有的markdown生成,感觉相当不错。不过在折腾这套工具的时候,遇到一个问题,自己展示的时候用html没有问题,但是归档发布给其他人的时候,当然最好还是以pdf的形式嘛。这套工具不知道是以为没适配高分屏还是什么原因,通过Chrome和decktype导出pdf时,文字布局一定会与页面上看到的不一致。
具体也没细看了,这套工具终究是个人使用为主,可能开发者的使用场景不怎么需要导出,比如纯外网或内网部署了自己的静态网站之类的情况下。
考虑到这套工具偏个性化,于是去找找适用性较广,功能做的比较完善的这类产品。
发现了reveal.js
这套目前来看star数应该是最多的,也支持所有我需要的功能,如markdown。还有人基于这套,做了一个一键使用的reveal-md
,更加满足我的需求了。
这篇文章主要讲的就是针对我的几个痛点,我怎么使用这套工具了。
emm,发现几个问题,看看remark和remark-slide和spectacle这俩star更多的有没有解决这个问题呢?reveal-md
作者没兴趣修这些问题……
1 | # 指定官方提供的 |
1 | --- |
1 | reveal-md slides.md --print slides.pdf |
html看到的公式和图片都是正常的。但是基于Puppeteer
导出的时候,公式无法加载。按照reveal.js
里说的,是已经支持了部分版本的mathjax了的,所以只能从这个工具使用的导出方式上来怀疑了。
可能因为headless浏览器那边加载的问题?没加载指定的js之类的?导致无法解析出mathjax
公式。可能是这个reveal-md
里对puppeteer的使用上存在一些问题?因为看decktape底层应该也是基于puppeteer来做的。
emm,有人说reveal.js原生的?print-pdf
已经修复了这些问题,但是reveal-md,怎么像是css不加载?
1 | reveal-md temp.md --static static |
要在html后的#
前增加?print-pdf,在#
后面加是没用的……总算是意识到了
1 | http://localhost:8000/index.html?print-pdf#/ |
奇怪,前两天操作的时候怎么好像没遇到这个问题呢?
node版本的问题嘛?不像啊
1 | 是因为我去掉了水平Separator的设置,但是保留了下面这个 |
使用reveal-md
提供的生成Static文件的方式,再通过decktape
将静态网页转成pdf。
1 | reveal-md temp.md --static static |
根据下面[^2]里提到的问题,目前找到的有两种解决方式 1. 使用screenshots输出,再写个脚本拼接成pdf,可能会丢失一些动态效果?如果尺寸比图片小,也会丢失下面的内容……不过这个和html是表现一致的,所以如果图片在html有问题,还可以调整。 2. 使用增加size的方式,直到找到可以正确显示的方式。但是这样的size针对不同的图片大小,还需要手动调整…… 3. 使用1.0.0版本的decktape,这个版本还在使用phantomjs,但是我发现在高分屏幕环境,似乎存在一点问题.只能显示一部分,原本的中间的内容跑到了右下角去了。 4. 使用原生的print-pdf,但是好像我这边生成的好像怎么都加载不了css,位置始终在左侧.
常用尺寸: * 2048x1536 * 1920x1080 * 2560x1440(screenshots)
绕过方式如下:
A workaround seems to be increasing (very much) the size. For example using --size='2048x1536' instead of --size='1024x768' works for me.
但是这个存在一个问题,因为图片分辨率一旦超过设置的宽度,他这边就会无法显示了。
可能相比之下还不如刚才那样直接输出。如果使用screenshots输出倒是没有一点问题。
在markdown文件每页中增加Note:
,这页的剩下的部分就都会显示在注释中,打开本地web端访问slides后,按s
会弹出注释页面,放到其他屏幕上就可以了。然后分别按Cmd+Ctrl+f
全屏即可了。[^5]
只不过这种只适合多屏幕场景了吧,不过对于投屏来说,的确就是多屏幕
本以为Reveal.js
生成的slides
是顺序的,但是发现实际是具有上下左右四个方向的……
本以为直接是按照目录结构保证的,但是实际上来看,和我写的目录结构并不是完全匹配的。
还得具体了解一下。
横向的幻灯片代表一章, 纵向的幻灯片代表一章中的一节。那么横向的幻灯片在播放时是左右切换的, 而纵向的幻灯片是上下切换的。
Reveal.js 里页面有两种页面类型,横向的一级页面、纵向的子页面。后者务必嵌套在前者里面。所谓的纵横比较好理解,键盘上的左右箭头控制一级页面,上下键移动子页面。
说是这么说的.根据[^7]也是有人直接改了,让可以在section切换时,能够直接进入下一个section的开头,而不是按Esc
看到的布局的右侧位置。
又仔细翻了下文档,其实是由开关控制,决定是否要继承当前所在的section下属的纵向index的,关闭navigationMode
就可以了。[^8]的Navigation Mode
有提开关,既然有空格可以工作,暂时就不动这个了。
还是暂时使用空格顺序切换吧。
1 | cd reveal.js |
在调试过程中,发现主要痛点在于默认主题的标题全部被强制大写了,导致看上去不是很符合我的预期,找了下可以在英文外增加代码符号```来完成绕过。
The pytest framework makes it easy to write small tests, yet scales to support complex functional testing for applications and libraries.
It’s unittest with plugins. nose2’s purpose is to extend unittest to make testing nicer and easier to understand. nose2的主要目的是扩展Python的标准单元测试库unittest,因此它的定位是“带插件的unittest”。
nose2 vs pytest
nose2 may or may not be a good fit for your project. If you are new to python testing, we encourage you to also consider pytest, a popular testing framework. 官方对于新使用单元测试的更推荐使用pytest。
nose(已进入维护阶段,不再开发新功能)
都是基于unittest原生库开发而来。
详见[转]Python测试框架对比----unittest, pytest, nose, robot framework对比 - bonelee - 博客园[^5]
基于测试功能上来说,均具有。
总体来说,pytest入门、扩展性最强,nose2比较要求写单元测试的功底的样子?
毫无疑问,用py.test就完事了。
网上申办上海新版社保卡,设备小米 8,支付宝一直提示此设备不支持刷脸,请更换设备后再试。官方没有任何其他的可以不使用支付宝的办理方式。
主任信箱回复了
搜了半天,有说支付宝这么报是因为下述等原因 * 设备root了 (我这台已经没root了,不过以前root过,直接移机的) * 设备上存在了xposed之类的软件,被判断不安全,不让刷脸 (的确以前root的时候装过,现在卸了也不行,难道支付宝的匹配规则这么严格?) * 就是设备不支持(小米8都不支持的话,这能线上办理的设备得多新啊) * 账户黑了(支付宝连政务办理都给卡?)
我是不能理解为什么小米8支持红外人脸识别(支持不支持3D人脸识别,是足够用在大部分非支付级别的人脸识别上了的)且没有root的设备也不能使用刷脸的。
这个刷脸只是这个办理的第一步,就直接被卡死了。
根据官网的说明(下图)
以前学信网那些都能允许笔记本摄像头验证,现在的业务办理必须走人脸了?
最后,有个大佬回复说用工行的app可以办理,我照着做成功了,这个阶段里根本没有用到人脸识别,只需要提交照片就可以了。看来社保负责的部门只提供了一个由银行申办的通道,至于银行有没有提供线上功能,根本不关心……
唉,这还只是新版社保卡的办理,要不是有银行的通道,我肯定是最后只能线下去处理这个事。要是以后别的身份证之类的事情也是这类全部用了支付宝之类的接口,然后这些接口的使用上做了限制,部分人始终没办法使用可怎么办呢……
猜测吧,支付宝这个刷脸接口做成通用功能了,导致自家支付程序要求严格的安全,金融级别嘛,不够安全的设备不用,这个属于正常。毕竟只是额外功能,刷脸支付可以不用。
但是这个接口如果同时也开给了政府部门,对于不要求金融级别的安全的情况下,却必须先满足这个级别的条件,才能使用政府提供的功能,那这种场景是不能接受的吧。毕竟我这种还算年轻的人都没有找到刷脸过不了的情况下的其他办理手段,那年纪更大的人呢?岂不是反而给很多人带来办理的负担了?
论人群来说,刷脸支付占全国比例,和使用政府提供的线上功能的人的比例,是存在绝对差距的(毕竟,政府功能除了线下没有其他通道了)。但是又没有渠道反馈这样的情况……试试962222这个官网提供的热线吧
猜测是因为手机号是外地的,需要加区号021.
https://www.962222.net/pages/cms/zrxx.html
发送了一封上面写的总结,不知道能不能收到反馈。
]]>回复:您好!非常抱歉给您带来了不便,我们已将您反应的情况提交至相关部门,如有不便,敬请谅解。有关新版社保卡办理相关事宜,您也可以拨打我们的服务热线962222。
基本使用方式如下,我主要用来记日志了
1 |
|
会直接把表达式按照字符串输出,并过滤掉注释部分以及前后的空白。 1
2
3
4
5
6
7
8
int main()
{
PRINT(print("hello world")/*new*/);
return 0;
}
这个就比较有意思了,可以通过这个结合gcc的typeof模拟出不少泛型的方式,在编译前预处理时扩展出不少函数或变量定义。参考[^4]文章的大佬用宏扩展出了弱图灵完备的函数。
按照大佬说的,这个库Cloak/cloak.h at master · pfultz2/Cloak非常多的例子
1 |
|
使用方式,_Generic((var), type1 : ..., type2 : ..., ……, default : ...)
,感觉果然和大佬们说的一样,比较像是对基本类型进行switch。
比起这个,可能gcc内建的像typeof这些关键字用起来更具有扩展性。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
int main(void)
{
int a = 10;
float f = 100.0f;
float _Complex fCex = 100.0f + 1.0if;
CUSTOM_GENERIC(a); //type signed int, var:10
CUSTOM_GENERIC(f); //type float, var:100.000000
CUSTOM_GENERIC(fCex); //type float _Complex, var:100.000000+1.000000i
CUSTOM_GENERIC(12); //type signed int, var:12
return 0;
}
在第一次尝试黑苹果的时候,按照大佬们的经验之谈,配置一个A卡,这样配置核显做硬解就能充分利用起显卡的性能来了。
但是很可惜的是,在做教程的时候,这些大佬们没提到关于温控的问题。
对于黑苹果来说,A卡独显的风扇默认是无法通过软件控制到的,即便注入了驱动,也只能控制机箱风扇。
对于A卡的风扇来说,需要单独定制操作。翻遍黑锅论坛,发现有大佬开发了针对AMD Vega64显卡风扇的温控操作。而rx系列就没有了……
在搜索过程中,发现除了直接软件接管控制之外,似乎还有一种直接写入到驱动的方法,不过这个难度可能稍大,也可能挺费时间……
我姑且选择了拔掉独显,单纯使用集显驱动了。但是方案姑且记录在这里吧。
如果你在 Windows 下使用过 AMD 显卡驱动程序,则你一定对于 AMD Watt Man 有印象。这是随驱动程序安装的用于用户自定义性能模式的工具。你可以调整风扇转速方案,设定核心和内存频率以及电压。其实质就是生成一个自定义的 Power Table 供 GPU 加载使用以控制其行为模式。如果我们可以将这个 Power Table 设定好并抽取出来,然后注入到 macOS 对于该显卡的驱动当中,就可以完成对于显卡行为的自定义控制。[^2]
embed the Soft Power Table in the kext.
上面两句是关键,根据上面两句来看,我之前构思的,能够切换到win10把显卡的bios里设置的参数改了,用北极星编辑器和flash工具刷进去,然后切回mac来使用的思路,是不是不太可行呢。因为从上面来看,这些参数可能实际是在驱动加载的时候生效的,而不是在硬件上。但是从刷bios这一个动作来看,应该也是存在于bios中的。可能这是2种方式吧。根据[^3]来看,的确是有2种方式了。
尤其是MAX AVAIL这个值。目前遇到的问题是发现在L版本,当osd out了之后,ceph df
拿到的MAX AVAIL
始终没有变化。
这样对于使用者来说是存在问题的,当出现降级的情况时,若业务层使用这个接口看到资源池仍有可用空间,但实际上依旧up的osd并不能提供这些空间,是会导致业务人员出现误判……直接导致业务数据写不下去的情况的。
代码里是这样写了,但是是否上面的想法只是我们理解错误产生的误用呢?
向上追溯一下这段关键计算的提交记录吧,看看有没有相关场景。
先搜索主要代码
1 | // PGMap.cc |
利用blame查找每行的记录,还是不错的。
这篇里只加了跳过kb=0的选项
mon: incorrect MAX AVAIL in "ceph df" by liuchang0812 · Pull Request #17513 · ceph/ceph
这篇里好像是之前漏了/raw_used_rate这个副本size
这个操作引入的full_ratio参数到MAX AVAIL计算中
mon/PGMap: Fix %USED calculation bug. · ceph/ceph@d10c6c2
修复%USED(raw的应该) 没有乘以副本数的问题。
osd,os,mon: extend 'ceph df' report to provide both USED and RAW_USED · ceph/ceph@7ca25df
这个应该是在L之后的版本,在这次commit中引入Statfs这个中间结构用来存储total、avail,免于直接接触kb
mon/PGMap: GLOBAL -> RAW STORAGE in 'df' output · ceph/ceph@738789b
这个也是L之后的版本,把ceph df输出的GLOBAL改成了RAW,防止错误理解
get_rule_weight_osd_map这个函数的新增并没有看出有立刻被用在计算资源池容量这里。
应该可以从这个接口被调用的地方来分析,新增的人是否没考虑过osd异常的场景?
mon/PGMap: move summary information into parent PGMapDigest object · ceph/ceph@8f25216
这里被重构,挪了一下位置……
mon/PGMap: factor mon_osd_full_ratio into MAX AVAIL calc · ceph/ceph@f223ac9
这里又被做了格式化,哎
mon: move "df" dump code from PGMonitor to PGMap · ceph/ceph@519a01d
这里又重构了……
在这次提交以前这部分代码在src/mon/PGMonitor.cc
mon/pgmonitor: use appropriate forced conversions in get_rule_avail · ceph/ceph@024caa7
哎,这次对这行改了下显式类型转换,从float改成double
这个看起来和OSD out相关,但很可惜,只处理了错误计算为0的问题,并没有考虑正常场景?
mon: include 'max avail' in df output · ceph/ceph@7a9652b
终于找到了引入的地方,这里增加的max avail的值。
PGMonitor: fix bug in caculating pool avail space · ceph/ceph@04d0526
这里处理的那个osd_map
在最新的分支里,也没有相关信息。看起来得提个ISSUE看看有没有答复了。
1 | Environment: Luminous 12.2.12 |
新建了这个issue, 希望能得到回复吧。
Bug #45809: When out a osd, the `MAX AVAIL` doesn't change. - Ceph - Ceph
看资源池为什么df输出的不对, 是否是osd_stat此时没收集完? 如何判断? 怀疑是pg peer完毕之前引起的. 以前创建的慢感知不到,现在可以感知了
osd_stat
TODO: 为什么网卡异常会引起应用层乱序?
资源池Df的信息, 目前来看, 其实是从crush rule里读的, 这里似乎维持了一个缓存?
crush_rule显示了一个3副本前的raw 容量.
如果是新建的rule, 则重新计算. 但是如果只是crush map变更呢? 如何感知重新计算呢?
不对啊, 看着是每次df都重新计算的, 那个avail_by_rule
是个函数内变量.
没有调用啊?
我建了个新的crush rule ,为啥日志里没变更呢?
一点没有?
难道走的这个avail_space_by_rule
理解错了, 的确是通过这个变量缓存的.
dump_pool_stats_full
在这里才会真实的去触发?
大致知道了, 如果这个crush rule的容量还没被加载, 返回就是0
1 | int64_t get_rule_avail(int ruleno) const { |
所以, 其实就是这个容量触发更新的地方怀疑不够及时?
目前疑似PGMapStatService
的dump_pool_stats
触发PGMap
的dump_pool_stats_full
只有这一个地方触发更新.
pgservice->dump_pool_stats(osdmon()->osdmap, &ds, f.get(), verbose);
需要的是这个.
mgr的接口和mon的ceph df
函数理论上触发更新?
根据这个const MonPGStatService *pgservice;
, 其实就一个触发来源才对呀? 走mgr
的service
, 似乎的确不走那个更新的地方, 那我理解错了.
不对, PGMapStatService
这里的调用是会走到PGMap
里的dump_pool_stats
.
这里其实继承了?class PGMonStatService : public MonPGStatService, public PGMapStatService
MonPGStatService
这个和上面这几个什么关联性呢?
和PGMapStatService
同时被继承到一个class里?
所以关键应该是在这个共同的派生类上?
1 | class PGMonitor : public PaxosService { |
PGMonStatService
的构造函数里会传入这个,
PGMonitor
是这个class PGMonitor : public PaxosService
, 有点遗忘了, 负责的是PaxosService
是不是可以理解为, 这个就是PGMap
的更新的一致性代码管理方.
TODO: 比如这个PGMonStatService
使用了PGMonitor
的哪些函数接口?
TODO: 题外话, PaxosService
的is_readable不知道怎么理解.
获取pgmap的元数据的时候, 好像是通过这个来查的
1 | void dump_info(Formatter *f) const override { |
所以刚才继承的那俩类, 还有在单独提供使用吗?
MonPGStatService
Monitor
里搞了个这个const MonPGStatService *pgservice;
现在看来这个并不是pg的paxosservice
TODO: MgrStatMonitor
和MonStatMonitor
有什么关联性?
MgrPGStatService : public MonPGStatService
继承关系
似乎这个是在MgrStatMonitor
这里初始化的, 所以又到了mgr这里?
根据下面这个, 是12
版本及以后用MgrStatMonitor
, PGMonitor
是以前的呢?
1 | // make sure we're using the right pg service.. remove me post-luminous! |
require_osd_release luminous
, 所以实际上走进的代码应该是MgrStatMonitor?
走的其实是digest
的dump_pool_stats
// FIXME: we don't guarantee avail_space_by_rule is up-to-date before this function is invoked
官方在这里其实也写了, 等待修复.
目前来看, 只有触发一下PGMap的get_rules_avail
才能够更新数据. 所以在Digest里有办法更新嘛?
目前初步看, 只有ActivePyModules
里会触发PGmap的
class ClusterState
这里是干啥的呢?
PGMapStatService
里也会.
但是这个目前只在ClusterState
里使用了, mon里实际上并不使用PGMonitor
的查询接口了.
似乎只有PGMonitor
使用PGMap
那digest这个到底?
mon/PGMap: move summary information into parent PGMapDigest object
Everything summary-ish that we need to send to the mon is moved into a parent class. The child PGMap retains the detail.
The parent gets its own encode(), and PGMap::encode_digest() will call it to encode just the summary info.
Squashed in here is a new num_pg_by_osd that could have been done in a preceding patch but I did things in the wrong order. :(
Signed-off-by: Sage Weil sage@redhat.com
所以其实这个digest就是pgmap的一个封装版本.
在void DaemonServer::send_report
这里会触发
1 | cluster_state.with_osdmap([&](const OSDMap& osdmap) { |
这里会定时将pgmap给更新?
mgr的捕捉得通过asok的文件链接直接找. mgr_tick_period
2s更新一次, 那问题就是这个日志在哪?
忘记重新编译mgr了, 重新编了之后抓到了.
现在要找的是触发的地方.
mgr_tick_period
根据下面的记录来看,确实就是2s更新一次.
如果这个间隔被我改大了之后, ceph df的结果是查不到.
所以关键问题还是crush的更新阶段?
抓一下那次异常时的osd map看看
11-12 10:38:55 1
2
3
4
5
6
7
8
9
10
11ceph osd getmap 110 -o osdmap.110
osdmaptool osdmap.110 --export-crush crush.110
crushtool -d crush.110 -o crush.110.txt
##测试均衡分布
osdmaptool osdmap.1034 --test-map-pgs-dump --pool 10
osdmaptool osdmap.1034 --tree
#打印ceph osd tree
ceph osd getcrushmap -o {compiled-crushmap-filename}
ceph-objectstore-tool --data-path /var/lib/ceph/osd/ceph-3/ --pgid 4.0 obj4 dump
2021-11-12 10:38:54.756557 7f87acd59700 0 log_channel(cluster) log [DBG] : osdmap e76: 6 total, 6 up, 6 in
ceph osd dump 76 得到的没有容量
osdmon()->osdmap
1 | class OSDMonitor *osdmon() { |
其实计算的时候, 还需要一个osd_stat
所以有没有办法得到完整的PGMap
?
资源池存在,但是crush rule是新建的这种.
复现了. emm.
第一种:
1 | (gdb) bt |
第二种
1 | (gdb) bt |
ceph mon remove {mon-id}
奇怪, 现阶段的问题是, 资源池都不显示. 而不是单纯不显示数字.
osdmap意思是也没更新引起的?
资源池名字和crush rule也都是从osd map里拿的. 那为啥会出现容量为0, 但是资源池存在的情况呢?
那就代表上一次的osd map里还存在crush rule为0, 但是pool存在的那次.
2021-11-15 10:00:02.105074 7f0947dbe700 4 wc 1: 128092917534 2021-11-15 10:02:01.659658 7f0947dbe700 4 wc 1: 128092671774 2021-11-15 10:03:01.454611 7f0947dbe700 4 wc 1: 128092426014 2021-11-15 10:10:01.699644 7f2e29b8c700 4 wc 1: 128092770078 2021-11-15 10:10:01.854774 7f2e29b8c700 4 wc 1: 128092770078 2021-11-15 10:11:01.675594 7f2e29b8c700 4 wc 1: 128092917534
TODO: 待整理本片的内容.
Mgr
里似乎做的初始化DaemonServer
, DaemonServer
初始化的时候从上级收到ClusterState
对象.
1 | Mgr::Mgr(MonClient *monc_, const MgrMap& mgrmap, |
TODO:cluster_state
是从monc来的?
c++ // ceph_mgr.c MgrStandby mgr(argc, argv); int rc = mgr.init();
我之前的方案是自己搭了个本地虚拟机做跳板,这个虚拟机里装了tmux,然后我通过windows上可以ssh的工具ssh上去使用tmux
, 再开多个pane
ssh开发机。
tmux通过快捷键切换、创建、分割、临时全屏化pane来操作还是非常便捷的。
目前我是使用的putty作为ssh工具。之前使用过cygwin(MSYS),不过在这些环境下启动tmux,当时是在创建切换pane上有些或多或少的问题。听说MSYS2之前也已经出了,似乎已经比较成熟了,大佬们也可以试试。
我最近已经比较图稳定了,之前遇到的或多或少的问题有比如我需要显示编码从UTF-8临时切换到GBK,在这个时候有些工具会把分隔栏变成错误字符。还有我需要简洁的全屏,只需要通过tmux来控制和切换窗口,像xshell和CRT等工具对我来说就有些功能过冗余了。当然,像能和这两个工具结合在一起的文件传输插件还是挺不错的,不过也可以通过samba和IDE的remote devetop的sync功能将就将就,对于偶尔才会有代码以外的文件需要替换时,winscp足够使用了。
去年好像windows 10 开始官方提供OpenSSH支持,我立马也就升级了。不过很可惜在powershell和cmd环境中,像Ctrl+C等快捷键会直接被powershell给捕获,导致即便用这两个窗口ssh到了设备上,表现也并不是那么好。另外powershell和cmd的默认字体和高亮的美观度也不是很喜欢……
根据windows terminal的文档[^2]中所说的快捷键使用,基本上我用到的tmux功能都可以修改windows terminal的profile来模拟一致。且针对windows上需要临时复制单pane的内容时,这个相比putty能够不用单独全屏化窗口再复制,算是一个优势。
不过现在唯一的不足就在于,在这个版本暂时没有临时zoom全屏化单个pane的功能,根据[^1]中说的,#996
暂时也没有纳入1.x
的计划中,在2.0
中才会发布。这就有点可惜了,只能再期待了。
1 | git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm |
1 | #Montor.cc文件入口ceph df |
ceph上个版本使用filestore时,计算是基于osd的所在磁盘的statfs来计算的。因此,rbd层面的大的稀疏文件,在落到osd层上之后实际就并不占用那么多了。
1 | # 主体函数 |
1 | PGMapDigest::dump_fs_stats |
是否可以理解为,如果不使用ceph df提供的接口,手动去计算可用pg的容量,就可以直接代表可用容量?
1 | PGMapDigest::dump_pool_stats_full |
待阅读
1 | PGMapDigest::dump_pool_stats_full |
根据[^5]可以知道
- ceph df [detail] output (POOLS section) has been modified in json format:
- 'bytes used' column renamed to 'stored'. Represents amount of data stored by the user.
- 'raw bytes used' column renamed to "stored_raw". Totals of user data over all OSD excluding degraded.
- new 'bytes_used' column now represent amount of space allocated by all OSD nodes.
- 'kb_used' column - the same as 'bytes_used' but in KB.
- new column 'compress_bytes_used' - amount of space allocated for compressed data. I.e. comrpessed data plus all the allocation, replication and erasure coding overhead.
- new column 'compress_under_bytes' amount of data passed through compression (summed over all replicas) and beneficial enough to be stored in a compressed form.
所以在这个版本中将之前版本中存在的多种容量统计给分类了.
pool级别分为 * stored(old bytes used) 逻辑空间占用量, 主要针对的是写入多少, 不考虑由于COW
引起的实际占用量较小 * store_nromalized = pool_stat.get_user_bytes(raw_used_rate, per_pool) * store_stats.data_stored / raw_used_rate * Bluestore.cc * res_statfs->data_stored += l.length * 传统模式 stats.sum.num_bytes + stats.sum.num_bytes_hit_set_archive; * 12版本好像没加命中集归档的容量 * 12版本这里直接拿的sum.num_bytes
* 通过ceph report可以看到资源池的这个统计 * stored_raw(old raw bytes used) 用户使用的总容量, 包含所有osd除去降级的(这个应该是带副本的吧) * pool_stat.get_user_bytes(1.0, per_pool) * 这里应该是统计store_stats.data_stored的时候一些down的osd就不统计了, 所以为1.0的raw_used_rate
. * new bytes used 实际这个资源池里的OSD分配的空间大小(这里应该是不包含其他资源池因为复用osd占用的空间吧?) * pool_stat.get_allocated_bytes * store_stats.allocated;
* compress_bytes_used compress的数据 , 分配的空间大小(包含副本/EC的开销), 也就是说除去副本数就是用户数据实际在设备里存储吧 * statfs.data_compressed_allocated * comprress_under_bytes compress的用户数据大小? * statfs.compressed_original
1 | diagraph calltrace { |
1 | struct pool_stat_t { |
PrimaryLogPG.cc
里进行统计问题: * 资源池显示的使用量实际上并不是osd上实际分配和占用的, 那为什么资源池显示已用满了基本上就不让用了呢? 还是说这两个基本上是一致的? * 按照bluestore这层应该也是和FileStore
时一样, 实际上是有洞的,这种情况下, 为什么不默认提供更多的空间呢? 是怕出问题吧? 只有当压缩的时候, 才提供? * compressed的容量是否 * used和 objects数量是否有关呢 * 像是rbd上层4K随机写, 在rados层看到的就是写了2W5的对象以后就100G了, rados还是按照4M对象创建了... * 按照之前我们看pg的op, 理论上应该和objects数量无关才对. * pg处理的是Rados层的请求, 所以其实问题在于rbd层的4k 是怎么转换到4M的. * 问题其实是写了这些对象以后, df看到的容量就满了, 而实际上osd还没满. * mon如何打印出他当前收集的容量信息呢? * ### Luminous
在代码中,在计算max_avail容量时,在PGMap::get_rules_avail
函数中,mon会去迭代所有的资源池,根据pool_id
拿到pool的如使用的ruleno
的rule id、类型和size信息。拿到上述信息后,使用ruleno
找到crush map
信息,在这里建立一张基于crush map对各级bucket及osd进行广度优先搜索查找到的map<int, float>
的id
为key,crush weight
为value的表,并进行归一化,让key更新为weight/sum
的值。
然后在计算资源池整体可用时,使用上面拿到的osd的kb_avail
除以此处拿到的表中osd id对应的归一化结果,取得资源池原始的最大可用空间。
现在要根据当前配置的副本数来获得对于资源池来说可用的空间,在PGMapDigest::dump_pool_stats_full
函数中副本数变量为raw_used_rate
。
Replicated
类型,直接赋为资源池size
Erasure
类型,从erasure_code_profile
中获取到k
和m
,赋为\((k+m)/k\)最后直接得到资源池可用空间\(\frac{max_avail}{raw_used_rate}\)
在PGMapDigest
的成员变量mempool::pgmap::unordered_map<int32_t,pool_stat_t> pg_pool_sum
中保存着pool的pg的对应资源池中的使用量num_bytes
的和。该数据在ceph df detail
中进行汇总
可以用下面这个命令看到这些统计 1
ceph report
1 | struct pool_stat_t { |
12.2.13这部分函数似乎被重构了, update_osd_stat
没有入参了.
1 | void OSD::heartbeat() |
这里有个available
, 为什么压缩后, 这里显示的减去之后获取到的容量还是实际使用了compress_origin
的空间?
1 | struct store_statfs_t |
这里的available
是什么时候计算的?
1 | ceph daemon osd.1 pref dump | grep bluestore |
这里的容量分别是什么意思?
11.76-9.89=1.86g
这个compressed
容量只有在占用正式结束了才会统计? 在流式使用空间的过程中, osd_used在逐渐增加, 但是这个compress一直没变.
似乎这里把struct store_statfs_t stbuf
拿到的stbuf
给通过OSDService::set_osd_stat
给传过去了, 然后这里的值就是osd_stat
的计数器里的值了. 稍后就直接传给mon
了. 好像初始化的时候, osd默认就占用1.01G
空间?
num_bytes
似乎和osd_stat
里的used
和available
没什么关系?目前看到的现象是ceph osd df
里看到的osd的used
只有18.8G, 但是ceph df
看到的资源池的使用量是61.6G
, 3个osd, 2副本. 而我真实写入的文件大小是用/dev/zero
生成的21G文件+ 一堆之前的, ceph df
看到的是用户的操作
我关闭Compress之后, osd的used增加大概是3个G, 差不多欸, 我写的是10G对象, 拆到每个osd上. 理论上应该有7个G左右.
而那几个开了compress的大概是增加1个G.
sum.num_bytes
在哪里计算的呢?
PGMapDigest
下的public
成员变量mempoool::pgmap::unordered_map<int32_t,pool_stat_t> pg_pool_sum
. 应该是这里更新的. auto it2 = pg_pool_sum.find(it.first)
->const pool_stat_t *pstat
->const object_stat_sum_t& sum
void PGMap::update_pool_deltas(
这个有点像, 不过这里计算的应该是差值吧? pg_pool_sum
里的差值有哪些?应该不是
尝试搜索num_bytes =
, 搜了下, 没有, 那应该意味着是通过聚合性质的做的计算, 按照C++
的限制,应该是会通过字符串作为key的列表, 转换成对应的变量. emm, 只找到dump
时的字符串与原始计数器 和 一个mon这里的pcb.add_u64(
的任务, 这里的这个mon
的东西是全局统计, 通过PGMonitor::update_logger
这里更新.
啊, 忘了, 应该搜num_bytes +=
, 在PrimaryLogPG.cc
里出现了osd的统计ctx->delta_stats->num_bytes
, 在osd_types.cc
里也出现了操作符重载的void object_stat_sum_t::add
的操作.
然后publish_stats_to_osd
pg_info_t -> pg_stat_t -> pg_stats_publish -> osd_stat_updated = true -> send_pg_stats -> m->pg_stat[pg->info.pgid.pgid] = pg->pg_stats_publish;
这里会将变化的pg的stat_queue_item入队到pg_stat_queue中。然后设置osd_stat_updated为True。入队之后,由tick_timer在C_Tick_WithoutOSDLock这个ctx中通过send_pg_stats()将PG的状态发送给Monitor。这样Monitor就可以知道pg的的变化了。
mon哪里处理这个接收到的最新的pg数据呢? 搜索这个msg结构体中的.pg_stat
, 看到个下面这个, 是不是ClusterState::ingest_pgstats
呢, 的确是这个, 在DaemonServer::ms_dispatch
中调用这个更新的数据
在ClusterState
中定义了PGMap::Incremental pending_inc;
这里填充的pg_stat_updates
在比如PGMap::apply_incremental
的好多地方都直接处理了这个更新的数据. 这样我就不好找到底把df的内容存哪去了.
最后应该是存到了PGMap
里的`mempool::pgmap::unordered_map<pg_t,pg_stat_t> pg_stat里.
这个pgmap应该是哪里实例化的呢?
PGMonitor ? 这个的初始化的Monitor
在ceph_mon.cc
里,
似乎初始化ceph-mon
的时候只读取了version_t v = store->get("monmap", "last_committed");
?
那数据难道还是在osd上? 而且mon数据丢失似乎是可以恢复过来的.
通过ceph pg dump --format json-pretty是可以导出num_bytes
的, 而daemon osd.x perf dump
反而拿不到.
通过ceph-objectstore-tool
可以拿到OSD里存的pg
的信息
如何解密然后修改呢
export pg信息似乎走的是RadosDump
欸,如果我要修改某个值, 要停掉这个pg所在的所有osd?
根据[^9这个来看,其实还是遵守了kv数据结构的,只是好像在哪层被过滤掉了?
试了下, get-bytes拿到的是对象的内容
那么是否说明其实也是计算, 而不是累加的呢?
info这个数据似乎无从修改, 只有pg的元数据可以修改的样子?
这个疑似PGLog::IndexedLog, 不知道怎么改
按照现在看到的objects的问题, 是不是这个pg提供的objects指标全都是负数,所以上层统计的也都是
但是现在在数据库里list, list不出这个pg, 是代表什么呢?
我应该只能从代码到底是怎么list, 用的什么接口然后查不到这个pg来看吧?
用rados ls -p data02
, 因为这个资源池理论上其实不应该有任何object, 所以应该也是查不到东西, 但是实际存在了几个对象信息为负数的object.
以osd级别来显示object, 看看带不带pg的信息.
pg上面统计的信息, 感觉像是以object为单位处理的啊. 假如说真是以pg自己来存储自己的信息, 那么object和pg之间就存在歧义性了.
ceph pg dump显示的pg id不完整, 被坑了.
在正常情况下ceph osd df 看到的osd的used是跟ceph df看到的stored一致的, 但是这个数据是在osd这里实际存储的, 而ceph df却并不是
这里看到的osd的容量是
pgs->get_osd_sum().kb
根据[^8]里可以看到这个MonPGStatService
是ceph-mon
把内容转嫁到ceph-mgr
的中间层.
所以根据上面来看, 这边的容量并不是根据osd_stat
汇总的了, 而是pg
这层自己维护的?
根据[^7]
COW
引起的实际占用量较小sum.num_bytes
sum.num_bytes属于pool_stat_t
里的object_stat_collection_t stats
, 这个东西怎么从pg_map
里得到的?
在PGMap::calc_stats
里似乎pg_sum = pool_stat_t()
, 但是这个pool_stat_t
的构造函数里只是构造并初始化为0, emm, 只在decode
的时候被调用, 倒是的确没什么影响.
cephfs上删除太多文件, 出现了ceph df看到的used为EiB
的情况, 通过json
打印出来, 看到实际上统计的bytes
为负数, 引起的.
根据[^6], 好像说是和cephfs
文件系统有关系?
在osd out之后,该osd在2.4.1中提到的kb_avail
会变成0,因此可用容量不需要计算
\[max.avail = min(\frac{osd\_avail_0}{\frac{weight_0}{\sum_{i=0}^nweight_i}}, \frac{osd\_avail_1}{\frac{weight_1}{\sum_{i=0}^nweight_i}}, \dots, \frac{osd\_avail_j}{\frac{weight_j}{\sum_{i=0}^nweight_i}})/pool.size\]
需要开启object-map fast-diff
功能之后统计拿到的数据才是正确的,否则就会出现每层快照占用的容量都会比原始数据要大
根据[^4], 好像rbd
的discard
选项, 影响这个容量的问题.
1 | rbd diff rbd/zp | awk '{ SUM += $2 } END { print SUM/1024/1024 " MB" }' |
对容量无影响 ### osd out 在未达到min size阶段可继续使用的pool,只有osd正式out了,容量才会变更。
仅当被从crush中挪走或不存在in的osd,才会真的容量变化。
1 | import rados |
这里的conf=keyring主要用于当打开了cephx等认证措施时,ceph.conf中又没有记录认证所用到的keyring文件路径时,进行额外设置使用。
最近我在参照python的librados使用,调用C的接口时,就一直遇到cephx认证打开之后,无法成功rados_connect
的情况,具体原理还得细看,但应该和cephx及这个keyring存在强相关性是可以确认的了。
连接成功之后,主要使用下属这几个接口来查询pool
,osd
,pg
等一些信息。
1 | Rados.mon_command(self, cmd, inbuf, timeout=0, target=None) |
就目前而言,我在抓取IO等指标时,用的比较多的接口是mon_command
和mgr_command
,使用这几个接口能进行的操作,都有提供cli和rest接口。我主要是在使用cli熟悉接口之后,会去代码中的MonCommands.h
和MgrCommands.h
文件中去找相近的prefix,然后参照着使用。
但是偶尔也是会遇到像ceph osd pool stats
和ceph pg ls-by-pools
这类没怎么能找到相关prefix的情况。这个时候可以充分利用起ceph这个cli入口其实是个python文本的功能了。
python -m pgd /bin/ceph osd pool stats
执行,在new_style_command
函数内的json_command
处打断点,进入后,打印cmddict
就可以看到cli发送出去的prefix拼接出来是什么样子的了,然后再拿着这个去代码里搜就好了。
make vstart
似乎有加载client name.
parse_config_files似乎还有指定不读取任何配置文件的时候, 即便是默认配置文件
"cdef class Rados(object):"->"name = 'client.admin'"
context 是什么?
rados_create2
librados::RadosClient::connect理解
3c2b30e这次提交, get_monmap_and_config
中重构成10次retry, 而这个函数之前就是在oneshot monmap fdde016
中引入的monclient过程中get_monmap_and_config以及设置10倍的interval, 之前12版本不存在该项, 所以不会10倍超时.
这个提交13版本就开始有了.
这条commit关联的pr中有144条commit, 该条commit也说了只是解决了问题, 所以设计初衷并不算很清晰
1 |
|
在开发过程中,经验遇到使用不同语言不同工具时,正则表达式无法直接复用的问题。主要原因在于不同语言使用的正则表达式的标准也是不一样的。
流派 | 说明 |
---|---|
BRE | () {} + ? |都必须转义使用 |
ERE | 元字符不必转义, + ? ( ) { } |可以直接使用 |
PRE | 除了ERE支持的之外,还支持 |
BRE、ERE可以使用POSIX
字符集来操作
支持BRE,通过参数控制,默认BRE, -P
开启PRE, -E
开启ERE
支持BRE,默认BRE,-r
开启ERE
支持ERE,默认ERE。
c语言的regex根据man 3 regex
看到的内容来看,支持BRE和ERE.
另外由于C语言字符串处理的性质,一般使用元字符时,需要额外的转义符,如\s
python使用的是PRE,所以处理起来要相对C要简单不少
不包含kuebelet
的行 1
^((?!kubelet).)*$
在aaa
后面的字符串, 不捕获aaa. 1
(?<=aaa).*
C99编译器允许可变参数宏(variadic macros), GCC默认使用的标准是GNU89/90标准,这个标准在C89的基础上增加了一些C99的功能(就比如这个Variadic macro)[^3]。
1 | man gcc |
1 | gcc -E -dM - </dev/null | grep "STDC_VERSION" |
1 |
|
在这个版本中增加的宏__VA_ARGS__
前增加##
可以完成对逗号的处理,在没有传入额外参数时,gcc对代码进行预处理时,识别到##
会将逗号给去除。如果不加入这个,那在不传入额外参数时,就会编译错误。[^4]
1 |
|
Clion支持基于cmake的远程开发,最近用来在windows上开发linux程序时的高亮、提示、补全。相对以前开发时很多unix特定的头文件无法提示出来,现在要好得多了。
基本配置方式就是在perference->Build\Execution\Deployment->toolchains
里添加remote debug环境,也可以配置gdb server做远程调试。
如果新引入了动态库,需要重新让clion去下载一下依赖的库建立索引的时候,点击Tools->Resync with Remote Hosts
重新同步就可以了。
另外,配合去年开源的Sourcetrail来看代码感觉也不错,调用图索引的也还可以。而且sourcetrail在建立C/C++的索引的时候,也可以使用cmake导出的一个cdb成果物。
1 | cmake -DCMAKE_EXPORT_COMPILE_COMMANDS .. |
编译时增加上述选项就可以导出compile_commands.json这个文件,包含代码src路径和include路径、编译选项等等,用这种方式建立索引的时候就不会报错了。但是好像大部分时候看代码时也不是那么需要某些头文件的提示,缺失也影响不大。
但是在开发的时候大部分代码其实主要还是linux环境偏多,并不兼容MinGW的时候,就容易出现错误文件数量过多,影响阅读的时候。针对这种情况,issue里也给出了一个方法,suorcetrail提供了command line功能,可以在编译环境中, 完成索引,然后再把生成的数据库文件拖回本机打开工程,就能直接看到代码了。
Sourcetrail index <path/to/your/project.srctrlprj>
与小黑相关的主要人物线
小黑虽然是本片的主角,故事围绕着他发展,是本片营造的矛盾的中心,但主要出于一个提供矛盾点的作用。
在片中,在风息和无限两个阵营中处于一个阵营切换的地位。他在片中的成长点主要在于对于好坏的认知,逐步从狭义到广义,再到是相对的概念。 * 谁对我好,谁是好人(风息的第一次接触、无限的陪伴过程) * 好人的概念是不是广义绝对的?(无限回答为何抓风息,风息在整体上做了什么) * 好人是有立场的(最后决战完,风息失败了,从他理解风息的角度,风息还是个好人)
风息的理念主要是人妖平等,指的是妖与人的互不侵犯的平等,根据本片来看,风息的目的主要是实现妖不用再流浪。
风息曾提到人与妖的过去曾经是人不知道妖的存在、人将妖看作为神、到如今人已不知晓妖,妖的生存空间(自然)在逐渐被开发。可以看到,从风息的立场来说,妖是处于受害者的位置的。
然后,从本片开场风息控制人类、无限来抓风息来看,风息应该是仇视人类,甚至是有伤害人类的行为的。开场时,风息就已经不像会馆里人认知的那样,是为了妖的整体考虑的了,已经是为了自己、为了大部分的妖了,从这时,风息已经为了实现妖有自己不变的家园,可以接受自己承担路上伤害无辜者的罪孽这点了。后面所做的,小黑等等,已经不在他心中不可伤害了。
无限的立场是会馆中的人类,是处于人妖均衡的立场的。从我们的认知来看,会馆当前维持的局面是,大众不知晓妖的存在,从而实现的和谐相处下的均衡关系(当前应该是高层可以知晓的)。从未来发展的角度猜测,会馆应该是有在向可以让大众知晓的情况下,人与妖还能和谐发展的均衡关系发展的。
所以无限开场,试图捕获风息,让风息去会馆冷静一下这点没有任何问题。至于抓错了小黑,在找不到风息时,将小黑顺便送回会馆是一个顺便的事情。在带回去的过程中,和小黑培养感情(这个的节奏就有些缓慢,也有TV版治愈的感觉了)。在带回去的过程中,小黑被抓走,甚至被抢走领域而生命垂危,这就是无限个人情感上不可接受的点了,无限冲入被风息控制的灵质空间也就有原因了。
最终,制止了风息,小黑救回来,然后抵达神秘的会馆。小黑因与无限的羁绊确立师徒关系算是一个圆满。
就像上面列的3个角色的发展那样,故事的发展应该主要围绕着双方立场和行为矛盾点展开。其外的内容比例一般应该是有所控制。但是电影中有一点虽然作为粉丝看着很开心,作为非观众的话,可能觉得节奏有问题的一部分,无限带小黑回会馆的这部分日常的故事这段,占用篇幅过长。因为从推进故事的角度来看,应该是没有那么重要。可以用于扩充其他角色的角度来阐述更多的背景或是矛盾点,可能会显得更好。不过这样小黑的出场可能就少了些许。
]]>首先,将next主题中一直用的addthis统计与分享功能引入。
官网上很简单,就是一行html和js,不过似乎在我使用时,可能排版上与js中携带的addthis模块排版冲突了,导致没能显示出来
主要的问题在于, 当写的Tag总计多了时, 观察不到哪些tag
的文章比较多
当时初步想的是hexo-tag-cloud
或者是雷达图效果.
不过想了下, 这方面如果更换, 可能css也得换, 修改较多.
试试看能不能直接在tag名字后面加一个统计数字.
这个API
里理论上应该有提供
翻了下代码, 感觉layout/tags.ejs
有点像显示的代码, 翻到个函数is_tag
, 全局没搜到, 那应该代表着这里的一些信息是从hexo或者hexo
的插件里获取到的.
的确,根据Helpers | Hexo, 在这里找到了这个函数.
对, 和页面上的id
, class
比对了下, 的确这里是正文中的tag
和post
显示的位置.
1 | <% |
开启debug
模式, hexo s --debug
实验了下, 还是header
处增加统计数字更明显一些. tags-bar.ejs
文件
1 | <a href="<%- url_for(tag.path) %>" style="-webkit-order:<%= order%>;order:<%= order%>" class="tags-list-item waves-effect waves-button waves-light<% if(is_current(tag.path)){%> active<%}%>"><%-tag.name%></a> |
增加了sup
选项, 加了个角标,看起来还可以 1
<a href="<%- url_for(tag.path) %>" style="-webkit-order:<%= order%>;order:<%= order%>" class="tags-list-item waves-effect waves-button waves-light<% if(is_current(tag.path)){%> active<%}%>"><%-tag.name%><sup><%-tag.length%></sup></a>
相对来说, 还是不太直观. 看下面这段代码, 看起来是在指定Tag在这个顺序中的位置.
1 | var options = []; |
webkit-order
的作用是什么呢... 理解错了, 这个其实还是为了布局用的.
\{\{\}\}
, 又没有被转义, 是会转义失败, 报下面的错误的.1 | Template render error: (unknown path) [Line 208, Column 5] |
首先就拿Counter
类个例子
1 | a = [10, 8, 6, 7, 2, 8, 4, 10, 3, 7, 8, 4, 5, 7, 2, 2, 3, 8, 8, 9, 6, 2, 2, 7, 8, 7, 4, 8, 5, 2] |
有4种方式,逐渐演变出来,内建函数就支持了计数功能
1 | import cProfile |
像这样就不需要再像下面这样手动写了 1
2
3
4
5
6start = time.time()
....
stop = time.time()
total = stop - start
memory_profiler
1 | from memory_profiler import profile |
1 | import configparser |
读取无section名的常用linux conf文件
1 | import configparser |
1 | with open('temp.xml', 'r') as f: |
1 | import json |
1 | from flask import Flask |
Python默认是前缀语法,是下面这么多个括号嵌套 1
sum(select(where(take_while(fib(), lambda x: x < 1000000) lambda x: x % 2), lambda x: x * x))
1
2
3
4
5
6
7from pipe import *
fib() | take_while(lambda x: x < 1000000) \
| where(lambda x: x % 2) \
| select(lambda x: x * x) \
| sum()
在这个背景下,我考虑设置一个数据库里的自增主键,之后这部分就可以自动生成了。
但是在我使用的ORM_LITE中没有提供这样的属性,那么是不是这个属性不推荐使用呢?
找到了下面这篇文章SQLite AUTOINCREMENT : Why You Should Avoid Using It
下文是这篇的翻译。
无论何时,创建表时不指定WITHOUT ROWID
选项,都会得到一个名为rowid的隐式自动增量列。
rowid列存储64位有符号整型,用于唯一标识表中的行。
我们来看下面的例子。
首先,创建一个包含两列first_name, last_name
的新表people
:
1 | CREATE TABLE people ( |
其次,用以下INSERT
语句插入people
一行: 1
2
3INSERT INTO people (first_name, last_name)
VALUES
('John', 'Doe');
第三,用以下SELECT
语句从people
中查询数据 1
2
3
4
5
6SELECT
rowid,
first_name,
last_name
FROM
people;
所以,SQLite会自动创建一个名为rowid的隐式列,并 在您插入新行时自动分配一个整数值。
可以通过rowid
的两个别名_rowid_
和oid
来使用它
如果创建具有INTEGER PRIMARY KEY
列的表,则该列指向rowid
列。
以下语句删除people
表并重新创建它,不过这一次,我们添加另一列带有INTEGER PRIMARY KEY
属性的名为person_id
的列。
1 | DROP TABLE people; |
现在person_id
列实际上就是rowid
列。
那么,Sqlite如何分配一个整型值给rowid列呢?
如果插入一个新行时,你没有指定一个rowid值或者使用NULL
值, Sqlite会分配一个比表中最大的rowid大1的整型。 当还没有插入任何行时, rowid是1。
首先,插入带有最大值的一行插入people
表。
1 | INSERT INTO people ( |
第二,插入不指定person_id
的另一行
1
2
3
4
5
6
7
8
9 INSERT INTO people (
first_name,
last_name
)
VALUES
(
'William',
'Gate'
);
SQLite 推荐你不应该使用AUTOCREMENT
属性,因为:
The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and disk I/O overhead and should be avoided if not strictly needed. It is usually not needed.
AUTOINCREMENT关键字会产生额外的CPU,内存,磁盘空间和磁盘I/O开销,如果不是严格需求,应该避免使用。通常不是必须的。
此外,SQLite为AUTOCREMENT
列分配值的方式与rowid
列的使用方式略有不同。
请参阅以下示例。
首先,再次删除并重新创建人员表。这次,我们使用AUTOINCREMENT
属性。
1 | DROP TABLE people; |
其次,将具有最大行id值的行添加到people表中。
1 | INSERT INTO people ( |
第三,在people表中插入另一行
1 | INSERT INTO people ( |
这次,SQLite发出了一条错误消息:
1
[Err] 13 - database or disk is full
因为它不会重新使用未被使用数字。
AUTOCREMENT
这个属性的主要目的是防止SQLite复用还未使用的值或者使用先前删除的行
如果还没有任何像这样的需求,那么你就不应该在主键中使用SQLite AUTOINCREMENT
属性。
在这篇教程中,你已经学习到AUTOINCREMENT
属性如何运作的,以及它如何影响SQLite
分配值给主键的方式。
# Reference 1. SQLite AUTOINCREMENT : Why You Should Avoid Using It
]]>众所周知,小学的时候都有讲过湿毛巾捂笔过滤有毒气体。还有一些摸墙跑,下层走不了就上楼之类的各种常识。不过具体问起发生火灾时,我们应该怎么判断,自己该怎么行动,这个流程倒是没有那么清晰了。
下面来捋捋这个流程。
根据知乎大佬们所说,理论上,我们能做到的自救方式,就是冷静,并能向下冲就冲,一旦被困,就只有祈祷高层防火措施完善,自动喷淋装置等能够加快消防员们灭火的措施齐全。一旦公寓防火有不完善的操作,被困就很难人力突破了。
我们实地考察了以下,大约以下几点 * 屋外走廊是否有自动喷淋装置 * 屋外走廊是否有灭火器等防火措施 * 每层的防火通道是否有2个,并且是否独立不连通
所幸这几点这个屋子都是具备的,让我心里踏实了不少。
使用libvirt管理kvm, 需要在不知道虚拟机ip的情况下,与虚拟机进行交互。
翻了翻,qemu有提供一个qemu-guest-agent的工具,在虚拟机内安装后就可以让宿主机获取虚拟机内的信息了。
根据redhat的指导[1], 在rhel7的系统的虚拟机xml里插入以下内容
1 | <channel type='unix'> |
原理上,上面这部分为虚拟机添加了一个叫做org.qemu.guest_agent.0
的串口,而在宿主机上则是在路径/var/lib/libvirt/qemu/channel/target/<domain-6-kvm01>/org.qemu.guest_agent.0
创建了一个unix socket。
不过为什么要在虚拟机内建立的是串口呢?而不是和宿主机一样的unix socket呢?
虚拟机内安装这个包之后,启动这个服务就可以了。 1
2
3yum install qemu-guest-agent
systemctl start qemu-guest-agent
systemctl enable qemu-guest-agent
这里需要注意,默认安装的qga的service文件中的串口名都是默认的guest_agent.0,如果在指定时修改了这个,手动指定了bind socket路径和target,那么虚拟机内的服务文件就需要替换这些默认名。并且,宿主机就监听不到我们所使用的端口了,需要手动使用下面的socat命令监听了。
然后宿主机,有3种 1. 执行socat /var/lib/libvirt/qemu/channel/target/<domain-6-kvm01>/org.qemu.guest_agent.0 readline
,在这里交互式输入json格式的命令可以得到结果 2. virsh qemu-agent-command kvm_instance '{"execute":"guest-network-get-interfaces"}' 3. 也可以通过qemu-guest-agent提供的api执行命令,不过似乎要添加他的so包,没尝试
1 | {"execute": "guest-info"} |
如果想要在虚拟机内执行没有默认提供的命令,就需要编辑qga源码,自己添加了。当时还以为是提供的一个类似配置文件的方式进行扩展,然后实际上是自己修改源码,扩展。
去github上svn checkout https://github.com/qemu/qemu/trunk/qga
拉下这个文件夹,参考[4]操作编译就好了。
如果只是需要执行shell脚本的话,参照[3],似乎可以通过上面的方法写入到fsfreeze-hook.d中,然后fsfreeze-hook来执行。
1 | virsh qemu-agent-command instance '{"execute":"guest-fsfreeze-freeze"}' |
不过也可以通过上面的network interface得到ip之后通过socket或者rpc等等自己定制接口来执行就是了。
偶然间看到带我的导师用的anydesk连的家里的电脑,推动了我马上开始捣鼓远程桌面。
在学校内网时,主要直接使用RDP来访问,现在都被NAT过了,那么就只有一些通过服务器来中转的软件可以使用了,第一反应就是teamviewer,不过经过这个月的摸索,公司对http请求都会ban掉,对于一些存在过风险的软件恐怕更加会ban了,果不其然,teamviewer的请求都是被拒绝了的。
简单了解,anydesk基本功能等同teamviewer,简单安装尝试,效果很棒,第一天从第一次连接到晚上下班,连接一直都没有断开。
只是第二天就是噩梦的来临,anydesk连接的持续时间不足5分钟就会断开,当时曾以为是公司网络的限制,回到家后同样如此,那么猜测可能是个人License的区别了,官网看了下,个人使用版本年费79刀,才能保证session。唉,只好选择其他方式了。
那么,就回归看上去最费事的frp穿透了。
配置说简单也简单,但是在像公司网络限制的地方,就难以确定可能出现的问题原因究竟是配置问题还是网络问题了。 1
2
3wget https://github.com/fatedier/frp/releases/download/v0.20.0/frp_0.20.0_linux_amd64.tar.gz
tar -zxvf frp_0.20.0_linux_amd64.tar.gz
cd frp_0.20.0_linux_amd64/
服务端基本可以不改配置,直接启动./frps -c ./frps.ini
也行 1
2
3# frps.ini
[common]
bind_port = 7000
客户端,按照需求从full.ini中选取需要的就可以了,比如rdp,启动方式同上 1
2
3
4
5
6
7
8
9
10# frpc.ini
[common]
server_addr = X.X.X.X
server_port = 7000
[RDP]
type = tcp
local_ip = 0.0.0.0
local_port = 3389
remote_port = 6000
两个都连上以后就可以看到start proxy success
的成功信息。
不过成功连上以后,性能却不甚理想……远比anydesk使用时卡顿的多,看来还需要探索其他方式。
如果不是使用rdp,只需要ssh,可能还是非常不错的。
需要后台的话,使用systemd管理的方式的,在/lib/systemd/system/
里添加一个service文件
1 | [Unit] |
上面这样基本就可以了,注意执行命令必须都是绝对路径
systemd的基本控制命令如下 1
2
3systemctl daemon-reload
systemctl restart frps
systemctl enable frps # 将frps作为开机启动项
目前来看,lsof只能查看该进程监听的socket,不显示它发送的socket。通过strace追踪,RecMsg会显示发送方的进程pid,从那里可以看到是哪个进程发送到自己监听的socket的信息。
1 |
|
见上图,可知默认rsyslog.conf中启动了imuxsock与imjournal两个模块,分别会通过不同的渠道获得syslog日志,因此会导致重复。配置方法详见下章配置信息对应项。
rsyslog的imjournal模块读取数据库有一个频率上限设置,而systemd-journald也有一个数据库读取频率上限设置。满足rsyslog频率上限,messages中就会drop日志;满足systemd-journald上限,journald就会miss日志。配置方法详见下章配置信息对应项。
1 | rsyslogd: imjournal: 84667 messages lost due to rate-limiting |
当前测试似乎是在/var/log/messages被移动或者被删除或者被轮转了导致的这个问题
在logrotate那里执行了达到100M就轮转的功能,会删除.1文件然后备份
但是这边的现象还是有点怪,在我关闭那个特别高频的日志写入之后,就会更新最新的了,但这是为什么呢?
这里的messages是在/b_iscsi/log/messages里的,这块的设置倒是和我没太大关系,不过原因还是得测试一下,在自己的53.31的/var/log/messages里测试是没什么问题的。
通过lsof,并且从logrotate那里删除自动备份的操作,结果发现,rsyslog还是会出现被删除的情况,但是没找到哪里触发的,
logrotate会在100M时删除 sys_space这个进程会在messages达到110M时删除
Aug 29 17:02:28 localhost syslog: [do_record_log_t:1013] get log info error
目前更换回/var/log/messages,没有出现被删的情况,但是每满130M会出现一次rsyslog不再读取journal的问题,需要移除那个快速写入的程序才能接着更新
看了下卡死的那个时候,rsyslog的lsof显示并不是像我之前想的那样,读取的全都是Deleted文件,反而有些是正常的文件。
刚才试了一下,top里可以看到rsyslog卡死之后读取了30M缓存。
欸,但是命名rsyslog是直接读取systemd-journald数据库啊,哪里有缓存的位置
数据库文件大小上限设置为2T,在尚未抵达文件大小上限时,出现了间歇的丢失日志
1 | Aug 7 15:53:23 localhost journal: Missed 68 kernel messages |
目前在设置了日志上限的情况下,并没有出现数据库卡死,都是在覆写之前的日志。
在公司镜像上测试,发现,在volatile状态下的日志,和在persistent状态下,同样会自动覆写之前的日志。
仅当journald日志大小达到该分区上限时,目前测试为当RunMaxUse大于分区可用空间时,会导致日志卡死。
这个问题在[7]中被提到,主要时由于systemd-journald正在轮转数据库文件,因此导致数据库文件变动,所以会出现这个reload日志。
根据[8]中添加的这个日志信息,可以看到是为了在journald切换文件位置时,为了不用重启rsyslog而添加的自动切换。
经过测试,journal日志每被切割一次,都会产生一个reloaded信息(日志level是info,不是Error,所以可以忽视)
参照[11],这个socket似乎在systemd设备上,是由systemd-journald.socket提供, 如果是单独的rsyslog的日志管理下,则是由imuxsock插件创建,
Normally, with rsyslogd, the imuxsock module will create the /dev/log socket on its own, unlinking the previous entry before creating it. When rsyslogd is stopped (possibly because restart which fails because of faulty configuration), rsyslogd removes /dev/log.
However, the rsyslog supplied with RHEL7 is expected to be used in conjunction with systemd, and the imuxsock module will actually open and remove /run/systemd/journal/syslog socket. Meanwhile, the /dev/log device is created by the system service-file systemd-journald.socket which triggers journald.
在systemd-journald.socket这个服务的Unit文件里是这么写的
1 | [root@localhost ~]# less /lib/systemd/system/systemd-journald.socket |
这里可以看到监听的/dev/log端口创建是由这个服务管理的。
暂时无法复现
理论上来说,应该是journald连接写入到数据库被阻断了,数据库无法访问导致的问题。
不过应该仅针对journalctl读取时的问题
欸,我试下了,挂载上后,新的日志就看不到了,而卸载掉后,这次启动的日志文件还是在的。
在测试中,新挂载的盘中与systemd-journal并没有建立连接,在lsof中看到的该路径下的文件是现在已经被隐藏了的目录,通过stat
查看了文件inode,的确如此。
1 | systemd-j 432 root 17u REG 8,1 8388608 531238 /root/log/journal/abf6c3e0a96f452ab2efd6c2d1a9c1e0/system.journal |
该模块导入监听本机syslog socket的功能,从syslog中接收日志,该配置项依赖systemd-journald.conf中的ForwardToSyslog=Yes
该配置信息测试结果与官网有所区别,测试中指定socketName为/dev/log(可修改),系统默认指向socket为/run/systemd/journal/syslog。使用详见下表。
1 | # 显式关闭 OmitLocalLog,未指定socketName || 非显式关闭,未指定socketName |
由上述可知,在不同情况下,rsyslog实际监听的socket如下
x | 显式开启 | 显式关闭 | 非显式开启 |
---|---|---|---|
指定socket | null | /dev/log | /dev/log |
不指定socket | null | /run/systemd/journal/syslog | /run/systemd/journal/syslog |
该模块导入直接读取systemd-journald数据库的功能,可直接从journald数据库中读取syslog日志、内核日志以及服务stdout。
imjournal在lsof中可以看到,直接读取的数据库 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89[root@localhost ~]# lsof /run/log/journal/a288ec3729494d3dad642453d0b272b7/system.journal
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd-j 10247 root mem REG 0,19 25165824 7373149 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system.journal
systemd-j 10247 root 12u REG 0,19 25165824 7373149 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system.journal
rsyslogd 10255 root mem REG 0,19 25165824 7373149 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system.journal
rsyslogd 10255 root 5r REG 0,19 25165824 7373149 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system.journal
[root@localhost ~]# lsof -c systemd-journal
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd-j 10247 root cwd DIR 253,0 224 64 /
systemd-j 10247 root rtd DIR 253,0 224 64 /
systemd-j 10247 root txt REG 253,0 274752 16874824 /usr/lib/systemd/systemd-journald
systemd-j 10247 root mem REG 0,19 25165824 7373149 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system.journal
systemd-j 10247 root mem REG 253,0 19888 87389 /usr/lib64/libattr.so.1.1.0
systemd-j 10247 root mem REG 253,0 402384 87259 /usr/lib64/libpcre.so.1.2.0
systemd-j 10247 root mem REG 253,0 19776 61006 /usr/lib64/libdl-2.17.so
systemd-j 10247 root mem REG 253,0 19384 87371 /usr/lib64/libgpg-error.so.0.10.0
systemd-j 10247 root mem REG 253,0 2127336 61000 /usr/lib64/libc-2.17.so
systemd-j 10247 root mem REG 253,0 144792 72706 /usr/lib64/libpthread-2.17.so
systemd-j 10247 root mem REG 253,0 88720 84 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
systemd-j 10247 root mem REG 253,0 44448 72710 /usr/lib64/librt-2.17.so
systemd-j 10247 root mem REG 253,0 37056 87391 /usr/lib64/libacl.so.1.1.0
systemd-j 10247 root mem REG 253,0 155744 87307 /usr/lib64/libselinux.so.1
systemd-j 10247 root mem REG 253,0 535064 87381 /usr/lib64/libgcrypt.so.11.8.2
systemd-j 10247 root mem REG 253,0 157424 87316 /usr/lib64/liblzma.so.5.2.2
systemd-j 10247 root mem REG 253,0 164264 60993 /usr/lib64/ld-2.17.so
systemd-j 10247 root mem REG 0,19 8 7972 /run/systemd/journal/kernel-seqnum
systemd-j 10247 root 0r CHR 1,3 0t0 5423 /dev/null
systemd-j 10247 root 1w CHR 1,3 0t0 5423 /dev/null
systemd-j 10247 root 2w CHR 1,3 0t0 5423 /dev/null
systemd-j 10247 root 3u unix 0xffff880037e83000 0t0 42542 /run/systemd/journal/stdout
systemd-j 10247 root 4u unix 0xffff880037e81400 0t0 42544 /run/systemd/journal/socket
systemd-j 10247 root 5u unix 0xffff880037e80800 0t0 42546 /dev/log
systemd-j 10247 root 6w CHR 1,11 0t0 5429 /dev/kmsg
systemd-j 10247 root 7u a_inode 0,9 0 5419 [eventpoll]
systemd-j 10247 root 8u a_inode 0,9 0 5419 [timerfd]
systemd-j 10247 root 9u CHR 1,11 0t0 5429 /dev/kmsg
systemd-j 10247 root 10r REG 0,3 0 7973 /proc/sys/kernel/hostname
systemd-j 10247 root 11u a_inode 0,9 0 5419 [signalfd]
systemd-j 10247 root 12u REG 0,19 25165824 7373149 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system.journal
systemd-j 10247 root 13u a_inode 0,9 0 5419 [timerfd]
[root@localhost ~]# lsof -c rsyslog
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
rsyslogd 10255 root cwd DIR 253,0 224 64 /
rsyslogd 10255 root rtd DIR 253,0 224 64 /
rsyslogd 10255 root txt REG 253,0 663960 961 /usr/sbin/rsyslogd
rsyslogd 10255 root mem REG 0,19 25165824 307917 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system@cea802f344b94cb6b1e2b867690eca83-0000000000000f6d-0005722d8a52e79b.journal
rsyslogd 10255 root mem REG 0,19 25165824 2282729 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system@cea802f344b94cb6b1e2b867690eca83-00000000000074f5-0005722d9bdab060.journal
rsyslogd 10255 root mem REG 0,19 25165824 4612418 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system@cea802f344b94cb6b1e2b867690eca83-000000000000dcd4-0005722db4fed7dd.journal
rsyslogd 10255 root mem REG 0,19 25165824 7373149 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system.journal
rsyslogd 10255 root mem REG 0,19 6455296 7976 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system@cea802f344b94cb6b1e2b867690eca83-0000000000000001-000571ff63cd3044.journal
rsyslogd 10255 root mem REG 253,0 68192 87353 /usr/lib64/libbz2.so.1.0.6
rsyslogd 10255 root mem REG 253,0 99944 87368 /usr/lib64/libelf-0.168.so
rsyslogd 10255 root mem REG 253,0 402384 87259 /usr/lib64/libpcre.so.1.2.0
rsyslogd 10255 root mem REG 253,0 19888 87389 /usr/lib64/libattr.so.1.1.0
rsyslogd 10255 root mem REG 253,0 297328 115274 /usr/lib64/libdw-0.168.so
rsyslogd 10255 root mem REG 253,0 111080 72708 /usr/lib64/libresolv-2.17.so
rsyslogd 10255 root mem REG 253,0 19384 87371 /usr/lib64/libgpg-error.so.0.10.0
rsyslogd 10255 root mem REG 253,0 535064 87381 /usr/lib64/libgcrypt.so.11.8.2
rsyslogd 10255 root mem REG 253,0 157424 87316 /usr/lib64/liblzma.so.5.2.2
rsyslogd 10255 root mem REG 253,0 155744 87307 /usr/lib64/libselinux.so.1
rsyslogd 10255 root mem REG 253,0 1139680 61008 /usr/lib64/libm-2.17.so
rsyslogd 10255 root mem REG 253,0 20032 87393 /usr/lib64/libcap.so.2.22
rsyslogd 10255 root mem REG 253,0 25072 9665 /usr/lib64/rsyslog/imjournal.so
rsyslogd 10255 root mem REG 253,0 24520 9672 /usr/lib64/rsyslog/lmnet.so
rsyslogd 10255 root mem REG 253,0 2127336 61000 /usr/lib64/libc-2.17.so
rsyslogd 10255 root mem REG 253,0 88720 84 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
rsyslogd 10255 root mem REG 253,0 20040 87327 /usr/lib64/libuuid.so.1.3.0
rsyslogd 10255 root mem REG 253,0 40824 322855 /usr/lib64/libfastjson.so.4.0.0
rsyslogd 10255 root mem REG 253,0 15424 322847 /usr/lib64/libestr.so.0.0.0
rsyslogd 10255 root mem REG 253,0 44448 72710 /usr/lib64/librt-2.17.so
rsyslogd 10255 root mem REG 253,0 19776 61006 /usr/lib64/libdl-2.17.so
rsyslogd 10255 root mem REG 253,0 144792 72706 /usr/lib64/libpthread-2.17.so
rsyslogd 10255 root mem REG 253,0 90664 87310 /usr/lib64/libz.so.1.2.7
rsyslogd 10255 root mem REG 253,0 164264 60993 /usr/lib64/ld-2.17.so
rsyslogd 10255 root mem REG 253,0 162560 237027 /usr/lib64/libsystemd.so.0.6.0
rsyslogd 10255 root 0r CHR 1,3 0t0 5423 /dev/null
rsyslogd 10255 root 1w CHR 1,3 0t0 5423 /dev/null
rsyslogd 10255 root 2w CHR 1,3 0t0 5423 /dev/null
rsyslogd 10255 root 3r a_inode 0,9 0 5419 inotify
rsyslogd 10255 root 4u unix 0xffff880037338800 0t0 9404081 socket
rsyslogd 10255 root 5r REG 0,19 25165824 7373149 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system.journal
rsyslogd 10255 root 6r REG 0,19 25165824 4612418 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system@cea802f344b94cb6b1e2b867690eca83-000000000000dcd4-0005722db4fed7dd.journal
rsyslogd 10255 root 7r REG 0,19 25165824 2282729 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system@cea802f344b94cb6b1e2b867690eca83-00000000000074f5-0005722d9bdab060.journal
rsyslogd 10255 root 8r REG 0,19 25165824 307917 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system@cea802f344b94cb6b1e2b867690eca83-0000000000000f6d-0005722d8a52e79b.journal
rsyslogd 10255 root 9r REG 0,19 6455296 7976 /run/log/journal/a288ec3729494d3dad642453d0b272b7/system@cea802f344b94cb6b1e2b867690eca83-0000000000000001-000571ff63cd3044.journal
rsyslogd 10255 root 10w REG 253,0 49250519 17152683 /var/log/messages
rsyslogd 10255 root 11w REG 253,0 4634 17152684 /var/log/secure
该属性设置为5s,代表以5s为一个间隙统计日志频次,达到下一条配置设置的频次,即放弃读取journald数据库信息(待验证是读取前放弃,还是读取后丢弃)。
该属性设置为上一条设置的间隙期间读取日志条数上限,如5s内读取1000条,达到该频次即停止。与上一条同时设置为0,即关闭该上限
Note that it is not recommended to turn of ratelimiting, except that you know for sure journal database entries will never be corrupted. Without ratelimiting, a corrupted systemd journal database may cause a kind of denial of service (we are stressing this point as multiple users have reported us such problems with the journal database - information current as of June 2013).
但是官方并不建议关闭该上限,可能会导致数据库阻塞等问题导致其他服务出现异常。
该模块导入直接从平台内核中读取内核日志的功能,可以避过journald数据库读写性能瓶颈。
该模块通过/dev/kmsg设备,获取结构化日志
导入该模块,并打开防火墙放行,可远程访问该主机获取日志信息
导入该模块,并打开防火墙放行,可远程访问该主机获取日志信息
rhel7系统中,一般log默认保存在下述目录,/var/log 目录保管由rsyslog维护的各种特定于系统和服务的日志文件。
/var/log/messages大多数系统日志消息记录在此。例外是与身份验证,电子邮件处理相关的定期运行作业的消息以及纯粹与调试相关的信息。 /var/log/secure安全和身份验证相关的消息和错误的日志文件。 /var/log/maillog与邮件服务器相关的日志文件。 /var/log/cron crond计划任务的日志 /var/log/boot.log与系统启动相关的消息记录在此。
建议不直接修改rsyslog.conf的规则,在这个目录$IncludeConfig /etc/rsyslog.d/*.conf
下存放自定义的转发规则
1 | :msg, contains, "of user root" ~ |
转发过滤规则,见文档
暂未使用的远程访问日志配置信息,语法见Legacy Action-Specific Configuration Statements
systemd-journald主要获得以下信息 * Kernel log messages, via kmsg * Simple system log messages, via the libc syslog(3) call * Structured system log messages via the native Journal API, see sd_journal_print(4) * Standard output and standard error of service units. For further details see below. * Audit records, originating from the kernel audit subsystem
频率间隙为30s
每个间隙频次上限为1000,直到下个间隙不会接收日志
该配置选项控制journald日志的存储位置,以下4个选项"volatile", "persistent", "auto" and "none",对应/run/log/journal
,/var/log/journal
,根据/var/log/journal
目录建立与否判断,收到即drop,但转发forward还是生效的(如imjournal等读取数据库文件等方式无效)。
设置内存中journald文件上限,一旦达到该上限,journald数据库就会阻塞住。
RuntimeKeepFree表示需要保留的内存空间,剩余空间不足其设置,journald数据库同样会阻塞住。
SystemMaxUse= 与 RuntimeMaxUse= 的默认值是10%空间与4G空间两者中的较小者; SystemKeepFree= 与 RuntimeKeepFree= 的默认值是15%空间与4G空间两者中的较大者; 如果在 systemd-journald 启动时, 文件系统即将被填满并且已经超越了 SystemKeepFree= 或 RuntimeKeepFree= 的限制,那么日志记录将被暂停。 也就是说,如果在创建日志文件时,文件系统有充足的空闲空间, 但是后来文件系统被其他非日志文件过多占用, 那么 systemd-journald 只会立即暂停日志记录, 但不会删除已经存在的日志文件。
SystemMaxFileSize= 与 RuntimeMaxFileSize= 限制单个日志文件的最大体积, 到达此限制后日志文件将会自动滚动。 默认值是对应的 SystemMaxUse= 与 RuntimeMaxUse= 值的1/8 , 这也意味着日志滚动默认保留7个历史文件。
日志大小的值可以使用以1024为基数的 K, M, G, T, P, E 后缀, 分别对应于 1024, 1024², … 字节。
转发syslog日志到syslog socket,从而使rsyslog调用imuxsock从该socket接收日志
该选项可被内核引导选项覆盖systemd.journald.forward_to_syslog=, systemd.journald.forward_to_kmsg=, systemd.journald.forward_to_console=, systemd.journald.forward_to_wall=
,允许/禁止将收集到的日志: 转发到传统的 syslog 守护进程, 转发到内核日志缓冲区, 转发到系统控制台, 作为wall警告信息转发给所有已登录的用户
以上配置控制在数据库上存储以及转发的最大日志等级。
日志等级一共分为"emerg", "alert", "crit", "err", "warning", "notice","info", "debug"
设置进程的标准输出(STDOUT)。 可设为 inherit, null, tty, journal, syslog, kmsg, journal+console, syslog+console, kmsg+console, socket, fd 之一。
如果单元的标准输出(StandardOutput=)或标准错误(StandardError=)中含有 journal, syslog, kmsg 之一, 那么该单元将会自动隐含的获得 After=systemd-journald.socket 依赖(见上文)。
仅对持久化后的/var/log/journal有效
持久化保存journal的日志,默认保存一个月的日志
直接修改journald.conf中的storage为persistent就切换到var路径下了,切换到volatile就自动回/run/log了。 1
2systemctl restart systemd-journald.service
systemctl restart systemd-journald.socket
如果出现了切换到persistent状态下,日志已经存到了/var/log/journal,但是/run/log/journal路径依旧存在的状况的话,可能是自动切换有些不同。就手动将volatile修改成auto,手动mkdir /var/log/journal
,这样再重启比较适合防丢日志。
在切换前,为了防止journal数据库文件大小刚好超过设置的上限,然后由于重启了服务,没能及时自动清理掉超过的部分,从而导致数据库假死,建议使用journalctl --vacuumm-size=250M
,可以清除日志直到满足这个大小限制。不过这个要求sytemd版本318及以上才支持这个选项。
如果版本不支持的话,那就还是rm -rf
掉这些日志或者手动删掉一些数据库文件吧,升级systemd的版本似乎带来的风险相比这些日志的价值要大得多。[12]
1 | # 可以让rsyslogd 进入 Debug模式 |
/dev/log
in systemd+rsyslog host?它挺过了我搬家的时候,没料到在偶然一天,合盖的时候可能被后侧的数据线卡到的,外屏裂痕扩大,内屏也多黑了1/4,这下,真没办法使用了。
首先,考虑到维修水深,转眼可能就会偷换零件,或者修坏什么,因此首选是有淘宝店的实体维修店。然而,结果很可惜,在上海的时候看到的评论比较真实的好评店铺,定位在北京以后,搜到的几近所有好评店铺,报价在1800以下的,咨询后给出的地址都是中关村……
既然这样,那就咨询以下v站大佬,看看有没有什么好消息。在apple分区发了2天,只有一个大佬回复了,经搜索得到的结果有些可惜,报价2200up,其他几家越是名声好的,都报价在2000+。
最后,只好进行第3个选择,自己买材料,更换。由于事前有搜到过有大佬自己更换过屏幕总成,这种不仅是内屏损坏的只有一条路,直接更换总成,这样的技术要求反而更低,很适合个人自己操作。
这条选择一波三折,公司前辈说有工具,然后都没有5角梅花的螺丝头,哎,迫于快速修理的急切心情,下楼找了个修理店借了波工具,结果螺丝刀头磨损太严重,特别费事。花了2个半小时,换完总成,结果老板下班了收走了合盖用的螺丝刀。走的时候一手手机,一手换下来的屏幕,恰恰忘了还没装上的小螺丝。
用胶带撑到了第二天小米的如风达螺丝刀抵达,一大早顶着被楼下8点的炮竹惊醒的困顿,去店里取回螺丝,成功装好。
总的来说,1400二手屏幕+100小米螺丝刀+2个半小时,还是挺不错的。
使用上,颜色稍稍感觉和以前有些差异,在外接屏幕时,似乎被系统也识别成外接的了,不能像原来那样单独修改一个屏幕的分辨率了。
]]>KVM主要硬盘镜像有qcow2和raw两种类型。
qcow2格式是copy on write的,使用时才占用硬盘空间,而raw则是创建时即分配了。
使用ll -h
和du -h
可以看到实际分配的和磁盘占用的大小。
1 | qemu-img convert -f qcow2 -c -O qcow2 xx.qcow2 xx_compressed.qcow2 |
通过上述命令可以实现qcow2格式的压缩,但是无法更改硬盘初始创建时上限的大小。只有raw格式可以减小上限,一开始以为只有上限压缩了以后,才能把这样的镜像放到较小的硬盘分区里。
首先尝试了 1
2
3qemu-img convert -f qcow2 -O raw xxx.qcow2 xxx.raw
qemu-img resize -f raw xxx.raw -10G
qemu-img convert -f raw -O qcow2 xxx.raw xxx.qcow2
如果要这样做,就需要首先让系统分区进行压缩,如使用win7的磁盘管理进行压缩卷操作,亦或是使用gparted、libguestfs进行处理。
这里有个问题,qcow2格式用qemu-img info
看到的大小是3.7G,用raw格式也仅有7.8G左右,而在win7内部磁盘管理看到的空间占用却是11.2G。这里的不对称是因为文件系统格式的原因吗?
一开始一直在尝试安装libguestfs,因为似乎virt-resize
可以做到压缩上限,但是很可惜,内网环境yum安装依赖实在是太艰难了,在将CentOS安装包的iso镜像挂载上作本地源之后,还是存在一些依赖问题。折腾了快有2天时间,在supermin5上还是没能搞定。
最后,在看qcow2压缩的博客时,发现没有人提到过要修改这个上限的问题,qcow2本身就是写时分配空间,,那么其实,是不是无关紧要呢?我分配了一个4G的硬盘,将3.7G压缩后的镜像移动到这块硬盘再启动,windows启动一点没有问题,在系统内部的文件分区显示依旧是11.2G左右。
哎,尝试了好多,最后还是回到了原点。
]]>按照食谱来做,没想到还是有很多细节上没有提到的东西,想当然了,导致最终的成品出现了大大的错误。
没有红肠,只好先买个蒜肠,还好最后味道也还能吃。
看情况吧?墙壁如果没问题那就没事了,要是有点脏那到时候再咨询房东能不能贴了。
简约的似乎直接用淘宝的就行,不用在意非要实木,毕竟我并不是有多个显示器外接的大佬。预算可以不用达到600了,300以内应该可以。
目前来看,不太喜欢那种椅子带滚轮的,价格低的情况下,办公椅似乎由于零部件太多容易出问题,宜家几十块的椅子带椅背就可以了
网购或是实体店,到时候逛逛
我台灯从高一用到大四,7年时间,关节处已经撑不住了。
宜家,爆款落地灯79,mark。
必选
宜家优先,不过我不确定我有哪些需要用这个
1 | curl http://static.moinmo.in/files/moin-1.9.9.tar.gz -o moin-1.9.9.tar.gz |
1 | wget https://dev.mysql.com/get/mysql80-community-release-el6-1.noarch.rpm |
使用zotero导出的bib中,文献类型同样是学位论文,但是bib中是phdThesis,而我本科的模板上是MasterThesis,暂时没找到如何让zotero修改这个的方法,倒是找到了在JabRef上修正这个的方法,看来暂时只能用JabRef了.
不过在LaTex的bst格式文件中也找到了phdthesis的输出样式,所以错误原因究竟是什么呢?
经过一个个与模板bib文件比照,发现是由于缺少了language={Chinese}
这行设置,补充后即可输出了,搞定。
由于JabRef的无法正常运行,又去了解了下zotero的强大,经过检索,发现还是zotero也是可以自己配置一些可选field之后导出bib的,在language位置设置为chinese,搞定。
根据知乎上作者所说,是在抓取网页解析的语法出现了问题,所以按照知乎所说修改解析方法,全部防止到姓中即可 >1. 首选项-高级-文件和文件夹-打开数据文件夹。 >找到translators文件夹,找到里面的CNKI.js,打开 >2. 注释掉189和190两行,在后面加上一行 creator.firstName = ""; >3. 刷新文献所在页面,重新获取条目,创建者即为作者全名。 >4. 文件重命名规则可使用%a
按照issue里说的换成4.3 beta版本就可以正常使用了,唔,在新建entry的时候还是和issue一样Freeze了。
根据[^4]这篇文章操作了下, 现在在一个独立目录里有所有的pdf了, 至少可以跨平台阅读了. 但是这样就出现了一个新的问题, 删除文献时, 是无法把已经被rename
出去的pdf也给删掉的.
不过这个倒是挺适合我的暂时, 我倒是不怎么有删文献的需求.
根据[^3]的说法? 设置软链也是个方案? 其实我一开始就是这么用的, 为了避免300MB
官方空间用完, 但是这样不就回到最初的状态了, 我就是因为用ipad访问同步盘的时候, zotero
的storage
目录结构不好找, 根本找不到想看的文献, 所以才引入zotfile
的方案的呀?
按照更新版|Zotero搭配Sci-Hub,真香!这篇文章中的配置, 找一个当前sci-hub
最新的域名即可.
使用Zotero进行从未有过的畅快学术体验 - Eddy's World
据现在知道的信息要遵守户档不分离的原则,那么户口和档案就都是回到上海,现在学校发了一张二分材料。
网上查到的上海的派遣地址存在3种可能性(上海人力资源和社会保障局、上海市各区县就业促进中心、上海市各区县人才服务中心),但是实际上现在2018年只有就业促进中心负责接收上海生源地毕业生档案,这个部分折腾了我很久,去找了些上海的大学的派遣方案里,发现写的很清楚,上海生源派遣回户口所在区县的就业促进中心就行。
确定了派遣地址: * 上海市浦东新区就业促进中心 * 上海市浦东新区浦东南路3995号309室 * 200126 * 58740409
不过又了解了一下,户档不分离的主要出处是北京毕业生集体户口迁出,既然如此,没有迁户口进入学校的情况似乎是可以不遵循户档不分离原则,是可以将档案迁移到单位所在地的。不过据别人论坛经验之谈,各省市政策不同。所以就姑且按照上面那样做吧。
派遣证信息要确保准确,就业去向为就业,签订三方协议的,需将三方协议由用人单位盖章后返回至学校,在就业信息网登记就业信息,并加盖就业指导中心公章,就业指导中心将根据毕业生在就业信息网上登记的就业信息进行派遣,其中单位名称为解决户口和档案的准确的单位名称(不能是简称),也有部分没有独立人事权的单位,需将单位名称写为放置户档的人才机构名称,一定要与用人单位确认后再上报!单位地址为解决户口的地址,(一般落实到市、区一级,如:北京市海淀区,陕西省西安市)。
最后选了学长转租的那个,小区物业什么的似乎稍好一些,房间也够放个大桌子,那就行了。
用的几个工具是有人做好的
中介平台转租的基本上都很贵
短租的民宿等, 也在150一天以上, 可能可以联系下月租能否减低? 但是反正没有合租的好
公寓也是长租的多, 除了酒店和民宿没有短租的. 这附近正常价格每天在250以上, 导致长租我觉得也降不到100出头
还是直接搜转租, 这样的好像比较合适
最好还是融泽
考虑到目前处于疫情期间, 搬家公司没办法进小区, 所以找同小区的估计自己用小推车搬家方便点
短租, 这种筛选小区的, 还是找中介? 目前筛选的我爱我家的?
看了下合同是6/16结束, 那看起来得提前搬了.
以下为18年的记录
下述这些最后都没用上……因为学长很省事,连押金都没要……
只租过自如的房子,说下关于租自如的经验: 1. 不要租新装修的,特别是那种“首次出租” 2. 中途转租需要扣至少50%的押金,就是半个月房租,如果是退租的话需要扣80%+好像 如果公司和自如有合作的话,服务费可以打88折,并且一年有一次免费换租的机会(可以趁12月淡季的时候换一个便宜点性价比高点的) 3. 合租最好租3居室,自如的房子都是原有的n居室+1个隔断,4居往上公共空间会很挤(厕所厨房冰箱啥的,水电费还容易超阶梯),2居室一般都没有客厅,只有一个窄窄的过道 4. 最好是租13层左右的,一般都是30+的高楼,10几层正好在中间,光照好不怕遮挡并且夏天不会太热冬天不会太冷 5. 看房子的时候问下正在租的租户,水电费每个月大概是多少钱,是不是商用水电(超过阶梯价格用起来会很可怕,一个月水电费可能要150) 6. 检查下卫生间有没有返味,房间的隔音如何 7. 避免租房东打算卖房的,虽然到时候会赔你一个月房租,但经常会带人来看房子,而且到时候还得费事去找新房子 8. 尽量租在公司附近,减少不必要的通勤时间,走路就可以上班就最好啦,当然如果和对象一起租还是商量一下吧 9. 实在不行也要尽量离地铁站近一些,小区周边的配套设施也很影响生活质量(超市、小饭店、早餐店、水果店等等) 10. 第一次签约大概要准备5个月的租金(1个月押金+3个月季付+1个月服务费),要准备好预算 11. 总的来说我觉得租自如就是省事一点吧,找起房源来没那么麻烦,但也相对没那么划算,建议最好找同学或者同事一起合租一个 以上~(两年搬了四次家的感想。。[1]
沙发说的很棒,补充一些吧
无论价格多低都不要租隔断,隔音什么的都在其次,住了一半被赶出去的经历估计谁都不想有
如果是合租,重点考虑朝向、隔音效果、是否带独立阳台和独立卫生间,如果是整租,还要考虑是否南北通透,如非迫不得已,不要租不向阳的房子(东南西南这种斜向的勉强可以接受)
楼层低的话要注意暖气是否够热,在室内还冻手冻脚的感觉还是挺闹心的,有条件的话尽量在冬天租房子
考虑是否有小区围墙,是否有门卫,楼道是否有门禁等,那种全开放式的楼房体验是很恐怖的,楼道和电梯里全部都是小广告,平时贴小广告的、送外卖的串来串去,安全隐患很大
离公司距离越近越好,步行上下班能极大的提升幸福感,不要想着省钱住远一点,每天坐地铁公交时间长了会把意志力都磨没的
租房需要搞清楚哪些事情?
1.交通、生活便利性。计算一下通勤的距离和费时,一共多少站,换乘几次,走到地铁站公交站要多久?附近共享单车好不好找?超市菜市场医院饭馆离得远不远?自己开车的话,早晚高峰附近的路堵不堵?
2.物业费、取暖费。怎么交?谁来交?物业费和集中供暖的房子取暖费一般都是房东交,自采暖的房子通常是租户自己交,这些租房的时候都要问清楚。
3.水、电、燃气、网络、有线电视费。水、电、燃气都有计数表,搬进去的时候抄一下底数,拍照留存,等搬出的时候一对比就知道你用了多少,要问清楚是水电燃气的费率是民用标准还是商用标准,两者差别还是很大的。网络要问清楚小区通了哪些运营商的网络,如果你之前办过包年的网络还没用完,新的住处也有同样的运营商的话,可以联系运营商办理迁移,如果是房东提供的网络,要问清楚还剩多少时长,有没有流量限制,剩下的时长是需要交费还是赠送。有线电视费同网费。
4.周边有无污染源噪音源。周边如有长期污染源和噪音源,看房的时候短时间内很难察觉,一定要问清楚,必要的时候可以跟小区的保安或大叔大妈打听一下,比如我有朋友之前住过的小区后面有一个混凝土厂,导致小区那个水质比较差,水垢很多,之前不知道,住进去以后就感觉被坑了。(原文来自微信公众号:我在北京租租房子)
5.小区安全性。小区门禁是否严格?保安是否24小时值守?摄像头有没有覆盖?晚间有无巡逻?小租前不久电动车停在楼下就被撬了,心累。除了保障财产安全,还有人身安全,所以必要的小区安保是必须的。如果觉得问房东和中介得不到实话,可以问问小区的大爷大妈或者住过该小区的朋友。
6.家具家电是否齐全?损耗情况如何?损坏赔偿等说明。住进去之前仔细检查家具家电是否完好,如果有损坏的,签合同的时候就要说明,可以拍照留记录,避免还房的时候因为一些损耗损毁归责不明起争执,还有约定好如果东西坏了怎么赔偿,哪些东西价格比较高,使用的时候需要尤其注意。ps女生很容易弄脏床垫,有的房东床很贵,最好自己平常注意垫上垫子,淘宝上卖的姨妈垫几十块一个并不贵,一旦弄脏了,不管是赔偿还是请人专门清洗床垫都不便宜,就不太划算啦。
7.朝向、采光、楼层和冬暖夏凉情况。一般来说朝东和朝南的房子采光好一些,东向的房子是早上的阳光,在我们北半球南向的房子一整天光线都比较充足,也不会太晒,朝西的房子夕晒,下午光线强烈,夏天会很热,朝北的房子跟朝南的相反, 一整天光线都一般,冬天会偏冷,其他不是正中朝向的房子,自己推断一下就知道啦。除了朝向,附近建筑物遮挡也会影响采光,都需要提前观察好。小租还去看过一个全是平房的公寓,屋顶虽说放了遮阳板,但是夏天阳光直射还是非常晒,感觉夏天不会很愉快。楼层高的房子通常都比楼层低的采光好,但是也会增加一些不便,比如一旦电梯坏了就很崩溃了,一楼通常会潮一些,而且私密性相对差,要么整天拉着窗帘,要么路过的人都能看到屋里在干吗,这些都要自己权衡。
8.能否养宠物。很多房东都不让养宠物,主要是怕挠坏真皮家具或者让屋里有异味,如果有宠物要跟房东协商好,双方接收并且约定好规则即可。
9.隔音情况。有的房子隔音效果不好,住起来也会有困扰,一般来说正常的小区房子之间的外墙隔音还不错,内墙稍次,隔断就基本不要指望隔音了。有的公寓用料不好不实,外墙隔音也有问题,高档的装修甚至会把墙壁刷成墙面凹凸的,有吸音的效果,这里面差别就很大了。
10.房租押几付几。通常情况下都是押一付三,现在有的公寓和芝麻信用合作可以免押金,也蛮不错的,有的房东如果屋内家具家电贵重也会要求押二付三,这些就看大家自己衡量啦。(原文来自微信公众号:我在北京租租房子)
11.停车位。如果有车,要问清楚房子带不带停车位?小区有没有长租停车位?管理是否安全?费用怎么交?
12.仔细看合同条款,有无其他限制,如合租人数,是否允许转租,退租是否提前说明等。有的房东会明确限制长住人数不超过几人,或者不允许转租,退租需要提前多久说明等等,合同一定要仔细看!合同一定要仔细看!合同一定要仔细看!否则签了什么霸王条款,到时候吃亏哭都没地方哭。小租被无赖二房东扣押金那次,最后找警察叔叔来一条条对合同,我没有任何违反合同条款的地方,无赖二房东也不得不退了我大半的押金,所以说,不管什么时候发生了纠纷,合同都是你最后一道保护伞,一定要看清楚记清楚。
搬家篇
现在搬家公司也很多啦,自如搬家、蓝犀牛、58、货拉拉、或者豆瓣租房小组直接找搬家师傅等等,多看看评价,比比价,选一个合适的即可。注意有的搬家会自带箱子,能省去部分自己打包的困扰,这些都要了解清楚。还有的搬家会看有无电梯,无电梯的房子会加收楼层费,都需要注意。(原文来自微信公众号:我在北京租租房子)
入住篇
租好了房子以后,一般建议换个门锁,以免中介或者房东留了钥匙,仍然可以随时出入你的家门,就很尴尬了。另外出门多熟悉熟悉周边环境,地铁站公交站、超市菜市场、物业位置和电话、附近医院药店、派出所、邮局、快递等等都去踩踩点,必要的时候不会两眼一抹黑。还有,记得修改网购、外卖默认地址!我就干过不止一次搬走了东西寄到原来住的地方的蠢事儿,还得回去取,肥肠麻烦的说。
最好将地址告知家人和朋友也留存一下,万一有什么事儿他们能及时找到你。容易丢钥匙或忘带钥匙的人,可以在靠谱亲友那里放一把钥匙,这样不会把自己关外面死活进不去。(原文来自微信公众号:我在北京租租房子)
一个在北京从事租房领域创业项目多年创业者,积累的防黑中介坑骗的重要经验与建议: 我是宋增建,根据我之前在北京进行的一个租房领域创业项目大量真实经验与数据,分享给大家,减少大家租房时,被黑中介坑骗的概率。 其实,通过网上的58、赶集等租房平台,完全可以找到大量的房东,而且其中中介公司的出租房源中,很大比例的房东,也都在58、赶集上个人登出来了,你完全可以找到他们。真正的风险,是租房过程中,被冒充房东的黑中介所坑骗,造成很大损失。这里将我之前做这方面创业时,通过大量实战,探索积累的很多经验、技巧与心得,和大家交流分享一下:1、在网上找房源,看到贴子里面的文字介绍,只有空格,而没有逗号、句号等标点时,要小心一些,这种贴子,大多是中介发的。2、主要的网上找房东个人房源的平台,就是58、赶集两个网站,其次是搜房网,别的就量很少了。在这些网站上,注意在房源中选择个人房源,但要注意,这些网站上登的个人房源,里面也有一定的比例是中介,甚至是黑中介,现有的手段,并不能完全保证对方真伪,还需要租房者自已去亲身验证。 现在一般58、赶集,都引导大家通过手机app,才可以看到房东的手机号,就可以和这套网上房源的房东联系了。3、打过去电话,可以先停顿一下不说话,静静地听对方的背景音,有时你可以听到中介公司那种很多人在打电话谈租房的交流声,这就可以判断是中介公司了。 同时,一般的房东,会主动先出声和你交流,你可以听其声音,判断其年龄、修养、气质等情况,再去聊。而如果打通后,对方很久不出声,想听你的声音,是中介的可能性较大。4、要把判断对方是否是中介作为最首要的一件事情。 在没有判断清楚时,避免说出过多个人信息,以及租房需求等。要等判断清楚了,比较自信了,再说更多个人情况。 很多中介公司会有一个系统,第一个和你通话的中介,会把你的手机号、个人信息和租房需求等录入进系统,这样其它人帮你租成了拿佣金,录入者也有业绩与分成。所以如果对方是中介,录入系统,全北京这家中介的各个分店都可以看到,并主动打电话给你,会比较烦,但作为租户,被电话骚扰的数量不会太多。你明确向对方说不用中介后,给你打电话的中介,也会把你的这个回复,录入系统中,其它中介会看到。5、在打电话前,详细记清租房贴子中的一些照片、房子情况等细节,比如地板什么样,电视什么样,楼层是多高,面积有多大,房东姓什么,租金,等等。最好打电话之前,提前记在电脑或纸上,列出这些问题与细节清单。 然后打电话时,用这些细节去验证,看房东是不是会记错与出现矛盾之处。 在刚打通电话时,注意不要自已主动说出你看到的小区的名字、几室几厅、多少租金等信息,而是先问一句:“您有房子出租吗?”,然后装作记不清小区、租金、甚至是对方姓氏等,主动去套对方的话,比如“不好意思,刚才网上看了几套,给您打电话时,手机上看不到相关信息了,想详细问您一下,我是个人租房者,想多了解了解。” 然后,将很多刚才记下的细节,一一去问,注意有时要故意用误导式提问,比如:明明照片上看到是木地板,却问其地面磁砖的颜色;或明明看到是绿色的沙发,问其蓝色的沙发是否干净等等。如果对方能及时纠正你故意说错之处,说明对方对房子情况非常了解,更可能是个人房东。而中介,有时登出很多套假的房源,是记不清这么多细节的,让你问多了,就会矛盾与错误百出,明显与网上登的细节不符。6、电话里,大量、详细地问很多东西、交流很多东西。 一般真实的房东,也会怕遇到黑中介冒充个人来租房,最后租下打隔断出租毁了房子,所以,你这样问的特别详细,真正的房东,也会对你更加信任,而中介,往往就会不耐烦。所以,就大量细节去问房东,可以偶尔加一点解释,就是强调自已是个人,所以也想找真实的个人房东,多问一点请别介意。真实的房东,也愿意多了解求租者个人信息,一般都愿意和你多交流,而中介往往受不了这么麻烦的问来问去。这是很重要的区别,两种人的出发点与目的不同。7、除了问房子情况之外,很重要的一点,是多聊聊房东是做什么工作的,最好还可以问出是哪一家公司或单位。 可以结合房东说自已的工作领域,聊一些你所知道的这个行业的一些人、事、公司等等,一方面,房东可以加深对你的赞叹与信任,另一方面,你也可以通过这些话题去验证对方真伪。 而且这里面,还可以故意说一些“陷阱性的问题”,去试探对方。比如明明你知道一个这个行业的事情,你说一个假的,或是相反的,或说一个你乱编的这行业公司的名字,看对方的反应,如果对方说是对的,顺着你说,没有纠正错误,而不是说不知道,或是对方对这个行业与领域根本说不上什么来,表现得很不耐烦或粗鲁,往往就暴露对方是中介。 如果你问对方工作,如果是中介,一般他们会说自已是做行政工作、市场营销等人们最常见的,但不会说具体的行业,中介很难说得出很具体的一些行业以及工作细节,这个就太难为他们了。所以具体到这个行业哪一家公司,或是深聊一些这个行业的一些东西,中介假冒房东,往往就会暴露。这样,你就不用白跑一趟了。8、注意对方的电话开头号段: 一般139的,或是186开头的,可信度比较高。当然这不是绝对的,而一些如155、156、131、132、133、185等开头的,是中介可能性相对较高。但这些只是相对的,不是绝对的。 现在实名制了,但中介仍然有办法找一些临时的号去发贴子,冒充房东,而139这种号的成本很高,而且这种号,往往不能多次在网上发贴,否则会被标记为经纪人,而且,过多用此号,也会被标注为经纪人,因为中介往往需要不断换号。9、在搜索引擎,去搜索看到的房东手机号,看是否被多人标注为经纪人: 安卓手机有一个功能,就是很多人标注上一个手机号是中介后,你打去时就可以看到。所以,可以找一个安装有腾迅手机管家、360手机管家等这类安卓手机打过去,这可以显示被多少人标注为中介。 还有一种方法,就是在百度、搜狗、360这三个搜索引擎中,搜索你看到的房东的手机号,这些网站与BAT出的手机安全类app是绑定的,在手机上被这些app标注的,也会在这些搜索引擎中展示出来此号被多少人标注为中介。 PC或手机浏览器上的搜索引擎,百度对应的是“百度手机卫士”app,搜狗对应的是“腾迅手机管家”app,360搜索对应的是“360手机卫士”app,这样在浏览器里用这些搜索引擎去搜索房东手机号,效果是一样的,看其标注情况,我说过,中介会经常大量换新号,这种方法只是一种辅助手段。10、看贴子、以及配的文字,感受房东是一个怎样的人。 如果是黑中介,只是希望把你骗来。所以看房子的介绍,感觉就比较粗糙,或是别处抄来的文字,就是黑中介。而如果是真正的房东,往往在照片、写的东西上面,会很用心,尤其是你一打电话,对方会对你的身份、工作、几个人住、什么关系等,希望多了解你,而这些情况,中介一般是没有兴趣过多问细节的。 通过声音,去感受到对方的气质、学识、修养、心理等很多细节,这需要你用心去感受,多注意自已的直觉,综合判断是不是黑中介,毕竟在黑中介里,有文化与修养、气质的人,还是比例很少的。 不过,很多时候,有些房东也只发布那种几乎就一句话说明的租房贴,特别简单,也没有图片,而中介有时也经常发布这种贴子,从这种很简单无图的贴子中,就很难判断。11、听电话或微信语音中的口音,判断对方身份与地域: 我管这种电话里的语气,叫“菜音”,就是听上去比较粗俗、浅薄、没多少文化、有时带有一种粗鲁,或是刻意装出来的一种有文化与文雅的感觉。 我个人,对各个地方的人,没有偏见,但整体上说,北京黑中介里面,东北口音偏多一些,还有中原地带也有少量,但南方口音,或是北京口音,并不一定就不是黑中介,只是概率小很多。 同时,从事黑中介的,绝大多数是20岁到30岁左右的年轻人,年龄大的要少很多,听年龄,也可以做出一定判断,如果对方听着50多岁,温文尔雅很有文化,那是黑中介的可能性就会很低,而毕竟,大多真实的个人房东,年龄并不轻了。 有时中介会找一些人来共同演戏,骗租房者上当。但这里面,有文化修养与高雅气质,就是大家在好的写字楼、好的公司,经常遇到的那种受到良好教育、有好的工作积淀与修养,这种气质的人,在黑中介中,比例还是很少的,但在个人房东中,就比例大多了。12、加对方微信,尽量与对方进行视频: 加上对方微信,可以看房东的朋友圈,了解这个人的情况,如果是中介,在很多方面,往往还是有差异的。同时,通过与对方微信视频,可以更清楚地看清房东的样貌、修养,而且还可以手机同步录音,截屏等,这些都是保留的一些制约假房东的证据,如果是黑中介,这些未来可以可以有法律作用,至少把录音、照片放在网上,或是提交给管理黑中介的住建委,或诉讼时交给法院,还是很有震撼作用的。 最关键不是事后,而是租房之前,在去看房前,如果通过微信视频,看到对方的气质、言谈、衣着等,如果修养、品味、气质、常识很不错的人,是中介的可能性就会很低,毕竟,这样的人往往工作不错、事业有前途,不会去做黑中介的。 当然,一切都不是绝对的,保有警惕之心很重要。13、搜索贴子中图片去判断,同时注意图片质量: 租房贴子里面的图片,用百度、搜狗、360的图片搜索功能,去拖进去,看看网上哪里,还有这样的图片,就可以判断一些房源的真伪。 中介往往从别处扒房子的图用,在这些搜索引擎的图片搜索功能中,往往会显其原形,但很多时候,也有房子的图,是中介们自已拍的,或之前多拍备用的。 另外,发在58、赶集上面的租房贴的图片质量,个人房东,有房子的人,相对收入往往较高,都是用苹果等高端手机,拍的图片质量很好,但那些黑中介发的,往往是来自于低劣的手机拍的,或是从别处扒的假房源图片粗糙处理以逃避58等网站的图片比较功能,所以,低劣质量的房源图片,看上去模糊的,是黑中介可能性较大,甚至很大。14、现场看房环节的骗局(尤其注意): 如果现场看房,遇到了黑中介,也有几种情况列给大家如下。1)、对方穿着中介常穿的深蓝色西装,年轻,而且直接告诉你他就是中介,并且说现在网上根本找不到真实的房东的房子,来打击你让你租他们的房子。其实你自已完全可以在网上找到真实个人房东的。2)、对方冒充房东,然后说这套房子代理给他们了,或是和房东是朋友,这套房子和他签就行了,可以给你看复印的房产证,以及租给他的合同等等,反正就是各种让你要特别相信他们,他们一定不会骗你等等。这种人,是黑中介的可能性很大。3)、对方是北京人,年龄较大,同时有时旁边还有年轻人,这个年龄大的,往往是黑中介找来的托儿,冒充房东看着更可信。而年轻的,是黑中介本人,来看看租客的情况。很多时候,假房东就是一个人出现。 上面情况中,如果他们说自已是房东本人,看其身份证、房产证,尤其再去物业核实其真假,往往就会露馅。但他们想办法会说服你,说和房东关系好,是亲戚或朋友,这套房子他来管,你和他签没有任何问题等,如果你没有能力去判断真伪,建议一定要找真实的个人房东本人出面,可以大大降低被骗概率。相信我,不要过度迷信自已高学历,或是工作经验丰富,在黑中介这里,骗的很多人,都高学历高工作背景的。15、租房合同的猫腻,以及如何选择合适的合同文本: 在现场,黑中介往往用各种身份,骗你签一份“代理合同”,注意,看到这种“代理”合同,要特别小心,这就是黑中介们通过他们的律师,帮助他们精心设计的条款,对租客非常不利,合同名称是某某出租代理合同,最后签的是一个中介公司的名字。但其实,黑中介往往都是蛮不讲理,有时就算签了非常正常的合同,也会坑得你损失很大。 因此,最关键的,是别太相信合同,而是以预防避免上当为主。否则上了当,你很难有大量精力与时间,去与这些像黑社会一样的黑中介去纠缠到底的。目前走法律诉讼的很少,而且在执行中,也难度极大。 所以,最好签自已提前打印的合同带去,尽量用自已的合同,在北京市住建委官方网站上的《北京市房屋租赁合同(自行成交版)》可下载,网站见下图:
作者:宋增建 链接:https://www.zhihu.com/question/54461291/answer/139471473 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
同时,想补充一点,签合同时,最好自已提前带着印泥,彼此都在名字上按上手印,并且双方把手印,像骑缝章一样,同进按在每一页边侧,证明合同的完整性。16、现场看房产证,以及去物业实地核实: 这是现场验证对方真假的重要途径。 对方是不是真实的房东,最安全的,还是直接和房东本人签合同,仔细看房产证,并且去物业验证这个人就是真实的房东,小区的物业是有责任帮助租房者验证房东真伪的。 房产证真件,印刷的很精致,很少有伪造的,但也要小心,而再加上去物业核实真假这一条,基本上是假房东的可能性就很小了。17、签合同现场,同时录下照片或视频: 最好签合同后,现场录下照片与视频,并且是一段连续视频,里面要出现合同,说出来时间、地点、这套房子的小区、楼、牌号等,并且房东要保证对方是房东等信息,这些法律证据非常有用。而且,未来如果是黑中介,这段视频用在网上,或交给政府相关部门的震撼作用非常大。18、可先交定金,租金支付时间上适当后置,但别太晚。 看房时,最好就提前如上面15条所讲,随着带着自已的合同两三份(至少双方一人一份,一份还可以打草稿)。看好房,验证好房东身份,可以现场马上打定金。最好可以网银转帐,或是用支付宝,这样保留下证据。而且尽量少一些,比如500,或是1000元,但也别太少。 同时,尽量让房东给你留出一定搬家的时间,比如一周后起算等,到时交全款。但根据我的经验,有时如果真是很看好房子,并且房子很抢手,还是签合同,尽量把押一付三,或是约定租金尽快交全为好,这样,房东就不能毁约再租给别人了。如果没有定金的约束,房东就算嘴上说得好,给你留着,但如果中介带人来,或别人看好给更多租金,房东也往往会和你毁约。至少定金,是一定的约束,如果合同里写出条款,让房东如果毁约支付一个月的违约金。但现实中,还是双方协商为主,真正打官司的很少。因此,当你确定对方是真实房东,房子也满意,就要尽快交清全款,才是真正的“定下来”,否则,后面的变化真得很多。 支付房租方式,可以在合同上写清楚,向房东的某某银行帐户打入的款项,就视作是支付的定金与租房款,这样,只要向此帐户打款,就视同交了租金。 尽量避免现金交接,这样,银行转帐记录本身也是法律证据。19、了解黑中介会怎么骗你: 黑中介,最后会不退押金,而且合同里各种陷阱,更恶劣的,是让你住在中途,就用各种手段说你违约为由,采用野蛮暴力的手段,把租户提前强行赶出去清房,他们行儿话,叫“做违约”,而且会把你提前多交的一两个月房租,以及押金等都找理由扣着不给。 还有在租房后,提出的各种另收的卫生费、服务费等等,先让你上钩,再收各种不合理费用。 黑中介不是每一个都这样恶劣,给你造成的损失也都不一样大,但如果租了黑中介的房子,有一定比例,会让你中途就被赶走,以北京的租金水平,你可能会被黑掉上万元。 还有的,是黑中介与房东签的合同,快到期了,却收入了你一年的钱,到时候,房东来收房,黑中介拿钱不管了,让你们双方去协商,麻烦很大。 你在交涉中,黑中介往往还会暴力化,而且无赖化,并且一家有事,周边各家黑中介一起帮忙对付你,各种手段,让你非常郁闷难受,所以尽量直接租真实个人房东的房子为好。20、如果着急找房,要找中介,尽量找大的、市场主流的中介公司,在北京市场,主要是链家、我爱我家、中原地产、麦田房产等。 基本上主流的就是这几家,尤其是以链家、我爱我家为主,他们要收一个月的中介费。注意,大中介的一些店有时也会有黑人的现象,但比黑中介还要好很多,各种什么看房费等,是不用交的,在北京看房,不用提前交任何费用。 重点小心中小型中介,有相当比例会存在一定的黑中介化。当然,规模大的一些中介,也有黑中介化的现象。所以,如果非要找中介,最好是找链家、我爱我家这两家。21、特别小心,那种所谓黑中介“代理”的房子,更是他们常用的方式。 很多黑中介,用的手段,就是“代理”,从房东那里把房子代理下来,再转租给租客。这种“代理”的房子,是黑中介的主要手段,他们相对做居间,就是让你和房东双方见面,最后只收入中介费的较少,主要利润,还是来源于这种“代理”。你和他们签这种“代理”的房子,最后就会造成很多的麻烦,上面我讲过了。22、如果合租,可以选择链家的“自如”,或是58同城租房版块下的“品牌公寓”,找一些在网上搜搜,可以看到一些风投融资等相关信息的公寓品牌。 当然,这些公寓,也不是都很令人满意,但是相对于黑中介那种合租,还是相对好一些。黑中介的合租,从房租价格上,表面上看很低,但最后算上坑你的钱,还有其它的一些乱收的钱,其实不比品牌公寓便宜。 如果你有有经验的朋友,他整租下来一套个人房源,你与他合租,是性价比相对更高的。23、租房遇到黑中介时,一定不能单纯只看租金,黑中介往往报的租金很低,这后面的坑就更大了。 你想,他们从房东那里代理来每月要6000元,而租给你5000元,甚至4000,怎么可能呢?租金明显低于市场价的,相当大的可能,会对你“做违约”,就是想办法很快让你违约,把你多交的房租,以及押金,都扣光,并赶你走。再一个个用此“代理”的房子,去陆续坑人。 因此,如果你和黑中介谈租金,他们很低都可以租给你,但最后损失的,一定是你自已。他们有太多手段对付你,你租不久就会知道的。24、找个对在北京个人租房非常有经验的朋友,甚至是认识两个黑中介,在租房过程中咨询他们: 我和黑中介打交道时间很长了,对这个行业的理解也很深。我特别想告诉大家,北京的租房黑中介行业,能够形成,是有很多方面非常复杂的因素综合造成的。这些黑中介,不能说他们每个人都是坏人,只是这个行业的商业模式,不断演化到这个样子,他们只能按照这套模式去坑你。但不能因此就简单地认为,这些黑中介个人都是坏人。在整个行业的大潮中,个人的力量与选择,往往是非常狭小的,明白了这一点,你就会更了解这些黑中介,也就更能避免被他们狠狠地坑你。 建议在你租房时,提前找找在北京个人租房非常有经验,了解如何避免上黑中介当的朋友,在你租房时,可以向他咨询和寻求帮助,最好陪你一起去。甚至你也可以认识一两个干黑中介这行的人,花几十块、上百块钱请他们吃顿饭,当你租房时,咨询一下他们,毕竟同行是最了解同行手段的,如果他们拿你当朋友,就会告诉你如何避免上当。25、最后,告诉大家一个经过实践中得来的一个重要的金律: 当你在租房时,感觉对方可能是黑中介,或是感觉这个房东不太对劲,就尽可能别租! 因为你要相信自已的直觉,你的直觉已经感受到一些地方不太对劲,这是潜意识在对你发了警告。而你没有听从这种来自感觉的警告,租了房子,往往后患与麻烦无穷。 所以,当你租房时,上面这一条条我写的,你都处理好了,理性也认为还可以,但就是直觉不断告诉你,不太对劲,你特别要相信这种直觉,通过我过去做租房领域创业项目大量的亲身体验,这种直觉,往往会在后期被验证。 记着,北京市场上,总同时有大量的房子在出租,从来不存在房源没有的情况,而且新的房子也会大量的不断空出来,只要还有可能,尽量避免避免麻烦,而不是后期真遇到黑中介,过高估计了自已处理麻烦的能力,经济损失、心情与尊严上的损失,都会让你久久不能忘怀。
威胁 | 定义 | 对应的安全属性 |
---|---|---|
Spoofing(伪装) | 冒充他人身份 | 认证 |
Tampering(篡改) | 修改数据或代码 | 完整性 |
Repudiation(抵赖) | 否认做过的事情 | 不可抵赖性 |
InformationDisclosure(信息泄露) | 机密信息泄露 | 机密性 |
Denial of Service(拒绝服务) | 拒绝服务 | 可用性 |
Elevation of Privilege(提升权限) | 未经授权获得许可 | 授权 |
windows Vista的UAC功能,询问用户是否允许该行为,这里说如果用户能分辨什么样的行为是安全的,那么还要安全软件做什么。这里说的很对,不过也是存在一种解释,这里的提醒只是一种用户个人对行为承担责任的方式吧,已经予以提醒,之后的操作的安全保障已经不是操作系统层面的安全性能够提供了。