0%

cassandra 2.2.6环境,有一台种子节点硬件故障,半年以后才修复重新上线。
其数据已经落后太多,而cassandra并不会在其重新上线后自动进行数据同步。
nodetool repair应该可以使其数据重新同步,但是那速度是无法忍受的,因此使用nodetool rebuild来重建其数据。

首先停止cassandra服务

1
$ sudo systemctl stop cassandra

或者

1
$ sudo service cassandra stop

然后删除掉数据目录下system和用户keyspace的所有数据
$ sudo rm -rf /var/lib/cassandra/data/system/*
$ sudo rm -rf /var/lib/cassandra/data/your_keyspaces/*

1
$ sudo rm -rf /var/lib/cassandra/*

如果不清除用户的keyspace,rebuild的时候并不会自动清除,而且rebuild是全量而不是增量,所以那些数据会成为垃圾数据,如果数据量很大,应该提前清除掉。

对于种子节点,还应该确认auto_bootstrap参数已经设置为false。

启动cassandra服务,执行rebuild

1
2
$ sudo service cassandra start
$ nodetool rebuild -- name_of_existing_data_center

指定源数据中心时,要指定与当前节点所在数据中心不同的数据中心。

查看rebuild进度

1
watch -n 10 'nodetool netstats | grep "Receiving\|Sending" | gawk {'"'"' print $1" - "$11/$4*100"% Complete, "($4-$11)/1024/1024/1024" GB remaining" '"'"'}'

等nodetool rebuild结束重建就算完成了,其实这与添加新的节点差别不大,不过就是原来的环境,所有的配置都不用动罢了。

同步完成后可以看看用户表的统计信息:

1
$ nodetool tablestats keyspace_name.table_name

updated:06/22/2019

这次种子节点下线重做RAID,系统重新安装,cassandra版本为2.2.14

rebuild的时候提示:

1
2
3
4
$nodetool rebuild -- DC2

nodetool: Unable to find sufficient sources for streaming range (2952258499581076301,2996932853512195336\] in keyspace system_traces
See 'nodetool help' or 'nodetool help <command>'.

需要将keyspace system_traces的replication strategy设置为NetworkTopologyStrategy并将其分布到所有的数据中心,其默认设置为SimpleStrategy

1
cqlsh> ALTER KEYSPACE system_traces WITH replication = {'class': 'NetworkTopologyStrategy', 'DC1':2,'DC2':1};

keyspace system_distributed的replication strategy也应该设置为NetworkTopologyStrategy并将其分布到所有的数据中心

1
cqlsh> ALTER KEYSPACE system_distributed WITH replication = {'class': 'NetworkTopologyStrategy', 'DC1':2,'DC2':1};

References:
[1]Unable to find sufficient sources for streaming range (,] in keyspace
[2]Unable to find sufficient sources for streaming range in keyspace
[3]system_distributed and system_traces keyspaces use hard-coded replication factors

怎么找到C源程序中没有被引用的function/死代码?
GCC的CFLAGS -Wall -Wextra只能报告未使用的变量,而函数要到代码全部链接完成,才能知道哪些是没有被任何代码引用的,为时已晚。

在编译链接时可以组合使用CFLAGS: -ffunction-sections -fdata-sections 和 LDFLAGS: -Wl,-gc-sections在目标文件里移除未使用的代码,那么如何在源文件里找到这些函数呢?

一个相当简单的办法,编译完成后,对比ELF目标文件和所有obj对象文件中符号表中符号的差异,就这一知道哪些函数在最终的目标文件中被移除了,也就是无用的函数。

首先编译代码:

1
$ cc foo.c -o foo -fdata-sections -ffunction-sections -Wl,--gc-sections -save-temps

注意要使用参数-save-temps保留中间文件,要用到是*.o对象文件
注意要保留符号表,不要strip all
gcc的--gc-sections在clang中对应的是-dead_strip

然后使用这个脚本find-unused-function.sh通过比较符号表找出从未被使用的函数名字

在工程目录下执行

1
$ ./find-unused-function.sh foo .

生成的./DebugSymbol/symbols.unused-binary文件中记载了未被使用的函数名称。

References:
[1]C-code—Find-Unused-functions

编译时把数据和函数放到单独的section中,然后链接的时候抛弃掉未使用的section就可以了。

也就是组合使用CFLAGS: -ffunction-sections -fdata-sections 和 LDFLAGS: -Wl,--gc-sections

1
$ cc foo.c -o foo -Os -fdata-sections -ffunction-sections -Wl,--gc-sections -s

-s 选项剥离掉调试信息,可以进一步减小目标文件的尺寸。

References:
[1]Removing Unused Functions/Dead Codes with GCC/GNU-ld
[2]Re: Removing unused functions/dead code

当用很大的字符串调用cwrap包装的wasm函数时,出现如下错误:

1
Stack overflow! Attempted to allocate 8588685 bytes on the stack, but stack has only 5242877 bytes available!

这是数据太大,把栈撑爆了。cwrap使用stack来传递临时数据,只能拷贝数据,不支持引用传递,不支持自动扩展,并且,stack上的数据是临时的,被调用的代码如果保存通过stack传递进来的指针以备后用,有可能会引用到无效的数据,这是不安全的。
所以这样来传递大量数据是不妥当的,应该使用WebAssembly模块的heap来传递这样的大字符串。

官方文档是这样说的:
cwrap uses the C stack for temporary values. If you pass a string then it is only “alive” until the call is complete. If the code being called saves the pointer to be used later, it may point to invalid data. If you need a string to live forever, you can create it, for example, using _malloc and stringToUTF8(). However, you must later delete it manually!

可以使用Module._malloc()和Module.stringToUTF8()函数通过heap来传递大量数据:
[cpp]
const bufferSize = Module.lengthBytesUTF8(veryLargeString) + 1;
const bufferPtr = Module._malloc(bufferSize);
Module.stringToUTF8(veryLargeString, bufferPtr, bufferSize);
const sample = Module.cwrap(‘sample’, null, [‘number’]); // not ‘string’, pointer is a number
sample(bufferPtr);
Module._free(bufferPtr);
[/cpp]

要注意Module.lengthBytesUTF8给出的长度并不包括空终结符,所以缓冲区大小要再加1。被调用的函数不再使用string类型的参数,而改用number类型,因为指针就是一个number。分配的缓冲区使用完之后记得要free掉,不然会造成内存泄露。

为了确保heap能自动增长,build模块时应该添加-s ALLOW_MEMORY_GROWTH=1参数,就不用害怕传递大型参数了。

还有一个更简洁的包装方法allocateUTF8可用,直接传递给它数据,它就可以将数据拷贝到堆上,并返回在heap上分配的空间地址,这样用:
[cpp]
const bufferPtr = allocateUTF8(veryLargeString);
const sample = Module.cwrap(‘sample’, null, [‘number’]); // not ‘string’, pointer is a number
sample(bufferPtr);
Module._free(bufferPtr);
[/cpp]

allocateUTF8的源代码:
[javascript]
// Allocate heap space for a JS string, and write it there.
// It is the responsibility of the caller to free() that memory.
function allocateUTF8(str) {
var size = lengthBytesUTF8(str) + 1;
var ret = _malloc(size);
if (ret) stringToUTF8Array(str, HEAP8, ret, size);
return ret;
}
[/javascript]

wasm模块可以从emscripten HEAP上返回字符串,只要给javascript传回一个空终结UTF8编码的字符串指针就可以了:

[javascript]
// Given a pointer ‘ptr’ to a null-terminated UTF8-encoded string in the emscripten HEAP, returns
// a copy of that string as a Javascript String object.

function UTF8ToString(ptr) {
return UTF8ArrayToString(HEAPU8,ptr);
}
[/javascript]

最后

其实stack和heap都分配自WebAssembly.memory对象,这是一个ArrayBuffer对象,可以使用不同的视图来存取。stack和heap的区别是使用和管理内存的方式不同。
调用instance.exports._malloc时是调用的是C/C++标准库的malloc函数,而emscripten实现的malloc函数正是在WebAssembly.memory上动态分配内存。

References:
[1]Stack overflow error when large string passed
[2]Automatically growing the stack

.wrap {
/* margin-left: auto; /
/
margin-right: auto; /
max-width: 100%;
/
padding-left: 2em; /
/
padding-right: 2em; */
}

