2021-03-11 17:48:47 +00:00
import os
import os . path as p
import pytest
import time
2021-08-03 19:47:59 +00:00
import logging
2021-03-11 17:48:47 +00:00
from helpers . cluster import ClickHouseCluster , run_and_check
cluster = ClickHouseCluster ( __file__ )
2021-03-26 07:05:25 +00:00
instance = cluster . add_instance ( ' instance ' ,
2021-07-30 14:20:57 +00:00
dictionaries = [ ' configs/dictionaries/dict1.xml ' ] , main_configs = [ ' configs/config.d/config.xml ' ] , stay_alive = True )
def create_dict_simple ( ) :
instance . query ( ' DROP DICTIONARY IF EXISTS lib_dict_c ' )
instance . query ( '''
CREATE DICTIONARY lib_dict_c ( key UInt64 , value1 UInt64 , value2 UInt64 , value3 UInt64 )
PRIMARY KEY key SOURCE ( library ( PATH ' /etc/clickhouse-server/config.d/dictionaries_lib/dict_lib.so ' ) )
LAYOUT ( CACHE (
SIZE_IN_CELLS 10000000
BLOCK_SIZE 4096
FILE_SIZE 16777216
READ_BUFFER_SIZE 1048576
MAX_STORED_KEYS 1048576 ) )
LIFETIME ( 2 ) ;
''' )
2021-03-11 17:48:47 +00:00
@pytest.fixture ( scope = " module " )
def ch_cluster ( ) :
try :
cluster . start ( )
instance . query ( ' CREATE DATABASE test ' )
container_lib_path = ' /etc/clickhouse-server/config.d/dictionarites_lib/dict_lib.cpp '
instance . copy_file_to_container ( os . path . join ( os . path . dirname ( os . path . realpath ( __file__ ) ) , " configs/dict_lib.cpp " ) ,
" /etc/clickhouse-server/config.d/dictionaries_lib/dict_lib.cpp " )
instance . query ( " SYSTEM RELOAD CONFIG " )
instance . exec_in_container (
[ ' bash ' , ' -c ' ,
' /usr/bin/g++ -shared -o /etc/clickhouse-server/config.d/dictionaries_lib/dict_lib.so -fPIC /etc/clickhouse-server/config.d/dictionaries_lib/dict_lib.cpp ' ] ,
user = ' root ' )
2021-08-18 14:14:53 +00:00
instance . exec_in_container (
[ ' bash ' , ' -c ' ,
' /usr/bin/g++ -shared -o /dict_lib_copy.so -fPIC /etc/clickhouse-server/config.d/dictionaries_lib/dict_lib.cpp ' ] , user = ' root ' )
instance . exec_in_container ( [ ' bash ' , ' -c ' , ' ln -s /dict_lib_copy.so /etc/clickhouse-server/config.d/dictionaries_lib/dict_lib_symlink.so ' ] )
2021-03-11 17:48:47 +00:00
yield cluster
finally :
cluster . shutdown ( )
@pytest.fixture ( autouse = True )
def setup_teardown ( ) :
yield # run test
def test_load_all ( ch_cluster ) :
2021-04-27 04:02:59 +00:00
if instance . is_built_with_memory_sanitizer ( ) :
pytest . skip ( " Memory Sanitizer cannot work with third-party shared libraries " )
2021-08-18 14:14:53 +00:00
instance . query ( ' DROP DICTIONARY IF EXISTS lib_dict ' )
2021-03-11 17:48:47 +00:00
instance . query ( '''
CREATE DICTIONARY lib_dict ( key UInt64 , value1 UInt64 , value2 UInt64 , value3 UInt64 )
PRIMARY KEY key
2021-03-26 07:05:25 +00:00
SOURCE ( library (
PATH ' /etc/clickhouse-server/config.d/dictionaries_lib/dict_lib.so '
SETTINGS ( test_type test_simple ) ) )
2021-03-11 17:48:47 +00:00
LAYOUT ( HASHED ( ) )
LIFETIME ( MIN 0 MAX 10 )
''' )
result = instance . query ( ' SELECT * FROM lib_dict ORDER BY key ' )
expected = (
" 0 \t 10 \t 20 \t 30 \n " +
" 1 \t 11 \t 21 \t 31 \n " +
" 2 \t 12 \t 22 \t 32 \n " +
" 3 \t 13 \t 23 \t 33 \n " +
" 4 \t 14 \t 24 \t 34 \n " +
" 5 \t 15 \t 25 \t 35 \n " +
" 6 \t 16 \t 26 \t 36 \n " +
" 7 \t 17 \t 27 \t 37 \n " +
" 8 \t 18 \t 28 \t 38 \n " +
" 9 \t 19 \t 29 \t 39 \n "
)
2021-03-26 07:05:25 +00:00
instance . query ( ' SYSTEM RELOAD DICTIONARY dict1 ' )
2021-03-12 21:22:46 +00:00
instance . query ( ' DROP DICTIONARY lib_dict ' )
2021-03-11 17:48:47 +00:00
assert ( result == expected )
2021-03-26 07:05:25 +00:00
instance . query ( """
CREATE TABLE IF NOT EXISTS ` dict1_table ` (
key UInt64 , value1 UInt64 , value2 UInt64 , value3 UInt64
) ENGINE = Dictionary ( dict1 )
""" )
result = instance . query ( ' SELECT * FROM dict1_table ORDER BY key ' )
assert ( result == expected )
2021-03-11 17:48:47 +00:00
def test_load_ids ( ch_cluster ) :
2021-04-27 04:02:59 +00:00
if instance . is_built_with_memory_sanitizer ( ) :
pytest . skip ( " Memory Sanitizer cannot work with third-party shared libraries " )
2021-08-07 09:35:59 +00:00
instance . query ( ' DROP DICTIONARY IF EXISTS lib_dict_c ' )
2021-03-11 17:48:47 +00:00
instance . query ( '''
CREATE DICTIONARY lib_dict_c ( key UInt64 , value1 UInt64 , value2 UInt64 , value3 UInt64 )
2021-03-12 21:22:46 +00:00
PRIMARY KEY key SOURCE ( library ( PATH ' /etc/clickhouse-server/config.d/dictionaries_lib/dict_lib.so ' ) )
2021-03-11 17:48:47 +00:00
LAYOUT ( CACHE (
SIZE_IN_CELLS 10000000
BLOCK_SIZE 4096
FILE_SIZE 16777216
READ_BUFFER_SIZE 1048576
MAX_STORED_KEYS 1048576 ) )
LIFETIME ( 2 ) ;
''' )
result = instance . query ( ''' select dictGet(lib_dict_c, ' value1 ' , toUInt64(0)); ''' )
2021-03-11 18:10:12 +00:00
assert ( result . strip ( ) == ' 100 ' )
2021-08-01 08:51:40 +00:00
# Just check bridge is ok with a large vector of random ids
2021-08-01 09:56:48 +00:00
instance . query ( ''' select number, dictGet(lib_dict_c, ' value1 ' , toUInt64(rand())) from numbers(1000); ''' )
2021-08-01 08:51:40 +00:00
2021-03-24 07:53:15 +00:00
result = instance . query ( ''' select dictGet(lib_dict_c, ' value1 ' , toUInt64(1)); ''' )
assert ( result . strip ( ) == ' 101 ' )
instance . query ( ' DROP DICTIONARY lib_dict_c ' )
2021-03-11 17:48:47 +00:00
def test_load_keys ( ch_cluster ) :
2021-04-27 04:02:59 +00:00
if instance . is_built_with_memory_sanitizer ( ) :
pytest . skip ( " Memory Sanitizer cannot work with third-party shared libraries " )
2021-08-18 14:14:53 +00:00
instance . query ( ' DROP DICTIONARY IF EXISTS lib_dict_ckc ' )
2021-03-11 17:48:47 +00:00
instance . query ( '''
2021-03-12 14:07:20 +00:00
CREATE DICTIONARY lib_dict_ckc ( key UInt64 , value1 UInt64 , value2 UInt64 , value3 UInt64 )
2021-03-11 17:48:47 +00:00
PRIMARY KEY key
2021-03-12 21:22:46 +00:00
SOURCE ( library ( PATH ' /etc/clickhouse-server/config.d/dictionaries_lib/dict_lib.so ' ) )
2021-03-12 14:07:20 +00:00
LAYOUT ( COMPLEX_KEY_CACHE ( SIZE_IN_CELLS 10000000 ) )
2021-03-11 17:48:47 +00:00
LIFETIME ( 2 ) ;
''' )
2021-03-24 07:53:15 +00:00
result = instance . query ( ''' select dictGet(lib_dict_ckc, ' value1 ' , tuple(toUInt64(0))); ''' )
assert ( result . strip ( ) == ' 100 ' )
2021-03-12 14:07:20 +00:00
result = instance . query ( ''' select dictGet(lib_dict_ckc, ' value2 ' , tuple(toUInt64(0))); ''' )
2021-03-11 18:10:12 +00:00
assert ( result . strip ( ) == ' 200 ' )
2021-03-24 07:53:15 +00:00
instance . query ( ' DROP DICTIONARY lib_dict_ckc ' )
2021-03-11 17:48:47 +00:00
2021-03-12 21:22:46 +00:00
def test_load_all_many_rows ( ch_cluster ) :
2021-04-27 04:02:59 +00:00
if instance . is_built_with_memory_sanitizer ( ) :
pytest . skip ( " Memory Sanitizer cannot work with third-party shared libraries " )
2021-03-12 21:22:46 +00:00
num_rows = [ 1000 , 10000 , 100000 , 1000000 ]
2021-08-18 14:14:53 +00:00
instance . query ( ' DROP DICTIONARY IF EXISTS lib_dict ' )
2021-03-12 21:22:46 +00:00
for num in num_rows :
instance . query ( '''
CREATE DICTIONARY lib_dict ( key UInt64 , value1 UInt64 , value2 UInt64 , value3 UInt64 )
PRIMARY KEY key
SOURCE ( library (
PATH ' /etc/clickhouse-server/config.d/dictionaries_lib/dict_lib.so '
2021-03-26 07:05:25 +00:00
SETTINGS ( num_rows { } test_type test_many_rows ) ) )
2021-03-12 21:22:46 +00:00
LAYOUT ( HASHED ( ) )
LIFETIME ( MIN 0 MAX 10 )
''' .format(num))
result = instance . query ( ' SELECT * FROM lib_dict ORDER BY key ' )
expected = instance . query ( ' SELECT number, number, number, number FROM numbers( {} ) ' . format ( num ) )
instance . query ( ' DROP DICTIONARY lib_dict ' )
assert ( result == expected )
2021-04-05 13:13:07 +00:00
def test_null_values ( ch_cluster ) :
2021-04-27 04:02:59 +00:00
if instance . is_built_with_memory_sanitizer ( ) :
pytest . skip ( " Memory Sanitizer cannot work with third-party shared libraries " )
2021-04-05 13:13:07 +00:00
instance . query ( ' SYSTEM RELOAD DICTIONARY dict2 ' )
instance . query ( """
CREATE TABLE IF NOT EXISTS ` dict2_table ` (
key UInt64 , value1 UInt64 , value2 UInt64 , value3 UInt64
) ENGINE = Dictionary ( dict2 )
""" )
result = instance . query ( ' SELECT * FROM dict2_table ORDER BY key ' )
expected = " 0 \t 12 \t 12 \t 12 \n "
assert ( result == expected )
2021-07-30 14:20:57 +00:00
def test_recover_after_bridge_crash ( ch_cluster ) :
if instance . is_built_with_memory_sanitizer ( ) :
pytest . skip ( " Memory Sanitizer cannot work with third-party shared libraries " )
create_dict_simple ( )
result = instance . query ( ''' select dictGet(lib_dict_c, ' value1 ' , toUInt64(0)); ''' )
assert ( result . strip ( ) == ' 100 ' )
result = instance . query ( ''' select dictGet(lib_dict_c, ' value1 ' , toUInt64(1)); ''' )
assert ( result . strip ( ) == ' 101 ' )
instance . exec_in_container ( [ ' bash ' , ' -c ' , ' kill -9 `pidof clickhouse-library-bridge` ' ] , user = ' root ' )
instance . query ( ' SYSTEM RELOAD DICTIONARY lib_dict_c ' )
result = instance . query ( ''' select dictGet(lib_dict_c, ' value1 ' , toUInt64(0)); ''' )
assert ( result . strip ( ) == ' 100 ' )
result = instance . query ( ''' select dictGet(lib_dict_c, ' value1 ' , toUInt64(1)); ''' )
assert ( result . strip ( ) == ' 101 ' )
instance . exec_in_container ( [ ' bash ' , ' -c ' , ' kill -9 `pidof clickhouse-library-bridge` ' ] , user = ' root ' )
instance . query ( ' DROP DICTIONARY lib_dict_c ' )
def test_server_restart_bridge_might_be_stil_alive ( ch_cluster ) :
if instance . is_built_with_memory_sanitizer ( ) :
pytest . skip ( " Memory Sanitizer cannot work with third-party shared libraries " )
create_dict_simple ( )
result = instance . query ( ''' select dictGet(lib_dict_c, ' value1 ' , toUInt64(1)); ''' )
assert ( result . strip ( ) == ' 101 ' )
instance . restart_clickhouse ( )
result = instance . query ( ''' select dictGet(lib_dict_c, ' value1 ' , toUInt64(1)); ''' )
assert ( result . strip ( ) == ' 101 ' )
instance . exec_in_container ( [ ' bash ' , ' -c ' , ' kill -9 `pidof clickhouse-library-bridge` ' ] , user = ' root ' )
instance . restart_clickhouse ( )
result = instance . query ( ''' select dictGet(lib_dict_c, ' value1 ' , toUInt64(1)); ''' )
assert ( result . strip ( ) == ' 101 ' )
instance . query ( ' DROP DICTIONARY lib_dict_c ' )
2021-08-01 08:51:40 +00:00
def test_bridge_dies_with_parent ( ch_cluster ) :
if instance . is_built_with_memory_sanitizer ( ) :
pytest . skip ( " Memory Sanitizer cannot work with third-party shared libraries " )
if instance . is_built_with_address_sanitizer ( ) :
pytest . skip ( " Leak sanitizer falsely reports about a leak of 16 bytes in clickhouse-odbc-bridge " )
create_dict_simple ( )
result = instance . query ( ''' select dictGet(lib_dict_c, ' value1 ' , toUInt64(1)); ''' )
assert ( result . strip ( ) == ' 101 ' )
clickhouse_pid = instance . get_process_pid ( " clickhouse server " )
bridge_pid = instance . get_process_pid ( " library-bridge " )
assert clickhouse_pid is not None
assert bridge_pid is not None
while clickhouse_pid is not None :
try :
instance . exec_in_container ( [ " kill " , str ( clickhouse_pid ) ] , privileged = True , user = ' root ' )
except :
pass
clickhouse_pid = instance . get_process_pid ( " clickhouse server " )
time . sleep ( 1 )
for i in range ( 30 ) :
time . sleep ( 1 )
bridge_pid = instance . get_process_pid ( " library-bridge " )
if bridge_pid is None :
break
if bridge_pid :
out = instance . exec_in_container ( [ " gdb " , " -p " , str ( bridge_pid ) , " --ex " , " thread apply all bt " , " --ex " , " q " ] ,
privileged = True , user = ' root ' )
logging . debug ( f " Bridge is running, gdb output: \n { out } " )
assert clickhouse_pid is None
assert bridge_pid is None
instance . start_clickhouse ( 20 )
2021-08-07 09:35:59 +00:00
instance . query ( ' DROP DICTIONARY lib_dict_c ' )
2021-08-01 08:51:40 +00:00
2021-08-18 14:14:53 +00:00
def test_path_validation ( ch_cluster ) :
if instance . is_built_with_memory_sanitizer ( ) :
pytest . skip ( " Memory Sanitizer cannot work with third-party shared libraries " )
instance . query ( ' DROP DICTIONARY IF EXISTS lib_dict_c ' )
instance . query ( '''
CREATE DICTIONARY lib_dict_c ( key UInt64 , value1 UInt64 , value2 UInt64 , value3 UInt64 )
PRIMARY KEY key SOURCE ( library ( PATH ' /etc/clickhouse-server/config.d/dictionaries_lib/dict_lib_symlink.so ' ) )
LAYOUT ( CACHE (
SIZE_IN_CELLS 10000000
BLOCK_SIZE 4096
FILE_SIZE 16777216
READ_BUFFER_SIZE 1048576
MAX_STORED_KEYS 1048576 ) )
LIFETIME ( 2 ) ;
''' )
result = instance . query ( ''' select dictGet(lib_dict_c, ' value1 ' , toUInt64(1)); ''' )
assert ( result . strip ( ) == ' 101 ' )
instance . query ( ' DROP DICTIONARY IF EXISTS lib_dict_c ' )
instance . query ( '''
CREATE DICTIONARY lib_dict_c ( key UInt64 , value1 UInt64 , value2 UInt64 , value3 UInt64 )
PRIMARY KEY key SOURCE ( library ( PATH ' /etc/clickhouse-server/config.d/dictionaries_lib/../../../../dict_lib_copy.so ' ) )
LAYOUT ( CACHE (
SIZE_IN_CELLS 10000000
BLOCK_SIZE 4096
FILE_SIZE 16777216
READ_BUFFER_SIZE 1048576
MAX_STORED_KEYS 1048576 ) )
LIFETIME ( 2 ) ;
''' )
result = instance . query_and_get_error ( ''' select dictGet(lib_dict_c, ' value1 ' , toUInt64(1)); ''' )
assert ( ' DB::Exception: File path /etc/clickhouse-server/config.d/dictionaries_lib/../../../../dict_lib_copy.so is not inside /etc/clickhouse-server/config.d/dictionaries_lib ' in result )
2021-03-11 17:48:47 +00:00
if __name__ == ' __main__ ' :
cluster . start ( )
input ( " Cluster created, press any key to destroy... " )
cluster . shutdown ( )