@media screen and (min-width: 48em) {
.wrap {
max-width: 100%;
/* padding-left: 3em; /
/
padding-right: 3em; */
}
}

.page.page-one-column:not(.twentyseventeen-front-page) #primary {
/margin-left: auto;/
/margin-right: auto;/
max-width: 100%;
}

@media screen and (min-width: 30em) {
.page-one-column .panel-content .wrap
{
max-width: 100%;
}
}

GIMP改用flatpak打包,flatpak以前叫xdg-app,现在flatpak是指flat package吗?

flatpak是一种致力于减少依赖的通用应用打包格式,与之竞争的还有snap和AppImage

安装

flatpak已经进入debian当前的testing buster源和sid源, debian stretch可以从官方的backports源deb http://ftp.tw.debian.org/debian stretch-backports main contrib non-free安装

1
$ apt install flatpak

添加flatpak官方源

1
$ sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo

因为flatpak本身就是一种app打包格式,官方应该提供一个自举安装的shell脚本才显得更专业:)

安装gimp

1
$ flatpak install https://flathub.org/repo/appstream/org.gimp.GIMP.flatpakref

可以使用桌面图标运行,或者使用以下命令:

1
$ flatpak run org.gimp.GIMP//stable

flatpak文件系统布局

flatpak支持系统范围安装和用户安装,系统安装在/var/lib/flatpak,
安装的app系统配置在/var/lib/flatpak/app,其他子目录为flatpak本身系统范围的配置
安装的app的当前用户配置在$HOME/.var/app,flatpak本身的当前用户配置在$HOME/.local/share/flatpak

References:
[1]Flatpak
[2]Flatpak filesystem layout

Emscripten提供了很多库的webassembly实现,甚至为了文件系统存取还提供了虚拟文件系统,这可以让很多C/C++代码做很少的修改就可以移植到web上来运行。

目前来讲js直接与wasm代码交互还不是那么方便,通过Emscripten提供的js glue代码可以使这个过程更简单一点。

胶水代码导出了一个名字空间Module,其中有两个方法ccall和cwarp作为桥接原生代码的桥梁,ccall适合一次性的调用C/C++函数,而cwrap则返回一个函数适配器,适合多次反复调用C/C++代码。

使用如下命令编译C/C++代码:

1
2
3
$ emcc area.c triangle.c -Os -s WASM=1 -o area.js 
-s "EXPORTED_FUNCTIONS=\['_getArea'\]"
-s EXTRA_EXPORTED_RUNTIME_METHODS='\["ccall", "cwrap"\]'

-Oz比-Os可以做更进一步的编译器优化,生成的代码更小。

除了要导出你自己的函数之外,还要导出这两个辅助方法ccall和cwrap
编译完后会生成area.wasm和胶水代码area.js

将area.js导入到html中,然后就可以写下面的代码来获取到导出的函数

1
var area = Module.cwrap('getArea', 'number', \['number', 'array'\]);

cwrap的第一个参数为导出的函数名,第二个参数为函数返回值,第三个参数为函数的参数。
目前数据类型支持的很少,有number, array, string, boolean, null等,而且array只接受Uint8Array类型。
对于C/C++的指针类型可以用array来传递数据。

对于ccall来讲,则除了cwrap的参数之外,还要将函数的实参传递进去,完成真正的函数调用,然后返回函数值。

References:
[1]Interacting with code

WebAssembly还是个很新鲜的玩意儿,ABI都还没稳定。
通常emscripten/emcc会产生厚厚的胶水代码来连接Javascript和wasm模块,通过import/export,Javascript和原生代码可以相互访问、调用,
除了简单数据类型,还可以通过memory和table来模拟指针、函数指针运算。
当前,emscripten/emcc已经支持生成standalone单独的wasm模块,无需胶水代码就可以简单使用标准的JS来访问wasm数据和代码,下面是一个简单的栗子:

C代码

1
2
3
4
5
6
7
8
9
10
11
12
13
double buf\[1024\];

double* getOffset(){
return buf;
}

double add(int count){
double sum = 0.0;
for(int i=0; i<count; i++){
sum += buf\[i\];
}
return sum;
}

add函数计算buf缓冲区中前count个浮点数之和,注意这里整个C代码没有任何其他库的依赖。

编译成wasm

1
$ emcc add.c -Os -s WASM=1 -s "EXPORTED_FUNCTIONS=\['_add', '_getOffset'\]" -s SIDE_MODULE=1 -o add.wasm

-Os 编译优化选项会将所有用不到的代码去除掉,也不会包括任何标准库
-s WASM=1 指示生成wasm文件
-s “EXPORTED_FUNCTIONS=[‘_add’, ‘_getOffset’]“ 指示导出add和getOffset这个两个函数,导出标识符要用C的方式,C++语言的时候要注意名字碾压,导出的函数需要使用extern “C”修饰
-s SIDE_MODULE=1 指示生成wasm动态库

生成的add.wasm可以使用wabt(The WebAssembly Binary Toolkit)工具集中的命令wasm2wat转换为WebAssembly的wat/wast文本S-Express格式

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
$ wasm2wat add.wasm
(module
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32) (result f64)))
(type (;2;) (func))
(import "env" "__memory_base" (global (;0;) i32))
(import "env" "memory" (memory (;0;) 256))
(func (;0;) (type 0) (result i32)
get_global 0)
(func (;1;) (type 1) (param i32) (result f64)
(local i32 f64)
get_local 0
i32.const 0
i32.gt_s
if ;; label = @1
loop ;; label = @2
get_local 2
get_global 0
get_local 1
i32.const 3
i32.shl
i32.add
f64.load
f64.add
set_local 2
get_local 1
i32.const 1
i32.add
tee_local 1
get_local 0
i32.ne
br_if 0 (;@2;)
end
end
get_local 2)
(func (;2;) (type 2)
nop)
(func (;3;) (type 2)
get_global 0
i32.const -8192
i32.sub
set_global 1
get_global 1
i32.const 5242880
i32.add
set_global 2)
(global (;1;) (mut i32) (i32.const 0))
(global (;2;) (mut i32) (i32.const 0))
(global (;3;) i32 (i32.const 0))
(export "__post_instantiate" (func 3))
(export "_add" (func 1))
(export "_getOffset" (func 0))
(export "runPostSets" (func 2))
(export "_buf" (global 3)))

可以看到add.wasm的import和export的各种东西global、func、memory,如果使用了函数指针还会有table
如果没用到memory,可以用wasm2wat将其转为wat/wast文本格式,将import memory相关去掉,
然后使用wat2wasm再编译回wasm格式就无需导入任何东西了。

调用wasm

写一个简单的页面来调用wasm代码:
[html]

\[/html\]

其实获取C代码缓冲区起始地址的getOffset并没有必要,因为wasm模块直接导出了缓冲器的标识符_buf,其实就是缓冲区的首地址
除了基本类型,JS和原生代码通过memory的buffer来交换大块的数据。

访问

需要起一个web server

1
$ python3 -m http.server 8081

然后访问http://127.0.0.1:8081/add.html,点击按钮可以看到JS调用WASM中的routine然后返回了计算结果。

最后

如果WASM模块依赖于其他库代码,目前还无法生成standalone模块,需要生成胶水js代码来访问。

另外,tableBase和memoryBase已经更名为__table_base和__memory_base

References:
[1]WebAssembly Standalone
[2]Disable linking libc in emscripten
[3]Rename tableBase/memoryBase to __table_base/__memory_base
[4]WebAssembly Dynamic Linking
[5]How to access WebAssembly linear memory from C/C++

使用WebAssembly.instantiateStreaming(fetch(‘add.wasm’), imports)以流方式获取wasmw文件时,chrome会抱怨:

1
2
Uncaught (in promise) TypeError: Failed to execute 'compile' on 'WebAssembly': 
Incorrect response MIME type. Expected 'application/wasm'.

查看server.py发现使用了mimetypes模块来管理mime types,调用mimetypes.init()方法时,会使用mimetypes.knownfiles文件中记载的mime types

查看knowfiles路径:

1
2
3
4
5
6
7
8
9
10
$ python3
>>> import mimetypes
>>> mimetypes.knownfiles
\['/etc/mime.types', '/etc/httpd/mime.types',
'/etc/httpd/conf/mime.types', '/etc/apache/mime.types',
'/etc/apache2/mime.types',
'/usr/local/etc/httpd/conf/mime.types',
'/usr/local/lib/netscape/mime.types',
'/usr/local/etc/httpd/conf/mime.types',
'/usr/local/etc/mime.types'\]

MacOS上这些文件基本都不存在,添加文件/usr/local/etc/mime.types,其内容添加如下行:

1
application/wasm wasm

重新启动http.server就好了,注意清除浏览器缓存再试。