From 86adb437478cd23abe6663e94e4519e4aefc95b7 Mon Sep 17 00:00:00 2001 From: Sam Hughes Date: Mon, 5 Jul 2021 01:25:59 -0700 Subject: [PATCH 01/37] admin split-out: Remove cpp source and build.mk --- .circleci/config.yml | 145 - mk/main.mk | 3 - mk/tools.mk | 4 - src/.gitignore | 1 - src/CPPLINT.cfg | 23 - src/Makefile | 4 - src/README.md | 38 - src/activity_logger.cc | 47 - src/activity_logger.hpp | 58 - src/arch/CPPLINT.cfg | 1 - src/arch/address.cc | 638 - src/arch/address.hpp | 241 - src/arch/arch.cc | 25 - src/arch/arch.hpp | 16 - src/arch/barrier.cc | 35 - src/arch/barrier.hpp | 25 - src/arch/compiler.hpp | 38 - src/arch/fd_send_recv.cc | 91 - src/arch/fd_send_recv.hpp | 32 - src/arch/filesystem.hpp | 30 - src/arch/io/blocker_pool.cc | 172 - src/arch/io/blocker_pool.hpp | 49 - src/arch/io/concurrency.hpp | 100 - src/arch/io/disk.cc | 678 - src/arch/io/disk.hpp | 131 - src/arch/io/disk/accounting.cc | 117 - src/arch/io/disk/accounting.hpp | 118 - src/arch/io/disk/conflict_resolving.cc | 285 - src/arch/io/disk/conflict_resolving.hpp | 112 - src/arch/io/disk/filestat.cc | 29 - src/arch/io/disk/filestat.hpp | 10 - src/arch/io/disk/pool.cc | 229 - src/arch/io/disk/pool.hpp | 164 - src/arch/io/disk/stats.cc | 28 - src/arch/io/disk/stats.hpp | 36 - src/arch/io/disk/stats_2.cc | 49 - src/arch/io/disk/stats_2.hpp | 40 - src/arch/io/event_watcher.cc | 253 - src/arch/io/event_watcher.hpp | 142 - src/arch/io/io_utils.cc | 88 - src/arch/io/io_utils.hpp | 63 - src/arch/io/network.cc | 1824 - src/arch/io/network.hpp | 562 - src/arch/io/openssl.hpp | 30 - src/arch/io/timer/timer_kqueue_provider.cc | 123 - src/arch/io/timer/timer_kqueue_provider.hpp | 29 - src/arch/io/timer/timer_signal_provider.cc | 102 - src/arch/io/timer/timer_signal_provider.hpp | 29 - src/arch/io/timer/timer_windows_provider.cc | 38 - src/arch/io/timer/timer_windows_provider.hpp | 27 - src/arch/io/timer/timerfd_provider.cc | 89 - src/arch/io/timer/timerfd_provider.hpp | 28 - src/arch/io/timer_provider.hpp | 49 - src/arch/os_signal.cc | 63 - src/arch/os_signal.hpp | 28 - src/arch/process.hpp | 30 - src/arch/runtime/callable_action.hpp | 87 - src/arch/runtime/context_switching.cc | 924 - src/arch/runtime/context_switching.hpp | 259 - src/arch/runtime/coro_profiler.cc | 389 - src/arch/runtime/coro_profiler.hpp | 197 - src/arch/runtime/coroutines.cc | 629 - src/arch/runtime/coroutines.hpp | 315 - src/arch/runtime/event_queue.cc | 43 - src/arch/runtime/event_queue.hpp | 52 - src/arch/runtime/event_queue/epoll.cc | 196 - src/arch/runtime/event_queue/epoll.hpp | 54 - src/arch/runtime/event_queue/iocp.cc | 191 - src/arch/runtime/event_queue/iocp.hpp | 40 - src/arch/runtime/event_queue/kqueue.cc | 203 - src/arch/runtime/event_queue/kqueue.hpp | 55 - src/arch/runtime/event_queue/poll.cc | 204 - src/arch/runtime/event_queue/poll.hpp | 38 - src/arch/runtime/event_queue_types.hpp | 28 - src/arch/runtime/message_hub.cc | 258 - src/arch/runtime/message_hub.hpp | 103 - src/arch/runtime/runtime.cc | 78 - src/arch/runtime/runtime.hpp | 48 - src/arch/runtime/runtime_utils.cc | 77 - src/arch/runtime/runtime_utils.hpp | 121 - src/arch/runtime/starter.hpp | 15 - src/arch/runtime/system_event.hpp | 23 - src/arch/runtime/system_event/eventfd.cc | 28 - src/arch/runtime/system_event/eventfd.hpp | 23 - .../runtime/system_event/eventfd_event.cc | 52 - .../runtime/system_event/eventfd_event.hpp | 32 - src/arch/runtime/system_event/pipe_event.cc | 67 - src/arch/runtime/system_event/pipe_event.hpp | 32 - .../runtime/system_event/windows_event.cc | 15 - .../runtime/system_event/windows_event.hpp | 24 - src/arch/runtime/thread_pool.cc | 523 - src/arch/runtime/thread_pool.hpp | 210 - src/arch/spinlock.cc | 46 - src/arch/spinlock.hpp | 58 - src/arch/timer.cc | 117 - src/arch/timer.hpp | 52 - src/arch/timing.cc | 100 - src/arch/timing.hpp | 73 - src/arch/types.cc | 37 - src/arch/types.hpp | 157 - src/arch/windows_stub/pthread.cc | 176 - src/arch/windows_stub/pthread.h | 53 - src/arch/windows_stub/sys/uio.h | 13 - src/arch/windows_stub/unistd.h | 64 - src/assignment_sentry.hpp | 32 - src/backtrace.cc | 406 - src/backtrace.hpp | 105 - src/btree/backfill.cc | 733 - src/btree/backfill.hpp | 166 - src/btree/backfill_debug.cc | 72 - src/btree/backfill_debug.hpp | 29 - src/btree/backfill_types.cc | 21 - src/btree/backfill_types.hpp | 98 - src/btree/concurrent_traversal.cc | 202 - src/btree/concurrent_traversal.hpp | 72 - src/btree/depth_first_traversal.cc | 274 - src/btree/depth_first_traversal.hpp | 158 - src/btree/get_distribution.cc | 100 - src/btree/get_distribution.hpp | 16 - src/btree/internal_node.cc | 466 - src/btree/internal_node.hpp | 84 - src/btree/keys.cc | 250 - src/btree/keys.hpp | 390 - src/btree/leaf_node.cc | 1822 - src/btree/leaf_node.hpp | 222 - src/btree/node.cc | 60 - src/btree/node.hpp | 92 - src/btree/operations.cc | 665 - src/btree/operations.hpp | 171 - src/btree/parallel_traversal.cc | 691 - src/btree/parallel_traversal.hpp | 187 - src/btree/reql_specific.cc | 429 - src/btree/reql_specific.hpp | 210 - src/btree/secondary_operations.cc | 273 - src/btree/secondary_operations.hpp | 127 - src/btree/stats.hpp | 48 - src/btree/superblock.hpp | 45 - src/btree/traversal_progress.cc | 58 - src/btree/traversal_progress.hpp | 73 - src/btree/types.hpp | 26 - src/buffer_cache/alt.cc | 846 - src/buffer_cache/alt.hpp | 362 - src/buffer_cache/blob.cc | 800 - src/buffer_cache/blob.hpp | 228 - src/buffer_cache/block_version.hpp | 46 - src/buffer_cache/cache_account.cc | 49 - src/buffer_cache/cache_account.hpp | 36 - src/buffer_cache/cache_balancer.cc | 259 - src/buffer_cache/cache_balancer.hpp | 202 - src/buffer_cache/evicter.cc | 257 - src/buffer_cache/evicter.hpp | 126 - src/buffer_cache/eviction_bag.cc | 65 - src/buffer_cache/eviction_bag.hpp | 47 - src/buffer_cache/free_list.cc | 88 - src/buffer_cache/free_list.hpp | 35 - src/buffer_cache/page.cc | 739 - src/buffer_cache/page.hpp | 256 - src/buffer_cache/page_cache.cc | 1575 - src/buffer_cache/page_cache.hpp | 679 - src/buffer_cache/serialize_onto_blob.cc | 20 - src/buffer_cache/serialize_onto_blob.hpp | 53 - src/buffer_cache/stats.cc | 36 - src/buffer_cache/stats.hpp | 36 - src/buffer_cache/types.cc | 10 - src/buffer_cache/types.hpp | 42 - src/build.mk | 405 - src/cjson/cJSON.cc | 924 - src/cjson/cJSON.hpp | 132 - src/cjson/json.cc | 130 - src/cjson/json.hpp | 133 - src/client_protocol/client_server_error.hpp | 26 - src/client_protocol/json.cc | 252 - src/client_protocol/json.hpp | 42 - src/client_protocol/protocols.cc | 25 - src/client_protocol/protocols.hpp | 26 - src/client_protocol/server.cc | 771 - src/client_protocol/server.hpp | 145 - .../administration/admin_op_exc.hpp | 98 - .../artificial_reql_cluster_interface.cc | 855 - .../artificial_reql_cluster_interface.hpp | 353 - .../auth/authentication_error.hpp | 38 - .../auth/base_artificial_table_backend.hpp | 57 - .../auth/base_authenticator.hpp | 33 - src/clustering/administration/auth/grant.cc | 99 - src/clustering/administration/auth/grant.hpp | 28 - .../administration/auth/password.cc | 119 - .../administration/auth/password.hpp | 47 - .../administration/auth/permission_error.hpp | 29 - .../administration/auth/permissions.cc | 204 - .../administration/auth/permissions.hpp | 63 - .../permissions_artificial_table_backend.cc | 680 - .../permissions_artificial_table_backend.hpp | 82 - .../auth/plaintext_authenticator.cc | 61 - .../auth/plaintext_authenticator.hpp | 30 - .../auth/scram_authenticator.cc | 250 - .../auth/scram_authenticator.hpp | 40 - src/clustering/administration/auth/user.cc | 298 - src/clustering/administration/auth/user.hpp | 80 - .../administration/auth/user_context.cc | 233 - .../administration/auth/user_context.hpp | 73 - .../administration/auth/username.cc | 42 - .../administration/auth/username.hpp | 33 - .../auth/users_artificial_table_backend.cc | 134 - .../auth/users_artificial_table_backend.hpp | 45 - .../administration/cluster_config.cc | 176 - .../administration/cluster_config.hpp | 107 - .../administration/datum_adapter.cc | 318 - .../administration/datum_adapter.hpp | 214 - .../administration/http/cyanide.hpp | 16 - src/clustering/administration/http/server.cc | 44 - src/clustering/administration/http/server.hpp | 47 - src/clustering/administration/issues/issue.cc | 38 - .../administration/issues/issue.hpp | 95 - .../administration/issues/issues_backend.cc | 132 - .../administration/issues/issues_backend.hpp | 81 - src/clustering/administration/issues/local.cc | 86 - .../administration/issues/local.hpp | 70 - .../administration/issues/log_write.cc | 94 - .../administration/issues/log_write.hpp | 66 - .../administration/issues/memory.cc | 101 - .../administration/issues/memory.hpp | 66 - .../administration/issues/name_collision.cc | 311 - .../administration/issues/name_collision.hpp | 34 - .../administration/issues/non_transitive.cc | 83 - .../administration/issues/non_transitive.hpp | 52 - .../administration/issues/outdated_index.cc | 163 - .../administration/issues/outdated_index.hpp | 55 - src/clustering/administration/issues/table.cc | 166 - .../administration/issues/table.hpp | 37 - src/clustering/administration/jobs/backend.cc | 237 - .../administration/jobs/backend.hpp | 81 - src/clustering/administration/jobs/manager.cc | 215 - .../administration/jobs/manager.hpp | 66 - src/clustering/administration/jobs/report.cc | 260 - src/clustering/administration/jobs/report.hpp | 177 - src/clustering/administration/jobs/report.tcc | 66 - .../administration/logs/log_transfer.cc | 74 - .../administration/logs/log_transfer.hpp | 61 - .../administration/logs/log_writer.cc | 747 - .../administration/logs/log_writer.hpp | 110 - .../administration/logs/logs_backend.cc | 601 - .../administration/logs/logs_backend.hpp | 96 - .../administration/main/cache_size.cc | 370 - .../administration/main/cache_size.hpp | 13 - .../administration/main/command_line.cc | 2876 - .../administration/main/command_line.hpp | 38 - .../administration/main/directory_lock.cc | 150 - .../administration/main/directory_lock.hpp | 103 - .../administration/main/initial_join.cc | 157 - .../administration/main/initial_join.hpp | 57 - .../administration/main/memory_checker.cc | 80 - .../administration/main/memory_checker.hpp | 47 - src/clustering/administration/main/names.cc | 130 - src/clustering/administration/main/names.hpp | 9 - src/clustering/administration/main/options.cc | 473 - .../administration/main/options.hpp | 262 - src/clustering/administration/main/path.cc | 34 - src/clustering/administration/main/path.hpp | 15 - src/clustering/administration/main/ports.hpp | 19 - src/clustering/administration/main/serve.cc | 707 - src/clustering/administration/main/serve.hpp | 164 - .../administration/main/watchable_fields.hpp | 72 - .../administration/main/windows_service.cc | 282 - .../administration/main/windows_service.hpp | 44 - src/clustering/administration/metadata.cc | 77 - src/clustering/administration/metadata.hpp | 209 - .../namespace_interface_repository.cc | 219 - .../namespace_interface_repository.hpp | 70 - .../administration/perfmon_collection_repo.cc | 23 - .../perfmon_collection_repo.hpp | 32 - .../persist/branch_history_manager.cc | 119 - .../persist/branch_history_manager.hpp | 44 - src/clustering/administration/persist/file.cc | 444 - .../administration/persist/file.hpp | 205 - .../administration/persist/file_keys.cc | 60 - .../administration/persist/file_keys.hpp | 57 - .../persist/migrate/metadata_v1_14.cc | 37 - .../persist/migrate/metadata_v1_14.hpp | 136 - .../persist/migrate/metadata_v1_16.cc | 87 - .../persist/migrate/metadata_v1_16.hpp | 178 - .../persist/migrate/metadata_v2_1.cc | 13 - .../persist/migrate/metadata_v2_1.hpp | 18 - .../persist/migrate/migrate_v1_14.cc | 475 - .../persist/migrate/migrate_v1_14.hpp | 15 - .../persist/migrate/migrate_v1_16.cc | 690 - .../persist/migrate/migrate_v1_16.hpp | 22 - .../persist/migrate/migrate_v2_1.cc | 91 - .../persist/migrate/migrate_v2_1.hpp | 21 - .../persist/migrate/migrate_v2_3.cc | 44 - .../persist/migrate/migrate_v2_3.hpp | 15 - .../persist/migrate/rewrite.hpp | 26 - .../persist/raft_storage_interface.cc | 244 - .../persist/raft_storage_interface.hpp | 90 - .../administration/persist/semilattice.cc | 32 - .../administration/persist/semilattice.hpp | 40 - .../administration/persist/table_interface.cc | 363 - .../persist/table_interface.hpp | 96 - .../real_reql_cluster_interface.cc | 1490 - .../real_reql_cluster_interface.hpp | 348 - .../administration/servers/auto_reconnect.cc | 137 - .../administration/servers/auto_reconnect.hpp | 57 - .../administration/servers/config_client.cc | 261 - .../administration/servers/config_client.hpp | 108 - .../administration/servers/config_server.cc | 98 - .../administration/servers/config_server.hpp | 50 - .../administration/servers/network_logger.cc | 62 - .../administration/servers/network_logger.hpp | 45 - .../administration/servers/server_common.cc | 119 - .../administration/servers/server_common.hpp | 72 - .../administration/servers/server_config.cc | 186 - .../administration/servers/server_config.hpp | 51 - .../administration/servers/server_metadata.cc | 49 - .../servers/server_metadata.hpp | 62 - .../administration/servers/server_status.cc | 172 - .../administration/servers/server_status.hpp | 54 - .../stats/debug_stats_backend.cc | 106 - .../stats/debug_stats_backend.hpp | 57 - .../administration/stats/request.cc | 578 - .../administration/stats/request.hpp | 230 - .../administration/stats/stat_manager.cc | 78 - .../administration/stats/stat_manager.hpp | 54 - .../administration/stats/stats_backend.cc | 215 - .../administration/stats/stats_backend.hpp | 79 - .../administration/tables/calculate_status.cc | 179 - .../tables/calculate_status.hpp | 79 - .../tables/database_metadata.hpp | 42 - .../administration/tables/db_config.cc | 266 - .../administration/tables/db_config.hpp | 67 - .../tables/debug_table_status.cc | 315 - .../tables/debug_table_status.hpp | 48 - .../administration/tables/generate_config.cc | 447 - .../administration/tables/generate_config.hpp | 45 - .../administration/tables/name_resolver.cc | 146 - .../administration/tables/name_resolver.hpp | 93 - .../administration/tables/split_points.cc | 243 - .../administration/tables/split_points.hpp | 63 - .../administration/tables/table_common.cc | 156 - .../administration/tables/table_common.hpp | 84 - .../administration/tables/table_config.cc | 843 - .../administration/tables/table_config.hpp | 89 - .../administration/tables/table_metadata.cc | 282 - .../administration/tables/table_metadata.hpp | 186 - .../administration/tables/table_status.cc | 210 - .../administration/tables/table_status.hpp | 63 - src/clustering/generic/minidir.hpp | 210 - src/clustering/generic/minidir.tcc | 265 - src/clustering/generic/multi_client_client.cc | 57 - .../generic/multi_client_client.hpp | 57 - .../generic/multi_client_metadata.hpp | 36 - .../generic/multi_client_server.hpp | 87 - .../generic/nonoverlapping_regions.cc | 52 - .../generic/nonoverlapping_regions.hpp | 54 - src/clustering/generic/raft_core.cc | 10 - src/clustering/generic/raft_core.hpp | 1015 - src/clustering/generic/raft_core.tcc | 2064 - src/clustering/generic/raft_network.hpp | 82 - src/clustering/generic/raft_network.tcc | 87 - src/clustering/generic/registrant.hpp | 76 - src/clustering/generic/registrar.hpp | 194 - .../generic/registration_metadata.hpp | 36 - src/clustering/id_types.hpp | 8 - .../backfill_item_seq.hpp | 178 - .../backfill_metadata.cc | 29 - .../backfill_metadata.hpp | 232 - .../backfill_throttler.hpp | 82 - .../immediate_consistency/backfillee.cc | 531 - .../immediate_consistency/backfillee.hpp | 157 - .../immediate_consistency/backfiller.cc | 500 - .../immediate_consistency/backfiller.hpp | 121 - .../immediate_consistency/history.cc | 257 - .../immediate_consistency/history.hpp | 246 - .../immediate_consistency/local_replicator.cc | 115 - .../local_replicator.hpp | 75 - .../primary_dispatcher.cc | 280 - .../primary_dispatcher.hpp | 232 - .../remote_replicator_client.cc | 495 - .../remote_replicator_client.hpp | 184 - .../remote_replicator_metadata.cc | 14 - .../remote_replicator_metadata.hpp | 61 - .../remote_replicator_server.cc | 105 - .../remote_replicator_server.hpp | 89 - .../immediate_consistency/replica.cc | 124 - .../immediate_consistency/replica.hpp | 74 - .../standard_backfill_throttler.cc | 89 - .../standard_backfill_throttler.hpp | 31 - .../query_routing/direct_query_server.cc | 54 - .../query_routing/direct_query_server.hpp | 39 - src/clustering/query_routing/metadata.cc | 24 - src/clustering/query_routing/metadata.hpp | 104 - .../query_routing/primary_query_client.cc | 123 - .../query_routing/primary_query_client.hpp | 68 - .../query_routing/primary_query_server.cc | 85 - .../query_routing/primary_query_server.hpp | 82 - .../query_routing/table_query_client.cc | 597 - .../query_routing/table_query_client.hpp | 182 - .../table_contract/branch_history_gc.cc | 102 - .../table_contract/branch_history_gc.hpp | 65 - .../table_contract/contract_metadata.cc | 299 - .../table_contract/contract_metadata.hpp | 323 - .../coordinator/calculate_contracts.cc | 762 - .../coordinator/calculate_contracts.hpp | 20 - .../coordinator/calculate_misc.cc | 104 - .../coordinator/calculate_misc.hpp | 21 - .../table_contract/coordinator/check_ready.cc | 49 - .../coordinator/check_ready.hpp | 14 - .../table_contract/coordinator/coordinator.cc | 276 - .../coordinator/coordinator.hpp | 94 - src/clustering/table_contract/cpu_sharding.cc | 34 - .../table_contract/cpu_sharding.hpp | 47 - .../table_contract/emergency_repair.cc | 238 - .../table_contract/emergency_repair.hpp | 22 - .../table_contract/executor/exec.cc | 6 - .../table_contract/executor/exec.hpp | 97 - .../table_contract/executor/exec_erase.cc | 44 - .../table_contract/executor/exec_erase.hpp | 31 - .../table_contract/executor/exec_primary.cc | 671 - .../table_contract/executor/exec_primary.hpp | 157 - .../table_contract/executor/exec_secondary.cc | 223 - .../executor/exec_secondary.hpp | 53 - .../table_contract/executor/executor.cc | 359 - .../table_contract/executor/executor.hpp | 168 - .../backfill_progress_tracker.cc | 27 - .../backfill_progress_tracker.hpp | 34 - .../table_manager/multi_table_manager.cc | 712 - .../table_manager/multi_table_manager.hpp | 433 - .../server_name_cache_updater.cc | 113 - .../server_name_cache_updater.hpp | 34 - .../table_manager/sindex_manager.cc | 140 - .../table_manager/sindex_manager.hpp | 42 - src/clustering/table_manager/table_manager.cc | 269 - .../table_manager/table_manager.hpp | 191 - .../table_manager/table_meta_client.cc | 855 - .../table_manager/table_meta_client.hpp | 297 - .../table_manager/table_metadata.cc | 116 - .../table_manager/table_metadata.hpp | 370 - src/concurrency/access.hpp | 14 - src/concurrency/auto_drainer.cc | 107 - src/concurrency/auto_drainer.hpp | 156 - src/concurrency/cache_line_padded.hpp | 22 - src/concurrency/cond_var.cc | 31 - src/concurrency/cond_var.hpp | 45 - src/concurrency/coro_pool.hpp | 106 - src/concurrency/cross_thread_mutex.cc | 60 - src/concurrency/cross_thread_mutex.hpp | 74 - src/concurrency/cross_thread_semaphore.hpp | 227 - src/concurrency/cross_thread_signal.cc | 27 - src/concurrency/cross_thread_signal.hpp | 73 - src/concurrency/cross_thread_watchable.hpp | 195 - src/concurrency/cross_thread_watchable.tcc | 96 - src/concurrency/exponential_backoff.hpp | 43 - src/concurrency/fifo_checker.cc | 158 - src/concurrency/fifo_checker.hpp | 220 - src/concurrency/fifo_enforcer.cc | 207 - src/concurrency/fifo_enforcer.hpp | 297 - src/concurrency/fifo_enforcer_queue.hpp | 60 - src/concurrency/fifo_enforcer_queue.tcc | 112 - src/concurrency/interruptor.cc | 12 - src/concurrency/interruptor.hpp | 24 - src/concurrency/mutex.cc | 50 - src/concurrency/mutex.hpp | 62 - src/concurrency/mutex_assertion.hpp | 247 - src/concurrency/new_mutex.hpp | 69 - src/concurrency/new_semaphore.cc | 121 - src/concurrency/new_semaphore.hpp | 99 - src/concurrency/one_per_thread.hpp | 90 - src/concurrency/pmap.hpp | 128 - src/concurrency/promise.hpp | 65 - src/concurrency/pubsub.hpp | 158 - src/concurrency/pump_coro.cc | 113 - src/concurrency/pump_coro.hpp | 90 - src/concurrency/queue/accounting.hpp | 131 - .../queue/disk_backed_queue_wrapper.hpp | 164 - src/concurrency/queue/limited_fifo.hpp | 63 - src/concurrency/queue/passive_producer.hpp | 101 - src/concurrency/queue/unlimited_fifo.hpp | 72 - src/concurrency/rwlock.cc | 116 - src/concurrency/rwlock.hpp | 88 - src/concurrency/semaphore.cc | 186 - src/concurrency/semaphore.hpp | 171 - src/concurrency/signal.cc | 60 - src/concurrency/signal.hpp | 121 - src/concurrency/timestamp_enforcer.cc | 34 - src/concurrency/timestamp_enforcer.hpp | 42 - src/concurrency/wait_any.cc | 35 - src/concurrency/wait_any.hpp | 48 - src/concurrency/watchable.hpp | 279 - src/concurrency/watchable.tcc | 257 - src/concurrency/watchable_map.hpp | 209 - src/concurrency/watchable_map.tcc | 303 - src/concurrency/watchable_transform.hpp | 255 - src/concurrency/watchable_transform.tcc | 232 - src/concurrency/watchdog_timer.cc | 79 - src/concurrency/watchdog_timer.hpp | 57 - src/config/args.hpp | 202 - src/containers/archive/archive.cc | 160 - src/containers/archive/archive.hpp | 278 - src/containers/archive/boost_types.hpp | 117 - src/containers/archive/buffer_group_stream.cc | 66 - .../archive/buffer_group_stream.hpp | 68 - src/containers/archive/buffer_stream.hpp | 48 - src/containers/archive/file_stream.cc | 76 - src/containers/archive/file_stream.hpp | 35 - src/containers/archive/optional.hpp | 32 - src/containers/archive/socket_stream.cc | 420 - src/containers/archive/socket_stream.hpp | 196 - src/containers/archive/stl_types.cc | 42 - src/containers/archive/stl_types.hpp | 273 - src/containers/archive/string_stream.cc | 42 - src/containers/archive/string_stream.hpp | 40 - src/containers/archive/tcp_conn_stream.cc | 168 - src/containers/archive/tcp_conn_stream.hpp | 99 - src/containers/archive/tuple.hpp | 80 - src/containers/archive/varint.cc | 41 - src/containers/archive/varint.hpp | 68 - src/containers/archive/vector_stream.cc | 52 - src/containers/archive/vector_stream.hpp | 44 - src/containers/archive/versioned.hpp | 274 - src/containers/auth_key.cc | 36 - src/containers/auth_key.hpp | 44 - src/containers/backindex_bag.hpp | 151 - src/containers/binary_blob.cc | 15 - src/containers/binary_blob.hpp | 65 - src/containers/buffer_group.cc | 47 - src/containers/buffer_group.hpp | 88 - src/containers/clone_ptr.hpp | 60 - src/containers/clone_ptr.tcc | 73 - src/containers/counted.hpp | 296 - src/containers/death_runner.hpp | 21 - src/containers/disk_backed_queue.cc | 228 - src/containers/disk_backed_queue.hpp | 164 - src/containers/empty_value.hpp | 20 - src/containers/half_intrusive_list.hpp | 95 - src/containers/incremental_lenses.hpp | 215 - src/containers/intrusive_list.hpp | 204 - src/containers/intrusive_priority_queue.hpp | 193 - src/containers/lazy_erase_vector.hpp | 51 - src/containers/lifetime.hpp | 57 - src/containers/lru_cache.hpp | 70 - src/containers/map_sentries.hpp | 164 - src/containers/name_string.cc | 59 - src/containers/name_string.hpp | 54 - src/containers/object_buffer.hpp | 115 - src/containers/optional.hpp | 183 - src/containers/printf_buffer.cc | 104 - src/containers/printf_buffer.hpp | 53 - src/containers/priority_queue.hpp | 65 - src/containers/priority_queue.tcc | 157 - src/containers/range_map.hpp | 396 - src/containers/scoped.hpp | 371 - src/containers/segmented_vector.hpp | 146 - src/containers/shared_buffer.cc | 32 - src/containers/shared_buffer.hpp | 117 - src/containers/tribool.hpp | 16 - src/containers/two_level_array.hpp | 99 - src/containers/uuid.cc | 402 - src/containers/uuid.hpp | 67 - src/crypto/base64.cc | 100 - src/crypto/base64.hpp | 30 - src/crypto/compare_equal.hpp | 20 - src/crypto/error.cc | 44 - src/crypto/error.hpp | 38 - src/crypto/hash.cc | 57 - src/crypto/hash.hpp | 33 - src/crypto/hmac.cc | 72 - src/crypto/hmac.hpp | 47 - src/crypto/initialization_guard.cc | 56 - src/crypto/initialization_guard.hpp | 15 - src/crypto/pbkcs5_pbkdf2_hmac.cc | 35 - src/crypto/pbkcs5_pbkdf2_hmac.hpp | 51 - src/crypto/random.cc | 16 - src/crypto/random.hpp | 26 - src/crypto/saslprep.cc | 63 - src/crypto/saslprep.hpp | 13 - src/debug.cc | 121 - src/debug.hpp | 95 - src/do_on_thread.hpp | 84 - src/errors.cc | 326 - src/errors.hpp | 271 - src/extproc/extproc_job.cc | 55 - src/extproc/extproc_job.hpp | 53 - src/extproc/extproc_pool.cc | 71 - src/extproc/extproc_pool.hpp | 87 - src/extproc/extproc_spawner.cc | 333 - src/extproc/extproc_spawner.hpp | 50 - src/extproc/extproc_worker.cc | 198 - src/extproc/extproc_worker.hpp | 59 - src/extproc/http_job.cc | 952 - src/extproc/http_job.hpp | 29 - src/extproc/http_runner.cc | 100 - src/extproc/http_runner.hpp | 141 - src/extproc/js_job.cc | 759 - src/extproc/js_job.hpp | 38 - src/extproc/js_runner.cc | 268 - src/extproc/js_runner.hpp | 77 - src/gen/web_assets.cc | 132320 --------------- src/http/CPPLINT.cfg | 1 - src/http/file_app.cc | 135 - src/http/file_app.hpp | 20 - src/http/http.cc | 699 - src/http/http.hpp | 173 - src/http/http_parser.cc | 2264 - src/http/http_parser.hpp | 325 - src/http/routing_app.cc | 31 - src/http/routing_app.hpp | 23 - src/http/web_assets.hpp | 12 - src/logger.hpp | 39 - src/main.cc | 117 - src/math.cc | 64 - src/math.hpp | 47 - src/memory_utils.cc | 68 - src/memory_utils.hpp | 18 - src/parsing/utf8.cc | 287 - src/parsing/utf8.hpp | 134 - src/parsing/util.cc | 71 - src/parsing/util.hpp | 40 - src/paths.cc | 223 - src/paths.hpp | 70 - src/perfmon/collect.cc | 22 - src/perfmon/collect.hpp | 13 - src/perfmon/core.cc | 159 - src/perfmon/core.hpp | 154 - src/perfmon/filter.cc | 80 - src/perfmon/filter.hpp | 28 - src/perfmon/perfmon.cc | 374 - src/perfmon/perfmon.hpp | 274 - src/perfmon/types.hpp | 13 - src/pprint/generic_term_walker.hpp | 25 - src/pprint/js_pprint.cc | 922 - src/pprint/js_pprint.hpp | 15 - src/pprint/pprint.cc | 713 - src/pprint/pprint.hpp | 125 - src/pprint/sexp_pprint.cc | 312 - src/pprint/sexp_pprint.hpp | 15 - src/protocol_api.cc | 59 - src/protocol_api.hpp | 219 - src/random.cc | 70 - src/random.hpp | 35 - src/rapidjson/allocators.h | 265 - src/rapidjson/document.h | 1932 - src/rapidjson/encodedstream.h | 290 - src/rapidjson/encodings.h | 630 - src/rapidjson/error/en.h | 71 - src/rapidjson/error/error.h | 150 - src/rapidjson/filereadstream.h | 94 - src/rapidjson/filestream.h | 73 - src/rapidjson/filewritestream.h | 97 - src/rapidjson/internal/biginteger.h | 300 - src/rapidjson/internal/diyfp.h | 268 - src/rapidjson/internal/dtoa.h | 231 - src/rapidjson/internal/ieee754.h | 90 - src/rapidjson/internal/itoa.h | 306 - src/rapidjson/internal/meta.h | 189 - src/rapidjson/internal/pow10.h | 59 - src/rapidjson/internal/stack.h | 183 - src/rapidjson/internal/strfunc.h | 43 - src/rapidjson/internal/strtod.h | 285 - src/rapidjson/memorybuffer.h | 76 - src/rapidjson/memorystream.h | 67 - src/rapidjson/msinttypes/inttypes.h | 312 - src/rapidjson/msinttypes/stdint.h | 296 - src/rapidjson/prettywriter.h | 214 - src/rapidjson/rapidjson.h | 629 - src/rapidjson/reader.h | 1477 - src/rapidjson/stringbuffer.h | 104 - src/rapidjson/writer.h | 431 - .../artificial_table/artificial_table.cc | 424 - .../artificial_table/artificial_table.hpp | 119 - src/rdb_protocol/artificial_table/backend.cc | 114 - src/rdb_protocol/artificial_table/backend.hpp | 129 - .../artificial_table/caching_cfeed_backend.cc | 307 - .../caching_cfeed_backend.hpp | 126 - .../artificial_table/cfeed_backend.cc | 181 - .../artificial_table/cfeed_backend.hpp | 110 - .../artificial_table/in_memory.hpp | 136 - src/rdb_protocol/base64.cc | 129 - src/rdb_protocol/base64.hpp | 10 - src/rdb_protocol/batching.cc | 330 - src/rdb_protocol/batching.hpp | 118 - src/rdb_protocol/blob_wrapper.cc | 41 - src/rdb_protocol/blob_wrapper.hpp | 36 - src/rdb_protocol/btree.cc | 2285 - src/rdb_protocol/btree.hpp | 397 - src/rdb_protocol/btree_store.cc | 1288 - src/rdb_protocol/changefeed.cc | 4066 - src/rdb_protocol/changefeed.hpp | 618 - src/rdb_protocol/configured_limits.cc | 63 - src/rdb_protocol/configured_limits.hpp | 46 - src/rdb_protocol/context.cc | 136 - src/rdb_protocol/context.hpp | 527 - src/rdb_protocol/datum.cc | 2279 - src/rdb_protocol/datum.hpp | 570 - src/rdb_protocol/datum_json.hpp | 15 - src/rdb_protocol/datum_stream.cc | 2598 - src/rdb_protocol/datum_stream.hpp | 218 - src/rdb_protocol/datum_stream/array.hpp | 34 - src/rdb_protocol/datum_stream/eq_join.hpp | 56 - src/rdb_protocol/datum_stream/fold.hpp | 48 - .../datum_stream/indexed_sort.hpp | 30 - src/rdb_protocol/datum_stream/lazy.hpp | 57 - src/rdb_protocol/datum_stream/map.hpp | 45 - src/rdb_protocol/datum_stream/offsets_of.hpp | 22 - .../datum_stream/ordered_distinct.hpp | 19 - .../datum_stream/ordered_union.hpp | 68 - src/rdb_protocol/datum_stream/range.hpp | 36 - src/rdb_protocol/datum_stream/readers.hpp | 209 - src/rdb_protocol/datum_stream/readgens.hpp | 224 - src/rdb_protocol/datum_stream/slice.hpp | 23 - src/rdb_protocol/datum_stream/union.hpp | 80 - src/rdb_protocol/datum_stream/vector.hpp | 36 - src/rdb_protocol/datum_string.cc | 142 - src/rdb_protocol/datum_string.hpp | 71 - src/rdb_protocol/datum_utils.hpp | 20 - src/rdb_protocol/datumspec.cc | 206 - src/rdb_protocol/datumspec.hpp | 143 - src/rdb_protocol/distribution_progress.cc | 55 - src/rdb_protocol/distribution_progress.hpp | 38 - src/rdb_protocol/env.cc | 130 - src/rdb_protocol/env.hpp | 197 - src/rdb_protocol/erase_range.cc | 181 - src/rdb_protocol/erase_range.hpp | 58 - src/rdb_protocol/error.cc | 84 - src/rdb_protocol/error.hpp | 272 - src/rdb_protocol/func.cc | 441 - src/rdb_protocol/func.hpp | 230 - src/rdb_protocol/geo/CPPLINT.cfg | 2 - src/rdb_protocol/geo/distances.cc | 134 - src/rdb_protocol/geo/distances.hpp | 49 - src/rdb_protocol/geo/ellipsoid.cc | 7 - src/rdb_protocol/geo/ellipsoid.hpp | 38 - src/rdb_protocol/geo/exceptions.hpp | 26 - src/rdb_protocol/geo/geo_visitor.hpp | 25 - src/rdb_protocol/geo/geojson.cc | 490 - src/rdb_protocol/geo/geojson.hpp | 118 - src/rdb_protocol/geo/inclusion.cc | 69 - src/rdb_protocol/geo/inclusion.hpp | 30 - src/rdb_protocol/geo/indexing.cc | 421 - src/rdb_protocol/geo/indexing.hpp | 97 - src/rdb_protocol/geo/intersection.cc | 162 - src/rdb_protocol/geo/intersection.hpp | 64 - src/rdb_protocol/geo/karney/00README.txt | 27 - src/rdb_protocol/geo/karney/LICENSE.txt | 23 - src/rdb_protocol/geo/karney/geodesic.cc | 1770 - src/rdb_protocol/geo/karney/geodesic.h | 745 - src/rdb_protocol/geo/lon_lat_types.cc | 5 - src/rdb_protocol/geo/lon_lat_types.hpp | 30 - src/rdb_protocol/geo/primitives.cc | 107 - src/rdb_protocol/geo/primitives.hpp | 30 - src/rdb_protocol/geo/s2/LICENSE-2.0.txt | 202 - src/rdb_protocol/geo/s2/base/basictypes.h | 111 - src/rdb_protocol/geo/s2/base/casts.h | 400 - src/rdb_protocol/geo/s2/base/docid.h | 348 - src/rdb_protocol/geo/s2/base/int128.cc | 23 - src/rdb_protocol/geo/s2/base/int128.h | 243 - src/rdb_protocol/geo/s2/base/integral_types.h | 111 - src/rdb_protocol/geo/s2/base/logging.cc | 42 - src/rdb_protocol/geo/s2/base/logging.h | 132 - src/rdb_protocol/geo/s2/base/macros.h | 217 - src/rdb_protocol/geo/s2/base/port.h | 682 - src/rdb_protocol/geo/s2/base/scoped_ptr.h | 424 - src/rdb_protocol/geo/s2/base/stl_decl.h | 29 - src/rdb_protocol/geo/s2/base/stl_decl_msvc.h | 130 - src/rdb_protocol/geo/s2/base/stringprintf.cc | 91 - src/rdb_protocol/geo/s2/base/stringprintf.h | 45 - src/rdb_protocol/geo/s2/base/strtoint.cc | 53 - src/rdb_protocol/geo/s2/base/strtoint.h | 96 - src/rdb_protocol/geo/s2/base/template_util.h | 102 - src/rdb_protocol/geo/s2/base/type_traits.h | 290 - src/rdb_protocol/geo/s2/r1interval.h | 189 - src/rdb_protocol/geo/s2/s1angle.cc | 44 - src/rdb_protocol/geo/s2/s1angle.h | 212 - src/rdb_protocol/geo/s2/s1interval.cc | 254 - src/rdb_protocol/geo/s2/s1interval.h | 228 - src/rdb_protocol/geo/s2/s2.cc | 783 - src/rdb_protocol/geo/s2/s2.h | 845 - src/rdb_protocol/geo/s2/s2cap.cc | 265 - src/rdb_protocol/geo/s2/s2cap.h | 179 - src/rdb_protocol/geo/s2/s2cell.cc | 227 - src/rdb_protocol/geo/s2/s2cell.h | 148 - src/rdb_protocol/geo/s2/s2cellid.cc | 522 - src/rdb_protocol/geo/s2/s2cellid.h | 509 - src/rdb_protocol/geo/s2/s2cellunion.cc | 457 - src/rdb_protocol/geo/s2/s2cellunion.h | 212 - src/rdb_protocol/geo/s2/s2edgeindex.cc | 458 - src/rdb_protocol/geo/s2/s2edgeindex.h | 261 - src/rdb_protocol/geo/s2/s2edgeutil.cc | 452 - src/rdb_protocol/geo/s2/s2edgeutil.h | 340 - src/rdb_protocol/geo/s2/s2latlng.cc | 75 - src/rdb_protocol/geo/s2/s2latlng.h | 193 - src/rdb_protocol/geo/s2/s2latlngrect.cc | 595 - src/rdb_protocol/geo/s2/s2latlngrect.h | 324 - src/rdb_protocol/geo/s2/s2loop.cc | 810 - src/rdb_protocol/geo/s2/s2loop.h | 434 - src/rdb_protocol/geo/s2/s2pointregion.cc | 56 - src/rdb_protocol/geo/s2/s2pointregion.h | 52 - src/rdb_protocol/geo/s2/s2polygon.cc | 1211 - src/rdb_protocol/geo/s2/s2polygon.h | 313 - src/rdb_protocol/geo/s2/s2polygonbuilder.cc | 559 - src/rdb_protocol/geo/s2/s2polygonbuilder.h | 311 - src/rdb_protocol/geo/s2/s2polyline.cc | 586 - src/rdb_protocol/geo/s2/s2polyline.h | 199 - src/rdb_protocol/geo/s2/s2r2rect.cc | 156 - src/rdb_protocol/geo/s2/s2r2rect.h | 210 - src/rdb_protocol/geo/s2/s2region.cc | 14 - src/rdb_protocol/geo/s2/s2region.h | 105 - src/rdb_protocol/geo/s2/s2regioncoverer.cc | 350 - src/rdb_protocol/geo/s2/s2regioncoverer.h | 210 - .../geo/s2/s2regionintersection.cc | 88 - .../geo/s2/s2regionintersection.h | 74 - src/rdb_protocol/geo/s2/s2regionunion.cc | 94 - src/rdb_protocol/geo/s2/s2regionunion.h | 77 - .../geo/s2/strings/ascii_ctype.cc | 91 - src/rdb_protocol/geo/s2/strings/ascii_ctype.h | 99 - src/rdb_protocol/geo/s2/strings/split.cc | 336 - src/rdb_protocol/geo/s2/strings/split.h | 158 - .../geo/s2/strings/stringprintf.cc | 58 - .../geo/s2/strings/stringprintf.h | 44 - src/rdb_protocol/geo/s2/strings/strutil.cc | 223 - src/rdb_protocol/geo/s2/strings/strutil.h | 218 - src/rdb_protocol/geo/s2/util/coding/coder.cc | 109 - src/rdb_protocol/geo/s2/util/coding/coder.h | 450 - src/rdb_protocol/geo/s2/util/coding/varint.cc | 269 - src/rdb_protocol/geo/s2/util/coding/varint.h | 511 - src/rdb_protocol/geo/s2/util/endian/endian.h | 191 - .../geo/s2/util/hash/hash_jenkins_lookup2.h | 157 - .../geo/s2/util/math/exactfloat/exactfloat.cc | 780 - .../geo/s2/util/math/exactfloat/exactfloat.h | 617 - .../geo/s2/util/math/mathlimits.cc | 86 - .../geo/s2/util/math/mathlimits.h | 244 - src/rdb_protocol/geo/s2/util/math/mathutil.cc | 189 - src/rdb_protocol/geo/s2/util/math/mathutil.h | 720 - .../geo/s2/util/math/matrix3x3-inl.h | 579 - src/rdb_protocol/geo/s2/util/math/matrix3x3.h | 31 - .../geo/s2/util/math/vector2-inl.h | 366 - src/rdb_protocol/geo/s2/util/math/vector2.h | 193 - .../geo/s2/util/math/vector3-inl.h | 435 - src/rdb_protocol/geo/s2/util/math/vector3.h | 193 - .../geo/s2/util/math/vector4-inl.h | 436 - src/rdb_protocol/geo/s2/util/math/vector4.h | 182 - src/rdb_protocol/geo_traversal.cc | 426 - src/rdb_protocol/geo_traversal.hpp | 235 - src/rdb_protocol/lazy_btree_val.cc | 44 - src/rdb_protocol/lazy_btree_val.hpp | 76 - src/rdb_protocol/math_utils.hpp | 32 - src/rdb_protocol/minidriver.cc | 131 - src/rdb_protocol/minidriver.hpp | 217 - src/rdb_protocol/op.cc | 355 - src/rdb_protocol/op.hpp | 242 - src/rdb_protocol/optargs.cc | 119 - src/rdb_protocol/optargs.hpp | 39 - src/rdb_protocol/order_util.cc | 144 - src/rdb_protocol/order_util.hpp | 65 - src/rdb_protocol/pathspec.cc | 271 - src/rdb_protocol/pathspec.hpp | 101 - src/rdb_protocol/profile.cc | 394 - src/rdb_protocol/profile.hpp | 213 - src/rdb_protocol/protocol.cc | 1593 - src/rdb_protocol/protocol.hpp | 765 - src/rdb_protocol/pseudo_binary.cc | 83 - src/rdb_protocol/pseudo_binary.hpp | 39 - src/rdb_protocol/pseudo_geometry.cc | 44 - src/rdb_protocol/pseudo_geometry.hpp | 17 - src/rdb_protocol/pseudo_literal.cc | 27 - src/rdb_protocol/pseudo_literal.hpp | 18 - src/rdb_protocol/pseudo_time.cc | 703 - src/rdb_protocol/pseudo_time.hpp | 60 - src/rdb_protocol/ql2proto.hpp | 11 - src/rdb_protocol/query_cache.cc | 379 - src/rdb_protocol/query_cache.hpp | 167 - src/rdb_protocol/query_params.cc | 78 - src/rdb_protocol/query_params.hpp | 57 - src/rdb_protocol/query_server.cc | 108 - src/rdb_protocol/query_server.hpp | 48 - src/rdb_protocol/rdb_backtrace.cc | 48 - src/rdb_protocol/rdb_backtrace.hpp | 68 - src/rdb_protocol/real_table.cc | 411 - src/rdb_protocol/real_table.hpp | 134 - src/rdb_protocol/response.cc | 89 - src/rdb_protocol/response.hpp | 54 - src/rdb_protocol/serialize_datum.cc | 1009 - src/rdb_protocol/serialize_datum.hpp | 107 - .../serialize_datum_onto_blob.hpp | 44 - src/rdb_protocol/shards.cc | 1150 - src/rdb_protocol/shards.hpp | 438 - src/rdb_protocol/store.cc | 1102 - src/rdb_protocol/store.hpp | 457 - src/rdb_protocol/store_metainfo.cc | 103 - src/rdb_protocol/store_metainfo.hpp | 44 - src/rdb_protocol/store_receive_backfill.cc | 537 - src/rdb_protocol/store_send_backfill.cc | 293 - src/rdb_protocol/sym.cc | 21 - src/rdb_protocol/sym.hpp | 36 - src/rdb_protocol/table_common.cc | 227 - src/rdb_protocol/table_common.hpp | 73 - src/rdb_protocol/term.cc | 309 - src/rdb_protocol/term.hpp | 90 - src/rdb_protocol/term_storage.cc | 620 - src/rdb_protocol/term_storage.hpp | 180 - src/rdb_protocol/term_walker.cc | 792 - src/rdb_protocol/term_walker.hpp | 29 - src/rdb_protocol/terms/arith.cc | 338 - src/rdb_protocol/terms/arr.cc | 747 - src/rdb_protocol/terms/arr.hpp | 16 - src/rdb_protocol/terms/case.cc | 35 - src/rdb_protocol/terms/control.cc | 158 - src/rdb_protocol/terms/datum_terms.cc | 171 - src/rdb_protocol/terms/db_table.cc | 1037 - src/rdb_protocol/terms/error.cc | 96 - src/rdb_protocol/terms/geo.cc | 566 - src/rdb_protocol/terms/http.cc | 789 - src/rdb_protocol/terms/js.cc | 65 - src/rdb_protocol/terms/json.cc | 96 - src/rdb_protocol/terms/obj.cc | 104 - src/rdb_protocol/terms/obj_or_seq.cc | 385 - src/rdb_protocol/terms/obj_or_seq.hpp | 63 - src/rdb_protocol/terms/pred.cc | 107 - src/rdb_protocol/terms/random.cc | 203 - src/rdb_protocol/terms/rewrites.cc | 262 - src/rdb_protocol/terms/seq.cc | 963 - src/rdb_protocol/terms/sindex.cc | 497 - src/rdb_protocol/terms/sort.cc | 208 - src/rdb_protocol/terms/string.cc | 308 - src/rdb_protocol/terms/terms.hpp | 368 - src/rdb_protocol/terms/time.cc | 276 - src/rdb_protocol/terms/type_manip.cc | 541 - src/rdb_protocol/terms/uuid.cc | 57 - src/rdb_protocol/terms/var.cc | 90 - src/rdb_protocol/terms/write_hook.cc | 182 - src/rdb_protocol/terms/write_hook.hpp | 15 - src/rdb_protocol/terms/writes.cc | 467 - src/rdb_protocol/terms/writes.hpp | 16 - src/rdb_protocol/val.cc | 897 - src/rdb_protocol/val.hpp | 320 - src/rdb_protocol/var_types.cc | 200 - src/rdb_protocol/var_types.hpp | 93 - src/rdb_protocol/wire_func.cc | 321 - src/rdb_protocol/wire_func.hpp | 182 - src/region/hash_region.cc | 281 - src/region/hash_region.hpp | 278 - src/region/region.cc | 67 - src/region/region.hpp | 32 - src/region/region_map.hpp | 315 - src/repli_timestamp.cc | 38 - src/repli_timestamp.hpp | 55 - src/rethinkdb_backtrace.cc | 126 - src/rethinkdb_backtrace.hpp | 12 - src/rpc/README.md | 25 - src/rpc/connectivity/cluster.cc | 1616 - src/rpc/connectivity/cluster.hpp | 442 - .../connectivity/cluster_message_handler.hpp | 46 - src/rpc/connectivity/peer_id.cc | 22 - src/rpc/connectivity/peer_id.hpp | 50 - src/rpc/connectivity/server_id.cc | 104 - src/rpc/connectivity/server_id.hpp | 63 - src/rpc/directory/map_read_manager.cc | 16 - src/rpc/directory/map_read_manager.hpp | 46 - src/rpc/directory/map_read_manager.tcc | 117 - src/rpc/directory/map_write_manager.cc | 16 - src/rpc/directory/map_write_manager.hpp | 67 - src/rpc/directory/map_write_manager.tcc | 138 - src/rpc/directory/read_manager.cc | 8 - src/rpc/directory/read_manager.hpp | 100 - src/rpc/directory/read_manager.tcc | 250 - src/rpc/directory/write_manager.cc | 8 - src/rpc/directory/write_manager.hpp | 60 - src/rpc/directory/write_manager.tcc | 154 - src/rpc/mailbox/disconnect_watcher.cc | 22 - src/rpc/mailbox/disconnect_watcher.hpp | 21 - src/rpc/mailbox/mailbox.cc | 254 - src/rpc/mailbox/mailbox.hpp | 79 - src/rpc/mailbox/raw_mailbox.cc | 53 - src/rpc/mailbox/raw_mailbox.hpp | 99 - src/rpc/mailbox/typed.hpp | 134 - src/rpc/semilattice/joins/deletable.hpp | 72 - src/rpc/semilattice/joins/deletable.tcc | 34 - src/rpc/semilattice/joins/macros.hpp | 895 - src/rpc/semilattice/joins/map.hpp | 26 - src/rpc/semilattice/joins/set.hpp | 21 - src/rpc/semilattice/joins/versioned.hpp | 121 - src/rpc/semilattice/semilattice_manager.cc | 7 - src/rpc/semilattice/semilattice_manager.hpp | 117 - src/rpc/semilattice/semilattice_manager.tcc | 584 - src/rpc/semilattice/view.hpp | 79 - src/rpc/semilattice/view/field.hpp | 35 - src/rpc/semilattice/view/field.tcc | 100 - src/rpc/semilattice/view/member.hpp | 36 - src/rpc/semilattice/view/member.tcc | 105 - src/rpc/semilattice/watchable.hpp | 50 - src/rpc/serialize_macros.hpp | 3223 - src/serializer/buf_ptr.cc | 89 - src/serializer/buf_ptr.hpp | 124 - src/serializer/log/config.hpp | 56 - src/serializer/log/data_block_manager.cc | 1569 - src/serializer/log/data_block_manager.hpp | 279 - src/serializer/log/extent_manager.cc | 324 - src/serializer/log/extent_manager.hpp | 158 - src/serializer/log/lba/disk_extent.cc | 76 - src/serializer/log/lba/disk_extent.hpp | 68 - src/serializer/log/lba/disk_format.hpp | 193 - src/serializer/log/lba/disk_structure.cc | 354 - src/serializer/log/lba/disk_structure.hpp | 95 - src/serializer/log/lba/extent.cc | 142 - src/serializer/log/lba/extent.hpp | 49 - src/serializer/log/lba/in_memory_index.cc | 50 - src/serializer/log/lba/in_memory_index.hpp | 81 - src/serializer/log/lba/lba_list.cc | 496 - src/serializer/log/lba/lba_list.hpp | 134 - src/serializer/log/log_serializer.cc | 1053 - src/serializer/log/log_serializer.hpp | 278 - src/serializer/log/metablock_manager.cc | 317 - src/serializer/log/metablock_manager.hpp | 162 - src/serializer/log/static_header.cc | 133 - src/serializer/log/static_header.hpp | 44 - src/serializer/log/stats.hpp | 46 - src/serializer/merger.cc | 119 - src/serializer/merger.hpp | 148 - src/serializer/serializer.cc | 27 - src/serializer/serializer.hpp | 131 - src/serializer/translator.cc | 375 - src/serializer/translator.hpp | 152 - src/serializer/types.hpp | 239 - src/stl_utils.cc | 20 - src/stl_utils.hpp | 61 - src/stl_utils.tcc | 77 - src/store_subview.hpp | 194 - src/store_view.hpp | 263 - src/thread_local.hpp | 117 - src/threading.cc | 90 - src/threading.hpp | 123 - src/time.cc | 195 - src/time.hpp | 28 - src/timestamps.cc | 10 - src/timestamps.hpp | 89 - src/unittest/archive_test.cc | 39 - src/unittest/auth_key_test.cc | 27 - src/unittest/barrier_test.cc | 81 - src/unittest/bignum_test.cc | 59 - src/unittest/blob_test.cc | 303 - src/unittest/branch_history_manager.cc | 64 - src/unittest/branch_history_manager.hpp | 40 - src/unittest/btree_metadata.cc | 108 - src/unittest/btree_metainfo.cc | 120 - src/unittest/btree_sindex.cc | 317 - src/unittest/btree_utils.hpp | 68 - src/unittest/btree_whole.cc | 507 - src/unittest/clone_ptr.cc | 56 - src/unittest/clustering_backfill.cc | 156 - src/unittest/clustering_branch.cc | 193 - src/unittest/clustering_branch_history.cc | 170 - .../clustering_contract_coordinator.cc | 627 - src/unittest/clustering_contract_executor.cc | 636 - src/unittest/clustering_contract_utils.cc | 147 - src/unittest/clustering_contract_utils.hpp | 94 - src/unittest/clustering_minidir.cc | 116 - src/unittest/clustering_query.cc | 174 - src/unittest/clustering_raft.cc | 148 - src/unittest/clustering_raft_fuzzer.cc | 261 - src/unittest/clustering_raft_storage.cc | 383 - src/unittest/clustering_registration.cc | 121 - src/unittest/clustering_utils.cc | 14 - src/unittest/clustering_utils.hpp | 268 - src/unittest/clustering_utils_raft.cc | 445 - src/unittest/clustering_utils_raft.hpp | 173 - src/unittest/context_switching.cc | 121 - src/unittest/coroutine_utils.cc | 183 - src/unittest/coroutines.cc | 133 - src/unittest/cross_thread_watchable.cc | 60 - src/unittest/datum_test.cc | 210 - src/unittest/dbm_test.cc | 53 - src/unittest/disk_backed_queue.cc | 107 - src/unittest/disk_conflict_resolution.cc | 465 - src/unittest/disk_format_test.cc | 133 - src/unittest/dummy_metadata_controller.hpp | 78 - src/unittest/dummy_namespace_interface.cc | 219 - src/unittest/dummy_namespace_interface.hpp | 123 - src/unittest/extproc_test.cc | 498 - src/unittest/extproc_test.hpp | 14 - src/unittest/fifo_enforcer.cc | 137 - src/unittest/geo_btree.cc | 135 - src/unittest/geo_indexes.cc | 479 - src/unittest/geo_primitives.cc | 111 - src/unittest/gtest.hpp | 24 - src/unittest/hash_region_test.cc | 122 - src/unittest/http.cc | 215 - src/unittest/http_escaping.cc | 26 - src/unittest/internal_node_test.cc | 66 - src/unittest/jsproc.cc | 334 - src/unittest/leaf_node_test.cc | 713 - src/unittest/log_message.cc | 76 - src/unittest/lru_cache_test.cc | 41 - src/unittest/main.cc | 18 - src/unittest/mock_file.cc | 105 - src/unittest/mock_file.hpp | 72 - src/unittest/mock_store.cc | 576 - src/unittest/mock_store.hpp | 117 - src/unittest/optional.cc | 19 - src/unittest/options_test.cc | 154 - src/unittest/page_test.cc | 1184 - src/unittest/perfmon_tests.cc | 60 - src/unittest/pprint_test.cc | 85 - src/unittest/print_secondary.cc | 54 - src/unittest/printf_buffer_test.cc | 59 - src/unittest/range_map.cc | 259 - src/unittest/rdb_backfill.cc | 548 - src/unittest/rdb_btree.cc | 456 - src/unittest/rdb_env.cc | 750 - src/unittest/rdb_env.hpp | 391 - src/unittest/rdb_interruptor.cc | 630 - src/unittest/rdb_protocol.cc | 1013 - src/unittest/rdb_protocol.hpp | 34 - src/unittest/rebalance.cc | 73 - src/unittest/region_map_test.cc | 59 - src/unittest/rpc_connectivity.cc | 803 - src/unittest/rpc_directory.cc | 134 - src/unittest/rpc_mailbox.cc | 180 - src/unittest/rpc_semilattice.cc | 222 - src/unittest/rwlock_test.cc | 61 - src/unittest/serializer_test.cc | 94 - src/unittest/server_id_test.cc | 73 - src/unittest/simple_mailbox_cluster.cc | 67 - src/unittest/simple_mailbox_cluster.hpp | 28 - src/unittest/sizeof_test.cc | 23 - src/unittest/timer_test.cc | 64 - src/unittest/unittest_utils.cc | 257 - src/unittest/unittest_utils.hpp | 97 - src/unittest/utf8.cc | 581 - src/unittest/utils_test.cc | 161 - src/unittest/uuid_test.cc | 94 - src/unittest/varint_test.cc | 88 - src/utils.cc | 370 - src/utils.hpp | 127 - src/valgrind.hpp | 16 - src/version.hpp | 113 - src/windows.hpp | 47 - 1133 files changed, 367104 deletions(-) delete mode 100644 .circleci/config.yml delete mode 100644 src/.gitignore delete mode 100644 src/CPPLINT.cfg delete mode 100644 src/Makefile delete mode 100644 src/README.md delete mode 100644 src/activity_logger.cc delete mode 100644 src/activity_logger.hpp delete mode 100644 src/arch/CPPLINT.cfg delete mode 100644 src/arch/address.cc delete mode 100644 src/arch/address.hpp delete mode 100644 src/arch/arch.cc delete mode 100644 src/arch/arch.hpp delete mode 100644 src/arch/barrier.cc delete mode 100644 src/arch/barrier.hpp delete mode 100644 src/arch/compiler.hpp delete mode 100644 src/arch/fd_send_recv.cc delete mode 100644 src/arch/fd_send_recv.hpp delete mode 100644 src/arch/filesystem.hpp delete mode 100644 src/arch/io/blocker_pool.cc delete mode 100644 src/arch/io/blocker_pool.hpp delete mode 100644 src/arch/io/concurrency.hpp delete mode 100644 src/arch/io/disk.cc delete mode 100644 src/arch/io/disk.hpp delete mode 100644 src/arch/io/disk/accounting.cc delete mode 100644 src/arch/io/disk/accounting.hpp delete mode 100644 src/arch/io/disk/conflict_resolving.cc delete mode 100644 src/arch/io/disk/conflict_resolving.hpp delete mode 100644 src/arch/io/disk/filestat.cc delete mode 100644 src/arch/io/disk/filestat.hpp delete mode 100644 src/arch/io/disk/pool.cc delete mode 100644 src/arch/io/disk/pool.hpp delete mode 100644 src/arch/io/disk/stats.cc delete mode 100644 src/arch/io/disk/stats.hpp delete mode 100644 src/arch/io/disk/stats_2.cc delete mode 100644 src/arch/io/disk/stats_2.hpp delete mode 100644 src/arch/io/event_watcher.cc delete mode 100644 src/arch/io/event_watcher.hpp delete mode 100644 src/arch/io/io_utils.cc delete mode 100644 src/arch/io/io_utils.hpp delete mode 100644 src/arch/io/network.cc delete mode 100644 src/arch/io/network.hpp delete mode 100644 src/arch/io/openssl.hpp delete mode 100644 src/arch/io/timer/timer_kqueue_provider.cc delete mode 100644 src/arch/io/timer/timer_kqueue_provider.hpp delete mode 100644 src/arch/io/timer/timer_signal_provider.cc delete mode 100644 src/arch/io/timer/timer_signal_provider.hpp delete mode 100644 src/arch/io/timer/timer_windows_provider.cc delete mode 100644 src/arch/io/timer/timer_windows_provider.hpp delete mode 100644 src/arch/io/timer/timerfd_provider.cc delete mode 100644 src/arch/io/timer/timerfd_provider.hpp delete mode 100644 src/arch/io/timer_provider.hpp delete mode 100644 src/arch/os_signal.cc delete mode 100644 src/arch/os_signal.hpp delete mode 100644 src/arch/process.hpp delete mode 100644 src/arch/runtime/callable_action.hpp delete mode 100644 src/arch/runtime/context_switching.cc delete mode 100644 src/arch/runtime/context_switching.hpp delete mode 100644 src/arch/runtime/coro_profiler.cc delete mode 100644 src/arch/runtime/coro_profiler.hpp delete mode 100644 src/arch/runtime/coroutines.cc delete mode 100644 src/arch/runtime/coroutines.hpp delete mode 100644 src/arch/runtime/event_queue.cc delete mode 100644 src/arch/runtime/event_queue.hpp delete mode 100644 src/arch/runtime/event_queue/epoll.cc delete mode 100644 src/arch/runtime/event_queue/epoll.hpp delete mode 100644 src/arch/runtime/event_queue/iocp.cc delete mode 100644 src/arch/runtime/event_queue/iocp.hpp delete mode 100644 src/arch/runtime/event_queue/kqueue.cc delete mode 100644 src/arch/runtime/event_queue/kqueue.hpp delete mode 100644 src/arch/runtime/event_queue/poll.cc delete mode 100644 src/arch/runtime/event_queue/poll.hpp delete mode 100644 src/arch/runtime/event_queue_types.hpp delete mode 100644 src/arch/runtime/message_hub.cc delete mode 100644 src/arch/runtime/message_hub.hpp delete mode 100644 src/arch/runtime/runtime.cc delete mode 100644 src/arch/runtime/runtime.hpp delete mode 100644 src/arch/runtime/runtime_utils.cc delete mode 100644 src/arch/runtime/runtime_utils.hpp delete mode 100644 src/arch/runtime/starter.hpp delete mode 100644 src/arch/runtime/system_event.hpp delete mode 100644 src/arch/runtime/system_event/eventfd.cc delete mode 100644 src/arch/runtime/system_event/eventfd.hpp delete mode 100644 src/arch/runtime/system_event/eventfd_event.cc delete mode 100644 src/arch/runtime/system_event/eventfd_event.hpp delete mode 100644 src/arch/runtime/system_event/pipe_event.cc delete mode 100644 src/arch/runtime/system_event/pipe_event.hpp delete mode 100644 src/arch/runtime/system_event/windows_event.cc delete mode 100644 src/arch/runtime/system_event/windows_event.hpp delete mode 100644 src/arch/runtime/thread_pool.cc delete mode 100644 src/arch/runtime/thread_pool.hpp delete mode 100644 src/arch/spinlock.cc delete mode 100644 src/arch/spinlock.hpp delete mode 100644 src/arch/timer.cc delete mode 100644 src/arch/timer.hpp delete mode 100644 src/arch/timing.cc delete mode 100644 src/arch/timing.hpp delete mode 100644 src/arch/types.cc delete mode 100644 src/arch/types.hpp delete mode 100644 src/arch/windows_stub/pthread.cc delete mode 100644 src/arch/windows_stub/pthread.h delete mode 100644 src/arch/windows_stub/sys/uio.h delete mode 100644 src/arch/windows_stub/unistd.h delete mode 100644 src/assignment_sentry.hpp delete mode 100644 src/backtrace.cc delete mode 100644 src/backtrace.hpp delete mode 100644 src/btree/backfill.cc delete mode 100644 src/btree/backfill.hpp delete mode 100644 src/btree/backfill_debug.cc delete mode 100644 src/btree/backfill_debug.hpp delete mode 100644 src/btree/backfill_types.cc delete mode 100644 src/btree/backfill_types.hpp delete mode 100644 src/btree/concurrent_traversal.cc delete mode 100644 src/btree/concurrent_traversal.hpp delete mode 100644 src/btree/depth_first_traversal.cc delete mode 100644 src/btree/depth_first_traversal.hpp delete mode 100644 src/btree/get_distribution.cc delete mode 100644 src/btree/get_distribution.hpp delete mode 100644 src/btree/internal_node.cc delete mode 100644 src/btree/internal_node.hpp delete mode 100644 src/btree/keys.cc delete mode 100644 src/btree/keys.hpp delete mode 100644 src/btree/leaf_node.cc delete mode 100644 src/btree/leaf_node.hpp delete mode 100644 src/btree/node.cc delete mode 100644 src/btree/node.hpp delete mode 100644 src/btree/operations.cc delete mode 100644 src/btree/operations.hpp delete mode 100644 src/btree/parallel_traversal.cc delete mode 100644 src/btree/parallel_traversal.hpp delete mode 100644 src/btree/reql_specific.cc delete mode 100644 src/btree/reql_specific.hpp delete mode 100644 src/btree/secondary_operations.cc delete mode 100644 src/btree/secondary_operations.hpp delete mode 100644 src/btree/stats.hpp delete mode 100644 src/btree/superblock.hpp delete mode 100644 src/btree/traversal_progress.cc delete mode 100644 src/btree/traversal_progress.hpp delete mode 100644 src/btree/types.hpp delete mode 100644 src/buffer_cache/alt.cc delete mode 100644 src/buffer_cache/alt.hpp delete mode 100644 src/buffer_cache/blob.cc delete mode 100644 src/buffer_cache/blob.hpp delete mode 100644 src/buffer_cache/block_version.hpp delete mode 100644 src/buffer_cache/cache_account.cc delete mode 100644 src/buffer_cache/cache_account.hpp delete mode 100644 src/buffer_cache/cache_balancer.cc delete mode 100644 src/buffer_cache/cache_balancer.hpp delete mode 100644 src/buffer_cache/evicter.cc delete mode 100644 src/buffer_cache/evicter.hpp delete mode 100644 src/buffer_cache/eviction_bag.cc delete mode 100644 src/buffer_cache/eviction_bag.hpp delete mode 100644 src/buffer_cache/free_list.cc delete mode 100644 src/buffer_cache/free_list.hpp delete mode 100644 src/buffer_cache/page.cc delete mode 100644 src/buffer_cache/page.hpp delete mode 100644 src/buffer_cache/page_cache.cc delete mode 100644 src/buffer_cache/page_cache.hpp delete mode 100644 src/buffer_cache/serialize_onto_blob.cc delete mode 100644 src/buffer_cache/serialize_onto_blob.hpp delete mode 100644 src/buffer_cache/stats.cc delete mode 100644 src/buffer_cache/stats.hpp delete mode 100644 src/buffer_cache/types.cc delete mode 100644 src/buffer_cache/types.hpp delete mode 100644 src/build.mk delete mode 100644 src/cjson/cJSON.cc delete mode 100644 src/cjson/cJSON.hpp delete mode 100644 src/cjson/json.cc delete mode 100644 src/cjson/json.hpp delete mode 100644 src/client_protocol/client_server_error.hpp delete mode 100644 src/client_protocol/json.cc delete mode 100644 src/client_protocol/json.hpp delete mode 100644 src/client_protocol/protocols.cc delete mode 100644 src/client_protocol/protocols.hpp delete mode 100644 src/client_protocol/server.cc delete mode 100644 src/client_protocol/server.hpp delete mode 100644 src/clustering/administration/admin_op_exc.hpp delete mode 100644 src/clustering/administration/artificial_reql_cluster_interface.cc delete mode 100644 src/clustering/administration/artificial_reql_cluster_interface.hpp delete mode 100644 src/clustering/administration/auth/authentication_error.hpp delete mode 100644 src/clustering/administration/auth/base_artificial_table_backend.hpp delete mode 100644 src/clustering/administration/auth/base_authenticator.hpp delete mode 100644 src/clustering/administration/auth/grant.cc delete mode 100644 src/clustering/administration/auth/grant.hpp delete mode 100644 src/clustering/administration/auth/password.cc delete mode 100644 src/clustering/administration/auth/password.hpp delete mode 100644 src/clustering/administration/auth/permission_error.hpp delete mode 100644 src/clustering/administration/auth/permissions.cc delete mode 100644 src/clustering/administration/auth/permissions.hpp delete mode 100644 src/clustering/administration/auth/permissions_artificial_table_backend.cc delete mode 100644 src/clustering/administration/auth/permissions_artificial_table_backend.hpp delete mode 100644 src/clustering/administration/auth/plaintext_authenticator.cc delete mode 100644 src/clustering/administration/auth/plaintext_authenticator.hpp delete mode 100644 src/clustering/administration/auth/scram_authenticator.cc delete mode 100644 src/clustering/administration/auth/scram_authenticator.hpp delete mode 100644 src/clustering/administration/auth/user.cc delete mode 100644 src/clustering/administration/auth/user.hpp delete mode 100644 src/clustering/administration/auth/user_context.cc delete mode 100644 src/clustering/administration/auth/user_context.hpp delete mode 100644 src/clustering/administration/auth/username.cc delete mode 100644 src/clustering/administration/auth/username.hpp delete mode 100644 src/clustering/administration/auth/users_artificial_table_backend.cc delete mode 100644 src/clustering/administration/auth/users_artificial_table_backend.hpp delete mode 100644 src/clustering/administration/cluster_config.cc delete mode 100644 src/clustering/administration/cluster_config.hpp delete mode 100644 src/clustering/administration/datum_adapter.cc delete mode 100644 src/clustering/administration/datum_adapter.hpp delete mode 100644 src/clustering/administration/http/cyanide.hpp delete mode 100644 src/clustering/administration/http/server.cc delete mode 100644 src/clustering/administration/http/server.hpp delete mode 100644 src/clustering/administration/issues/issue.cc delete mode 100644 src/clustering/administration/issues/issue.hpp delete mode 100644 src/clustering/administration/issues/issues_backend.cc delete mode 100644 src/clustering/administration/issues/issues_backend.hpp delete mode 100644 src/clustering/administration/issues/local.cc delete mode 100644 src/clustering/administration/issues/local.hpp delete mode 100644 src/clustering/administration/issues/log_write.cc delete mode 100644 src/clustering/administration/issues/log_write.hpp delete mode 100644 src/clustering/administration/issues/memory.cc delete mode 100644 src/clustering/administration/issues/memory.hpp delete mode 100644 src/clustering/administration/issues/name_collision.cc delete mode 100644 src/clustering/administration/issues/name_collision.hpp delete mode 100644 src/clustering/administration/issues/non_transitive.cc delete mode 100644 src/clustering/administration/issues/non_transitive.hpp delete mode 100644 src/clustering/administration/issues/outdated_index.cc delete mode 100644 src/clustering/administration/issues/outdated_index.hpp delete mode 100644 src/clustering/administration/issues/table.cc delete mode 100644 src/clustering/administration/issues/table.hpp delete mode 100644 src/clustering/administration/jobs/backend.cc delete mode 100644 src/clustering/administration/jobs/backend.hpp delete mode 100644 src/clustering/administration/jobs/manager.cc delete mode 100644 src/clustering/administration/jobs/manager.hpp delete mode 100644 src/clustering/administration/jobs/report.cc delete mode 100644 src/clustering/administration/jobs/report.hpp delete mode 100644 src/clustering/administration/jobs/report.tcc delete mode 100644 src/clustering/administration/logs/log_transfer.cc delete mode 100644 src/clustering/administration/logs/log_transfer.hpp delete mode 100644 src/clustering/administration/logs/log_writer.cc delete mode 100644 src/clustering/administration/logs/log_writer.hpp delete mode 100644 src/clustering/administration/logs/logs_backend.cc delete mode 100644 src/clustering/administration/logs/logs_backend.hpp delete mode 100644 src/clustering/administration/main/cache_size.cc delete mode 100644 src/clustering/administration/main/cache_size.hpp delete mode 100644 src/clustering/administration/main/command_line.cc delete mode 100644 src/clustering/administration/main/command_line.hpp delete mode 100644 src/clustering/administration/main/directory_lock.cc delete mode 100644 src/clustering/administration/main/directory_lock.hpp delete mode 100644 src/clustering/administration/main/initial_join.cc delete mode 100644 src/clustering/administration/main/initial_join.hpp delete mode 100644 src/clustering/administration/main/memory_checker.cc delete mode 100644 src/clustering/administration/main/memory_checker.hpp delete mode 100644 src/clustering/administration/main/names.cc delete mode 100644 src/clustering/administration/main/names.hpp delete mode 100644 src/clustering/administration/main/options.cc delete mode 100644 src/clustering/administration/main/options.hpp delete mode 100644 src/clustering/administration/main/path.cc delete mode 100644 src/clustering/administration/main/path.hpp delete mode 100644 src/clustering/administration/main/ports.hpp delete mode 100644 src/clustering/administration/main/serve.cc delete mode 100644 src/clustering/administration/main/serve.hpp delete mode 100644 src/clustering/administration/main/watchable_fields.hpp delete mode 100644 src/clustering/administration/main/windows_service.cc delete mode 100644 src/clustering/administration/main/windows_service.hpp delete mode 100644 src/clustering/administration/metadata.cc delete mode 100644 src/clustering/administration/metadata.hpp delete mode 100644 src/clustering/administration/namespace_interface_repository.cc delete mode 100644 src/clustering/administration/namespace_interface_repository.hpp delete mode 100644 src/clustering/administration/perfmon_collection_repo.cc delete mode 100644 src/clustering/administration/perfmon_collection_repo.hpp delete mode 100644 src/clustering/administration/persist/branch_history_manager.cc delete mode 100644 src/clustering/administration/persist/branch_history_manager.hpp delete mode 100644 src/clustering/administration/persist/file.cc delete mode 100644 src/clustering/administration/persist/file.hpp delete mode 100644 src/clustering/administration/persist/file_keys.cc delete mode 100644 src/clustering/administration/persist/file_keys.hpp delete mode 100644 src/clustering/administration/persist/migrate/metadata_v1_14.cc delete mode 100644 src/clustering/administration/persist/migrate/metadata_v1_14.hpp delete mode 100644 src/clustering/administration/persist/migrate/metadata_v1_16.cc delete mode 100644 src/clustering/administration/persist/migrate/metadata_v1_16.hpp delete mode 100644 src/clustering/administration/persist/migrate/metadata_v2_1.cc delete mode 100644 src/clustering/administration/persist/migrate/metadata_v2_1.hpp delete mode 100644 src/clustering/administration/persist/migrate/migrate_v1_14.cc delete mode 100644 src/clustering/administration/persist/migrate/migrate_v1_14.hpp delete mode 100644 src/clustering/administration/persist/migrate/migrate_v1_16.cc delete mode 100644 src/clustering/administration/persist/migrate/migrate_v1_16.hpp delete mode 100644 src/clustering/administration/persist/migrate/migrate_v2_1.cc delete mode 100644 src/clustering/administration/persist/migrate/migrate_v2_1.hpp delete mode 100644 src/clustering/administration/persist/migrate/migrate_v2_3.cc delete mode 100644 src/clustering/administration/persist/migrate/migrate_v2_3.hpp delete mode 100644 src/clustering/administration/persist/migrate/rewrite.hpp delete mode 100644 src/clustering/administration/persist/raft_storage_interface.cc delete mode 100644 src/clustering/administration/persist/raft_storage_interface.hpp delete mode 100644 src/clustering/administration/persist/semilattice.cc delete mode 100644 src/clustering/administration/persist/semilattice.hpp delete mode 100644 src/clustering/administration/persist/table_interface.cc delete mode 100644 src/clustering/administration/persist/table_interface.hpp delete mode 100644 src/clustering/administration/real_reql_cluster_interface.cc delete mode 100644 src/clustering/administration/real_reql_cluster_interface.hpp delete mode 100644 src/clustering/administration/servers/auto_reconnect.cc delete mode 100644 src/clustering/administration/servers/auto_reconnect.hpp delete mode 100644 src/clustering/administration/servers/config_client.cc delete mode 100644 src/clustering/administration/servers/config_client.hpp delete mode 100644 src/clustering/administration/servers/config_server.cc delete mode 100644 src/clustering/administration/servers/config_server.hpp delete mode 100644 src/clustering/administration/servers/network_logger.cc delete mode 100644 src/clustering/administration/servers/network_logger.hpp delete mode 100644 src/clustering/administration/servers/server_common.cc delete mode 100644 src/clustering/administration/servers/server_common.hpp delete mode 100644 src/clustering/administration/servers/server_config.cc delete mode 100644 src/clustering/administration/servers/server_config.hpp delete mode 100644 src/clustering/administration/servers/server_metadata.cc delete mode 100644 src/clustering/administration/servers/server_metadata.hpp delete mode 100644 src/clustering/administration/servers/server_status.cc delete mode 100644 src/clustering/administration/servers/server_status.hpp delete mode 100644 src/clustering/administration/stats/debug_stats_backend.cc delete mode 100644 src/clustering/administration/stats/debug_stats_backend.hpp delete mode 100644 src/clustering/administration/stats/request.cc delete mode 100644 src/clustering/administration/stats/request.hpp delete mode 100644 src/clustering/administration/stats/stat_manager.cc delete mode 100644 src/clustering/administration/stats/stat_manager.hpp delete mode 100644 src/clustering/administration/stats/stats_backend.cc delete mode 100644 src/clustering/administration/stats/stats_backend.hpp delete mode 100644 src/clustering/administration/tables/calculate_status.cc delete mode 100644 src/clustering/administration/tables/calculate_status.hpp delete mode 100644 src/clustering/administration/tables/database_metadata.hpp delete mode 100644 src/clustering/administration/tables/db_config.cc delete mode 100644 src/clustering/administration/tables/db_config.hpp delete mode 100644 src/clustering/administration/tables/debug_table_status.cc delete mode 100644 src/clustering/administration/tables/debug_table_status.hpp delete mode 100644 src/clustering/administration/tables/generate_config.cc delete mode 100644 src/clustering/administration/tables/generate_config.hpp delete mode 100644 src/clustering/administration/tables/name_resolver.cc delete mode 100644 src/clustering/administration/tables/name_resolver.hpp delete mode 100644 src/clustering/administration/tables/split_points.cc delete mode 100644 src/clustering/administration/tables/split_points.hpp delete mode 100644 src/clustering/administration/tables/table_common.cc delete mode 100644 src/clustering/administration/tables/table_common.hpp delete mode 100644 src/clustering/administration/tables/table_config.cc delete mode 100644 src/clustering/administration/tables/table_config.hpp delete mode 100644 src/clustering/administration/tables/table_metadata.cc delete mode 100644 src/clustering/administration/tables/table_metadata.hpp delete mode 100644 src/clustering/administration/tables/table_status.cc delete mode 100644 src/clustering/administration/tables/table_status.hpp delete mode 100644 src/clustering/generic/minidir.hpp delete mode 100644 src/clustering/generic/minidir.tcc delete mode 100644 src/clustering/generic/multi_client_client.cc delete mode 100644 src/clustering/generic/multi_client_client.hpp delete mode 100644 src/clustering/generic/multi_client_metadata.hpp delete mode 100644 src/clustering/generic/multi_client_server.hpp delete mode 100644 src/clustering/generic/nonoverlapping_regions.cc delete mode 100644 src/clustering/generic/nonoverlapping_regions.hpp delete mode 100644 src/clustering/generic/raft_core.cc delete mode 100644 src/clustering/generic/raft_core.hpp delete mode 100644 src/clustering/generic/raft_core.tcc delete mode 100644 src/clustering/generic/raft_network.hpp delete mode 100644 src/clustering/generic/raft_network.tcc delete mode 100644 src/clustering/generic/registrant.hpp delete mode 100644 src/clustering/generic/registrar.hpp delete mode 100644 src/clustering/generic/registration_metadata.hpp delete mode 100644 src/clustering/id_types.hpp delete mode 100644 src/clustering/immediate_consistency/backfill_item_seq.hpp delete mode 100644 src/clustering/immediate_consistency/backfill_metadata.cc delete mode 100644 src/clustering/immediate_consistency/backfill_metadata.hpp delete mode 100644 src/clustering/immediate_consistency/backfill_throttler.hpp delete mode 100644 src/clustering/immediate_consistency/backfillee.cc delete mode 100644 src/clustering/immediate_consistency/backfillee.hpp delete mode 100644 src/clustering/immediate_consistency/backfiller.cc delete mode 100644 src/clustering/immediate_consistency/backfiller.hpp delete mode 100644 src/clustering/immediate_consistency/history.cc delete mode 100644 src/clustering/immediate_consistency/history.hpp delete mode 100644 src/clustering/immediate_consistency/local_replicator.cc delete mode 100644 src/clustering/immediate_consistency/local_replicator.hpp delete mode 100644 src/clustering/immediate_consistency/primary_dispatcher.cc delete mode 100644 src/clustering/immediate_consistency/primary_dispatcher.hpp delete mode 100644 src/clustering/immediate_consistency/remote_replicator_client.cc delete mode 100644 src/clustering/immediate_consistency/remote_replicator_client.hpp delete mode 100644 src/clustering/immediate_consistency/remote_replicator_metadata.cc delete mode 100644 src/clustering/immediate_consistency/remote_replicator_metadata.hpp delete mode 100644 src/clustering/immediate_consistency/remote_replicator_server.cc delete mode 100644 src/clustering/immediate_consistency/remote_replicator_server.hpp delete mode 100644 src/clustering/immediate_consistency/replica.cc delete mode 100644 src/clustering/immediate_consistency/replica.hpp delete mode 100644 src/clustering/immediate_consistency/standard_backfill_throttler.cc delete mode 100644 src/clustering/immediate_consistency/standard_backfill_throttler.hpp delete mode 100644 src/clustering/query_routing/direct_query_server.cc delete mode 100644 src/clustering/query_routing/direct_query_server.hpp delete mode 100644 src/clustering/query_routing/metadata.cc delete mode 100644 src/clustering/query_routing/metadata.hpp delete mode 100644 src/clustering/query_routing/primary_query_client.cc delete mode 100644 src/clustering/query_routing/primary_query_client.hpp delete mode 100644 src/clustering/query_routing/primary_query_server.cc delete mode 100644 src/clustering/query_routing/primary_query_server.hpp delete mode 100644 src/clustering/query_routing/table_query_client.cc delete mode 100644 src/clustering/query_routing/table_query_client.hpp delete mode 100644 src/clustering/table_contract/branch_history_gc.cc delete mode 100644 src/clustering/table_contract/branch_history_gc.hpp delete mode 100644 src/clustering/table_contract/contract_metadata.cc delete mode 100644 src/clustering/table_contract/contract_metadata.hpp delete mode 100644 src/clustering/table_contract/coordinator/calculate_contracts.cc delete mode 100644 src/clustering/table_contract/coordinator/calculate_contracts.hpp delete mode 100644 src/clustering/table_contract/coordinator/calculate_misc.cc delete mode 100644 src/clustering/table_contract/coordinator/calculate_misc.hpp delete mode 100644 src/clustering/table_contract/coordinator/check_ready.cc delete mode 100644 src/clustering/table_contract/coordinator/check_ready.hpp delete mode 100644 src/clustering/table_contract/coordinator/coordinator.cc delete mode 100644 src/clustering/table_contract/coordinator/coordinator.hpp delete mode 100644 src/clustering/table_contract/cpu_sharding.cc delete mode 100644 src/clustering/table_contract/cpu_sharding.hpp delete mode 100644 src/clustering/table_contract/emergency_repair.cc delete mode 100644 src/clustering/table_contract/emergency_repair.hpp delete mode 100644 src/clustering/table_contract/executor/exec.cc delete mode 100644 src/clustering/table_contract/executor/exec.hpp delete mode 100644 src/clustering/table_contract/executor/exec_erase.cc delete mode 100644 src/clustering/table_contract/executor/exec_erase.hpp delete mode 100644 src/clustering/table_contract/executor/exec_primary.cc delete mode 100644 src/clustering/table_contract/executor/exec_primary.hpp delete mode 100644 src/clustering/table_contract/executor/exec_secondary.cc delete mode 100644 src/clustering/table_contract/executor/exec_secondary.hpp delete mode 100644 src/clustering/table_contract/executor/executor.cc delete mode 100644 src/clustering/table_contract/executor/executor.hpp delete mode 100644 src/clustering/table_manager/backfill_progress_tracker.cc delete mode 100644 src/clustering/table_manager/backfill_progress_tracker.hpp delete mode 100644 src/clustering/table_manager/multi_table_manager.cc delete mode 100644 src/clustering/table_manager/multi_table_manager.hpp delete mode 100644 src/clustering/table_manager/server_name_cache_updater.cc delete mode 100644 src/clustering/table_manager/server_name_cache_updater.hpp delete mode 100644 src/clustering/table_manager/sindex_manager.cc delete mode 100644 src/clustering/table_manager/sindex_manager.hpp delete mode 100644 src/clustering/table_manager/table_manager.cc delete mode 100644 src/clustering/table_manager/table_manager.hpp delete mode 100644 src/clustering/table_manager/table_meta_client.cc delete mode 100644 src/clustering/table_manager/table_meta_client.hpp delete mode 100644 src/clustering/table_manager/table_metadata.cc delete mode 100644 src/clustering/table_manager/table_metadata.hpp delete mode 100644 src/concurrency/access.hpp delete mode 100644 src/concurrency/auto_drainer.cc delete mode 100644 src/concurrency/auto_drainer.hpp delete mode 100644 src/concurrency/cache_line_padded.hpp delete mode 100644 src/concurrency/cond_var.cc delete mode 100644 src/concurrency/cond_var.hpp delete mode 100644 src/concurrency/coro_pool.hpp delete mode 100644 src/concurrency/cross_thread_mutex.cc delete mode 100644 src/concurrency/cross_thread_mutex.hpp delete mode 100644 src/concurrency/cross_thread_semaphore.hpp delete mode 100644 src/concurrency/cross_thread_signal.cc delete mode 100644 src/concurrency/cross_thread_signal.hpp delete mode 100644 src/concurrency/cross_thread_watchable.hpp delete mode 100644 src/concurrency/cross_thread_watchable.tcc delete mode 100644 src/concurrency/exponential_backoff.hpp delete mode 100644 src/concurrency/fifo_checker.cc delete mode 100644 src/concurrency/fifo_checker.hpp delete mode 100644 src/concurrency/fifo_enforcer.cc delete mode 100644 src/concurrency/fifo_enforcer.hpp delete mode 100644 src/concurrency/fifo_enforcer_queue.hpp delete mode 100644 src/concurrency/fifo_enforcer_queue.tcc delete mode 100644 src/concurrency/interruptor.cc delete mode 100644 src/concurrency/interruptor.hpp delete mode 100644 src/concurrency/mutex.cc delete mode 100644 src/concurrency/mutex.hpp delete mode 100644 src/concurrency/mutex_assertion.hpp delete mode 100644 src/concurrency/new_mutex.hpp delete mode 100644 src/concurrency/new_semaphore.cc delete mode 100644 src/concurrency/new_semaphore.hpp delete mode 100644 src/concurrency/one_per_thread.hpp delete mode 100644 src/concurrency/pmap.hpp delete mode 100644 src/concurrency/promise.hpp delete mode 100644 src/concurrency/pubsub.hpp delete mode 100644 src/concurrency/pump_coro.cc delete mode 100644 src/concurrency/pump_coro.hpp delete mode 100644 src/concurrency/queue/accounting.hpp delete mode 100644 src/concurrency/queue/disk_backed_queue_wrapper.hpp delete mode 100644 src/concurrency/queue/limited_fifo.hpp delete mode 100644 src/concurrency/queue/passive_producer.hpp delete mode 100644 src/concurrency/queue/unlimited_fifo.hpp delete mode 100644 src/concurrency/rwlock.cc delete mode 100644 src/concurrency/rwlock.hpp delete mode 100644 src/concurrency/semaphore.cc delete mode 100644 src/concurrency/semaphore.hpp delete mode 100644 src/concurrency/signal.cc delete mode 100644 src/concurrency/signal.hpp delete mode 100644 src/concurrency/timestamp_enforcer.cc delete mode 100644 src/concurrency/timestamp_enforcer.hpp delete mode 100644 src/concurrency/wait_any.cc delete mode 100644 src/concurrency/wait_any.hpp delete mode 100644 src/concurrency/watchable.hpp delete mode 100644 src/concurrency/watchable.tcc delete mode 100644 src/concurrency/watchable_map.hpp delete mode 100644 src/concurrency/watchable_map.tcc delete mode 100644 src/concurrency/watchable_transform.hpp delete mode 100644 src/concurrency/watchable_transform.tcc delete mode 100644 src/concurrency/watchdog_timer.cc delete mode 100644 src/concurrency/watchdog_timer.hpp delete mode 100644 src/config/args.hpp delete mode 100644 src/containers/archive/archive.cc delete mode 100644 src/containers/archive/archive.hpp delete mode 100644 src/containers/archive/boost_types.hpp delete mode 100644 src/containers/archive/buffer_group_stream.cc delete mode 100644 src/containers/archive/buffer_group_stream.hpp delete mode 100644 src/containers/archive/buffer_stream.hpp delete mode 100644 src/containers/archive/file_stream.cc delete mode 100644 src/containers/archive/file_stream.hpp delete mode 100644 src/containers/archive/optional.hpp delete mode 100644 src/containers/archive/socket_stream.cc delete mode 100644 src/containers/archive/socket_stream.hpp delete mode 100644 src/containers/archive/stl_types.cc delete mode 100644 src/containers/archive/stl_types.hpp delete mode 100644 src/containers/archive/string_stream.cc delete mode 100644 src/containers/archive/string_stream.hpp delete mode 100644 src/containers/archive/tcp_conn_stream.cc delete mode 100644 src/containers/archive/tcp_conn_stream.hpp delete mode 100644 src/containers/archive/tuple.hpp delete mode 100644 src/containers/archive/varint.cc delete mode 100644 src/containers/archive/varint.hpp delete mode 100644 src/containers/archive/vector_stream.cc delete mode 100644 src/containers/archive/vector_stream.hpp delete mode 100644 src/containers/archive/versioned.hpp delete mode 100644 src/containers/auth_key.cc delete mode 100644 src/containers/auth_key.hpp delete mode 100644 src/containers/backindex_bag.hpp delete mode 100644 src/containers/binary_blob.cc delete mode 100644 src/containers/binary_blob.hpp delete mode 100644 src/containers/buffer_group.cc delete mode 100644 src/containers/buffer_group.hpp delete mode 100644 src/containers/clone_ptr.hpp delete mode 100644 src/containers/clone_ptr.tcc delete mode 100644 src/containers/counted.hpp delete mode 100644 src/containers/death_runner.hpp delete mode 100644 src/containers/disk_backed_queue.cc delete mode 100644 src/containers/disk_backed_queue.hpp delete mode 100644 src/containers/empty_value.hpp delete mode 100644 src/containers/half_intrusive_list.hpp delete mode 100644 src/containers/incremental_lenses.hpp delete mode 100644 src/containers/intrusive_list.hpp delete mode 100644 src/containers/intrusive_priority_queue.hpp delete mode 100644 src/containers/lazy_erase_vector.hpp delete mode 100644 src/containers/lifetime.hpp delete mode 100644 src/containers/lru_cache.hpp delete mode 100644 src/containers/map_sentries.hpp delete mode 100644 src/containers/name_string.cc delete mode 100644 src/containers/name_string.hpp delete mode 100644 src/containers/object_buffer.hpp delete mode 100644 src/containers/optional.hpp delete mode 100644 src/containers/printf_buffer.cc delete mode 100644 src/containers/printf_buffer.hpp delete mode 100644 src/containers/priority_queue.hpp delete mode 100644 src/containers/priority_queue.tcc delete mode 100644 src/containers/range_map.hpp delete mode 100644 src/containers/scoped.hpp delete mode 100644 src/containers/segmented_vector.hpp delete mode 100644 src/containers/shared_buffer.cc delete mode 100644 src/containers/shared_buffer.hpp delete mode 100644 src/containers/tribool.hpp delete mode 100644 src/containers/two_level_array.hpp delete mode 100644 src/containers/uuid.cc delete mode 100644 src/containers/uuid.hpp delete mode 100644 src/crypto/base64.cc delete mode 100644 src/crypto/base64.hpp delete mode 100644 src/crypto/compare_equal.hpp delete mode 100644 src/crypto/error.cc delete mode 100644 src/crypto/error.hpp delete mode 100644 src/crypto/hash.cc delete mode 100644 src/crypto/hash.hpp delete mode 100644 src/crypto/hmac.cc delete mode 100644 src/crypto/hmac.hpp delete mode 100644 src/crypto/initialization_guard.cc delete mode 100644 src/crypto/initialization_guard.hpp delete mode 100644 src/crypto/pbkcs5_pbkdf2_hmac.cc delete mode 100644 src/crypto/pbkcs5_pbkdf2_hmac.hpp delete mode 100644 src/crypto/random.cc delete mode 100644 src/crypto/random.hpp delete mode 100644 src/crypto/saslprep.cc delete mode 100644 src/crypto/saslprep.hpp delete mode 100644 src/debug.cc delete mode 100644 src/debug.hpp delete mode 100644 src/do_on_thread.hpp delete mode 100644 src/errors.cc delete mode 100644 src/errors.hpp delete mode 100644 src/extproc/extproc_job.cc delete mode 100644 src/extproc/extproc_job.hpp delete mode 100644 src/extproc/extproc_pool.cc delete mode 100644 src/extproc/extproc_pool.hpp delete mode 100644 src/extproc/extproc_spawner.cc delete mode 100644 src/extproc/extproc_spawner.hpp delete mode 100644 src/extproc/extproc_worker.cc delete mode 100644 src/extproc/extproc_worker.hpp delete mode 100644 src/extproc/http_job.cc delete mode 100644 src/extproc/http_job.hpp delete mode 100644 src/extproc/http_runner.cc delete mode 100644 src/extproc/http_runner.hpp delete mode 100644 src/extproc/js_job.cc delete mode 100644 src/extproc/js_job.hpp delete mode 100644 src/extproc/js_runner.cc delete mode 100644 src/extproc/js_runner.hpp delete mode 100644 src/gen/web_assets.cc delete mode 100644 src/http/CPPLINT.cfg delete mode 100644 src/http/file_app.cc delete mode 100644 src/http/file_app.hpp delete mode 100644 src/http/http.cc delete mode 100644 src/http/http.hpp delete mode 100644 src/http/http_parser.cc delete mode 100644 src/http/http_parser.hpp delete mode 100644 src/http/routing_app.cc delete mode 100644 src/http/routing_app.hpp delete mode 100644 src/http/web_assets.hpp delete mode 100644 src/logger.hpp delete mode 100644 src/main.cc delete mode 100644 src/math.cc delete mode 100644 src/math.hpp delete mode 100644 src/memory_utils.cc delete mode 100644 src/memory_utils.hpp delete mode 100644 src/parsing/utf8.cc delete mode 100644 src/parsing/utf8.hpp delete mode 100644 src/parsing/util.cc delete mode 100644 src/parsing/util.hpp delete mode 100644 src/paths.cc delete mode 100644 src/paths.hpp delete mode 100644 src/perfmon/collect.cc delete mode 100644 src/perfmon/collect.hpp delete mode 100644 src/perfmon/core.cc delete mode 100644 src/perfmon/core.hpp delete mode 100644 src/perfmon/filter.cc delete mode 100644 src/perfmon/filter.hpp delete mode 100644 src/perfmon/perfmon.cc delete mode 100644 src/perfmon/perfmon.hpp delete mode 100644 src/perfmon/types.hpp delete mode 100644 src/pprint/generic_term_walker.hpp delete mode 100644 src/pprint/js_pprint.cc delete mode 100644 src/pprint/js_pprint.hpp delete mode 100644 src/pprint/pprint.cc delete mode 100644 src/pprint/pprint.hpp delete mode 100644 src/pprint/sexp_pprint.cc delete mode 100644 src/pprint/sexp_pprint.hpp delete mode 100644 src/protocol_api.cc delete mode 100644 src/protocol_api.hpp delete mode 100644 src/random.cc delete mode 100644 src/random.hpp delete mode 100644 src/rapidjson/allocators.h delete mode 100644 src/rapidjson/document.h delete mode 100644 src/rapidjson/encodedstream.h delete mode 100644 src/rapidjson/encodings.h delete mode 100644 src/rapidjson/error/en.h delete mode 100644 src/rapidjson/error/error.h delete mode 100644 src/rapidjson/filereadstream.h delete mode 100644 src/rapidjson/filestream.h delete mode 100644 src/rapidjson/filewritestream.h delete mode 100755 src/rapidjson/internal/biginteger.h delete mode 100644 src/rapidjson/internal/diyfp.h delete mode 100644 src/rapidjson/internal/dtoa.h delete mode 100644 src/rapidjson/internal/ieee754.h delete mode 100644 src/rapidjson/internal/itoa.h delete mode 100644 src/rapidjson/internal/meta.h delete mode 100644 src/rapidjson/internal/pow10.h delete mode 100644 src/rapidjson/internal/stack.h delete mode 100644 src/rapidjson/internal/strfunc.h delete mode 100644 src/rapidjson/internal/strtod.h delete mode 100644 src/rapidjson/memorybuffer.h delete mode 100644 src/rapidjson/memorystream.h delete mode 100644 src/rapidjson/msinttypes/inttypes.h delete mode 100644 src/rapidjson/msinttypes/stdint.h delete mode 100644 src/rapidjson/prettywriter.h delete mode 100644 src/rapidjson/rapidjson.h delete mode 100644 src/rapidjson/reader.h delete mode 100644 src/rapidjson/stringbuffer.h delete mode 100644 src/rapidjson/writer.h delete mode 100644 src/rdb_protocol/artificial_table/artificial_table.cc delete mode 100644 src/rdb_protocol/artificial_table/artificial_table.hpp delete mode 100644 src/rdb_protocol/artificial_table/backend.cc delete mode 100644 src/rdb_protocol/artificial_table/backend.hpp delete mode 100644 src/rdb_protocol/artificial_table/caching_cfeed_backend.cc delete mode 100644 src/rdb_protocol/artificial_table/caching_cfeed_backend.hpp delete mode 100644 src/rdb_protocol/artificial_table/cfeed_backend.cc delete mode 100644 src/rdb_protocol/artificial_table/cfeed_backend.hpp delete mode 100644 src/rdb_protocol/artificial_table/in_memory.hpp delete mode 100644 src/rdb_protocol/base64.cc delete mode 100644 src/rdb_protocol/base64.hpp delete mode 100644 src/rdb_protocol/batching.cc delete mode 100644 src/rdb_protocol/batching.hpp delete mode 100644 src/rdb_protocol/blob_wrapper.cc delete mode 100644 src/rdb_protocol/blob_wrapper.hpp delete mode 100644 src/rdb_protocol/btree.cc delete mode 100644 src/rdb_protocol/btree.hpp delete mode 100644 src/rdb_protocol/btree_store.cc delete mode 100644 src/rdb_protocol/changefeed.cc delete mode 100644 src/rdb_protocol/changefeed.hpp delete mode 100644 src/rdb_protocol/configured_limits.cc delete mode 100644 src/rdb_protocol/configured_limits.hpp delete mode 100644 src/rdb_protocol/context.cc delete mode 100644 src/rdb_protocol/context.hpp delete mode 100644 src/rdb_protocol/datum.cc delete mode 100644 src/rdb_protocol/datum.hpp delete mode 100644 src/rdb_protocol/datum_json.hpp delete mode 100644 src/rdb_protocol/datum_stream.cc delete mode 100644 src/rdb_protocol/datum_stream.hpp delete mode 100644 src/rdb_protocol/datum_stream/array.hpp delete mode 100644 src/rdb_protocol/datum_stream/eq_join.hpp delete mode 100644 src/rdb_protocol/datum_stream/fold.hpp delete mode 100644 src/rdb_protocol/datum_stream/indexed_sort.hpp delete mode 100644 src/rdb_protocol/datum_stream/lazy.hpp delete mode 100644 src/rdb_protocol/datum_stream/map.hpp delete mode 100644 src/rdb_protocol/datum_stream/offsets_of.hpp delete mode 100644 src/rdb_protocol/datum_stream/ordered_distinct.hpp delete mode 100644 src/rdb_protocol/datum_stream/ordered_union.hpp delete mode 100644 src/rdb_protocol/datum_stream/range.hpp delete mode 100644 src/rdb_protocol/datum_stream/readers.hpp delete mode 100644 src/rdb_protocol/datum_stream/readgens.hpp delete mode 100644 src/rdb_protocol/datum_stream/slice.hpp delete mode 100644 src/rdb_protocol/datum_stream/union.hpp delete mode 100644 src/rdb_protocol/datum_stream/vector.hpp delete mode 100644 src/rdb_protocol/datum_string.cc delete mode 100644 src/rdb_protocol/datum_string.hpp delete mode 100644 src/rdb_protocol/datum_utils.hpp delete mode 100644 src/rdb_protocol/datumspec.cc delete mode 100644 src/rdb_protocol/datumspec.hpp delete mode 100644 src/rdb_protocol/distribution_progress.cc delete mode 100644 src/rdb_protocol/distribution_progress.hpp delete mode 100644 src/rdb_protocol/env.cc delete mode 100644 src/rdb_protocol/env.hpp delete mode 100644 src/rdb_protocol/erase_range.cc delete mode 100644 src/rdb_protocol/erase_range.hpp delete mode 100644 src/rdb_protocol/error.cc delete mode 100644 src/rdb_protocol/error.hpp delete mode 100644 src/rdb_protocol/func.cc delete mode 100644 src/rdb_protocol/func.hpp delete mode 100644 src/rdb_protocol/geo/CPPLINT.cfg delete mode 100644 src/rdb_protocol/geo/distances.cc delete mode 100644 src/rdb_protocol/geo/distances.hpp delete mode 100644 src/rdb_protocol/geo/ellipsoid.cc delete mode 100644 src/rdb_protocol/geo/ellipsoid.hpp delete mode 100644 src/rdb_protocol/geo/exceptions.hpp delete mode 100644 src/rdb_protocol/geo/geo_visitor.hpp delete mode 100644 src/rdb_protocol/geo/geojson.cc delete mode 100644 src/rdb_protocol/geo/geojson.hpp delete mode 100644 src/rdb_protocol/geo/inclusion.cc delete mode 100644 src/rdb_protocol/geo/inclusion.hpp delete mode 100644 src/rdb_protocol/geo/indexing.cc delete mode 100644 src/rdb_protocol/geo/indexing.hpp delete mode 100644 src/rdb_protocol/geo/intersection.cc delete mode 100644 src/rdb_protocol/geo/intersection.hpp delete mode 100644 src/rdb_protocol/geo/karney/00README.txt delete mode 100644 src/rdb_protocol/geo/karney/LICENSE.txt delete mode 100644 src/rdb_protocol/geo/karney/geodesic.cc delete mode 100644 src/rdb_protocol/geo/karney/geodesic.h delete mode 100644 src/rdb_protocol/geo/lon_lat_types.cc delete mode 100644 src/rdb_protocol/geo/lon_lat_types.hpp delete mode 100644 src/rdb_protocol/geo/primitives.cc delete mode 100644 src/rdb_protocol/geo/primitives.hpp delete mode 100644 src/rdb_protocol/geo/s2/LICENSE-2.0.txt delete mode 100644 src/rdb_protocol/geo/s2/base/basictypes.h delete mode 100644 src/rdb_protocol/geo/s2/base/casts.h delete mode 100644 src/rdb_protocol/geo/s2/base/docid.h delete mode 100644 src/rdb_protocol/geo/s2/base/int128.cc delete mode 100644 src/rdb_protocol/geo/s2/base/int128.h delete mode 100644 src/rdb_protocol/geo/s2/base/integral_types.h delete mode 100644 src/rdb_protocol/geo/s2/base/logging.cc delete mode 100644 src/rdb_protocol/geo/s2/base/logging.h delete mode 100644 src/rdb_protocol/geo/s2/base/macros.h delete mode 100644 src/rdb_protocol/geo/s2/base/port.h delete mode 100644 src/rdb_protocol/geo/s2/base/scoped_ptr.h delete mode 100644 src/rdb_protocol/geo/s2/base/stl_decl.h delete mode 100644 src/rdb_protocol/geo/s2/base/stl_decl_msvc.h delete mode 100644 src/rdb_protocol/geo/s2/base/stringprintf.cc delete mode 100644 src/rdb_protocol/geo/s2/base/stringprintf.h delete mode 100644 src/rdb_protocol/geo/s2/base/strtoint.cc delete mode 100644 src/rdb_protocol/geo/s2/base/strtoint.h delete mode 100644 src/rdb_protocol/geo/s2/base/template_util.h delete mode 100644 src/rdb_protocol/geo/s2/base/type_traits.h delete mode 100644 src/rdb_protocol/geo/s2/r1interval.h delete mode 100644 src/rdb_protocol/geo/s2/s1angle.cc delete mode 100644 src/rdb_protocol/geo/s2/s1angle.h delete mode 100644 src/rdb_protocol/geo/s2/s1interval.cc delete mode 100644 src/rdb_protocol/geo/s2/s1interval.h delete mode 100644 src/rdb_protocol/geo/s2/s2.cc delete mode 100644 src/rdb_protocol/geo/s2/s2.h delete mode 100644 src/rdb_protocol/geo/s2/s2cap.cc delete mode 100644 src/rdb_protocol/geo/s2/s2cap.h delete mode 100644 src/rdb_protocol/geo/s2/s2cell.cc delete mode 100644 src/rdb_protocol/geo/s2/s2cell.h delete mode 100644 src/rdb_protocol/geo/s2/s2cellid.cc delete mode 100644 src/rdb_protocol/geo/s2/s2cellid.h delete mode 100644 src/rdb_protocol/geo/s2/s2cellunion.cc delete mode 100644 src/rdb_protocol/geo/s2/s2cellunion.h delete mode 100644 src/rdb_protocol/geo/s2/s2edgeindex.cc delete mode 100644 src/rdb_protocol/geo/s2/s2edgeindex.h delete mode 100644 src/rdb_protocol/geo/s2/s2edgeutil.cc delete mode 100644 src/rdb_protocol/geo/s2/s2edgeutil.h delete mode 100644 src/rdb_protocol/geo/s2/s2latlng.cc delete mode 100644 src/rdb_protocol/geo/s2/s2latlng.h delete mode 100644 src/rdb_protocol/geo/s2/s2latlngrect.cc delete mode 100644 src/rdb_protocol/geo/s2/s2latlngrect.h delete mode 100644 src/rdb_protocol/geo/s2/s2loop.cc delete mode 100644 src/rdb_protocol/geo/s2/s2loop.h delete mode 100644 src/rdb_protocol/geo/s2/s2pointregion.cc delete mode 100644 src/rdb_protocol/geo/s2/s2pointregion.h delete mode 100644 src/rdb_protocol/geo/s2/s2polygon.cc delete mode 100644 src/rdb_protocol/geo/s2/s2polygon.h delete mode 100644 src/rdb_protocol/geo/s2/s2polygonbuilder.cc delete mode 100644 src/rdb_protocol/geo/s2/s2polygonbuilder.h delete mode 100644 src/rdb_protocol/geo/s2/s2polyline.cc delete mode 100644 src/rdb_protocol/geo/s2/s2polyline.h delete mode 100644 src/rdb_protocol/geo/s2/s2r2rect.cc delete mode 100644 src/rdb_protocol/geo/s2/s2r2rect.h delete mode 100644 src/rdb_protocol/geo/s2/s2region.cc delete mode 100644 src/rdb_protocol/geo/s2/s2region.h delete mode 100644 src/rdb_protocol/geo/s2/s2regioncoverer.cc delete mode 100644 src/rdb_protocol/geo/s2/s2regioncoverer.h delete mode 100644 src/rdb_protocol/geo/s2/s2regionintersection.cc delete mode 100644 src/rdb_protocol/geo/s2/s2regionintersection.h delete mode 100644 src/rdb_protocol/geo/s2/s2regionunion.cc delete mode 100644 src/rdb_protocol/geo/s2/s2regionunion.h delete mode 100644 src/rdb_protocol/geo/s2/strings/ascii_ctype.cc delete mode 100644 src/rdb_protocol/geo/s2/strings/ascii_ctype.h delete mode 100644 src/rdb_protocol/geo/s2/strings/split.cc delete mode 100644 src/rdb_protocol/geo/s2/strings/split.h delete mode 100644 src/rdb_protocol/geo/s2/strings/stringprintf.cc delete mode 100644 src/rdb_protocol/geo/s2/strings/stringprintf.h delete mode 100644 src/rdb_protocol/geo/s2/strings/strutil.cc delete mode 100644 src/rdb_protocol/geo/s2/strings/strutil.h delete mode 100644 src/rdb_protocol/geo/s2/util/coding/coder.cc delete mode 100644 src/rdb_protocol/geo/s2/util/coding/coder.h delete mode 100644 src/rdb_protocol/geo/s2/util/coding/varint.cc delete mode 100644 src/rdb_protocol/geo/s2/util/coding/varint.h delete mode 100644 src/rdb_protocol/geo/s2/util/endian/endian.h delete mode 100644 src/rdb_protocol/geo/s2/util/hash/hash_jenkins_lookup2.h delete mode 100644 src/rdb_protocol/geo/s2/util/math/exactfloat/exactfloat.cc delete mode 100644 src/rdb_protocol/geo/s2/util/math/exactfloat/exactfloat.h delete mode 100644 src/rdb_protocol/geo/s2/util/math/mathlimits.cc delete mode 100644 src/rdb_protocol/geo/s2/util/math/mathlimits.h delete mode 100644 src/rdb_protocol/geo/s2/util/math/mathutil.cc delete mode 100644 src/rdb_protocol/geo/s2/util/math/mathutil.h delete mode 100644 src/rdb_protocol/geo/s2/util/math/matrix3x3-inl.h delete mode 100644 src/rdb_protocol/geo/s2/util/math/matrix3x3.h delete mode 100644 src/rdb_protocol/geo/s2/util/math/vector2-inl.h delete mode 100644 src/rdb_protocol/geo/s2/util/math/vector2.h delete mode 100644 src/rdb_protocol/geo/s2/util/math/vector3-inl.h delete mode 100644 src/rdb_protocol/geo/s2/util/math/vector3.h delete mode 100644 src/rdb_protocol/geo/s2/util/math/vector4-inl.h delete mode 100644 src/rdb_protocol/geo/s2/util/math/vector4.h delete mode 100644 src/rdb_protocol/geo_traversal.cc delete mode 100644 src/rdb_protocol/geo_traversal.hpp delete mode 100644 src/rdb_protocol/lazy_btree_val.cc delete mode 100644 src/rdb_protocol/lazy_btree_val.hpp delete mode 100644 src/rdb_protocol/math_utils.hpp delete mode 100644 src/rdb_protocol/minidriver.cc delete mode 100644 src/rdb_protocol/minidriver.hpp delete mode 100644 src/rdb_protocol/op.cc delete mode 100644 src/rdb_protocol/op.hpp delete mode 100644 src/rdb_protocol/optargs.cc delete mode 100644 src/rdb_protocol/optargs.hpp delete mode 100644 src/rdb_protocol/order_util.cc delete mode 100644 src/rdb_protocol/order_util.hpp delete mode 100644 src/rdb_protocol/pathspec.cc delete mode 100644 src/rdb_protocol/pathspec.hpp delete mode 100644 src/rdb_protocol/profile.cc delete mode 100644 src/rdb_protocol/profile.hpp delete mode 100644 src/rdb_protocol/protocol.cc delete mode 100644 src/rdb_protocol/protocol.hpp delete mode 100644 src/rdb_protocol/pseudo_binary.cc delete mode 100644 src/rdb_protocol/pseudo_binary.hpp delete mode 100644 src/rdb_protocol/pseudo_geometry.cc delete mode 100644 src/rdb_protocol/pseudo_geometry.hpp delete mode 100644 src/rdb_protocol/pseudo_literal.cc delete mode 100644 src/rdb_protocol/pseudo_literal.hpp delete mode 100644 src/rdb_protocol/pseudo_time.cc delete mode 100644 src/rdb_protocol/pseudo_time.hpp delete mode 100644 src/rdb_protocol/ql2proto.hpp delete mode 100644 src/rdb_protocol/query_cache.cc delete mode 100644 src/rdb_protocol/query_cache.hpp delete mode 100644 src/rdb_protocol/query_params.cc delete mode 100644 src/rdb_protocol/query_params.hpp delete mode 100644 src/rdb_protocol/query_server.cc delete mode 100644 src/rdb_protocol/query_server.hpp delete mode 100644 src/rdb_protocol/rdb_backtrace.cc delete mode 100644 src/rdb_protocol/rdb_backtrace.hpp delete mode 100644 src/rdb_protocol/real_table.cc delete mode 100644 src/rdb_protocol/real_table.hpp delete mode 100644 src/rdb_protocol/response.cc delete mode 100644 src/rdb_protocol/response.hpp delete mode 100644 src/rdb_protocol/serialize_datum.cc delete mode 100644 src/rdb_protocol/serialize_datum.hpp delete mode 100644 src/rdb_protocol/serialize_datum_onto_blob.hpp delete mode 100644 src/rdb_protocol/shards.cc delete mode 100644 src/rdb_protocol/shards.hpp delete mode 100644 src/rdb_protocol/store.cc delete mode 100644 src/rdb_protocol/store.hpp delete mode 100644 src/rdb_protocol/store_metainfo.cc delete mode 100644 src/rdb_protocol/store_metainfo.hpp delete mode 100644 src/rdb_protocol/store_receive_backfill.cc delete mode 100644 src/rdb_protocol/store_send_backfill.cc delete mode 100644 src/rdb_protocol/sym.cc delete mode 100644 src/rdb_protocol/sym.hpp delete mode 100644 src/rdb_protocol/table_common.cc delete mode 100644 src/rdb_protocol/table_common.hpp delete mode 100644 src/rdb_protocol/term.cc delete mode 100644 src/rdb_protocol/term.hpp delete mode 100644 src/rdb_protocol/term_storage.cc delete mode 100644 src/rdb_protocol/term_storage.hpp delete mode 100644 src/rdb_protocol/term_walker.cc delete mode 100644 src/rdb_protocol/term_walker.hpp delete mode 100644 src/rdb_protocol/terms/arith.cc delete mode 100644 src/rdb_protocol/terms/arr.cc delete mode 100644 src/rdb_protocol/terms/arr.hpp delete mode 100644 src/rdb_protocol/terms/case.cc delete mode 100644 src/rdb_protocol/terms/control.cc delete mode 100644 src/rdb_protocol/terms/datum_terms.cc delete mode 100644 src/rdb_protocol/terms/db_table.cc delete mode 100644 src/rdb_protocol/terms/error.cc delete mode 100644 src/rdb_protocol/terms/geo.cc delete mode 100644 src/rdb_protocol/terms/http.cc delete mode 100644 src/rdb_protocol/terms/js.cc delete mode 100644 src/rdb_protocol/terms/json.cc delete mode 100644 src/rdb_protocol/terms/obj.cc delete mode 100644 src/rdb_protocol/terms/obj_or_seq.cc delete mode 100644 src/rdb_protocol/terms/obj_or_seq.hpp delete mode 100644 src/rdb_protocol/terms/pred.cc delete mode 100644 src/rdb_protocol/terms/random.cc delete mode 100644 src/rdb_protocol/terms/rewrites.cc delete mode 100644 src/rdb_protocol/terms/seq.cc delete mode 100644 src/rdb_protocol/terms/sindex.cc delete mode 100644 src/rdb_protocol/terms/sort.cc delete mode 100644 src/rdb_protocol/terms/string.cc delete mode 100644 src/rdb_protocol/terms/terms.hpp delete mode 100644 src/rdb_protocol/terms/time.cc delete mode 100644 src/rdb_protocol/terms/type_manip.cc delete mode 100644 src/rdb_protocol/terms/uuid.cc delete mode 100644 src/rdb_protocol/terms/var.cc delete mode 100644 src/rdb_protocol/terms/write_hook.cc delete mode 100644 src/rdb_protocol/terms/write_hook.hpp delete mode 100644 src/rdb_protocol/terms/writes.cc delete mode 100644 src/rdb_protocol/terms/writes.hpp delete mode 100644 src/rdb_protocol/val.cc delete mode 100644 src/rdb_protocol/val.hpp delete mode 100644 src/rdb_protocol/var_types.cc delete mode 100644 src/rdb_protocol/var_types.hpp delete mode 100644 src/rdb_protocol/wire_func.cc delete mode 100644 src/rdb_protocol/wire_func.hpp delete mode 100644 src/region/hash_region.cc delete mode 100644 src/region/hash_region.hpp delete mode 100644 src/region/region.cc delete mode 100644 src/region/region.hpp delete mode 100644 src/region/region_map.hpp delete mode 100644 src/repli_timestamp.cc delete mode 100644 src/repli_timestamp.hpp delete mode 100644 src/rethinkdb_backtrace.cc delete mode 100644 src/rethinkdb_backtrace.hpp delete mode 100644 src/rpc/README.md delete mode 100644 src/rpc/connectivity/cluster.cc delete mode 100644 src/rpc/connectivity/cluster.hpp delete mode 100644 src/rpc/connectivity/cluster_message_handler.hpp delete mode 100644 src/rpc/connectivity/peer_id.cc delete mode 100644 src/rpc/connectivity/peer_id.hpp delete mode 100644 src/rpc/connectivity/server_id.cc delete mode 100644 src/rpc/connectivity/server_id.hpp delete mode 100644 src/rpc/directory/map_read_manager.cc delete mode 100644 src/rpc/directory/map_read_manager.hpp delete mode 100644 src/rpc/directory/map_read_manager.tcc delete mode 100644 src/rpc/directory/map_write_manager.cc delete mode 100644 src/rpc/directory/map_write_manager.hpp delete mode 100644 src/rpc/directory/map_write_manager.tcc delete mode 100644 src/rpc/directory/read_manager.cc delete mode 100644 src/rpc/directory/read_manager.hpp delete mode 100644 src/rpc/directory/read_manager.tcc delete mode 100644 src/rpc/directory/write_manager.cc delete mode 100644 src/rpc/directory/write_manager.hpp delete mode 100644 src/rpc/directory/write_manager.tcc delete mode 100644 src/rpc/mailbox/disconnect_watcher.cc delete mode 100644 src/rpc/mailbox/disconnect_watcher.hpp delete mode 100644 src/rpc/mailbox/mailbox.cc delete mode 100644 src/rpc/mailbox/mailbox.hpp delete mode 100644 src/rpc/mailbox/raw_mailbox.cc delete mode 100644 src/rpc/mailbox/raw_mailbox.hpp delete mode 100644 src/rpc/mailbox/typed.hpp delete mode 100644 src/rpc/semilattice/joins/deletable.hpp delete mode 100644 src/rpc/semilattice/joins/deletable.tcc delete mode 100644 src/rpc/semilattice/joins/macros.hpp delete mode 100644 src/rpc/semilattice/joins/map.hpp delete mode 100644 src/rpc/semilattice/joins/set.hpp delete mode 100644 src/rpc/semilattice/joins/versioned.hpp delete mode 100644 src/rpc/semilattice/semilattice_manager.cc delete mode 100644 src/rpc/semilattice/semilattice_manager.hpp delete mode 100644 src/rpc/semilattice/semilattice_manager.tcc delete mode 100644 src/rpc/semilattice/view.hpp delete mode 100644 src/rpc/semilattice/view/field.hpp delete mode 100644 src/rpc/semilattice/view/field.tcc delete mode 100644 src/rpc/semilattice/view/member.hpp delete mode 100644 src/rpc/semilattice/view/member.tcc delete mode 100644 src/rpc/semilattice/watchable.hpp delete mode 100644 src/rpc/serialize_macros.hpp delete mode 100644 src/serializer/buf_ptr.cc delete mode 100644 src/serializer/buf_ptr.hpp delete mode 100644 src/serializer/log/config.hpp delete mode 100644 src/serializer/log/data_block_manager.cc delete mode 100644 src/serializer/log/data_block_manager.hpp delete mode 100644 src/serializer/log/extent_manager.cc delete mode 100644 src/serializer/log/extent_manager.hpp delete mode 100644 src/serializer/log/lba/disk_extent.cc delete mode 100644 src/serializer/log/lba/disk_extent.hpp delete mode 100644 src/serializer/log/lba/disk_format.hpp delete mode 100644 src/serializer/log/lba/disk_structure.cc delete mode 100644 src/serializer/log/lba/disk_structure.hpp delete mode 100644 src/serializer/log/lba/extent.cc delete mode 100644 src/serializer/log/lba/extent.hpp delete mode 100644 src/serializer/log/lba/in_memory_index.cc delete mode 100644 src/serializer/log/lba/in_memory_index.hpp delete mode 100644 src/serializer/log/lba/lba_list.cc delete mode 100644 src/serializer/log/lba/lba_list.hpp delete mode 100644 src/serializer/log/log_serializer.cc delete mode 100644 src/serializer/log/log_serializer.hpp delete mode 100644 src/serializer/log/metablock_manager.cc delete mode 100644 src/serializer/log/metablock_manager.hpp delete mode 100644 src/serializer/log/static_header.cc delete mode 100644 src/serializer/log/static_header.hpp delete mode 100644 src/serializer/log/stats.hpp delete mode 100644 src/serializer/merger.cc delete mode 100644 src/serializer/merger.hpp delete mode 100644 src/serializer/serializer.cc delete mode 100644 src/serializer/serializer.hpp delete mode 100644 src/serializer/translator.cc delete mode 100644 src/serializer/translator.hpp delete mode 100644 src/serializer/types.hpp delete mode 100644 src/stl_utils.cc delete mode 100644 src/stl_utils.hpp delete mode 100644 src/stl_utils.tcc delete mode 100644 src/store_subview.hpp delete mode 100644 src/store_view.hpp delete mode 100644 src/thread_local.hpp delete mode 100644 src/threading.cc delete mode 100644 src/threading.hpp delete mode 100644 src/time.cc delete mode 100644 src/time.hpp delete mode 100644 src/timestamps.cc delete mode 100644 src/timestamps.hpp delete mode 100644 src/unittest/archive_test.cc delete mode 100644 src/unittest/auth_key_test.cc delete mode 100644 src/unittest/barrier_test.cc delete mode 100644 src/unittest/bignum_test.cc delete mode 100644 src/unittest/blob_test.cc delete mode 100644 src/unittest/branch_history_manager.cc delete mode 100644 src/unittest/branch_history_manager.hpp delete mode 100644 src/unittest/btree_metadata.cc delete mode 100644 src/unittest/btree_metainfo.cc delete mode 100644 src/unittest/btree_sindex.cc delete mode 100644 src/unittest/btree_utils.hpp delete mode 100644 src/unittest/btree_whole.cc delete mode 100644 src/unittest/clone_ptr.cc delete mode 100644 src/unittest/clustering_backfill.cc delete mode 100644 src/unittest/clustering_branch.cc delete mode 100644 src/unittest/clustering_branch_history.cc delete mode 100644 src/unittest/clustering_contract_coordinator.cc delete mode 100644 src/unittest/clustering_contract_executor.cc delete mode 100644 src/unittest/clustering_contract_utils.cc delete mode 100644 src/unittest/clustering_contract_utils.hpp delete mode 100644 src/unittest/clustering_minidir.cc delete mode 100644 src/unittest/clustering_query.cc delete mode 100644 src/unittest/clustering_raft.cc delete mode 100644 src/unittest/clustering_raft_fuzzer.cc delete mode 100644 src/unittest/clustering_raft_storage.cc delete mode 100644 src/unittest/clustering_registration.cc delete mode 100644 src/unittest/clustering_utils.cc delete mode 100644 src/unittest/clustering_utils.hpp delete mode 100644 src/unittest/clustering_utils_raft.cc delete mode 100644 src/unittest/clustering_utils_raft.hpp delete mode 100644 src/unittest/context_switching.cc delete mode 100644 src/unittest/coroutine_utils.cc delete mode 100644 src/unittest/coroutines.cc delete mode 100644 src/unittest/cross_thread_watchable.cc delete mode 100644 src/unittest/datum_test.cc delete mode 100644 src/unittest/dbm_test.cc delete mode 100644 src/unittest/disk_backed_queue.cc delete mode 100644 src/unittest/disk_conflict_resolution.cc delete mode 100644 src/unittest/disk_format_test.cc delete mode 100644 src/unittest/dummy_metadata_controller.hpp delete mode 100644 src/unittest/dummy_namespace_interface.cc delete mode 100644 src/unittest/dummy_namespace_interface.hpp delete mode 100644 src/unittest/extproc_test.cc delete mode 100644 src/unittest/extproc_test.hpp delete mode 100644 src/unittest/fifo_enforcer.cc delete mode 100644 src/unittest/geo_btree.cc delete mode 100644 src/unittest/geo_indexes.cc delete mode 100644 src/unittest/geo_primitives.cc delete mode 100644 src/unittest/gtest.hpp delete mode 100644 src/unittest/hash_region_test.cc delete mode 100644 src/unittest/http.cc delete mode 100644 src/unittest/http_escaping.cc delete mode 100644 src/unittest/internal_node_test.cc delete mode 100644 src/unittest/jsproc.cc delete mode 100644 src/unittest/leaf_node_test.cc delete mode 100644 src/unittest/log_message.cc delete mode 100644 src/unittest/lru_cache_test.cc delete mode 100644 src/unittest/main.cc delete mode 100644 src/unittest/mock_file.cc delete mode 100644 src/unittest/mock_file.hpp delete mode 100644 src/unittest/mock_store.cc delete mode 100644 src/unittest/mock_store.hpp delete mode 100644 src/unittest/optional.cc delete mode 100644 src/unittest/options_test.cc delete mode 100644 src/unittest/page_test.cc delete mode 100644 src/unittest/perfmon_tests.cc delete mode 100644 src/unittest/pprint_test.cc delete mode 100644 src/unittest/print_secondary.cc delete mode 100644 src/unittest/printf_buffer_test.cc delete mode 100644 src/unittest/range_map.cc delete mode 100644 src/unittest/rdb_backfill.cc delete mode 100644 src/unittest/rdb_btree.cc delete mode 100644 src/unittest/rdb_env.cc delete mode 100644 src/unittest/rdb_env.hpp delete mode 100644 src/unittest/rdb_interruptor.cc delete mode 100644 src/unittest/rdb_protocol.cc delete mode 100644 src/unittest/rdb_protocol.hpp delete mode 100644 src/unittest/rebalance.cc delete mode 100644 src/unittest/region_map_test.cc delete mode 100644 src/unittest/rpc_connectivity.cc delete mode 100644 src/unittest/rpc_directory.cc delete mode 100644 src/unittest/rpc_mailbox.cc delete mode 100644 src/unittest/rpc_semilattice.cc delete mode 100644 src/unittest/rwlock_test.cc delete mode 100644 src/unittest/serializer_test.cc delete mode 100644 src/unittest/server_id_test.cc delete mode 100644 src/unittest/simple_mailbox_cluster.cc delete mode 100644 src/unittest/simple_mailbox_cluster.hpp delete mode 100644 src/unittest/sizeof_test.cc delete mode 100644 src/unittest/timer_test.cc delete mode 100644 src/unittest/unittest_utils.cc delete mode 100644 src/unittest/unittest_utils.hpp delete mode 100644 src/unittest/utf8.cc delete mode 100644 src/unittest/utils_test.cc delete mode 100644 src/unittest/uuid_test.cc delete mode 100644 src/unittest/varint_test.cc delete mode 100644 src/utils.cc delete mode 100644 src/utils.hpp delete mode 100644 src/valgrind.hpp delete mode 100644 src/version.hpp delete mode 100644 src/windows.hpp diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index c70f770f431..00000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,145 +0,0 @@ -# NOTE: This yaml file will need a refactor. Right now we just need a CI/CD -# integration to ensure that we are running DB tests at every change. This -# pipeline will not do a release, or rql test right now. The later will be -# done after the drivers are extracted from the main repo. - -version: 2.1 -commands: - install_dependencies: - description: Install system requirements - parameters: - additional_packages: - type: string - default: '' - pre_hook: - type: string - default: '' - steps: - - run: | - << parameters.pre_hook >> - apt install -y git-core build-essential protobuf-compiler python \ - libprotobuf-dev libboost-all-dev python nodejs npm \ - libncurses5-dev libjemalloc-dev wget m4 clang libssl1.0-dev \ - debhelper curl << parameters.additional_packages >> - configure: - description: Configure project - parameters: - fetch: - type: string - default: '--allow-fetch' - steps: - - run: ./configure << parameters.fetch >> CXX=clang++ - prepare: - description: 'Checkout, configure and unstash build' - parameters: - fetch: - type: string - default: '--allow-fetch' - additional_packages: - type: string - default: '' - steps: - - install_dependencies: - pre_hook: apt update -qqy - additional_packages: << parameters.additional_packages >> - - checkout - - restore_cache: - key: 'rethinkdb-{{ .Revision }}' - - configure: - fetch: << parameters.fetch >> - test: - description: Run RethinkDB test - parameters: - pattern: - type: string - artifact_dst: - type: string - steps: - - run: | - test/run -v -H << parameters.pattern >> - test -e test/results/*/test_results.html - - store_artifacts: - path: test/results/ - destination: << parameters.artifact_dst >> -jobs: - build: - docker: - - image: 'ubuntu:bionic' - steps: - - install_dependencies: - pre_hook: apt update -qqy - - checkout - - configure - - run: make -j7 support - - run: make -j7 DEBUG=1 - - run: 'cp build/debug_clang/rethinkdb{,-unittest} build/' - - store_artifacts: - path: build/rethinkdb - destination: rethinkdb - - store_artifacts: - path: build/rethinkdb-unittest - destination: rethinkdb-unittest - - save_cache: - key: 'rethinkdb-{{ .Revision }}' - paths: - - ./build/ - - ./config.mk - check_style: - docker: - - image: 'ubuntu:bionic' - steps: - - prepare - - run: bash scripts/check_style.sh - unit_test: - docker: - - image: 'ubuntu:bionic' - steps: - - prepare - - run: ls -lah build/ - - test: - pattern: unit - artifact_dst: unit_tests - integration_test: - docker: - - image: 'ubuntu:bionic' - steps: - - prepare - - test: - pattern: all '!unit' '!cpplint' - artifact_dst: integration_tests -workflows: - version: 2 - nightly: - triggers: - - schedule: - cron: 0 0 * * * - filters: - branches: - only: - - next - - v2.4.x - - v2.5.x - jobs: - - check_style - - build: - requires: - - check_style - - unit_test: - requires: - - build - - integration_test: - requires: - - build - pull_request_pipeline: - jobs: - - check_style - - build: - requires: - - check_style - - unit_test: - requires: - - build - - integration_test: - requires: - - build - diff --git a/mk/main.mk b/mk/main.mk index c895b6df57b..952977df67e 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -58,9 +58,6 @@ include $(TOP)/drivers/build.mk # Build the web assets include $(TOP)/admin/build.mk -# Building the rethinkdb executable -include $(TOP)/src/build.mk - # Packaging for deb, osx, ... include $(TOP)/mk/packaging.mk diff --git a/mk/tools.mk b/mk/tools.mk index a52bb5fab9a..e165565df04 100644 --- a/mk/tools.mk +++ b/mk/tools.mk @@ -52,10 +52,6 @@ endif ##### Code information -analyze: $(SOURCES) - $P CLANG-ANALYZE - clang --analyze $(RT_CXXFLAGS) $(SOURCES) - coffeelint: $P COFFEELINT "" -coffeelint -f $(TOP)/scripts/coffeelint.json -r $(TOP)/admin/ diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100644 index f5bd37c129c..00000000000 --- a/src/.gitignore +++ /dev/null @@ -1 +0,0 @@ -core diff --git a/src/CPPLINT.cfg b/src/CPPLINT.cfg deleted file mode 100644 index a3876c6b96f..00000000000 --- a/src/CPPLINT.cfg +++ /dev/null @@ -1,23 +0,0 @@ -# cpplint settings. See scripts/check_style.sh and scripts/cpplint -# These settings can be overridden in any subfolder by placing a CPPLINT.cfg file in it - -set noparent - -filter=+readability/casting - -filter=-build/c++11 -filter=-build/include_what_you_use -filter=-whitespace/forcolon -filter=-whitespace/end_of_line -filter=-whitespace/parens -filter=-whitespace/line_length -filter=-whitespace/braces -filter=-readability/todo -filter=-legal/copyright -filter=-whitespace/comments -filter=-whitespace/labels -filter=-whitespace/blank_line -filter=-readability/function - -exclude_files=rapidjson -exclude_files=cjson diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index 2ba0d39230b..00000000000 --- a/src/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -OVERRIDE_GOALS += clean=build-clean - -TOP := .. -include $(TOP)/Makefile diff --git a/src/README.md b/src/README.md deleted file mode 100644 index 42fb76a3dba..00000000000 --- a/src/README.md +++ /dev/null @@ -1,38 +0,0 @@ -## Overview - -### Basic primitives - -Each RethinkDB process has a thread pool (see `arch/runtime/thread_pool.hpp`) with a fixed number of threads. Each of these threads is running an event loop that processes IO notifications from the OS and messages from other threads. The `arch/` directory contains primitives for timers, file and network IO, and so on in the context of the RethinkDB thread pool. The main event loop threads never run blocking IO operations; instead, there is a separate set "blocker pool" (see `arch/io/blocker_pool.hpp`) of threads used for blocking operations. - -RethinkDB makes heavy use of cooperatively-scheduled coroutines; see `arch/runtime/coroutines.hpp`. The `concurrency/` directory contains many concurrency primitives designed for use with RethinkDB's coroutines. Coroutines can switch between threads by scheduling themselves to be resumed on another thread and then suspending themselves on the current thread. - -Each RethinkDB server in a cluster maintains a TCP connection to every other server in the cluster. The messages sent across these TCP connections mostly fit into two basic paradigms: "mailboxes", and the "directory". A mailbox is an object that is created dynamically on a specific server. It has an address, which is serializable; if one server has the address of a mailbox on another server, it can send messages to that mailbox. This works well for point-to-point communication but not for service discovery. The directory consists of a set of values that each server broadcasts to every other server it's connected to. Typically a server will set up a mailbox to process a certain type of request and then send that mailbox's address over the directory so other servers can discover it. These mailbox addresses in the directory are often called "business cards". See `rpc/README.md` for more information about RethinkDB's networking primitives. - -### Server start-up process - -The `main()` function is defined in `main.cc`, but it quickly transfers control to `clustering/administration/main/command_line.cc`. This file sets up the thread pool and event loop, then starts an instance of `do_serve()` (defined in `clustering/administration/main/serve.cc`) in a coroutine inside the new thread pool. `do_serve()` is responsible for setting up and tearing down all of the important components of the RethinkDB server in the right order, and passing them pointers to each other as necessary. - -### Executing queries - -One of the objects constructed by `do_serve()` is a `rdb_query_server_t` instance (see `rdb_protocol/query_server.hpp`), which listens for incoming TCP connections from client drivers. When it receives a message, `ql::compile_term()` (see `rdb_protocol/term.hpp`) converts the message from the client into a tree of `ql::term_t`s. The `rdb_protocol/terms/` directory contains many subclasses of `ql::term_t` for the different ReQL terms. Each subclass has an `eval()` method that does the actual work of executing the term. - -The `ql::val_t` type (defined in `rdb_protocol/val.hpp`) is the basic type of a ReQL value, such as a string, table, database, etc. Simple ReQL values such as strings, arrays, objects, dates, etc. are called "datums"; they are represented by the `ql::datum_t` type (defined in `rdb_protocol/datum.hpp`). - -Most queries will need to perform operations on one or more tables. An operation on a table is represented as a `read_t` or `write_t` object (defined in `rdb_protocol/protocol.hpp`); the result of the operation is represented as a `read_response_t` or `write_response_t` object. The ReQL evaluation code passes the `read_t` and `write_t` objects to the `table_query_client_t` (see `clustering/query_routing/table_query_client.hpp`). The `table_query_client_t` finds the relevant `primary_query_server_t` by looking for its business card in the directory, and sends the query to the mailbox listed on the business card. (If the query affects multiple keys, there may be multiple relevant `primary_query_server_t`s; the `read_t` or `write_t` will be broken into several sub-`read_t`s or sub-`write_t`s, which will each be sent to a different `primary_query_server_t`.) The queries pass from the `primary_query_server_t` via a series of types defined in `clustering/immediate_consistency/` until they eventually reach the `store_t`. If `read_mode` is `"outdated"`, read queries will reach the `store_t` through the `direct_query_server_t` instead of the `primary_query_server_t`. - -`store_t` (defined in `rdb_protocol/store.hpp`) is responsible for executing the `read_t` and `write_t` operations on the RethinkDB storage engine and collecting the results. Every RethinkDB table is represented as a B-tree. The blocks of the B-tree are stored in RethinkDB's custom page cache, `page_cache_t` (defined in `buffer_cache/page_cache.hpp`). The `btree/` directory contains functions for executing insertion, deletion, lookup, traversal, etc. operations against the B-tree stored in the page cache. Modified pages are eventually flushed to disk via RethinkDB's log-structured serializer, `log_serializer_t` (defined in `serializer/log/log_serializer.hpp`). - -When the `store_t` finishes executing the `read_t` or `write_t` against the B-tree, it returns a `read_response_t` or `write_response_t`, which is passed back via the same way the `read_t` or `write_t` arrived until it gets back to the `ql::term_t` that initiated the request. When the `eval()` method on the root term finishes executing, the response is sent back over the TCP connection to the client driver that initiated the request. - -Changefeeds are handled differently from normal reads. For normal reads, the machine handling the query will send a `read_t` to the shards for every chunk of data it gets back. The exception to this rule is changefeeds, where the shards will instead push data to the server handling the query. The server handling the query subscribes to a changefeed by sending the mailbox address for its `changefeed::client_t` to the shards inside of a `read_t`. The shards will then subscribe that client to their `changefeed::server_t` (which is a member variable of `store_t`). The relevant classes can be found in `rdb_protocol/changefeed.hpp`. - -### Table configuration - -The above description has mentioned several types that are involved in the process of executing queries against a table: the `store_t`, the `primary_query_server_t`, the various types defined in `clustering/immediate_consistency/`, and so on. The table's configuration (as exposed to the user in the `rethinkdb.table_config` system table) describes which servers should be performing which role for which part of the table. When the user changes the table's configuration, objects on various servers must be created and destroyed to implement the user's new configuration, while making sure that the tables' contents are correctly copied from server to server. - -There is one `multi_table_manager_t` (defined in `clustering/table_manager/`) per server, created by `do_serve()`. It keeps a record for each table that is known to exist, whether that table has a replica on that server or not. If the server is supposed to be a replica for a given table, the `multi_table_manager_t` will construct a `table_manager_t` for that table. - -RethinkDB uses the Raft consensus algorithm to manage table metadata, but not to manage user queries. RethinkDB uses a custom implementation of Raft that's designed to work with the RethinkDB event loop and networking primitives; it's implemented by the type `raft_member_t`, defined in `clustering/generic/raft_core.hpp`. There is one Raft cluster per RethinkDB table. Each `table_manager_t` instantiates a `raft_member_t`. The state managed by the Raft instance is defined as `table_raft_state_t`, defined in `clustering/table_contract/contract_metadata.hpp`. Among other things, the `table_raft_state_t` contains the table's current configuration and a collection of `contract_t` objects. - -The `contract_t`s describe what each server is supposed to be doing with respect to each region of the table. The difference between the contracts and the config is that the user can change the config freely, but the contracts can only change in specific well-defined ways that ensure that the data in the tables is preserved and remains consistent. The `table_manager_t` constructs a `contract_coordinator_t` (defined in `clustering/table_contract/coordinator/`) on whichever server is the leader of the Raft cluster; it's responsible for updating the contracts to match the config. The `table_manager_t` also constructs a `contract_executor_t` (defined in `clustering/table_contract/executor/`) on every server; the `contract_executor_t` is responsible for carrying out the instructions in the contracts by constructing the `store_t`, the `primary_query_server_t`, the various types in `clustering/immediate_consistency/`, etc. The details of how this works are quite complicated; the comments in `clustering/table_contract/contract_metadata.hpp` are a good introduction. - diff --git a/src/activity_logger.cc b/src/activity_logger.cc deleted file mode 100644 index 13c571eb692..00000000000 --- a/src/activity_logger.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "activity_logger.hpp" - -#include - -#include "debug.hpp" -#include "utils.hpp" - -log_event_t::log_event_t(const std::string &_msg, bool log_bt) - : timestamp(current_microtime()), msg(_msg) { - if (log_bt) bt.init(new lazy_backtrace_formatter_t()); -} -std::string log_event_t::print(bool print_bt) { - std::string bt_str = print_bt && bt.has() ? bt->addrs() : ""; - return strprintf("%" PRIu64 "\n -- %s\n%s", timestamp, msg.c_str(), bt_str.c_str()); -} - -activity_logger_t::activity_logger_t(bool _log_bt) : log_bt_(_log_bt) { - add("constructed"); -} -void activity_logger_t::add(const std::string &msg) { - return add(msg, log_bt_); -} -void activity_logger_t::add(const std::string &msg, bool log_bt) { - events.push_back(make_scoped(msg, log_bt)); -} - -size_t activity_logger_t::size() { return events.size(); } - -std::string activity_logger_t::print(bool print_bt) { - return print_range(0, size(), print_bt); -} -std::string activity_logger_t::print_range(size_t start, size_t end, bool print_bt) { - std::string s; - for (size_t i = start; i < end; ++i) { - if (i >= events.size()) break; - s += events[i]->print(print_bt); - } - return s; -} - -// For interactive use in GDB. -bool alprng(activity_logger_t *l, size_t start, size_t end, bool print_bt) { - std::string s = l->print_range(start, end, print_bt); - debugf("%s", s.c_str()); - return true; -} diff --git a/src/activity_logger.hpp b/src/activity_logger.hpp deleted file mode 100644 index 00e6c26cd85..00000000000 --- a/src/activity_logger.hpp +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef ACTIVITY_LOGGER_HPP_ -#define ACTIVITY_LOGGER_HPP_ - -#include - -#include -#include - -#include "backtrace.hpp" -#include "containers/scoped.hpp" -#include "threading.hpp" -#include "time.hpp" - - -class log_event_t { -public: - explicit log_event_t(const std::string &_msg, bool log_bt = true); - std::string print(bool print_bt); - -private: - microtime_t timestamp; - std::string msg; - scoped_ptr_t bt; - - DISABLE_COPYING(log_event_t); -}; - -class activity_logger_t : private home_thread_mixin_t { -public: - explicit activity_logger_t(bool _log_bt = true); - void add(const std::string &msg); // Use value of [log_bt] from constructor - void add(const std::string &msg, bool log_bt); - size_t size(); - std::string print(bool print_bt = true); - std::string print_range(size_t start, size_t end, bool print_bt = true); - -private: - bool log_bt_; - std::vector > events; - - DISABLE_COPYING(activity_logger_t); -}; - -struct fake_activity_logger_t : private home_thread_mixin_t { - explicit fake_activity_logger_t(UNUSED bool b = true) { } - void add(const std::string &msg) { - assert_thread(); - events.push_back(msg); - } - -private: - std::vector events; - - DISABLE_COPYING(fake_activity_logger_t); -}; - -#endif /* ACTIVITY_LOGGER_HPP_ */ diff --git a/src/arch/CPPLINT.cfg b/src/arch/CPPLINT.cfg deleted file mode 100644 index 1af2f056dfe..00000000000 --- a/src/arch/CPPLINT.cfg +++ /dev/null @@ -1 +0,0 @@ -exclude_files=windows_stub diff --git a/src/arch/address.cc b/src/arch/address.cc deleted file mode 100644 index 1ef364340d2..00000000000 --- a/src/arch/address.cc +++ /dev/null @@ -1,638 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "arch/address.hpp" - -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include "windows.hpp" -#include // NOLINT -#else -#include -#endif - -#include - -#include "arch/io/network.hpp" -#include "arch/runtime/thread_pool.hpp" -#include "logger.hpp" - -host_lookup_exc_t::host_lookup_exc_t(const std::string &_host, - int _res, int _errno_res) - : host(_host), - res(_res), - errno_res(_errno_res) { - std::string info = -#ifndef _WIN32 - (res == EAI_SYSTEM) ? - strprintf("%s (errno %d)", errno_string(errno_res).c_str(), errno_res) : -#endif - strprintf("%s (gai_errno %d)", gai_strerror(res), res); - error_string = strprintf("getaddrinfo() failed for hostname '%s': %s", - host.c_str(), info.c_str()); -} - -/* Get our hostname as an std::string. */ -std::string str_gethostname() { -#ifdef _WIN32 - const int namelen = 256; // (cf. RFC 1035) -#else - const int namelen = _POSIX_HOST_NAME_MAX; -#endif - - std::vector bytes(namelen + 1); - bytes[namelen] = '0'; - - int res = gethostname(bytes.data(), namelen); - -#ifdef _WIN32 - guarantee_winerr(res == 0, "gethostname() failed"); -#else - guarantee_err(res == 0, "gethostname() failed"); -#endif - return std::string(bytes.data()); -} - -void do_getaddrinfo(const char *node, - const char *service, - const struct addrinfo *hints, - struct addrinfo **res, - int *retval, - int *errno_res) { - *errno_res = 0; - *retval = getaddrinfo(node, service, hints, res); - if (*retval != 0) { - *errno_res = get_errno(); - } -} - -/* Format an `in_addr` in dotted decimal notation. */ -template -std::string ip_to_string(const addr_t &addr, int address_family) { - char buffer[INET6_ADDRSTRLEN] = { 0 }; - // On Windows, inet_ntop doesn't take a const addr - const char *result = inet_ntop(address_family, (void*)&addr, // NOLINT - buffer, INET6_ADDRSTRLEN); - guarantee(result == buffer, "Could not format IP address"); - return std::string(buffer); -} - -size_t min_sockaddr_size_for_address_family(int address_family) { - switch (address_family) { - case AF_INET: - return sizeof(sockaddr_in); - case AF_INET6: - return sizeof(sockaddr_in6); - default: - return 0; - } -} - -void hostname_to_ips_internal(const std::string &host, - int address_family, - std::set *ips) { - struct addrinfo hint; - memset(&hint, 0, sizeof(hint)); - hint.ai_family = address_family; - hint.ai_socktype = SOCK_STREAM; - - int res; - int errno_res; - struct addrinfo *addrs; - std::function fn = - std::bind(do_getaddrinfo, host.c_str(), static_cast(nullptr), - &hint, &addrs, &res, &errno_res); - thread_pool_t::run_in_blocker_pool(fn); - - if (res != 0) { - throw host_lookup_exc_t(host, res, errno_res); - } - - guarantee(addrs); - for (struct addrinfo *ai = addrs; ai; ai = ai->ai_next) { - // Workaround for bug in eglibc 2.19 (used in Ubuntu 14.04) combined with nscd - // See bug report here: https://sourceware.org/bugzilla/show_bug.cgi?id=16743 - if (ai->ai_family != address_family || - ai->ai_addrlen < min_sockaddr_size_for_address_family(address_family)) { - logWRN("Invalid address family returned by `getaddrinfo` (%d), " - "are you using nscd with EGLIBC 2.19?", ai->ai_family); - } else { - ips->insert(ip_address_t(ai->ai_addr)); - } - } - - freeaddrinfo(addrs); -} - -std::set hostname_to_ips(const std::string &host) { - std::set ips; - - // Allow an error on one lookup, but not both - bool errored = false; - - try { - hostname_to_ips_internal(host, AF_INET, &ips); - } catch (const host_lookup_exc_t &ex) { - errored = true; - } - - try { - hostname_to_ips_internal(host, AF_INET6, &ips); - } catch (const host_lookup_exc_t &ex) { - if (errored) throw; - } - - return ips; -} - -std::set get_local_ips(const std::set &filter, - local_ip_filter_t filter_type) { - std::set all_ips; -#ifdef _WIN32 - // NOTE: We used to use hostname_to_ips for all OSes, we stopped using it because - // DNS resolution could be slow. Now we only use it for Windows because nobody's - // fixed the Windows implementation to get the network interfaces more sensibly. - try { - all_ips = hostname_to_ips(str_gethostname()); - } catch (const host_lookup_exc_t &ex) { - // Continue on, this probably means there's no DNS entry for this host - } - - // TODO WINDOWS: is this enough? - all_ips.emplace("127.0.0.1"); -#else - struct ifaddrs *addrs; - int res = getifaddrs(&addrs); - guarantee_err(res == 0, - "getifaddrs() failed, could not determine local network interfaces"); - - for (auto *current_addr = addrs; - current_addr != nullptr; - current_addr = current_addr->ifa_next) { - struct sockaddr *addr_data = current_addr->ifa_addr; - if (addr_data == nullptr) { - continue; - } else if (addr_data->sa_family == AF_INET || addr_data->sa_family == AF_INET6) { - all_ips.insert(ip_address_t(addr_data)); - } - } - freeifaddrs(addrs); -#endif - - std::set filtered_ips; - // Remove any addresses that don't fit the filter - for (auto const &ip : all_ips) { - if (filter_type == local_ip_filter_t::ALL || - filter.find(ip) != filter.end() || - (filter_type == local_ip_filter_t::MATCH_FILTER_OR_LOOPBACK && - ip.is_loopback())) { - filtered_ips.insert(ip); - } - } - - return filtered_ips; -} - -addr_type_t sanitize_address_family(int address_family) { - addr_type_t result; - - switch (address_family) { - case AF_INET: - result = RDB_IPV4_ADDR; - break; - case AF_INET6: - result = RDB_IPV6_ADDR; - break; - default: - crash("ip_address_t constructed with unexpected address family: %d", - address_family); - } - - return result; -} - -ip_address_t ip_address_t::any(int address_family) { - ip_address_t result; - result.addr_type = sanitize_address_family(address_family); - - if (result.is_ipv4()) { - result.ipv4_addr.s_addr = INADDR_ANY; - } else if (result.is_ipv6()) { - result.ipv6_addr = in6addr_any; - result.ipv6_scope_id = 0; - } else { - throw invalid_address_exc_t("unknown address family"); - } - - return result; -} - -ip_address_t::ip_address_t(const sockaddr *sa) { - addr_type = sanitize_address_family(sa->sa_family); - - if (is_ipv4()) { - ipv4_addr = reinterpret_cast(sa)->sin_addr; - } else if (is_ipv6()) { - ipv6_addr = reinterpret_cast(sa)->sin6_addr; - ipv6_scope_id = reinterpret_cast(sa)->sin6_scope_id; - } else { - throw invalid_address_exc_t("unknown address family"); - } -} - -ip_address_t::ip_address_t(const std::string &addr_str) { - // First attempt to parse as IPv4 - if (inet_pton(AF_INET, addr_str.c_str(), &ipv4_addr) == 1) { - addr_type = RDB_IPV4_ADDR; - } else { - // Try to parse as in IPv6 address, but it may contain scope ID, - // which complicates things - addr_type = RDB_IPV6_ADDR; - ipv6_scope_id = 0; - size_t percent_pos = addr_str.find_first_of('%'); - if (percent_pos != std::string::npos) { - // There is a scope in the string, first make sure the beginning - // is an IPv6 address - if (inet_pton(AF_INET6, - addr_str.substr(0, percent_pos).c_str(), - &ipv6_addr) != 1) { - throw invalid_address_exc_t(strprintf("Could not parse IP address from " - "string: '%s'", addr_str.c_str())); - } - - // Then, use getaddrinfo to figure out the value for the scope - std::set addresses; - hostname_to_ips_internal(addr_str, AF_INET6, &addresses); - - bool scope_id_found = false; - // We may get multiple ips (not sure what that case would be, - // try to match to the one we have) - for (auto it = addresses.begin(); - it != addresses.end() && !scope_id_found; - ++it) { - if (IN6_ARE_ADDR_EQUAL(&it->ipv6_addr, &ipv6_addr)) { - ipv6_scope_id = it->ipv6_scope_id; - scope_id_found = true; - } - } - - if (!scope_id_found) { - throw invalid_address_exc_t(strprintf("Could not determine the scope " - "id of address: '%s'", - addr_str.c_str())); - } - } else if (inet_pton(AF_INET6, addr_str.c_str(), &ipv6_addr) != 1) { - throw invalid_address_exc_t(strprintf("Could not parse IP address from " - "string: '%s'", addr_str.c_str())); - } - } -} - -int ip_address_t::get_address_family() const { - int result; - - switch (addr_type) { - case RDB_UNSPEC_ADDR: - result = AF_UNSPEC; - break; - case RDB_IPV4_ADDR: - result = AF_INET; - break; - case RDB_IPV6_ADDR: - result = AF_INET6; - break; - default : - crash("unknown ip_address_t address type: %d", addr_type); - } - - return result; -} - -bool ip_address_t::is_ipv6_link_local() const { - return addr_type == RDB_IPV6_ADDR && IN6_IS_ADDR_LINKLOCAL(&ipv6_addr); -} - -const struct in_addr &ip_address_t::get_ipv4_addr() const { - if (!is_ipv4()) { - throw invalid_address_exc_t("get_ipv4_addr() called on a non-IPv4 ip_address_t"); - } - return ipv4_addr; -} - -const struct in6_addr &ip_address_t::get_ipv6_addr() const { - if (!is_ipv6()) { - throw invalid_address_exc_t("get_ipv6_addr() called on a non-IPv6 ip_address_t"); - } - return ipv6_addr; -} - -uint32_t ip_address_t::get_ipv6_scope_id() const { - if (!is_ipv6()) { - throw invalid_address_exc_t("get_ipv6_scope_id() called on a " - "non-IPv6 ip_address_t"); - } - return ipv6_scope_id; -} - -std::string ip_address_t::to_string() const { - std::string result; - - if (is_ipv4()) { - result = ip_to_string(ipv4_addr, AF_INET); - } else if (is_ipv6()) { - result = ip_to_string(ipv6_addr, AF_INET6); - if (IN6_IS_ADDR_LINKLOCAL(&ipv6_addr)) { - // Add on the scope id (which is only valid for link-local addresses) - result += strprintf("%%%d", ipv6_scope_id); - } - } else { - crash("to_string called on an uninitialized ip_address_t, addr_type: %d", - addr_type); - } - - return result; -} - -bool ip_address_t::operator == (const ip_address_t &x) const { - if (addr_type == x.addr_type) { - if (is_ipv4()) { - return ipv4_addr.s_addr == x.ipv4_addr.s_addr; - } else if (is_ipv6()) { - return IN6_ARE_ADDR_EQUAL(&ipv6_addr, &x.ipv6_addr) && - ipv6_scope_id == x.ipv6_scope_id; - } - return true; - } - return false; -} - -bool ip_address_t::operator < (const ip_address_t &x) const { - if (addr_type == x.addr_type) { - if (is_ipv4()) { - return ipv4_addr.s_addr < x.ipv4_addr.s_addr; - } else if (is_ipv6()) { - int cmp_res = memcmp(&ipv6_addr, &x.ipv6_addr, sizeof(ipv6_addr)); - if (cmp_res == 0) { - return ipv6_scope_id < x.ipv6_scope_id; - } - return cmp_res < 0; - } else { - return false; - } - } - return addr_type < x.addr_type; -} - -bool ip_address_t::is_loopback() const { -#ifdef _WIN32 -#define IN_LOOPBACKNET 127 // from on Linux -#endif - if (is_ipv4()) { - return (ntohl(ipv4_addr.s_addr) >> 24) == IN_LOOPBACKNET; - } else if (is_ipv6()) { - return IN6_IS_ADDR_LOOPBACK(&ipv6_addr); - } - return false; -} - -bool ip_address_t::is_any() const { - if (is_ipv4()) { - return ipv4_addr.s_addr == INADDR_ANY; - } else if (is_ipv6()) { - return IN6_IS_ADDR_UNSPECIFIED(&ipv6_addr); - } - return false; -} - -port_t::port_t(int _value) - : value_(_value) { } - -port_t::port_t(sockaddr const *sa) { - switch (sa->sa_family) { - case AF_INET: - value_ = ntohs(reinterpret_cast(sa)->sin_port); - break; - case AF_INET6: - value_ = ntohs(reinterpret_cast(sa)->sin6_port); - break; - default: - crash("port_t constructed with unexpected address family: %d", sa->sa_family); - } -} - -uint16_t port_t::value() const { - return value_; -} - -std::string port_t::to_string() const { - return std::to_string(value_); -} - -ip_and_port_t::ip_and_port_t() - : port_(0) -{ } - -ip_and_port_t::ip_and_port_t(const ip_address_t &_ip, port_t _port) - : ip_(_ip), port_(_port) -{ } - -ip_and_port_t::ip_and_port_t(sockaddr const *sa) - : ip_(sa), port_(sa) -{ } - -bool ip_and_port_t::operator < (const ip_and_port_t &other) const { - if (ip_ == other.ip_) { - return port_.value() < other.port_.value(); - } - return ip_ < other.ip_; -} - -bool ip_and_port_t::operator == (const ip_and_port_t &other) const { - return ip_ == other.ip_ && port_.value() == other.port_.value(); -} - -const ip_address_t & ip_and_port_t::ip() const { - return ip_; -} - -port_t ip_and_port_t::port() const { - return port_; -} - -std::string ip_and_port_t::to_string() const { - if (ip_.is_ipv6()) { - return strprintf("[%s]:%u", ip_.to_string().c_str(), port_.value()); - } else { - return strprintf("%s:%u", ip_.to_string().c_str(), port_.value()); - } -} - -bool is_similar_ip_address(const ip_and_port_t &left, - const ip_and_port_t &right) { - if (left.port().value() != right.port().value() || - left.ip().get_address_family() != right.ip().get_address_family()) { - return false; - } - - if (left.ip().is_ipv4()) { - return left.ip().get_ipv4_addr().s_addr == right.ip().get_ipv4_addr().s_addr; - } else { - return IN6_ARE_ADDR_EQUAL(&left.ip().get_ipv6_addr(), - &right.ip().get_ipv6_addr()); - } -} - -host_and_port_t::host_and_port_t() : - port_(0) -{ } - -host_and_port_t::host_and_port_t(const std::string& _host, port_t _port) : - host_(_host), port_(_port) -{ } - -bool host_and_port_t::operator < (const host_and_port_t &other) const { - if (host_ == other.host_) { - return port_.value() < other.port_.value(); - } - return host_ < other.host_; -} - -bool host_and_port_t::operator == (const host_and_port_t &other) const { - return host_ == other.host_ && port_.value() == other.port_.value(); -} - -std::set host_and_port_t::resolve() const { - std::set result; - std::set host_ips = hostname_to_ips(host_); - for (auto jt = host_ips.begin(); jt != host_ips.end(); ++jt) { - result.insert(ip_and_port_t(*jt, port_)); - } - return result; -} - -const std::string & host_and_port_t::host() const { - return host_; -} - -port_t host_and_port_t::port() const { - return port_; -} - -peer_address_t::peer_address_t(const std::set &_hosts) : - hosts_(_hosts) -{ - for (auto it = hosts_.begin(); it != hosts_.end(); ++it) { - std::set host_ips = it->resolve(); - resolved_ips.insert(host_ips.begin(), host_ips.end()); - } -} - -peer_address_t::peer_address_t() { } - -const std::set& peer_address_t::hosts() const { - return hosts_; -} - -host_and_port_t peer_address_t::primary_host() const { - guarantee(!hosts_.empty()); - return *hosts_.begin(); -} - -const std::set& peer_address_t::ips() const { - return resolved_ips; -} - -void peer_address_t::erase_ip(const ip_and_port_t &ip) { - auto it = resolved_ips.find(ip); - if (it != resolved_ips.end()) { - resolved_ips.erase(it); - } -} - -// Two addresses are considered equal if all of their hosts match -bool peer_address_t::operator == (const peer_address_t &a) const { - std::set::const_iterator it, jt; - for (it = hosts_.begin(), jt = a.hosts_.begin(); - it != hosts_.end() && jt != a.hosts_.end(); ++it, ++jt) { - if (it->port().value() != jt->port().value() || - it->host() != jt->host()) { - return false; - } - } - return true; -} - -bool peer_address_t::operator != (const peer_address_t &a) const { - return !(*this == a); -} - -// We specifically use the version 1.14 serialization method as the "universal one". -// If that no longer works... you might want to change the caller in cluster.cc. Or -// maybe you'd want a more explicit implementation here. -void serialize_universal(write_message_t *wm, const std::set &x) { - serialize(wm, x); -} -archive_result_t deserialize_universal(read_stream_t *s, - std::set *thing) { - return deserialize(s, thing); -} - -bool is_similar_peer_address(const peer_address_t &left, - const peer_address_t &right) { - bool left_loopback_only = true; - bool right_loopback_only = true; - - // We ignore any loopback addresses because they don't give us any useful information - // Return true if any non-loopback addresses match - for (auto left_it = left.ips().begin(); - left_it != left.ips().end(); ++left_it) { - if (left_it->ip().is_loopback()) { - continue; - } else { - left_loopback_only = false; - } - - for (auto right_it = right.ips().begin(); - right_it != right.ips().end(); ++right_it) { - if (right_it->ip().is_loopback()) { - continue; - } else { - right_loopback_only = false; - } - - if (is_similar_ip_address(*right_it, *left_it)) { - return true; - } - } - } - - // No non-loopback addresses matched, return true if either side was *only* loopback - // addresses because we can't easily prove if they are the same or different - // addresses - return left_loopback_only || right_loopback_only; -} - -void debug_print(printf_buffer_t *buf, const ip_address_t &addr) { - buf->appendf("%s", addr.to_string().c_str()); -} - -void debug_print(printf_buffer_t *buf, const ip_and_port_t &addr) { - buf->appendf("%s:%d", addr.ip().to_string().c_str(), addr.port().value()); -} - -void debug_print(printf_buffer_t *buf, const host_and_port_t &addr) { - buf->appendf("%s:%d", addr.host().c_str(), addr.port().value()); -} - -void debug_print(printf_buffer_t *buf, const peer_address_t &address) { - buf->appendf("peer_address ["); - const std::set &hosts = address.hosts(); - for (auto it = hosts.begin(); it != hosts.end(); ++it) { - if (it != hosts.begin()) buf->appendf(", "); - debug_print(buf, *it); - } - buf->appendf("]"); -} diff --git a/src/arch/address.hpp b/src/arch/address.hpp deleted file mode 100644 index 77041ed2192..00000000000 --- a/src/arch/address.hpp +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef ARCH_ADDRESS_HPP_ -#define ARCH_ADDRESS_HPP_ - -#ifdef _WIN32 -#include "windows.hpp" -#include // NOLINT -#else -#include /* for `inet_ntop()` */ -#include -#include -#include -#include -#endif - -#include -#include -#include -#include - -#include "containers/archive/archive.hpp" -#include "containers/archive/stl_types.hpp" -#include "errors.hpp" -#include "rpc/serialize_macros.hpp" - -#define MAX_PORT 65535 - -class printf_buffer_t; - -class host_lookup_exc_t : public std::exception { -public: - host_lookup_exc_t(const std::string &_host, int _res, int _errno_res); - ~host_lookup_exc_t() throw () { } - const char *what() const throw () { - return error_string.c_str(); - } - const std::string host; - const int res; - const int errno_res; - std::string error_string; -}; - -class invalid_address_exc_t : public std::exception { -public: - explicit invalid_address_exc_t(const std::string &_info) : - info(_info) { } - ~invalid_address_exc_t() throw () { } - const char *what() const throw () { - return info.c_str(); - } - const std::string info; -}; - -// Used for internal representation of address family, especially for serialization, -// since the AF_* constants can't be guaranteed to be the same across architectures -enum addr_type_t { - RDB_UNSPEC_ADDR = 0, - RDB_IPV4_ADDR = 1, - RDB_IPV6_ADDR = 2 -}; - -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE(addr_type_t, int8_t, RDB_UNSPEC_ADDR, RDB_IPV6_ADDR); - -/* ip_address_t represents an IPv4 address. */ -class ip_address_t { -public: - ip_address_t() : addr_type(RDB_UNSPEC_ADDR) { } // for deserialization - - explicit ip_address_t(const struct sockaddr *sa); - explicit ip_address_t(const std::string &addr_str); - - static ip_address_t any(int address_family); - - bool operator<(const ip_address_t &x) const; - bool operator==(const ip_address_t &x) const; - bool operator!=(const ip_address_t &x) const { return !(*this == x); } - - std::string to_string() const; - bool is_loopback() const; - bool is_any() const; - - int get_address_family() const; - bool is_ipv4() const { return addr_type == RDB_IPV4_ADDR; } - bool is_ipv6() const { return addr_type == RDB_IPV6_ADDR; } - bool is_ipv6_link_local() const; - - const struct in_addr &get_ipv4_addr() const; - const struct in6_addr &get_ipv6_addr() const; - uint32_t get_ipv6_scope_id() const; - - RDB_MAKE_ME_SERIALIZABLE_4(ip_address_t, - addr_type, ipv4_addr, ipv6_addr, ipv6_scope_id); - -private: - addr_type_t addr_type; - in_addr ipv4_addr; - in6_addr ipv6_addr; - uint32_t ipv6_scope_id; -}; - -std::string str_gethostname(); - -std::set hostname_to_ips(const std::string &host); - -enum class local_ip_filter_t { - MATCH_FILTER, - MATCH_FILTER_OR_LOOPBACK, - ALL -}; - -std::set get_local_ips(const std::set &filter, - local_ip_filter_t filter_type); - -class port_t { -public: - static constexpr int max_port = MAX_PORT; - - explicit port_t(int _port); - explicit port_t(struct sockaddr const *); - - uint16_t value() const; - - std::string to_string() const; - - RDB_MAKE_ME_SERIALIZABLE_1(port_t, value_); - -private: - uint16_t value_; -}; - -class ip_and_port_t { -public: - ip_and_port_t(); - ip_and_port_t(const ip_address_t &_ip, port_t _port); - explicit ip_and_port_t(sockaddr const *); - - bool operator < (const ip_and_port_t &other) const; - bool operator == (const ip_and_port_t &other) const; - - const ip_address_t &ip() const; - port_t port() const; - - std::string to_string() const; - - RDB_MAKE_ME_SERIALIZABLE_2(ip_and_port_t, ip_, port_); - -private: - ip_address_t ip_; - port_t port_; -}; - -// This implementation is used over operator == because we want to ignore different scope -// ids in the case of IPv6 -bool is_similar_ip_address(const ip_and_port_t &left, - const ip_and_port_t &right); - -class host_and_port_t { -public: - host_and_port_t(); - host_and_port_t(const std::string &_host, port_t _port); - - bool operator < (const host_and_port_t &other) const; - bool operator == (const host_and_port_t &other) const; - - std::set resolve() const; - - const std::string &host() const; - port_t port() const; - - RDB_MAKE_ME_SERIALIZABLE_2(host_and_port_t, host_, port_); - -private: - std::string host_; - port_t port_; -}; - -class peer_address_t { -public: - // Constructor will look up all the hosts and convert them to ip addresses - explicit peer_address_t(const std::set &_hosts); - peer_address_t(); - - const std::set &hosts() const; - const std::set &ips() const; - - host_and_port_t primary_host() const; - void erase_ip(const ip_and_port_t &ip); - - // Two addresses are considered equal if all of their hosts match - bool operator == (const peer_address_t &a) const; - bool operator != (const peer_address_t &a) const; - -private: - std::set hosts_; - std::set resolved_ips; -}; - -void serialize_universal(write_message_t *wm, const std::set &x); -archive_result_t deserialize_universal(read_stream_t *s, - std::set *thing); - -bool is_similar_peer_address(const peer_address_t &left, - const peer_address_t &right); - -/* TODO: This should either be a `std::set` with a custom comparator, or -a generic container type that contains a set of anything equality-comparable. */ -class peer_address_set_t { -public: - size_t erase(const peer_address_t &addr) { - size_t erased = 0; - for (auto it = vec.begin(); it != vec.end(); ++it) { - if (*it == addr) { - vec.erase(it); - ++erased; - break; - } - } - return erased; - } - size_t size() const { return vec.size(); } - typedef std::vector::const_iterator iterator; - iterator begin() const { return vec.begin(); } - iterator end() const { return vec.end(); } - iterator find(const peer_address_t &addr) const { - return std::find(vec.begin(), vec.end(), addr); - } - iterator insert(const peer_address_t &addr) { - guarantee(find(addr) == vec.end()); - return vec.insert(vec.end(), addr); - } - bool empty() const { return vec.empty(); } -private: - std::vector vec; -}; - -void debug_print(printf_buffer_t *buf, const ip_address_t &addr); -void debug_print(printf_buffer_t *buf, const ip_and_port_t &addr); -void debug_print(printf_buffer_t *buf, const host_and_port_t &addr); -void debug_print(printf_buffer_t *buf, const peer_address_t &address); - -#endif /* ARCH_ADDRESS_HPP_ */ diff --git a/src/arch/arch.cc b/src/arch/arch.cc deleted file mode 100644 index 130e3f1483e..00000000000 --- a/src/arch/arch.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "arch/arch.hpp" - -#include "arch/runtime/coroutines.hpp" - -struct io_coroutine_adapter_t : public iocallback_t { - coro_t *cont; - io_coroutine_adapter_t() : cont(coro_t::self()) { } - void on_io_complete() { - cont->notify_later_ordered(); - } -}; - -void co_read(file_t *file, int64_t offset, size_t length, void *buf, file_account_t *account) { - io_coroutine_adapter_t adapter; - file->read_async(offset, length, buf, account, &adapter); - coro_t::wait(); -} - -void co_write(file_t *file, int64_t offset, size_t length, void *buf, - file_account_t *account, file_t::wrap_in_datasyncs_t wrap_in_datasyncs) { - io_coroutine_adapter_t adapter; - file->write_async(offset, length, buf, account, &adapter, wrap_in_datasyncs); - coro_t::wait(); -} diff --git a/src/arch/arch.hpp b/src/arch/arch.hpp deleted file mode 100644 index aa0a82b9942..00000000000 --- a/src/arch/arch.hpp +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_ARCH_HPP_ -#define ARCH_ARCH_HPP_ - -/* The original plan was to have all Linux-specific code in the `linux` -directory and to have corresponding directories for other OSes. This was a bad -idea because so much code is shared between OSes, so it was (incompletely) -changed back. We'll sort this mess out when it's time to support another OS. */ - -#include "arch/types.hpp" - -void co_read(file_t *file, int64_t offset, size_t length, void *buf, file_account_t *account); -void co_write(file_t *file, int64_t offset, size_t length, void *buf, file_account_t *account, - file_t::wrap_in_datasyncs_t wrap_in_datasyncs); - -#endif /* ARCH_ARCH_HPP_ */ diff --git a/src/arch/barrier.cc b/src/arch/barrier.cc deleted file mode 100644 index 1e35552b5d6..00000000000 --- a/src/arch/barrier.cc +++ /dev/null @@ -1,35 +0,0 @@ -#include "arch/barrier.hpp" - -thread_barrier_t::thread_barrier_t(int num_workers) - : num_workers_(num_workers), num_waiters_(0) { - guarantee(num_workers > 0); - - int res = pthread_mutex_init(&mutex_, nullptr); - guarantee_xerr(res == 0, res, "could not initialize barrier mutex"); - res = pthread_cond_init(&cond_, nullptr); - guarantee_xerr(res == 0, res, "could not initialize barrier condition variable"); -} - -thread_barrier_t::~thread_barrier_t() { - guarantee(num_waiters_ == 0); - int res = pthread_cond_destroy(&cond_); - guarantee_xerr(res == 0, res, "could not destroy barrier condition variable"); - res = pthread_mutex_destroy(&mutex_); - guarantee_xerr(res == 0, res, "could not destroy barrier mutex"); -} - -void thread_barrier_t::wait() { - int res = pthread_mutex_lock(&mutex_); - guarantee_xerr(res == 0, res, "could not acquire barrier mutex"); - ++num_waiters_; - if (num_waiters_ == num_workers_) { - num_waiters_ = 0; - res = pthread_cond_broadcast(&cond_); - guarantee_xerr(res == 0, res, "barrier could not broadcast to condition variable"); - } else { - res = pthread_cond_wait(&cond_, &mutex_); - guarantee_xerr(res == 0, res, "barrier could not wait for condition variable"); - } - res = pthread_mutex_unlock(&mutex_); - guarantee_xerr(res == 0, res, "barrier could not unlock mutex"); -} diff --git a/src/arch/barrier.hpp b/src/arch/barrier.hpp deleted file mode 100644 index b36816faa66..00000000000 --- a/src/arch/barrier.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef ARCH_BARRIER_HPP_ -#define ARCH_BARRIER_HPP_ - -#include - -#include "errors.hpp" - -// We call this a thread_barrier_t so as to differentiate from other barrier types. -class thread_barrier_t { -public: - explicit thread_barrier_t(int num_workers); - ~thread_barrier_t(); - - void wait(); - -private: - const int num_workers_; - int num_waiters_; - pthread_mutex_t mutex_; - pthread_cond_t cond_; - - DISABLE_COPYING(thread_barrier_t); -}; - -#endif // ARCH_BARRIER_HPP_ diff --git a/src/arch/compiler.hpp b/src/arch/compiler.hpp deleted file mode 100644 index 9200770fd94..00000000000 --- a/src/arch/compiler.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef ARCH_COMPILER_HPP_ -#define ARCH_COMPILER_HPP_ - -// Compiler-specific macros - -#ifdef _MSC_VER // msc - -#define ATTR_ALIGNED(size) __declspec(align(size)) -#define ATTR_PACKED(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop)) -#define ATTR_FORMAT(...) -#define THREAD_LOCAL thread_local -#define CURRENT_FUNCTION_PRETTY __FUNCSIG__ -#define NORETURN __declspec(noreturn) -#define UNUSED __pragma(warning(suppress: 4100 4101)) -#define MUST_USE _Check_return_ -#define NOINLINE __declspec(noinline) - -#else // gcc, clang - -#define ATTR_ALIGNED(size) __attribute__((aligned(size))) -#define ATTR_PACKED(...) __VA_ARGS__ __attribute__((__packed__)) - -#ifdef __MINGW32__ -#define ATTR_FORMAT(...) // mingw64 complains about PRIu64 -#else -#define ATTR_FORMAT(...) __attribute__((format(__VA_ARGS__))) -#endif - -#define THREAD_LOCAL __thread -#define CURRENT_FUNCTION_PRETTY __PRETTY_FUNCTION__ -#define NORETURN __attribute__((__noreturn__)) -#define UNUSED __attribute__((unused)) -#define MUST_USE __attribute__((warn_unused_result)) -#define NOINLINE __attribute__((noinline)) - -#endif - -#endif diff --git a/src/arch/fd_send_recv.cc b/src/arch/fd_send_recv.cc deleted file mode 100644 index 658f72fd9a3..00000000000 --- a/src/arch/fd_send_recv.cc +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef _WIN32 - -#include -#include - -#include - -#include "containers/scoped.hpp" -#include "arch/fd_send_recv.hpp" - -// The code for {send,recv}_fds was determined by careful reading of the man -// pages for sendmsg(2), recvmsg(2), unix(7), and particularly cmsg(3), which -// contains a helpful example. -// was also helpful. - -int send_fds(int socket_fd, size_t num_fds, int *fds) { - // Set-up for a call to sendmsg(). - struct msghdr msg; - memset(&msg, 0, sizeof(msg)); - - // We send a single null byte along with the data. Sending a zero-length - // message is dubious; receiving one is even more dubious. - struct iovec iov; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - char c = '\0'; - iov.iov_base = &c; - iov.iov_len = 1; - - // The control message is the important part for sending fds. - scoped_array_t control(CMSG_SPACE(sizeof(int) * num_fds)); - msg.msg_control = control.data(); - msg.msg_controllen = control.size(); - - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; // see unix(7) for explanation - cmsg->cmsg_type = SCM_RIGHTS; // says that we're sending an array of fds - cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds); - memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * num_fds); - - msg.msg_controllen = cmsg->cmsg_len; - - // Write it out. - ssize_t res = sendmsg(socket_fd, &msg, 0); - if (res == 1) - return 0; // success! - - guarantee(res == -1); - return -1; -} - -fd_recv_result_t recv_fds(int socket_fd, size_t num_fds, int *fds) { - // Set-up for a call to recvmsg() - struct msghdr msg; - memset(&msg, 0, sizeof(msg)); - - // We expect to receive a single null byte. - struct iovec iov; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - char c = 'x'; // a value that is not what we expect - iov.iov_base = &c; - iov.iov_len = 1; - - // The control message. - scoped_array_t control(CMSG_SPACE(sizeof(int) * num_fds)); - msg.msg_control = control.data(); - msg.msg_controllen = control.size(); - - // Read it in. - ssize_t res = recvmsg(socket_fd, &msg, 0); - if (res == 0) return FD_RECV_EOF; - if (res == -1) return FD_RECV_ERROR; - - guarantee(res == 1); - - // Check the message. - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); - rassert(c == '\0' && cmsg && - msg.msg_controllen == CMSG_SPACE(sizeof(int) * num_fds) && - cmsg->cmsg_len == CMSG_LEN(sizeof(int) * num_fds)); - - // Success! - memcpy(fds, CMSG_DATA(cmsg), sizeof(int) * num_fds); - return FD_RECV_OK; -} - -#endif diff --git a/src/arch/fd_send_recv.hpp b/src/arch/fd_send_recv.hpp deleted file mode 100644 index 85012db27c8..00000000000 --- a/src/arch/fd_send_recv.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_FD_SEND_RECV_HPP_ -#define ARCH_FD_SEND_RECV_HPP_ - -#ifndef _WIN32 - -#include "errors.hpp" -#include "arch/runtime/runtime_utils.hpp" // fd_t - -// WARNING. Each call to these functions involves copying `sizeof(int) * -// num_fds` bytes. Don't do it in a tight loop if `num_fds` is large. If you -// need to do this, refactor this code. - -// Sends open file descriptors. Must be matched by a call to recv_fds() on the -// other end, or weird shit could happen. -// -// Return -1 on error, 0 on success. Sets errno on failure. -MUST_USE int send_fds(int socket_fd, size_t num_fds, fd_t *fds); - -enum fd_recv_result_t { - FD_RECV_OK, FD_RECV_ERROR, FD_RECV_EOF -}; - -// Receives open file descriptors. Must only be called to match a call to -// write_fds() on the other end; otherwise undefined behavior could result! -// -// Sets errno if it returns FD_RECV_ERROR. -MUST_USE fd_recv_result_t recv_fds(int socket_fd, size_t num_fds, fd_t *fds); - -#endif - -#endif // ARCH_FD_SEND_RECV_HPP_ diff --git a/src/arch/filesystem.hpp b/src/arch/filesystem.hpp deleted file mode 100644 index e18372fcb7b..00000000000 --- a/src/arch/filesystem.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef ARCH_FILESYSTEM_HPP_ -#define ARCH_FILESYSTEM_HPP_ - -#ifndef _WIN32 -#include -#endif - -#include -#include - -#include "errors.hpp" - -std::string strdirname(const std::string path) { -#ifdef _WIN32 - char drive[_MAX_DRIVE]; - char dir[_MAX_DIR]; - errno_t err = _splitpath_s(path.c_str(), drive, _MAX_DRIVE, dir, _MAX_DIR, NULL, 0, NULL, 0); - guarantee_xerr(!err, err, "_splitpath_s failed"); - char ret[_MAX_PATH]; - err = _makepath_s(ret, _MAX_PATH, drive, dir, NULL, NULL); - guarantee_xerr(!err, err, "_makepath_s failed"); - return ret; -#else - std::vector path_copy(path.begin(), path.end()); - path_copy.push_back('\0'); - return ::dirname(path_copy.data()); -#endif -} - -#endif diff --git a/src/arch/io/blocker_pool.cc b/src/arch/io/blocker_pool.cc deleted file mode 100644 index 9a99a5dda18..00000000000 --- a/src/arch/io/blocker_pool.cc +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "arch/io/blocker_pool.hpp" - -#include - -#include "arch/compiler.hpp" -#include "config/args.hpp" -#include "utils.hpp" - -THREAD_LOCAL int thread_is_blocker_pool_thread = 0; -// Access functions to thread_is_blocker_pool_thread. Marked as NOINLINE -// to avoid certain compiler optimizations that can break TLS. See -// thread_local.hpp for a more detailed explanation. -NOINLINE void set_in_blocker_pool_thread(int value) { - thread_is_blocker_pool_thread = value; -} -NOINLINE bool i_am_in_blocker_pool_thread() { - return thread_is_blocker_pool_thread == 1; -} -// Make sure thread_is_blocker_pool_thread is not accessed directly -#ifdef __GNUC__ -#pragma GCC poison thread_is_blocker_pool_thread -#endif - -// IO thread function -void* blocker_pool_t::event_loop(void *arg) { - - set_in_blocker_pool_thread(1); - - blocker_pool_t *parent = reinterpret_cast(arg); - -#ifndef _WIN32 - // Disable signals on this thread. This ensures that signals like SIGINT are - // handled by one of the main threads. - { - sigset_t sigmask; - int res = sigfillset(&sigmask); - guarantee_err(res == 0, "Could not get a full sigmask"); - - res = pthread_sigmask(SIG_SETMASK, &sigmask, nullptr); - guarantee_xerr(res == 0, res, "Could not block signal"); - } -#endif - - while (true) { - // Wait for an IO command or shutdown command. The while-loop guards against spurious - // wakeups. - system_mutex_t::lock_t or_lock(&parent->or_mutex); - while (parent->outstanding_requests.empty() && !parent->shutting_down) { - parent->or_cond.wait(&parent->or_mutex); - } - - if (parent->shutting_down) { - // or_lock's destructor gets called, so everything is OK. - return nullptr; - - } else { - - // Grab a request - job_t *request = parent->outstanding_requests.front(); - parent->outstanding_requests.erase(parent->outstanding_requests.begin(), - parent->outstanding_requests.begin() + 1); - - // Unlock the mutex manually instead of waiting for its destructor so that other jobs - // will get to proceed - or_lock.unlock(); - - // Perform the request. It may block. This is the raison d'etre for blocker_pool_t. - request->run(); - - // Notify that the request is done - { - system_mutex_t::lock_t ce_lock(&parent->ce_mutex); - parent->completed_events.push_back(request); - } - // It seems critical for performance that we release ce_lock *before* we write to the signal! - // This is probably because the kernel thinks "hey, somebody is blocking on the signal. - // Now that it has been written to, that thread will want to handle it, so let's wake it up." - // If we don't have ce_lock released at that point, the following happens: - // The signalled thread immediately has to acquire ce_lock, so the kernel has to yield control - // again. Only after an additional scheduler roundtrip, we get control again and can release the ce_lock. - parent->ce_signal.wakey_wakey(); - } - } -} - -blocker_pool_t::blocker_pool_t(int nthreads, linux_event_queue_t *_queue) - : threads(nthreads), shutting_down(false), queue(_queue) -{ - // Start the worker threads - for (size_t i = 0; i < threads.size(); ++i) { - pthread_attr_t attr; - int res = pthread_attr_init(&attr); - guarantee_xerr(res == 0, res, "pthread_attr_init failed."); - - // The coroutine stack size should be enough for blocker pool stacks. Right - // now that's 128 KB. - #if defined (__powerpc64__) - static_assert(COROUTINE_STACK_SIZE == 262144, - "Expecting COROUTINE_STACK_SIZE to be 262144. If you changed " - "it, please double-check whether the value is appropriate for " - "blocker pool threads."); - #else - static_assert(COROUTINE_STACK_SIZE == 131072, - "Expecting COROUTINE_STACK_SIZE to be 131072. If you changed " - "it, please double-check whether the value is appropriate for " - "blocker pool threads."); - // Disregard failure -- we'll just use the default stack size if this somehow - // fails. - #endif - UNUSED int ignored_res = pthread_attr_setstacksize(&attr, COROUTINE_STACK_SIZE); - - res = pthread_create(&threads[i], &attr, - &blocker_pool_t::event_loop, reinterpret_cast(this)); - guarantee_xerr(res == 0, res, "Could not create blocker-pool thread."); - - res = pthread_attr_destroy(&attr); - guarantee_xerr(res == 0, res, "pthread_attr_destroy failed."); - } - - // Register with event queue so we get the completion events - queue->watch_event(&ce_signal, this); -} - -blocker_pool_t::~blocker_pool_t() { - - // Deregister with the event queue - queue->forget_event(&ce_signal, this); - - /* Send out the order to shut down */ - { - system_mutex_t::lock_t or_lock(&or_mutex); - - shutting_down = true; - - /* It is an error to shut down the blocker pool while requests are still out */ - rassert(outstanding_requests.size() == 0); - - or_cond.broadcast(); - } - - /* Wait for stuff to actually shut down */ - for (size_t i = 0; i < threads.size(); ++i) { - int res = pthread_join(threads[i], nullptr); - guarantee_xerr(res == 0, res, "Could not join blocker-pool thread."); - } -} - -void blocker_pool_t::do_job(job_t *job) { - - system_mutex_t::lock_t or_lock(&or_mutex); - outstanding_requests.push_back(job); - or_cond.signal(); -} - -void blocker_pool_t::on_event(DEBUG_VAR int event) { - - rassert(event == poll_event_in); - ce_signal.consume_wakey_wakeys(); // So pipe doesn't get backed up - - std::vector local_completed_events; - - { - system_mutex_t::lock_t ce_lock(&ce_mutex); - local_completed_events.swap(completed_events); - } - - for (size_t i = 0; i < local_completed_events.size(); ++i) { - local_completed_events[i]->done(); - } -} - diff --git a/src/arch/io/blocker_pool.hpp b/src/arch/io/blocker_pool.hpp deleted file mode 100644 index 75554791f66..00000000000 --- a/src/arch/io/blocker_pool.hpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_IO_BLOCKER_POOL_HPP_ -#define ARCH_IO_BLOCKER_POOL_HPP_ - -#include - -#include - -#include "arch/runtime/event_queue.hpp" -#include "arch/io/concurrency.hpp" -#include "arch/runtime/system_event.hpp" - -class blocker_pool_t : public linux_event_callback_t { -public: - blocker_pool_t(int nthreads, linux_event_queue_t *queue); - ~blocker_pool_t(); - - struct job_t { - /* run() will not be run within the main thread pool. It may call blocking system calls and - the like without disrupting performance of the main server thread pool. */ - virtual void run() = 0; - - /* done() will be called within the main thread pool once run() is done. */ - virtual void done() = 0; - - protected: - virtual ~job_t() {} - }; - void do_job(job_t *job); - -private: - static void *event_loop(void*); - - std::vector threads; - bool shutting_down; - std::vector outstanding_requests; - system_mutex_t or_mutex; - system_cond_t or_cond; - - std::vector completed_events; - linux_event_queue_t *queue; - system_event_t ce_signal; - system_mutex_t ce_mutex; - void on_event(int); -}; - -bool i_am_in_blocker_pool_thread(); - -#endif /* ARCH_IO_BLOCKER_POOL_HPP_ */ diff --git a/src/arch/io/concurrency.hpp b/src/arch/io/concurrency.hpp deleted file mode 100644 index aeb0f67ca09..00000000000 --- a/src/arch/io/concurrency.hpp +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_IO_CONCURRENCY_HPP_ -#define ARCH_IO_CONCURRENCY_HPP_ - -#include - -#include "errors.hpp" - -// Class that wraps a pthread mutex -class system_mutex_t { - pthread_mutex_t m; - friend class lock_t; - friend class system_cond_t; -public: - system_mutex_t() { - int res = pthread_mutex_init(&m, nullptr); - guarantee_xerr(res == 0, res, "Could not initialize pthread mutex."); - } - ~system_mutex_t() { - int res = pthread_mutex_destroy(&m); - guarantee_xerr(res == 0, res, "Could not destroy pthread mutex."); - } - class lock_t { - system_mutex_t *parent; - public: - explicit lock_t(system_mutex_t *p) : parent(p) { - int res = pthread_mutex_lock(&parent->m); - guarantee_xerr(res == 0, res, "Could not acquire pthread mutex."); - } - void unlock() { - guarantee(parent); - int res = pthread_mutex_unlock(&parent->m); - guarantee_xerr(res == 0, res, "Could not release pthread mutex."); - parent = nullptr; - } - ~lock_t() { - if (parent) unlock(); - } - }; -}; - -// Class that wraps a pthread cond -class system_cond_t { - pthread_cond_t c; -public: - system_cond_t() { - int res = pthread_cond_init(&c, nullptr); - guarantee_xerr(res == 0, res, "Could not initialize pthread cond."); - } - ~system_cond_t() { - int res = pthread_cond_destroy(&c); - guarantee_xerr(res == 0, res, "Could not destroy pthread cond."); - } - void wait(system_mutex_t *mutex) { - int res = pthread_cond_wait(&c, &mutex->m); - guarantee_xerr(res == 0, res, "Could not wait on pthread cond."); - } - void signal() { - int res = pthread_cond_signal(&c); - guarantee_xerr(res == 0, res, "Could not signal pthread cond."); - } - void broadcast() { - int res = pthread_cond_broadcast(&c); - guarantee_xerr(res == 0, res, "Could not broadcast pthread cond."); - } -}; - -// Class that wraps a pthread rwlock -class system_rwlock_t { - pthread_rwlock_t l; -public: - system_rwlock_t() { - int res = pthread_rwlock_init(&l, nullptr); - guarantee_xerr(res == 0, res, "Could not initialize pthread rwlock."); - } - ~system_rwlock_t() { - int res = pthread_rwlock_destroy(&l); - guarantee_xerr(res == 0, res, "Could not destroy pthread rwlock."); - } - - // For low-level use only. If you want to use this for anything else, please - // define a matching acq_t RAII type. - void lock_read() { - int res = pthread_rwlock_rdlock(&l); - guarantee_xerr(res == 0, res, "Could not acquire pthread rwlock for read."); - } - void lock_write() { - int res = pthread_rwlock_wrlock(&l); - guarantee_xerr(res == 0, res, "Could not acquire pthread rwlock for write."); - } - void unlock() { - int res = pthread_rwlock_unlock(&l); - guarantee_xerr(res == 0, res, "Could not release pthread rwlock."); - } -private: - DISABLE_COPYING(system_rwlock_t); -}; - -#endif /* ARCH_IO_CONCURRENCY_HPP_ */ - diff --git a/src/arch/io/disk.cc b/src/arch/io/disk.cc deleted file mode 100644 index 5953f996042..00000000000 --- a/src/arch/io/disk.cc +++ /dev/null @@ -1,678 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "arch/io/disk.hpp" - -#include - -#ifndef _WIN32 -#include -#include -#include -#include -#include -#endif - -#include -#include - -#include -#include - -#include "arch/types.hpp" -#include "arch/runtime/thread_pool.hpp" -#include "arch/runtime/runtime.hpp" -#include "arch/io/disk/filestat.hpp" -#include "arch/io/disk/pool.hpp" -#include "arch/io/disk/conflict_resolving.hpp" -#include "arch/io/disk/stats.hpp" -#include "arch/io/disk/accounting.hpp" -#include "backtrace.hpp" -#include "config/args.hpp" -#include "do_on_thread.hpp" -#include "logger.hpp" -#include "utils.hpp" - -void verify_aligned_file_access(DEBUG_VAR int64_t file_size, DEBUG_VAR int64_t offset, - DEBUG_VAR size_t length, - DEBUG_VAR const scoped_array_t &bufs); - -/* Disk manager object takes care of queueing operations, collecting statistics, preventing - conflicts, and actually sending them to the disk. */ -class linux_disk_manager_t : public home_thread_mixin_t { -public: - struct action_t : public stats_diskmgr_t::action_t { - action_t(threadnum_t _cb_thread, linux_iocallback_t *_cb) - : cb_thread(_cb_thread), cb(_cb) { } - threadnum_t cb_thread; - linux_iocallback_t *cb; - }; - - - linux_disk_manager_t(linux_event_queue_t *queue, - int batch_factor, - int max_concurrent_io_requests, - perfmon_collection_t *stats) : - stack_stats(stats, "stack"), - conflict_resolver(stats), - accounter(batch_factor), - backend_stats(stats, "backend", accounter.producer), - backend(queue, backend_stats.producer, max_concurrent_io_requests), - outstanding_txn(0) - { - /* Hook up the `submit_fun`s of the parts of the IO stack that are above the - queue. (The parts below the queue use the `passive_producer_t` interface instead - of a callback function.) */ - stack_stats.submit_fun = std::bind(&conflict_resolving_diskmgr_t::submit, - &conflict_resolver, ph::_1); - conflict_resolver.submit_fun = std::bind(&accounting_diskmgr_t::submit, - &accounter, ph::_1); - - /* Hook up everything's `done_fun`. */ - backend.done_fun = std::bind(&stats_diskmgr_2_t::done, &backend_stats, ph::_1); - backend_stats.done_fun = std::bind(&accounting_diskmgr_t::done, &accounter, ph::_1); - accounter.done_fun = std::bind(&conflict_resolving_diskmgr_t::done, - &conflict_resolver, ph::_1); - conflict_resolver.done_fun = std::bind(&stats_diskmgr_t::done, &stack_stats, ph::_1); - stack_stats.done_fun = std::bind(&linux_disk_manager_t::done, this, ph::_1); - } - - ~linux_disk_manager_t() { - rassert(outstanding_txn == 0, - "Closing a file with outstanding txns (%" PRIiPTR " of them)\n", - outstanding_txn); - } - - void *create_account(int pri, int outstanding_requests_limit) { - return new accounting_diskmgr_t::account_t(&accounter, pri, outstanding_requests_limit); - } - - void destroy_account(void *account) { - coro_t::spawn_on_thread([account] { - // The account destructor can block if there are outstanding requests. - delete static_cast(account); - }, home_thread()); - } - - void submit_action_to_stack_stats(action_t *a) { - assert_thread(); - outstanding_txn++; - stack_stats.submit(a); - } - - void submit_write(fd_t fd, const void *buf, size_t count, int64_t offset, - void *account, linux_iocallback_t *cb, - bool wrap_in_datasyncs) { - threadnum_t calling_thread = get_thread_id(); - - action_t *a = new action_t(calling_thread, cb); - a->make_write(fd, buf, count, offset, wrap_in_datasyncs); - a->account = static_cast(account); - - do_on_thread(home_thread(), - std::bind(&linux_disk_manager_t::submit_action_to_stack_stats, this, - a)); - } - - void submit_resize(fd_t fd, int64_t old_size, int64_t new_size, - void *account, linux_iocallback_t *cb, - bool wrap_in_datasyncs) { - threadnum_t calling_thread = get_thread_id(); - - action_t *a = new action_t(calling_thread, cb); - a->make_resize(fd, old_size, new_size, wrap_in_datasyncs); - a->account = static_cast(account); - - do_on_thread(home_thread(), - std::bind(&linux_disk_manager_t::submit_action_to_stack_stats, this, - a)); - } - -#ifndef USE_WRITEV -#error "USE_WRITEV not defined. Did you include pool.hpp?" -#elif USE_WRITEV - void submit_writev(fd_t fd, scoped_array_t &&bufs, size_t count, - int64_t offset, void *account, linux_iocallback_t *cb) { - threadnum_t calling_thread = get_thread_id(); - - action_t *a = new action_t(calling_thread, cb); - a->make_writev(fd, std::move(bufs), count, offset); - a->account = static_cast(account); - - do_on_thread(home_thread(), - std::bind(&linux_disk_manager_t::submit_action_to_stack_stats, this, - a)); - } -#endif // USE_WRITEV - - void submit_read(fd_t fd, void *buf, size_t count, int64_t offset, void *account, linux_iocallback_t *cb) { - threadnum_t calling_thread = get_thread_id(); - - action_t *a = new action_t(calling_thread, cb); - a->make_read(fd, buf, count, offset); - a->account = static_cast(account); - - do_on_thread(home_thread(), - std::bind(&linux_disk_manager_t::submit_action_to_stack_stats, this, - a)); - } - - void done(stats_diskmgr_t::action_t *a) { - assert_thread(); - outstanding_txn--; - action_t *a2 = static_cast(a); - bool succeeded = a2->get_succeeded(); - if (succeeded) { - do_on_thread(a2->cb_thread, - std::bind(&linux_iocallback_t::on_io_complete, a2->cb)); - } else { - do_on_thread(a2->cb_thread, - std::bind(&linux_iocallback_t::on_io_failure, - a2->cb, a2->get_io_errno(), - static_cast(a2->get_offset()), - static_cast(a2->get_count()))); - } - delete a2; - } - -private: - /* These fields describe the entire IO stack. At the top level, we allocate a new - action_t object for each operation and record its callback. Then it passes through - the conflict resolver, which enforces ordering constraints between IO operations by - holding back operations that must be run after other, currently-running, operations. - Then it goes to the account manager, which queues up running IO operations according - to which account they are part of. Finally the "backend" pops the IO operations - from the queue. - - At two points in the process--once as soon as it is submitted, and again right - as the backend pops it off the queue--its statistics are recorded. The "stack stats" - will tell you how many IO operations are queued. The "backend stats" will tell you - how long the OS takes to perform the operations. Note that it's not perfect, because - it counts operations that have been queued by the backend but not sent to the OS yet - as having been sent to the OS. */ - - stats_diskmgr_t stack_stats; - conflict_resolving_diskmgr_t conflict_resolver; - accounting_diskmgr_t accounter; - stats_diskmgr_2_t backend_stats; - pool_diskmgr_t backend; - - - intptr_t outstanding_txn; - - DISABLE_COPYING(linux_disk_manager_t); -}; - -io_backender_t::io_backender_t(file_direct_io_mode_t _direct_io_mode, - int max_concurrent_io_requests) - : direct_io_mode(_direct_io_mode), - diskmgr(new linux_disk_manager_t(&linux_thread_pool_t::get_thread()->queue, - DEFAULT_IO_BATCH_FACTOR, - max_concurrent_io_requests, - &stats)) { } - -io_backender_t::~io_backender_t() { } - -file_direct_io_mode_t io_backender_t::get_direct_io_mode() const { return direct_io_mode; } - - -/* Disk file object */ - -linux_file_t::linux_file_t(scoped_fd_t &&_fd, int64_t _file_size, linux_disk_manager_t *_diskmgr) - : fd(std::move(_fd)), file_size(_file_size), diskmgr(_diskmgr) { - // TODO: Why do we care whether we're in a thread pool? (Maybe it's that you can't create a - // file_account_t outside of the thread pool? But they're associated with the diskmgr, - // aren't they?) - if (linux_thread_pool_t::get_thread()) { - default_account.init(new file_account_t(this, 1, UNLIMITED_OUTSTANDING_REQUESTS)); - } -} - -int64_t linux_file_t::get_file_size() { - return file_size; -} - -void linux_file_t::set_file_size(int64_t new_size) { - assert_thread(); - rassert(diskmgr, "No diskmgr has been constructed (are we running without an event queue?)"); - - struct rs_callback_t : public linux_iocallback_t, cond_t { - void on_io_complete() { - delete this; - } - - void on_io_failure(int errsv, int64_t offset, int64_t) { - crash("ftruncate failed. (%s) (target size = %" PRIi64 ")", - errno_string(errsv).c_str(), offset); - } - - auto_drainer_t::lock_t lock; - }; - rs_callback_t *rs_callback = new rs_callback_t(); - rs_callback->lock = file_size_ops_drainer.lock(); - diskmgr->submit_resize(fd.get(), file_size, new_size, - default_account->get_account(), - rs_callback, true); - - file_size = new_size; -} - -// For growing in large chunks at a time. -int64_t chunk_factor(int64_t size, int64_t extent_size) { - // x is at most 12.5% of size. Overall we align to chunks no larger than 64 extents. - // This ratio was increased from 6.25% for performance reasons. Resizing a file - // (with ftruncate) doesn't actually use disk space, so it's OK if we pick a value - // that's too big. - - // We round off at an extent_size because it would be silly to allocate a partial - // extent. - int64_t x = (size / (extent_size * 8)) * extent_size; - return clamp(x, extent_size, extent_size * 64); -} - -void linux_file_t::set_file_size_at_least(int64_t size, int64_t extent_size) { - assert_thread(); - if (file_size < size) { - /* Grow in large chunks at a time. */ - set_file_size(ceil_aligned(size, chunk_factor(size, extent_size))); - } -} - -void linux_file_t::read_async(int64_t offset, size_t length, void *buf, file_account_t *account, linux_iocallback_t *callback) { - rassert(diskmgr, "No diskmgr has been constructed (are we running without an event queue?)"); - verify_aligned_file_access(file_size, offset, length, buf); - diskmgr->submit_read(fd.get(), buf, length, offset, - account == DEFAULT_DISK_ACCOUNT ? default_account->get_account() : account->get_account(), - callback); -} - -void linux_file_t::write_async(int64_t offset, size_t length, const void *buf, - file_account_t *account, linux_iocallback_t *callback, - wrap_in_datasyncs_t wrap_in_datasyncs) { - rassert(diskmgr, "No diskmgr has been constructed (are we running without an event queue?)"); - verify_aligned_file_access(file_size, offset, length, buf); - diskmgr->submit_write(fd.get(), buf, length, offset, - account == DEFAULT_DISK_ACCOUNT ? default_account->get_account() : account->get_account(), - callback, - wrap_in_datasyncs == WRAP_IN_DATASYNCS); -} - -void linux_file_t::writev_async(int64_t offset, size_t length, - scoped_array_t &&bufs, - file_account_t *account, linux_iocallback_t *callback) { - rassert(diskmgr != nullptr, - "No diskmgr has been constructed (are we running without an event queue?)"); - verify_aligned_file_access(file_size, offset, length, bufs); - -#ifndef USE_WRITEV -#error "USE_WRITEV not defined. Did you include pool.hpp?" -#elif USE_WRITEV - diskmgr->submit_writev(fd.get(), std::move(bufs), length, offset, - account == DEFAULT_DISK_ACCOUNT - ? default_account->get_account() - : account->get_account(), - callback); -#else // USE_WRITEV - // OS X doesn't have pwritev. Using lseek followed by a writev would - // require adding a mutex for OS X. We simply break up the writes into - // separate write calls. - - struct intermediate_cb_t : public linux_iocallback_t { - void on_io_complete() { - guarantee(refcount > 0); - --refcount; - if (refcount == 0) { - linux_iocallback_t *local_cb = cb; - delete this; - local_cb->on_io_complete(); - } - } - - size_t refcount; - linux_iocallback_t *cb; - }; - - intermediate_cb_t *intermediate_cb = new intermediate_cb_t; - // Hold a refcount while we launch writes. - intermediate_cb->refcount = 1; - intermediate_cb->cb = callback; - - int64_t partial_offset = offset; - for (size_t i = 0; i < bufs.size(); ++i) { - ++intermediate_cb->refcount; - diskmgr->submit_write(fd.get(), bufs[i].iov_base, bufs[i].iov_len, - partial_offset, account == DEFAULT_DISK_ACCOUNT - ? default_account->get_account() - : account->get_account(), - intermediate_cb, - false); - partial_offset += bufs[i].iov_len; - } - guarantee(partial_offset - offset == static_cast(length)); - - // Release its refcount. - intermediate_cb->on_io_complete(); -#endif // USE_WRITEV - -} - -bool linux_file_t::coop_lock_and_check() { -#ifdef _WIN32 - // TODO WINDOWS - return true; -#else - if (flock(fd.get(), LOCK_EX | LOCK_NB) != 0) { - rassert(get_errno() == EWOULDBLOCK); - return false; - } - return true; -#endif -} - -void *linux_file_t::create_account(int priority, int outstanding_requests_limit) { - assert_thread(); - return diskmgr->create_account(priority, outstanding_requests_limit); -} - -void linux_file_t::destroy_account(void *account) { - assert_thread(); - diskmgr->destroy_account(account); -} - - - -linux_file_t::~linux_file_t() { - // scoped_fd_t's destructor takes care of close()ing the file -} - -void verify_aligned_file_access(DEBUG_VAR int64_t file_size, DEBUG_VAR int64_t offset, - DEBUG_VAR size_t length, - DEBUG_VAR const scoped_array_t &bufs) { -#ifndef NDEBUG - rassert(static_cast(offset + length) <= file_size); - rassert(divides(DEVICE_BLOCK_SIZE, offset)); - rassert(divides(DEVICE_BLOCK_SIZE, length)); - - size_t sum = 0; - for (size_t i = 0; i < bufs.size(); ++i) { - rassert(divides(DEVICE_BLOCK_SIZE, bufs[i].iov_len)); - rassert(divides(DEVICE_BLOCK_SIZE, reinterpret_cast(bufs[i].iov_base))); - sum += bufs[i].iov_len; - } - rassert(sum == length); -#endif // NDEBUG -} - -void verify_aligned_file_access(DEBUG_VAR int64_t file_size, DEBUG_VAR int64_t offset, - DEBUG_VAR size_t length, DEBUG_VAR const void *buf) { - rassert(buf); - rassert(static_cast(offset + length) <= file_size); - rassert(divides(DEVICE_BLOCK_SIZE, reinterpret_cast(buf))); - rassert(divides(DEVICE_BLOCK_SIZE, offset)); - rassert(divides(DEVICE_BLOCK_SIZE, length)); -} - -file_open_result_t open_file(const char *path, const int mode, io_backender_t *backender, - scoped_ptr_t *out) { - scoped_fd_t fd; - -#ifdef _WIN32 - DWORD create_mode; - if (mode & linux_file_t::mode_truncate) { - create_mode = CREATE_ALWAYS; - } else if (mode & linux_file_t::mode_create) { - create_mode = OPEN_ALWAYS; - } else { - create_mode = OPEN_EXISTING; - } - - DWORD access_mode = 0; - if (mode & linux_file_t::mode_write) { - access_mode |= GENERIC_WRITE; - } - if (mode & linux_file_t::mode_read) { - access_mode |= GENERIC_READ; - } - if (access_mode == 0) { - crash("Bad file access mode."); - } - - // TODO WINDOWS: is all this sharing necessary? - DWORD share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; - - DWORD attributes = FILE_ATTRIBUTE_NORMAL; - - // Supporting fully unbuffered file i/o on Windows would require - // aligning all reads and writes to the sector size of the volume - // (See docs for FILE_FLAG_NO_BUFFERING). - // - // Instead we only use FILE_FLAG_WRITE_THROUGH - DWORD flags = - backender->get_direct_io_mode() == file_direct_io_mode_t::direct_desired - ? FILE_FLAG_WRITE_THROUGH - : 0; - - fd.reset(CreateFile(path, access_mode, share_mode, nullptr, create_mode, flags | attributes, nullptr)); - if (fd.get() == INVALID_FD) { - logERR("CreateFile failed: %s: %s", path, winerr_string(GetLastError()).c_str()); - return file_open_result_t(file_open_result_t::ERROR, EIO); - } - - file_open_result_t open_res = file_open_result_t(flags & FILE_FLAG_WRITE_THROUGH - ? file_open_result_t::DIRECT - : file_open_result_t::BUFFERED, - 0); - -#else - // Construct file flags - - // Let's have a sanity check for our attempt to check whether O_DIRECT and O_NOATIME are - // available as preprocessor defines. -#ifndef O_CREAT -#error "O_CREAT and other open flags are apparently not defined in the preprocessor." -#endif - - int flags = 0; - - if (mode & linux_file_t::mode_create) { - flags |= O_CREAT; - } - - // We support file truncation for opening temporary files used when starting up creation of some - // serializer file that needs some initialization before being put in its permanent place. - if (mode & linux_file_t::mode_truncate) { - flags |= O_TRUNC; - } - - // For now, we have a whitelist of kernels that don't support O_LARGEFILE. Linux is - // the only known kernel that has (or may need) the O_LARGEFILE flag. -#ifdef __linux__ - flags |= O_LARGEFILE; -#endif - - if ((mode & linux_file_t::mode_write) && (mode & linux_file_t::mode_read)) { - flags |= O_RDWR; - } else if (mode & linux_file_t::mode_write) { - flags |= O_WRONLY; - } else if (mode & linux_file_t::mode_read) { - flags |= O_RDONLY; - } else { - crash("Bad file access mode."); - } - - // Makes writes not update the access time of the file where available. That's more efficient. -#ifdef O_NOATIME - flags |= O_NOATIME; -#endif - - // Open the file. - - { - int res_open; - do { - res_open = open(path, flags, 0644); - } while (res_open == -1 && get_errno() == EINTR); - - fd.reset(res_open); - } - - if (fd.get() == INVALID_FD) { - return file_open_result_t(file_open_result_t::ERROR, get_errno()); - } - - // When building, we must either support O_DIRECT or F_NOCACHE. The former works on Linux, - // the latter works on OS X. - file_open_result_t open_res; - - switch (backender->get_direct_io_mode()) { - case file_direct_io_mode_t::direct_desired: { -#ifdef __linux__ - // fcntl(2) is documented to take an argument of type long, not of type int, with the - // F_SETFL command, on Linux. But POSIX says it's supposed to take an int? Passing long - // should be generally fine, with either the x86 or amd64 calling convention, on another - // system (that supports O_DIRECT) but we use "#ifdef __linux__" (and not "#ifdef O_DIRECT") - // specifically to avoid such concerns. - const int fcntl_res = fcntl(fd.get(), F_SETFL, - static_cast(flags | O_DIRECT)); // NOLINT(runtime/int) -#elif defined(__APPLE__) - const int fcntl_res = fcntl(fd.get(), F_NOCACHE, 1); -#elif defined(_WIN32) - // TODO WINDOWS - const int fcntl_res = -1; -#else -#error "Figure out how to do direct I/O and fsync correctly (despite your operating system's lies) on your platform." -#endif // __linux__, defined(__APPLE__) - open_res = file_open_result_t(fcntl_res == -1 ? - file_open_result_t::BUFFERED_FALLBACK : - file_open_result_t::DIRECT, - 0); - } break; - case file_direct_io_mode_t::buffered_desired: { - // We can typically improve read performance by disabling read-ahead. - // Our access patterns are usually pretty random, and on startup we already - // do read-ahead internally in our cache. - int disable_readahead_res = -1; -#ifdef __linux__ - // From the man-page: - // Under Linux, POSIX_FADV_NORMAL sets the readahead window to the - // default size for the backing device; POSIX_FADV_SEQUENTIAL doubles - // this size, and POSIX_FADV_RANDOM disables file readahead entirely. - // These changes affect the entire file, not just the specified region - // (but other open file handles to the same file are unaffected) - disable_readahead_res = posix_fadvise(fd.get(), 0, 0, POSIX_FADV_RANDOM); -#elif defined(__APPLE__) - const int fcntl_res = fcntl(fd.get(), F_RDAHEAD, 0); - disable_readahead_res = fcntl_res == -1 - ? get_errno() - : 0; -#elif defined(_WIN32) - // TODO WINDOWS -#endif - if (disable_readahead_res != 0) { - // Non-critical error. Just print a warning and keep going. - logWRN("Failed to disable read-ahead on '%s' (errno %d). You might see " - "decreased read performance.", path, disable_readahead_res); - } - - open_res = file_open_result_t(file_open_result_t::BUFFERED, 0); - } break; - default: - unreachable(); - } -#endif - - const int64_t file_size = get_file_size(fd.get()); - - // Call fsync() on the parent directory to guarantee that the newly - // created file's directory entry is persisted to disk. - warn_fsync_parent_directory(path); - - out->init(new linux_file_t(std::move(fd), file_size, backender->get_diskmgr_ptr())); - - return open_res; -} - -void crash_due_to_inaccessible_database_file(const char *path, file_open_result_t open_res) { - guarantee(open_res.outcome == file_open_result_t::ERROR); - fail_due_to_user_error( - "Inaccessible database file: \"%s\": %s" - "\nSome possible reasons:" - "\n- the database file couldn't be created or opened for reading and writing" - "\n- the user which was used to start the database is not an owner of the file" - , path, errno_string(open_res.errsv).c_str()); -} - -// Upon error, returns the errno value. -int perform_datasync(fd_t fd) { - // On OS X, we use F_FULLFSYNC because fsync lies. fdatasync is not available. On - // Linux we just use fdatasync. - -#ifdef __MACH__ - - int fcntl_res; - do { - fcntl_res = fcntl(fd, F_FULLFSYNC); - } while (fcntl_res == -1 && get_errno() == EINTR); - - return fcntl_res == -1 ? get_errno() : 0; - -#elif defined(_WIN32) - - BOOL res = FlushFileBuffers(fd); - if (!res) { - logWRN("FlushFileBuffers failed: %s", winerr_string(GetLastError()).c_str()); - return EIO; - } - return 0; - -#elif defined(__linux__) - - int res = fdatasync(fd); - return res == -1 ? get_errno() : 0; - -#else -#error "perform_datasync not implemented" -#endif // __MACH__ -} - -MUST_USE int fsync_parent_directory(const char *path) { - // Locate the parent directory -#ifdef _WIN32 - // TODO WINDOWS - (void) path; - return 0; -#else - char absolute_path[PATH_MAX]; - char *abs_res = realpath(path, absolute_path); - guarantee_err(abs_res != nullptr, "Failed to determine absolute path for '%s'", path); - char *parent_path = dirname(absolute_path); // Note: modifies absolute_path - - // Get a file descriptor on the parent directory - int res; - do { - res = open(parent_path, O_RDONLY); - } while (res == -1 && get_errno() == EINTR); - if (res == -1) { - return get_errno(); - } - scoped_fd_t fd(res); - - do { - res = fsync(fd.get()); - } while (res == -1 && get_errno() == EINTR); - if (res == -1) { - return get_errno(); - } - - return 0; -#endif -} - -void warn_fsync_parent_directory(const char *path) { - int sync_res = fsync_parent_directory(path); - if (sync_res != 0) { - logWRN("Failed to sync parent directory of \"%s\" (errno: %d - %s). " - "You may encounter data loss in case of a system failure. " - "(Is the file located on a filesystem that doesn't support directory sync? " - "e.g. VirtualBox shared folders)", - path, sync_res, errno_string(sync_res).c_str()); - } -} diff --git a/src/arch/io/disk.hpp b/src/arch/io/disk.hpp deleted file mode 100644 index f1a7c03fb29..00000000000 --- a/src/arch/io/disk.hpp +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef ARCH_IO_DISK_HPP_ -#define ARCH_IO_DISK_HPP_ - -#include "arch/io/io_utils.hpp" -#include "arch/types.hpp" -#include "concurrency/auto_drainer.hpp" -#include "containers/scoped.hpp" -#include "utils.hpp" - -#include "perfmon/core.hpp" - -// The maximum concurrent IO requests per event queue.. the default value. -const int DEFAULT_MAX_CONCURRENT_IO_REQUESTS = 64; - -// The maximum user-specifiable value how many concurrent I/O requests may be done per event -// queue. (A million is a ridiculously high value, but also safely nowhere near INT_MAX.) -const int MAXIMUM_MAX_CONCURRENT_IO_REQUESTS = MILLION; - -struct iovec; - -class linux_iocallback_t; - -class linux_disk_manager_t; - -class io_backender_t : public home_thread_mixin_debug_only_t { -public: - // This takes what is effectively a global flag whether to use O_DIRECT here. Nothing technical - // stops us from specifying this on a file-by-file basis, but right now there's no desire for - // that. See https://github.com/rethinkdb/rethinkdb/issues/97#issuecomment-19778177 . - io_backender_t(file_direct_io_mode_t direct_io_mode, - int max_concurrent_io_requests = DEFAULT_MAX_CONCURRENT_IO_REQUESTS); - ~io_backender_t(); - linux_disk_manager_t *get_diskmgr_ptr() { return diskmgr.get(); } - file_direct_io_mode_t get_direct_io_mode() const; - -protected: - const file_direct_io_mode_t direct_io_mode; - perfmon_collection_t stats; - scoped_ptr_t diskmgr; - -private: - DISABLE_COPYING(io_backender_t); -}; - -// A file_open_result_t is either FILE_OPEN_DIRECT, FILE_OPEN_BUFFERED, or an errno value. -struct file_open_result_t { - enum outcome_t { - DIRECT, // file opened in direct I/O mode - BUFFERED, // file deliberately opened in buffered mode - BUFFERED_FALLBACK, // attempted direct I/O mode, failed - ERROR // error in opening file - }; - outcome_t outcome; - int errsv; - - file_open_result_t(outcome_t _outcome, int _errsv) : outcome(_outcome), errsv(_errsv) { - guarantee(outcome == ERROR || errsv == 0); - } - file_open_result_t() : outcome(ERROR), errsv(0) { } -}; - -class linux_file_t : public file_t, public home_thread_mixin_debug_only_t { -public: - enum mode_t { - mode_read = 1 << 0, - mode_write = 1 << 1, - mode_create = 1 << 2, - mode_truncate = 1 << 3 - }; - - int64_t get_file_size(); - void set_file_size(int64_t size); - void set_file_size_at_least(int64_t size, int64_t extent_size); - - void read_async(int64_t offset, size_t length, void *buf, file_account_t *account, linux_iocallback_t *cb); - void write_async(int64_t offset, size_t length, const void *buf, file_account_t *account, linux_iocallback_t *cb, - wrap_in_datasyncs_t wrap_in_datasyncs); - - // Does not guarantee the atomicity that writev guarantees. - void writev_async(int64_t offset, size_t length, scoped_array_t &&bufs, - file_account_t *account, linux_iocallback_t *cb); - - bool coop_lock_and_check(); - - void *create_account(int priority, int outstanding_requests_limit); - void destroy_account(void *account); - - ~linux_file_t(); - -private: - linux_file_t(scoped_fd_t &&fd, int64_t file_size, linux_disk_manager_t *diskmgr); - friend file_open_result_t open_file(const char *path, int mode, - io_backender_t *backender, - scoped_ptr_t *out); - - scoped_fd_t fd; - int64_t file_size; - - linux_disk_manager_t *diskmgr; - - scoped_ptr_t default_account; - - // Used to make sure we do not destruct the linux_file_t until all file size - // operations have completed. - auto_drainer_t file_size_ops_drainer; - - DISABLE_COPYING(linux_file_t); -}; - -file_open_result_t open_file(const char *path, int mode, - io_backender_t *backender, - scoped_ptr_t *out); - -NORETURN void crash_due_to_inaccessible_database_file(const char *path, file_open_result_t open_res); - -// Runs some assertios to make sure that we're aligned to DEVICE_BLOCK_SIZE, not overrunning the -// file size, and that buf is not null. -void verify_aligned_file_access(int64_t file_size, int64_t offset, size_t length, const void *buf); - -// Makes blocking syscalls. Upon error, returns the errno value. -int perform_datasync(fd_t fd); - -// Calls fsync() on the parent directory of the given path. -// Returns the errno value in case of an error and 0 otherwise. -MUST_USE int fsync_parent_directory(const char *path); -// Like fsync_parent_directory but warns the user on an error -void warn_fsync_parent_directory(const char *path); - -#endif // ARCH_IO_DISK_HPP_ - diff --git a/src/arch/io/disk/accounting.cc b/src/arch/io/disk/accounting.cc deleted file mode 100644 index 8ee72dea257..00000000000 --- a/src/arch/io/disk/accounting.cc +++ /dev/null @@ -1,117 +0,0 @@ -#include "arch/io/disk/accounting.hpp" - -#include "containers/printf_buffer.hpp" - -/* Each account on the `accounting_diskmgr_t` has its own - `unlimited_fifo_queue_t` associated with it. Operations for that account - queue up on that queue while they wait for the `accounting_queue_t` on the - `accounting_diskmgr_t` to draw from that account. */ -struct accounting_diskmgr_eager_account_t : public semaphore_available_callback_t { - typedef accounting_diskmgr_action_t action_t; - - accounting_diskmgr_eager_account_t(accounting_diskmgr_t *par, - int pri, - int outstanding_requests_limit) : - outstanding_requests_limiter(outstanding_requests_limit == UNLIMITED_OUTSTANDING_REQUESTS ? SEMAPHORE_NO_LIMIT : outstanding_requests_limit), - account(&par->queue, &queue, pri), - accounter_lock(par->get_auto_drainer()) { - rassert(outstanding_requests_limit == UNLIMITED_OUTSTANDING_REQUESTS || outstanding_requests_limit > 0); - } - - void push(action_t *action) { - throttled_queue.push_back(action); - outstanding_requests_limiter.lock(this, 1); - } - void on_semaphore_available() { - action_t *action = throttled_queue.head(); - throttled_queue.pop_front(); - queue.push(action); - } - co_semaphore_t *get_outstanding_requests_limiter() { - return &outstanding_requests_limiter; - } - -private: - // It would be nice if we could just use a limited_fifo_queue to - // implement the limitation of outstanding requests. - // However this part of the code must not rely on coroutines, therefore - // we have to implement that functionality manually. - // throttled_queue contains requests which can not be put on queue right now, - // because the number of outstanding requests has been exceeded - intrusive_list_t throttled_queue; - unlimited_fifo_queue_t > queue; - static_semaphore_t outstanding_requests_limiter; - accounting_queue_t::account_t account; - auto_drainer_t::lock_t accounter_lock; - - DISABLE_COPYING(accounting_diskmgr_eager_account_t); -}; - -accounting_diskmgr_account_t::accounting_diskmgr_account_t(accounting_diskmgr_t *_par, - int _pri, - int _outstanding_requests_limit) - : par(_par), pri(_pri), - outstanding_requests_limit(_outstanding_requests_limit) { } - -accounting_diskmgr_account_t::~accounting_diskmgr_account_t() { - par->assert_thread(); -} - -void accounting_diskmgr_account_t::push(action_t *action) { - maybe_init(); - if (!requests_drainer.has()) { - // Create the requests_drainer now rather than during construction of the - // accounting_diskmgr_account_t. We are doing that since - // accounting_diskmgr_account_t can be constructed on any thread, and - // auto_drainer_t doesn't like that. - requests_drainer.init(new auto_drainer_t()); - } - action->account_acq = requests_drainer->lock(); - eager_account->push(action); -} - -void accounting_diskmgr_account_t::on_semaphore_available() { - maybe_init(); - eager_account->on_semaphore_available(); -} - -co_semaphore_t *accounting_diskmgr_account_t::get_outstanding_requests_limiter() { - maybe_init(); - return eager_account->get_outstanding_requests_limiter(); -} - -void accounting_diskmgr_account_t::maybe_init(){ - if (!eager_account.has()) { - par->assert_thread(); - eager_account.init(new eager_account_t(par, pri, outstanding_requests_limit)); - } -} - - - - - -void debug_print(printf_buffer_t *buf, - const accounting_diskmgr_action_t &action) { - buf->appendf("accounting_diskmgr_action{...}<"); - const accounting_payload_t &parent_action = action; - debug_print(buf, parent_action); -} - - -accounting_diskmgr_t::~accounting_diskmgr_t() { - auto_drainer.reset(); // Make absolutely sure this happens first. -} - -void accounting_diskmgr_t::submit(action_t *a) { - a->account->push(a); -} - -void accounting_diskmgr_t::done(accounting_payload_t *p) { - // p really is an action_t... - action_t *a = static_cast(p); - a->account->get_outstanding_requests_limiter()->unlock(1); - a->account_acq.reset(); - done_fun(static_cast(p)); -} - diff --git a/src/arch/io/disk/accounting.hpp b/src/arch/io/disk/accounting.hpp deleted file mode 100644 index 49cb99636b7..00000000000 --- a/src/arch/io/disk/accounting.hpp +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef ARCH_IO_DISK_ACCOUNTING_HPP_ -#define ARCH_IO_DISK_ACCOUNTING_HPP_ - -#include - -#include "containers/intrusive_list.hpp" -#include "containers/scoped.hpp" -#include "concurrency/auto_drainer.hpp" -#include "concurrency/queue/accounting.hpp" -#include "concurrency/queue/unlimited_fifo.hpp" -#include "concurrency/semaphore.hpp" -#include "arch/io/disk.hpp" -#include "arch/io/disk/stats_2.hpp" - -/* `casting_passive_producer_t` is useful when you have a -`passive_producer_t` but you need a `passive_producer_t`, where `X` can -be cast to `Y`. */ - -template -struct casting_passive_producer_t : public passive_producer_t { - explicit casting_passive_producer_t(passive_producer_t *_source) : - passive_producer_t(_source->available), source(_source) { } - - output_t produce_next_value() { - /* Implicit cast from `input_t` to `output_t` in return */ - return source->pop(); - } - -private: - passive_producer_t *source; -}; - -/* `accounting_diskmgr_t` shares disk throughput proportionally between a -number of different "accounts". */ - -typedef stats_diskmgr_2_t::action_t accounting_payload_t; - -class accounting_diskmgr_t; - -struct accounting_diskmgr_action_t; - -struct accounting_diskmgr_eager_account_t; - -struct accounting_diskmgr_account_t { - typedef accounting_diskmgr_action_t action_t; - - accounting_diskmgr_account_t(accounting_diskmgr_t *_par, - int _pri, - int _outstanding_requests_limit); - - ~accounting_diskmgr_account_t(); - - void push(action_t *action); - void on_semaphore_available(); - co_semaphore_t *get_outstanding_requests_limiter(); - -private: - typedef accounting_diskmgr_eager_account_t eager_account_t; - - void maybe_init(); - - accounting_diskmgr_t *par; - int pri; - int outstanding_requests_limit; - scoped_ptr_t eager_account; - // A scoped pointer because we create the drainer lazily on first use. - scoped_ptr_t requests_drainer; - - DISABLE_COPYING(accounting_diskmgr_account_t); -}; - -struct accounting_diskmgr_action_t - : public intrusive_list_node_t, - public accounting_payload_t { - accounting_diskmgr_account_t *account; - auto_drainer_t::lock_t account_acq; -}; - -void debug_print(printf_buffer_t *buf, - const accounting_diskmgr_action_t &action); - -class accounting_diskmgr_t : public home_thread_mixin_t { -public: - explicit accounting_diskmgr_t(int batch_factor) - : producer(&caster), - queue(batch_factor), - caster(&queue), - auto_drainer(new auto_drainer_t()) { } - - ~accounting_diskmgr_t(); - - typedef accounting_diskmgr_account_t account_t; - - typedef accounting_diskmgr_action_t action_t; - - void submit(action_t *a); - - std::function done_fun; - - passive_producer_t * const producer; - void done(accounting_payload_t *p); - - auto_drainer_t *get_auto_drainer() { - return auto_drainer.get(); - } - -private: - friend struct accounting_diskmgr_eager_account_t; - - accounting_queue_t queue; - casting_passive_producer_t caster; - scoped_ptr_t auto_drainer; - - DISABLE_COPYING(accounting_diskmgr_t); -}; - -#endif /* ARCH_IO_DISK_ACCOUNTING_HPP_ */ diff --git a/src/arch/io/disk/conflict_resolving.cc b/src/arch/io/disk/conflict_resolving.cc deleted file mode 100644 index 347f805bc50..00000000000 --- a/src/arch/io/disk/conflict_resolving.cc +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "arch/io/disk/conflict_resolving.hpp" - -#include -#include - -#include "containers/printf_buffer.hpp" -#include "perfmon/perfmon.hpp" - -void debug_print(printf_buffer_t *buf, - const conflict_resolving_diskmgr_action_t &action) { - buf->appendf("cr_diskmgr_action{conflict_count=%d}<", action.conflict_count); - const accounting_diskmgr_action_t &parent_action = action; - debug_print(buf, parent_action); -} - - -conflict_resolving_diskmgr_t::conflict_resolving_diskmgr_t(perfmon_collection_t *stats) : - conflict_sampler(secs_to_ticks(1), true), - conflict_sampler_membership(stats, &conflict_sampler, "conflict") -{ } - -conflict_resolving_diskmgr_t::~conflict_resolving_diskmgr_t() { -#ifndef NDEBUG - /* Make sure there are no requests still out. */ - for (std::map > >::iterator - fd_t_chunk_queues = all_chunk_queues.begin(); - fd_t_chunk_queues != all_chunk_queues.end(); - ++fd_t_chunk_queues) { - rassert(fd_t_chunk_queues->second.empty()); - } - for (auto it = resize_waiter_queues.begin(); - it != resize_waiter_queues.end(); - ++it) { - rassert(it->second.empty()); - } - for (auto it = resize_active.begin(); it != resize_active.end(); ++it) { - rassert(it->second == 0); - } -#endif -} - -// Fills dest's bufs with data from source. source's range is a superset of dest's. -void copy_full_action_buf(pool_diskmgr_action_t *dest, pool_diskmgr_action_t *source, - const size_t offset_into_source) { - guarantee(source->get_offset() <= dest->get_offset() - && source->get_offset() + source->get_count() >= dest->get_offset() + dest->get_count()); - - iovec *dest_vecs; - size_t dest_size; - dest->get_bufs(&dest_vecs, &dest_size); - iovec *source_vecs; - size_t source_size; - source->get_bufs(&source_vecs, &source_size); - - fill_bufs_from_source(dest_vecs, dest_size, source_vecs, source_size, offset_into_source); -} - -void conflict_resolving_diskmgr_t::get_range( - const action_t *a, int64_t *begin, int64_t *end) { - int64_t begin_offset, end_offset; - if (a->get_is_resize()) { - if (a->get_size_change() < 0) { - /* Shrinks the file from `end_offset` to the new size `begin_offset`. */ - begin_offset = a->get_offset(); - end_offset = a->get_offset() - a->get_size_change(); - } else { - /* Grows the file from `begin_offset` to the new size `end_offset`. */ - begin_offset = a->get_offset() - a->get_size_change(); - end_offset = a->get_offset(); - } - } else { - begin_offset = a->get_offset(); - end_offset = a->get_offset() + a->get_count(); - } - *begin = begin_offset / DEVICE_BLOCK_SIZE; - *end = ceil_aligned(end_offset, DEVICE_BLOCK_SIZE) / DEVICE_BLOCK_SIZE; -} - -void conflict_resolving_diskmgr_t::submit(action_t *action) { - action->conflict_count = 0; - - if (resize_active[action->get_fd()] > 0) { - /* There is a resizing operation going on. Get in line for it. */ - - action->conflict_count += resize_active[action->get_fd()]; - resize_waiter_queues[action->get_fd()].push_back(action); - } - - if (action->get_is_resize()) { - /* Block out subsequent operations; reads, writes and resizes alike. */ - ++resize_active[action->get_fd()]; - } - - /* - We calculate per-block conflicts for all operations except for resizes - that increase the file size. - Resizes that reduce the file size must wait for any queued operation - that accesses the affected region of the file, while size increases - are safe to run at any time, and we can skip the check. */ - if (!action->get_is_resize() || action->get_size_change() < 0) { - std::map > *chunk_queues = &all_chunk_queues[action->get_fd()]; - /* Determine the range of file-blocks that this action spans */ - int64_t start; - int64_t end; - get_range(action, &start, &end); - - /* Determine if there are chunk conflicts and put ourself on the queues */ - for (int64_t block = start; block < end; block++) { - std::map >::iterator it; - it = chunk_queues->find(block); - - if (it != chunk_queues->end()) { - /* We conflict on this block. */ - action->conflict_count++; - } - - /* Put ourself on the queue for this chunk. - The exception is if we are a resize and there is no queue yet. - This is safe because resize operations have their own queue - `resize_waiter_queues` on which other operations will queue up. */ - if (it == chunk_queues->end() && !action->get_is_resize()) { - /* Start a queue because there isn't one already */ - it = chunk_queues->insert( - it, std::make_pair(block, std::deque())); - } - if (it != chunk_queues->end()) { - rassert(it->first == block); - it->second.push_back(action); - } - } - } - - /* If there are no conflicts, we can start right away. */ - if (action->conflict_count == 0) { - submit_action_downwards(action); - } else { - // TODO: Refine the perfmon such that it measures the actual time that ops spend - // in a waiting state - conflict_sampler.record(1); - } -} - -void conflict_resolving_diskmgr_t::submit_action_downwards(action_t *action) { - accounting_diskmgr_action_t *action_payload = - static_cast(action); - submit_fun(action_payload); -} - -void conflict_resolving_diskmgr_t::done(accounting_diskmgr_action_t *payload) { - /* The only payloads we get back via done() should be payloads that we sent into - submit_fun(), which means they should actually be action_t objects secretly. */ - action_t *action = static_cast(payload); - - if (action->get_is_resize()) { - /* Mark the resize as done. */ - rassert(resize_active[action->get_fd()] > 0); - --resize_active[action->get_fd()]; - - /* Resume all operations that were waiting for us. */ - std::deque &waiter_queue = resize_waiter_queues[action->get_fd()]; - - std::vector waiters_to_unblock; - waiters_to_unblock.reserve(waiter_queue.size()); - - while (!waiter_queue.empty()) { - /* Decrease the conflict count for the oldest remaining waiter. */ - action_t *waiter = waiter_queue.front(); - waiter_queue.pop_front(); - - rassert(waiter->conflict_count > 0); - waiter->conflict_count--; - - if (waiter->conflict_count == 0) { - /* The waiter isn't waiting on anything else. Unblock it. */ - waiters_to_unblock.push_back(waiter); - - if (waiter->get_is_resize()) { - /* We are about to submit another resize. waiters that come after - this must remain on the resize_waiter_queue, because they - are still waiting for that resize. Continue by - just decrementing the conflict_count, but not - popping the waiters off the queue nor unblocking them. */ - for (auto it = waiter_queue.begin(); - it != waiter_queue.end(); - ++it) { - --(*it)->conflict_count; - /* The waiter must at least still be waiting for the - resize we are about to unblock. */ - rassert((*it)->conflict_count > 0); - } - - /* Sorry, the remaining waiters must wait for a little longer. */ - break; - } - } - } - - /* Now actually unblock some waiters. */ - for (size_t i = 0; i < waiters_to_unblock.size(); ++i) { - submit_action_downwards(waiters_to_unblock[i]); - } - } - - if (!action->get_is_resize() || action->get_size_change() < 0) { - std::map > *chunk_queues = &all_chunk_queues[action->get_fd()]; - - int64_t start, end; - get_range(action, &start, &end); - - /* Visit every chunk queue in the block range and see if anything is - blocking on us. */ - for (auto it = chunk_queues->lower_bound(start); - it != chunk_queues->end();) { - if (it->first >= end) { - break; - } - - std::deque &queue = it->second; - - /* Remove ourselves from the queue. - Exception: If we are a resize and weren't on the queue in the first - place, we can skip this queue. - This can happen because resizes only go on queues that already - existed when the resize was added. */ - guarantee(!queue.empty()); - if (queue.front() != action) { - guarantee(action->get_is_resize()); - ++it; - continue; - } - queue.pop_front(); - - if (!queue.empty()) { - /* Continue with the next chunk queue. - We have to move on now, because we might call done() recursively and that might - invalidate the iterator. */ - ++it; - - /* Something was blocking on us for this block. Get the first waiter from the queue. */ - action_t *waiter = queue.front(); - - rassert(waiter->conflict_count > 0); - waiter->conflict_count--; - - if (waiter->conflict_count == 0) { - /* We were the last thing it was waiting on */ - - /* If the waiter is a read, and the range it was supposed to read is a subrange of - our range, then we can just fill its buffer directly instead of going to disk. */ - if ((action->get_is_read() || action->get_is_write()) && - waiter->get_is_read() && - waiter->get_offset() >= action->get_offset() && - waiter->get_offset() + waiter->get_count() <= action->get_offset() + action->get_count() ) { - - copy_full_action_buf(waiter, action, waiter->get_offset() - action->get_offset()); - - waiter->set_successful_due_to_conflict(); - - /* Recursively calling done() is safe. "waiter" must end at or before - our current location, or else its conflict_count would still be nonzero. - Therefore it will not touch any part of the multimap that we have not - yet gotten to. If it touches the queue that we popped it from, that's - also safe, because we've already moved our iterator past it. - Note that we can potentially lose some short-circuit opportunities, - because we might be able to provide a larger range than the request - that we just provided a subset of our data to. So that request might - not be able to short-circuit another watier, while we might have - been able. This should be more of an academic concern though. */ - done(waiter); - - } else { - /* The shortcut didn't work out; do things the normal way */ - submit_action_downwards(waiter); - } - } - } else { - /* The queue is empty, erase it. */ - chunk_queues->erase(it++); - } - } - } - - done_fun(action); -} diff --git a/src/arch/io/disk/conflict_resolving.hpp b/src/arch/io/disk/conflict_resolving.hpp deleted file mode 100644 index ea35e960503..00000000000 --- a/src/arch/io/disk/conflict_resolving.hpp +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_IO_DISK_CONFLICT_RESOLVING_HPP_ -#define ARCH_IO_DISK_CONFLICT_RESOLVING_HPP_ - -#include -#include -#include - -#include "arch/io/disk/accounting.hpp" -#include "arch/runtime/runtime_utils.hpp" -#include "config/args.hpp" -#include "math.hpp" -#include "perfmon/perfmon.hpp" - -/* The purpose of the conflict-resolving disk manager is to deal with the case -where we have been sent a number of different reads or writes for overlapping -regions of the file. - -The IO layer is supposed to guarantee that read and write operations reach the -device in the same order they are begun; that is, if a write is begun and then -a read is begun that overlaps the write, the read is guaranteed to see the -changes the write has made. This places constraints on the reordering of IO -operations. - -To make things simple, all of that work is handled by the -conflict_resolving_diskmgr_t; no matter what conflicts are present in the stream -of read and write operations sent to the conflict_resolving_diskmgr_t, it will -never send two potentially conflicting operations to the next phase of the -IO layer. - -conflict_resolving_diskmgr_t is meant to be used as one link in a "chain" of -objects that process disk operations. To pass an operation to the -conflict_resolving_diskmgr_t, construct an conflict_resolving_diskmgr_t::action_t -object. Pass a pointer to it to submit(). At the appropriate time, -conflict_resolving_diskmgr_t will pass it down the chain by calling its -submit_fun(), which you provide; typically it will be a method on the object that -actually runs the disk request. When the disk request is done, the object that -actually runs the disk request should call done() on the -conflict_resolving_diskmgr_t, which in turn will call done_fun(), which is a -function that you provide. - -"payload_t" should contain the actual information that the next link in the chain -needs in order to complete the disk request. It must expose the following member -functions: - bool get_is_read() const; - bool get_is_write() const; - int64_t get_offset() const; - size_t get_count() const; - void *get_buf() const; - -You should make a separate conflict_resolving_diskmgr_t for each file. */ - - -struct conflict_resolving_diskmgr_action_t : public accounting_diskmgr_action_t { - int conflict_count; -}; - -void debug_print(printf_buffer_t *buf, - const conflict_resolving_diskmgr_action_t &action); - -struct conflict_resolving_diskmgr_t { - explicit conflict_resolving_diskmgr_t(perfmon_collection_t *stats); - ~conflict_resolving_diskmgr_t(); - - typedef conflict_resolving_diskmgr_action_t action_t; - - /* Call submit() to send an action to the conflict_resolving_diskmgr_t. - conflict_resolving_diskmgr_t calls done_fun() when the operation is done. */ - void submit(action_t *action); - std::function done_fun; - - /* conflict_resolving_diskmgr_t calls submit_fun() to send actions down to the next - level. The next level should call done() when the operation passed to submit_fun() - is done. */ - std::function submit_fun; - void done(accounting_diskmgr_action_t *payload); - -private: - - /* Memory usage analysis: If there are no conflicts, we use 1 bit of memory per - DEVICE_BLOCK_SIZE-sized chunk of the file. */ - - /* For efficiency reasons, we think in terms of DEVICE_BLOCK_SIZE-sized chunks - of the file. We assume that if two operations touch the same chunk, then they - are potentially conflicting. */ - void get_range(const action_t *a, int64_t *begin, int64_t *end); - - /* Does a type cast and calls submit_fun. */ - void submit_action_downwards(action_t *action); - - /* For each chunk B in the file FD: all_chunk_queues[FD][B] contains a deque - of things that are either (a) waiting to operate on that chunk but cannot - because something else is currently operating on that chunk, or (b) which - are currently operating on that chunk. In case (b), that operation is always - the first one on the deque and there can just be one such operation. If no - operation is active on FD/B, all_chunk_queues[FD][B] does not have an entry - for B. It could be a multimap instead, but that would mean depending on - properties of multimaps that are not guaranteed by the C++ standard. */ - - std::map > > all_chunk_queues; - - /* `resize_waiter_queues` contains actions that are waiting for an ongoing - resize to finish. Right now, a resize operation blocks the whole file. */ - std::map > resize_waiter_queues; - /* Contains an entry > 0 for as long as any resize operation is active. */ - std::map resize_active; - - perfmon_sampler_t conflict_sampler; - perfmon_membership_t conflict_sampler_membership; -}; - -#endif /* ARCH_IO_DISK_CONFLICT_RESOLVING_HPP_ */ diff --git a/src/arch/io/disk/filestat.cc b/src/arch/io/disk/filestat.cc deleted file mode 100644 index 3d1dc2f2981..00000000000 --- a/src/arch/io/disk/filestat.cc +++ /dev/null @@ -1,29 +0,0 @@ -#include - -#include "errors.hpp" -#include "arch/io/disk/filestat.hpp" - -int64_t get_file_size(fd_t fd) { - guarantee(fd != INVALID_FD); - -#ifdef _WIN32 - ULARGE_INTEGER size; - size.LowPart = GetFileSize(fd, &size.HighPart); - guarantee(size.LowPart != INVALID_FILE_SIZE, "GetFileSize failed"); - return size.QuadPart; -#else -#ifdef __MACH__ - struct stat buf; - int res = fstat(fd, &buf); -#elif defined(__linux__) - struct stat64 buf; - int res = fstat64(fd, &buf); -#endif - // This compile-time assertion is the most important line in the function. - CT_ASSERT(sizeof(buf.st_size) == sizeof(int64_t)); - - guarantee_err(res == 0, "fstat failed"); - guarantee(buf.st_size >= 0); - return buf.st_size; -#endif -} diff --git a/src/arch/io/disk/filestat.hpp b/src/arch/io/disk/filestat.hpp deleted file mode 100644 index 1bfa4ccff02..00000000000 --- a/src/arch/io/disk/filestat.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef ARCH_IO_DISK_FILESTAT_HPP_ -#define ARCH_IO_DISK_FILESTAT_HPP_ - -#include - -#include "arch/runtime/runtime_utils.hpp" - -int64_t get_file_size(fd_t fd); - -#endif // ARCH_IO_DISK_FILESTAT_HPP_ diff --git a/src/arch/io/disk/pool.cc b/src/arch/io/disk/pool.cc deleted file mode 100644 index 369c673924a..00000000000 --- a/src/arch/io/disk/pool.cc +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "arch/io/disk/pool.hpp" - -#include -#include -#include -#include -#include - -#include "arch/io/disk.hpp" -#include "config/args.hpp" -#include "containers/printf_buffer.hpp" -#include "logger.hpp" - -int blocker_pool_queue_depth(int max_concurrent_io_requests) { - guarantee(max_concurrent_io_requests > 0); - guarantee(max_concurrent_io_requests < MAXIMUM_MAX_CONCURRENT_IO_REQUESTS); - // TODO: Why make this twice MAX_CONCURRENT_IO_REQUESTS? - return max_concurrent_io_requests * 2; -} - - -void debug_print(printf_buffer_t *buf, - const pool_diskmgr_action_t &action) { - buf->appendf("pool_diskmgr_action{is_read=%s, fd=%d, count=%zu, " - "offset=%" PRIi64 ", errno=%d}", - action.get_is_read() ? "true" : "false", - action.get_fd(), - action.get_count(), - action.get_offset(), - action.get_succeeded() ? 0 : action.get_io_errno()); -} - -pool_diskmgr_t::pool_diskmgr_t(linux_event_queue_t *queue, - passive_producer_t *_source, - int max_concurrent_io_requests) - : queue_depth(blocker_pool_queue_depth(max_concurrent_io_requests)), - source(_source), - blocker_pool(max_concurrent_io_requests, queue), - n_pending(0) { - if (source->available->get()) { pump(); } - source->available->set_callback(this); -} - -pool_diskmgr_t::~pool_diskmgr_t() { - assert_thread(); - source->available->unset_callback(); -} - -// Advance the vector, while respecting the device block size alignment for direct I/O -size_t pool_diskmgr_t::action_t::advance_vector(iovec **vecs, - size_t *count, - size_t bytes_done) { - // Note: if somehow the read/write call returns a result unaligned with - // DEVICE_BLOCK_SIZE, there will probably be an error when we try again with the - // advanced vector. - size_t bytes_to_advance = bytes_done; - - while (bytes_to_advance > 0) { - rassert(*count > 0); - size_t cur_done = std::min((*vecs)->iov_len, bytes_to_advance); - bytes_to_advance -= cur_done; - (*vecs)->iov_len -= cur_done; - (*vecs)->iov_base = static_cast((*vecs)->iov_base) + cur_done; - - while (*count > 0 && (*vecs)->iov_len == 0) { - ++(*vecs); - --(*count); - } - } - - return bytes_done; -} - -ssize_t pool_diskmgr_t::action_t::vectored_read_write(iovec *vecs, - size_t vecs_len, - int64_t partial_offset) { - int64_t real_offset = offset + partial_offset; -#ifndef USE_WRITEV -#error "USE_WRITEV not defined. Did you include pool.hpp?" -#elif USE_WRITEV - size_t vecs_to_use = std::min(vecs_len, IOV_MAX); - if (type == ACTION_READ) { - return preadv(fd, vecs, vecs_to_use, real_offset); - } - rassert(type == ACTION_WRITE); - return pwritev(fd, vecs, vecs_to_use, real_offset); -#else - guarantee(vecs_len == 1); - guarantee(partial_offset == 0); - if (type == ACTION_READ) { - return pread(fd, vecs[0].iov_base, vecs[0].iov_len, real_offset); - } - rassert(type == ACTION_WRITE); - return pwrite(fd, vecs[0].iov_base, vecs[0].iov_len, real_offset); -#endif -} - -void pool_diskmgr_t::action_t::copy_vectors(scoped_array_t *vectors_out) { - iovec *vectors; - size_t count; - get_bufs(&vectors, &count); - vectors_out->init(count); - - for (size_t i = 0; i < count; ++i) { - (*vectors_out)[i] = vectors[i]; - } -} - -int64_t pool_diskmgr_t::action_t::perform_read_write(iovec *vecs, - size_t vecs_len) { - ssize_t res = 0; - int64_t partial_offset = 0; - - int64_t total_bytes = 0; - for (size_t i = 0; i < vecs_len; ++i) { - total_bytes += vecs[i].iov_len; - } - - while (vecs_len > 0 && partial_offset < total_bytes) { - do { - res = vectored_read_write(vecs, vecs_len, partial_offset); - } while (res == -1 && get_errno() == EINTR); - - if (res == -1) { - return -get_errno(); - } else if (res == 0 && type == ACTION_WRITE) { - // This happens when running out of disk space. - // The errno in that case is 0 and doesn't tell us about the - // real reason for the failed i/o. - // We set the io_result to be -ENOSPC and print an error stating - // what actually happened. - logERR("Failed I/O: vectored write of %" PRIi64 " bytes stopped after " - "%" PRIi64 " bytes. Assuming we ran out of disk space.", - total_bytes, partial_offset); - return -ENOSPC; - } else if (res == 0 && type == ACTION_READ) { - // A 0 result from a read means that we've tried to read - // behind the file. - logERR("Failed I/O: we tried to read from behind the end of the file. " - "Either the file got truncated, or there is a bug in RethinkDB."); - return -EINVAL; - } - - // Advance the vector in DEVICE_BLOCK_SIZE chunks and update our offset - partial_offset += advance_vector(&vecs, &vecs_len, res); - } - return total_bytes; -} - -void pool_diskmgr_t::action_t::run() { - if (wrap_in_datasyncs) { - int errcode = perform_datasync(fd); - if (errcode != 0) { - io_result = -errcode; - return; - } - } - - switch (type) { - case ACTION_RESIZE: { -#ifdef _WIN32 - FILE_END_OF_FILE_INFO new_eof; - new_eof.EndOfFile.QuadPart = offset; - BOOL res = SetFileInformationByHandle(fd, FileEndOfFileInfo, &new_eof, sizeof(new_eof)); - if (!res) { - logWRN("SetFileInformationByHandle failed: %s", winerr_string(GetLastError()).c_str()); - io_result = -EIO; - return; - } - io_result = 0; -#else - CT_ASSERT(sizeof(off_t) == sizeof(int64_t)); - int res; - do { - res = ftruncate(fd, offset); - } while (res == -1 && get_errno() == EINTR); - if (res == 0) { - io_result = 0; - } else { - io_result = -get_errno(); - return; - } -#endif - } break; - case ACTION_READ: - case ACTION_WRITE: { - // Copy the io vectors because perform_read_write will modify them - scoped_array_t vectors; - copy_vectors(&vectors); - io_result = perform_read_write(vectors.data(), vectors.size()); - } break; - default: - unreachable("Unknown I/O action"); - } - - if (wrap_in_datasyncs) { - int errcode = perform_datasync(fd); - if (errcode != 0) { - io_result = -errcode; - return; - } - } -} - -void pool_diskmgr_action_t::done() { - parent->assert_thread(); - parent->n_pending--; - parent->pump(); - parent->done_fun(this); -} - -void pool_diskmgr_t::on_source_availability_changed() { - assert_thread(); - /* This is called when the queue used to be empty but now has requests on - it, and also when the queue's last request is consumed. */ - if (source->available->get()) pump(); -} - -void pool_diskmgr_t::pump() { - assert_thread(); - while (source->available->get() && n_pending < queue_depth) { - action_t *a = source->pop(); - a->parent = this; - n_pending++; - blocker_pool.do_job(a); - } -} - diff --git a/src/arch/io/disk/pool.hpp b/src/arch/io/disk/pool.hpp deleted file mode 100644 index 18fce5d0008..00000000000 --- a/src/arch/io/disk/pool.hpp +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef ARCH_IO_DISK_POOL_HPP_ -#define ARCH_IO_DISK_POOL_HPP_ - -#include - -#include -#include - -#include "arch/runtime/event_queue.hpp" -#include "arch/io/blocker_pool.hpp" -#include "concurrency/queue/passive_producer.hpp" -#include "containers/scoped.hpp" - -#ifndef __linux__ -#define USE_WRITEV 0 -#else -#define USE_WRITEV 1 -#endif - -struct iovec; -class pool_diskmgr_t; -class printf_buffer_t; - -/* The pool disk manager uses a thread pool in conjunction with synchronous -(blocking) IO calls to asynchronously run IO requests. */ - -struct pool_diskmgr_action_t - : private blocker_pool_t::job_t { - pool_diskmgr_action_t() { } - - void make_write(fd_t _fd, const void *_buf, size_t _count, int64_t _offset, - bool _wrap_in_datasyncs) { - type = ACTION_WRITE; - wrap_in_datasyncs = _wrap_in_datasyncs; - fd = _fd; - buf_and_count.iov_base = const_cast(_buf); - buf_and_count.iov_len = _count; - offset = _offset; - size_change = 0; - } - - void make_resize(fd_t _fd, int64_t _old_size, int64_t _new_size, - bool _wrap_in_datasyncs) { - type = ACTION_RESIZE; - wrap_in_datasyncs = _wrap_in_datasyncs; - fd = _fd; - buf_and_count.iov_base = nullptr; - buf_and_count.iov_len = 0; - offset = _new_size; - size_change = _new_size - _old_size; - } - -#ifndef USE_WRITEV -#error "USE_WRITEV not defined... but we are in pool.hpp. Where is it?" -#elif USE_WRITEV - void make_writev(fd_t _fd, scoped_array_t &&_bufs, size_t _count, int64_t _offset) { - type = ACTION_WRITE; - wrap_in_datasyncs = false; - fd = _fd; - iovecs = std::move(_bufs); - buf_and_count.iov_base = nullptr; - buf_and_count.iov_len = _count; - offset = _offset; - size_change = 0; - } -#endif - - void make_read(fd_t _fd, void *_buf, size_t _count, int64_t _offset) { - type = ACTION_READ; - wrap_in_datasyncs = false; - fd = _fd; - buf_and_count.iov_base = _buf; - buf_and_count.iov_len = _count; - offset = _offset; - size_change = 0; - } - - bool get_is_write() const { return type == ACTION_WRITE; } - bool get_is_resize() const { return type == ACTION_RESIZE; } - bool get_is_read() const { return type == ACTION_READ; } - fd_t get_fd() const { return fd; } - void get_bufs(iovec **iovecs_out, size_t *iovecs_len_out) { - if (buf_and_count.iov_base != nullptr) { - *iovecs_out = &buf_and_count; - *iovecs_len_out = 1; - } else { - *iovecs_out = iovecs.data(); - *iovecs_len_out = iovecs.size(); - } - } - size_t get_count() const { return buf_and_count.iov_len; } - int64_t get_offset() const { return offset; } - int64_t get_size_change() const { return size_change; } - - void set_successful_due_to_conflict() { io_result = get_count(); } - bool get_succeeded() const { return io_result == static_cast(get_count()); } - int get_io_errno() const { - rassert(io_result < 0); - return -io_result; - } - -private: - friend class pool_diskmgr_t; - pool_diskmgr_t *parent; - - enum action_type_t {ACTION_READ, ACTION_WRITE, ACTION_RESIZE}; - action_type_t type; - bool wrap_in_datasyncs; - fd_t fd; - - // Either type is ACTION_RESIZE, or buf_and_count.iov_base is used, or iovecs - // is used (for writev). If iovecs is used, then buf_and_count.iov_len is the - // sum of the iovecs' iov_len fields. Currently readv is not supported, but if - // you need it, it should be easy to add. - scoped_array_t iovecs; - iovec buf_and_count; - int64_t offset; - // `size_change` is the number of bytes by which the file size gets - // changed by this operation. 0 unless the type is ACTION_RESIZE. - int64_t size_change; - - // Helper functions for vectored reads/writes - static size_t advance_vector(iovec **vecs, size_t *count, size_t bytes_done); - ssize_t vectored_read_write(iovec *vecs, size_t vecs_len, int64_t partial_offset); - void copy_vectors(scoped_array_t *vectors_out); - int64_t perform_read_write(iovec *vecs, size_t count); - - int64_t io_result; - - void run(); - void done(); - - DISABLE_COPYING(pool_diskmgr_action_t); -}; - -void debug_print(printf_buffer_t *buf, - const pool_diskmgr_action_t &action); - -class pool_diskmgr_t : private availability_callback_t, public home_thread_mixin_debug_only_t { -public: - friend struct pool_diskmgr_action_t; - typedef pool_diskmgr_action_t action_t; - - /* The `pool_diskmgr_t` will draw actions to run from `source`. It will call `done_fun` - on each one when it's done. */ - pool_diskmgr_t(linux_event_queue_t *queue, passive_producer_t *source, - int max_concurrent_io_requests); - std::function done_fun; - ~pool_diskmgr_t(); - -private: - const int queue_depth; - passive_producer_t *source; - blocker_pool_t blocker_pool; - - void on_source_availability_changed(); - int n_pending; - void pump(); - - DISABLE_COPYING(pool_diskmgr_t); -}; - -#endif /* ARCH_IO_DISK_POOL_HPP_ */ diff --git a/src/arch/io/disk/stats.cc b/src/arch/io/disk/stats.cc deleted file mode 100644 index 3f916e7448e..00000000000 --- a/src/arch/io/disk/stats.cc +++ /dev/null @@ -1,28 +0,0 @@ -#include "arch/io/disk/stats.hpp" - -stats_diskmgr_t::stats_diskmgr_t(perfmon_collection_t *stats, const std::string &name) : - read_sampler(secs_to_ticks(1)), - write_sampler(secs_to_ticks(1)), - stats_membership(stats, - &read_sampler, (name + "_read").c_str(), - &write_sampler, (name + "_write").c_str()) { } - - -void stats_diskmgr_t::submit(action_t *a) { - if (a->get_is_read()) { - read_sampler.begin(&a->start_time); - } else { - write_sampler.begin(&a->start_time); - } - submit_fun(a); -} - -void stats_diskmgr_t::done(conflict_resolving_diskmgr_action_t *p) { - action_t *a = static_cast(p); - if (a->get_is_read()) { - read_sampler.end(&a->start_time); - } else { - write_sampler.end(&a->start_time); - } - done_fun(a); -} diff --git a/src/arch/io/disk/stats.hpp b/src/arch/io/disk/stats.hpp deleted file mode 100644 index af70ac225f8..00000000000 --- a/src/arch/io/disk/stats.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef ARCH_IO_DISK_STATS_HPP_ -#define ARCH_IO_DISK_STATS_HPP_ - -#include -#include - -#include "arch/io/disk/pool.hpp" -#include "arch/io/disk/conflict_resolving.hpp" -#include "perfmon/types.hpp" - -/* There are two types of stat-collectors in the disk stack. One type is a passive -consumer and active producer of disk operations. The other type is an active consumer -and passive producer. */ - -struct stats_diskmgr_t { - stats_diskmgr_t(perfmon_collection_t *stats, const std::string &name); - - struct action_t : public conflict_resolving_diskmgr_action_t { - ticks_t start_time; - }; - - void submit(action_t *a); - - std::function done_fun; - - std::function submit_fun; - - void done(conflict_resolving_diskmgr_action_t *p); - -private: - perfmon_duration_sampler_t read_sampler, write_sampler; - perfmon_multi_membership_t stats_membership; -}; - -#endif /* ARCH_IO_DISK_STATS_HPP_ */ diff --git a/src/arch/io/disk/stats_2.cc b/src/arch/io/disk/stats_2.cc deleted file mode 100644 index 1e5abd6be48..00000000000 --- a/src/arch/io/disk/stats_2.cc +++ /dev/null @@ -1,49 +0,0 @@ -#include "arch/io/disk/stats_2.hpp" - -#include - -#include "containers/printf_buffer.hpp" - -void debug_print(printf_buffer_t *buf, - const stats_diskmgr_2_action_t &action) { - buf->appendf("stats_diskmgr_2_action{start_time=%" PRIu64 "}<", - action.start_time); - const pool_diskmgr_t::action_t &parent_action = action; - debug_print(buf, parent_action); -} - -/* Give the `stats_diskmgr_2_t` constructor a - `passive_producer_t`; it will get its operations from there. It - will call `done_fun` on each one when it's done. */ -stats_diskmgr_2_t::stats_diskmgr_2_t( - perfmon_collection_t *stats, const std::string &name, - passive_producer_t *_source) : - passive_producer_t(_source->available), - producer(this), - source(_source), - read_sampler(secs_to_ticks(1)), - write_sampler(secs_to_ticks(1)), - stats_membership(stats, - &read_sampler, (name + "_read").c_str(), - &write_sampler, (name + "_write").c_str()) { } - - -void stats_diskmgr_2_t::done(pool_diskmgr_t::action_t *p) { - action_t *a = static_cast(p); - if (a->get_is_read()) { - read_sampler.end(&a->start_time); - } else { - write_sampler.end(&a->start_time); - } - done_fun(a); -} - -pool_diskmgr_t::action_t *stats_diskmgr_2_t::produce_next_value() { - action_t *a = source->pop(); - if (a->get_is_read()) { - read_sampler.begin(&a->start_time); - } else { - write_sampler.begin(&a->start_time); - } - return a; -} diff --git a/src/arch/io/disk/stats_2.hpp b/src/arch/io/disk/stats_2.hpp deleted file mode 100644 index f893c7e9782..00000000000 --- a/src/arch/io/disk/stats_2.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef ARCH_IO_DISK_STATS_2_HPP_ -#define ARCH_IO_DISK_STATS_2_HPP_ - -#include - -#include "arch/io/disk/pool.hpp" -#include "perfmon/perfmon.hpp" - -struct stats_diskmgr_2_action_t : public pool_diskmgr_t::action_t { - ticks_t start_time; -}; - -void debug_print(printf_buffer_t *buf, - const stats_diskmgr_2_action_t &action); - -struct stats_diskmgr_2_t : private passive_producer_t { - typedef stats_diskmgr_2_action_t action_t; - - /* Give the `stats_diskmgr_2_t` constructor a - `passive_producer_t`; it will get its operations from there. It - will call `done_fun` on each one when it's done. */ - stats_diskmgr_2_t( - perfmon_collection_t *stats, const std::string &name, - passive_producer_t *_source); - - std::function done_fun; - - passive_producer_t *const producer; - void done(pool_diskmgr_t::action_t *p); - -private: - pool_diskmgr_t::action_t *produce_next_value(); - - passive_producer_t *source; - perfmon_duration_sampler_t read_sampler, write_sampler; - perfmon_multi_membership_t stats_membership; -}; - - -#endif // ARCH_IO_DISK_STATS_2_HPP_ diff --git a/src/arch/io/event_watcher.cc b/src/arch/io/event_watcher.cc deleted file mode 100644 index 7eab1c13a4c..00000000000 --- a/src/arch/io/event_watcher.cc +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. - -#include "arch/io/event_watcher.hpp" -#include "arch/runtime/thread_pool.hpp" - -#ifdef _WIN32 - -#include "concurrency/wait_any.hpp" - -#ifdef TRACE_OVERLAPPED -#include "debug.hpp" -#define debugf_overlapped(...) debugf("overlapped: " __VA_ARGS__) -#else -#define debugf_overlapped(...) ((void)0) -#endif - -overlapped_operation_t::overlapped_operation_t(windows_event_watcher_t *ew) : event_watcher(ew) { - debugf_overlapped("[%p] init from watcher %p\n", this, ew); - rassert(event_watcher != nullptr); - rassert(event_watcher->current_thread() == get_thread_id()); - memset(&overlapped, 0, sizeof(overlapped)); -} - -overlapped_operation_t::~overlapped_operation_t() { - debugf_overlapped("[%p] destroy\n", this); - // Always call wait_abortable, set_cancel, set_result or abort_op before destructing - rassert(completed.is_pulsed()); -} - -void overlapped_operation_t::set_cancel() { - debugf_overlapped("[%p] set_cancel\n", this); - set_result(0, ERROR_CANCELLED); -} - -void overlapped_operation_t::abort_op() { - if (completed.is_pulsed()) { - debugf_overlapped("[%p] abort_op: already pulsed\n", this); - } else { - debugf_overlapped("[%p] abort_op: cancelling\n", this); - BOOL res = CancelIoEx(event_watcher->handle, &overlapped); - if (!res) { - switch (GetLastError()) { - case ERROR_NOT_FOUND: - case ERROR_INVALID_HANDLE: - debugf_overlapped("[%p] abort_op: not found or invalid\n", this); - // Assume the operation has already completed asynchonously and is - // currently queued on the completion port - break; - default: - guarantee_winerr(res, "CancelIoEx failed"); - } - } - - // After a successful CancelIoEx, the operation gets queued - // onto the completion port. In most cases the error code will - // be ERROR_OPERATION_ABORTED. However the operation might - // also complete succesfully or fail with another error. - completed.wait_lazily_unordered(); - } -} - -void overlapped_operation_t::set_result(size_t nb_bytes_, DWORD error_) { - debugf_overlapped("[%p] set_result(%zu, %u)\n", this, nb_bytes_, error_); - rassert(!completed.is_pulsed()); - nb_bytes = nb_bytes_; - error = error_; - if (error != NO_ERROR) { - event_watcher->on_error(error); - } - completed.pulse(); -} - -windows_event_watcher_t::windows_event_watcher_t(fd_t handle_, linux_event_callback_t *eh) : - handle(handle_), error_handler(eh), original_thread(get_thread_id()), current_thread_(get_thread_id()) { - linux_thread_pool_t::get_thread()->queue.add_handle(handle); -} - -void windows_event_watcher_t::rethread(threadnum_t new_thread) { - current_thread_ = new_thread; -} - -void windows_event_watcher_t::stop_watching_for_errors() { - error_handler = nullptr; -} - -void windows_event_watcher_t::on_error(UNUSED DWORD error) { - rassert(current_thread_ == get_thread_id()); - if (error_handler != nullptr) { - linux_event_callback_t *eh = error_handler; - error_handler = nullptr; - eh->on_event(poll_event_err); - } -} - -windows_event_watcher_t::~windows_event_watcher_t() { - // WINDOWS TODO: Is there a way to make sure no more operations are - // queued for this handle, or that the handle is closed? -} - -void overlapped_operation_t::wait_interruptible(const signal_t *interruptor) { - debugf_overlapped("[%p] wait_interruptible\n", this); - try { - ::wait_interruptible(&completed, interruptor); - } catch (const interrupted_exc_t &) { - debugf_overlapped("[%p] interrupted\n", this); - abort_op(); - throw; - } -} - -void overlapped_operation_t::wait_abortable(const signal_t *aborter) { - debugf_overlapped("[%p] wait_abortable\n", this); - wait_any_t waiter(&completed, aborter); - waiter.wait_lazily_unordered(); - if (aborter->is_pulsed()) { - debugf_overlapped("[%p] aborted\n", this); - abort_op(); - } -} - -#else - -linux_event_watcher_t::linux_event_watcher_t(fd_t f, linux_event_callback_t *eh) : - fd(f), error_handler(eh), - in_watcher(nullptr), out_watcher(nullptr), -#ifdef __linux - rdhup_watcher(nullptr), -#endif - watching_for_errors(true), - old_mask(0), - old_watching_for_errors(false) -{ - /* At first, only register for error events */ - remask(); -} - -linux_event_watcher_t::~linux_event_watcher_t() { - guarantee(!in_watcher); - guarantee(!out_watcher); -#ifdef __linux - guarantee(!rdhup_watcher); -#endif - - stop_watching_for_errors(); -} - -void linux_event_watcher_t::stop_watching_for_errors() { - if (watching_for_errors) { - watching_for_errors = false; - remask(); - } -} - -linux_event_watcher_t::watch_t::watch_t(linux_event_watcher_t *p, int e) : - parent(p), event(e) -{ - rassert(!*parent->get_watch_slot(event), "something's already watching that event."); - *parent->get_watch_slot(event) = this; - parent->remask(); -} - -linux_event_watcher_t::watch_t::~watch_t() { - rassert(*parent->get_watch_slot(event) == this); - *parent->get_watch_slot(event) = nullptr; - parent->remask(); -} - -bool linux_event_watcher_t::is_watching(int event) { - assert_thread(); - return *get_watch_slot(event) == nullptr; -} - -linux_event_watcher_t::watch_t **linux_event_watcher_t::get_watch_slot(int event) { - switch (event) { - case poll_event_in: return &in_watcher; - case poll_event_out: return &out_watcher; -#ifdef __linux - case poll_event_rdhup: return &rdhup_watcher; -#endif - default: crash("bad event"); - } -} - -void linux_event_watcher_t::remask() { - int new_mask = 0; - if (in_watcher) new_mask |= poll_event_in; - if (out_watcher) new_mask |= poll_event_out; -#ifdef __linux - if (rdhup_watcher) new_mask |= poll_event_rdhup; -#endif - - // What we do (watch_resource, adjust_resource, forget_resource) depends on whether we are - // currently registered to watch the resource. - - const bool old_registered = (old_mask != 0 || old_watching_for_errors); - const bool new_registered = (new_mask != 0 || watching_for_errors); - - if (old_registered) { - if (new_registered) { - if (old_mask != new_mask) { - linux_thread_pool_t::get_thread()->queue.adjust_resource(fd, new_mask, this); - } - } else { - linux_thread_pool_t::get_thread()->queue.forget_resource(fd, this); - } - } else { - if (new_registered) { - linux_thread_pool_t::get_thread()->queue.watch_resource(fd, new_mask, this); - } - } - - old_watching_for_errors = watching_for_errors; - old_mask = new_mask; -} - -void linux_event_watcher_t::on_event(int event) { - int error_mask = poll_event_err | poll_event_hup; -#ifdef __linux - error_mask |= poll_event_rdhup; -#endif - guarantee((event & (error_mask | old_mask)) == event, "Unexpected event received (from operating system?)."); - - if (event & error_mask) { -#ifdef __linux - if (event & ~poll_event_rdhup) { - error_handler->on_event(event & error_mask); - } else { - rassert(event & poll_event_rdhup); - if (!rdhup_watcher->is_pulsed()) rdhup_watcher->pulse(); - } -#else - error_handler->on_event(event & error_mask); -#endif // __linux - } - - // An error condition could cause spurious wakeups of in and out watchers, - // but that's okay because they're supposed to be able to handle spurious - // wakeups. (They'll just get EAGAIN.) - - if ((event & poll_event_in) || (event & error_mask)) { - if (in_watcher != nullptr && !in_watcher->is_pulsed()) { - in_watcher->pulse(); - } - } - - if ((event & poll_event_out) || (event & error_mask)) { - if (out_watcher != nullptr && !out_watcher->is_pulsed()) { - out_watcher->pulse(); - } - } -} - -#endif diff --git a/src/arch/io/event_watcher.hpp b/src/arch/io/event_watcher.hpp deleted file mode 100644 index 4c58ec77fa6..00000000000 --- a/src/arch/io/event_watcher.hpp +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef ARCH_IO_EVENT_WATCHER_HPP_ -#define ARCH_IO_EVENT_WATCHER_HPP_ - -#ifdef _WIN32 - -#include "windows.hpp" -#include "concurrency/cond_var.hpp" -#include "concurrency/interruptor.hpp" -#include "arch/runtime/runtime_utils.hpp" -#include "arch/runtime/event_queue_types.hpp" -#include "errors.hpp" - -class windows_event_watcher_t; - -// An asynchronous operation -struct overlapped_operation_t { - explicit overlapped_operation_t(windows_event_watcher_t *); - - // The destructor will crash if the operation is incomplete. - ~overlapped_operation_t(); - - void reset() { - completed.reset(); - } - - // The operation was not queued or is dequeued. Mark it as - // completed and set the result. The IOCP event loop - // automatically calls set_result when dequeing an - // overlapped_operation_t. - void set_result(size_t nb_bytes_, DWORD error_); - - // The operation was queued. Abort it, wait for it to be dequeued - // and mark it as completed with an error. - void abort_op(); - - // The operation was not queued. Mark it as completed with an error. - void set_cancel(); - - // Wait for the operation to complete. - void wait_interruptible(const signal_t *interruptor) THROWS_ONLY(interrupted_exc_t); - void wait_abortable(const signal_t *aborter); - - OVERLAPPED overlapped; // Used by Windows to track the queued operation - windows_event_watcher_t *event_watcher; // event_watcher for the associated handle - size_t nb_bytes; // Number of bytes read or written - DWORD error; // Success indicator - - signal_t *get_completed_signal() { return &completed; } - -private: - cond_t completed; // Signaled when the operation completes or fails - - DISABLE_COPYING(overlapped_operation_t); -}; - -class windows_event_watcher_t { -public: - // Assign a handle to the thread's IOCP. Should only be called once per handle. - windows_event_watcher_t(fd_t handle, linux_event_callback_t *eh); - - // This destructor is a no-op: handle's cannot be removed from an IOCP - ~windows_event_watcher_t(); - - // TODO WINDOWS: - // After being rethreaded, the original thread still recieves completion events - // and it forwards them to the new thread. - void rethread(threadnum_t new_thread); - - void stop_watching_for_errors(); - void on_error(DWORD error); - threadnum_t current_thread() { return current_thread_; } - - const fd_t handle; - -private: - linux_event_callback_t *error_handler; - threadnum_t original_thread; - threadnum_t current_thread_; -}; - -typedef windows_event_watcher_t event_watcher_t; - -#else - -#include "arch/runtime/event_queue.hpp" -#include "utils.hpp" -#include "concurrency/signal.hpp" - -class linux_event_watcher_t : - public home_thread_mixin_debug_only_t, - private linux_event_callback_t -{ -public: - linux_event_watcher_t(fd_t f, linux_event_callback_t *eh); - ~linux_event_watcher_t(); - - /* To monitor for a specific event happening, instantiate `watch_t`. It will - get pulsed the first time that the given event arrives after you create the - `watch_t`. To wait for the event to happen again, destroy the first - `watch_t` and create another one. */ - struct watch_t : public signal_t { - watch_t(linux_event_watcher_t *p, int e); - ~watch_t(); - private: - friend class linux_event_watcher_t; - linux_event_watcher_t *parent; - int event; - }; - - bool is_watching(int event); - - // TODO: This is a complete hack, kill yourself out of shame for the human race. - void stop_watching_for_errors(); - -private: - fd_t fd; - linux_event_callback_t *error_handler; - - watch_t **get_watch_slot(int event); - watch_t *in_watcher; - watch_t *out_watcher; -#ifdef __linux - watch_t *rdhup_watcher; -#endif - - bool watching_for_errors; - - int old_mask; - bool old_watching_for_errors; - void remask(); - - void on_event(int event); - - DISABLE_COPYING(linux_event_watcher_t); -}; - -typedef linux_event_watcher_t event_watcher_t; - -#endif // _WIN32 - -#endif /* ARCH_IO_EVENT_WATCHER_HPP_ */ diff --git a/src/arch/io/io_utils.cc b/src/arch/io/io_utils.cc deleted file mode 100644 index caa883c3ec3..00000000000 --- a/src/arch/io/io_utils.cc +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "arch/io/io_utils.hpp" - -#ifdef _WIN32 -#include "windows.hpp" -#else -#include -#endif - -#include -#include -#include - -#include - -#include "logger.hpp" - -#ifndef _WIN32 -int _gettid() { - return syscall(SYS_gettid); -} -#endif - -fd_t scoped_fd_t::reset(fd_t f2) { - if (fd != INVALID_FD) { -#ifdef _WIN32 - BOOL res = CloseHandle(fd); - guarantee_winerr(res != 0, "CloseHandle failed"); -#else - int res; - do { - res = close(fd); - } while (res != 0 && get_errno() == EINTR); - guarantee_err(res == 0, "error returned by close(2)"); -#endif - } - fd = f2; - return f2; -} - -// This completely fills dest_vecs with data from source_vecs (starting at -// offset_into_source). Crashes if source_vecs is too small! -void fill_bufs_from_source(iovec *dest_vecs, const size_t dest_size, - iovec *source_vecs, const size_t source_size, - const size_t offset_into_source) { - - // dest_byte always less than dest_vecs[dest_buf].iov_len. Same is true of - // source. - size_t dest_buf = 0; - size_t dest_byte = 0; - size_t source_buf = 0; - size_t source_byte = 0; - - // Walk source forward to the offset. - for (size_t n = offset_into_source; ; ) { - guarantee(source_buf < source_size); - if (n < source_vecs[source_buf].iov_len) { - source_byte = n; - break; - } else { - n -= source_vecs[source_buf].iov_len; - ++source_buf; - } - } - - // Copy bytes until dest is filled. - while (dest_buf < dest_size) { - guarantee(source_buf < source_size); - size_t copy_size = std::min(dest_vecs[dest_buf].iov_len - dest_byte, - source_vecs[source_buf].iov_len - source_byte); - - memcpy(static_cast(dest_vecs[dest_buf].iov_base) + dest_byte, - static_cast(source_vecs[source_buf].iov_base) + source_byte, - copy_size); - - dest_byte += copy_size; - if (dest_byte == dest_vecs[dest_buf].iov_len) { - ++dest_buf; - dest_byte = 0; - } - - source_byte += copy_size; - if (source_byte == source_vecs[source_buf].iov_len) { - ++source_buf; - source_byte = 0; - } - } -} diff --git a/src/arch/io/io_utils.hpp b/src/arch/io/io_utils.hpp deleted file mode 100644 index 88e39687f9d..00000000000 --- a/src/arch/io/io_utils.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef ARCH_IO_IO_UTILS_HPP_ -#define ARCH_IO_IO_UTILS_HPP_ - -#include - -#include "arch/runtime/runtime_utils.hpp" - -struct iovec; - -// Thanks glibc for not providing a wrapper for this syscall :( -int _gettid(); - -/* scoped_fd_t is like scoped_ptr_t, but for a file descriptor */ -class scoped_fd_t { -public: - scoped_fd_t() : fd(INVALID_FD) { } - explicit scoped_fd_t(fd_t f) : fd(f) { } - scoped_fd_t(scoped_fd_t &&other) : fd(other.fd) { - other.fd = INVALID_FD; - } - - ~scoped_fd_t() { - reset(INVALID_FD); - } - - scoped_fd_t &operator=(scoped_fd_t &&other) { - scoped_fd_t tmp(std::move(other)); - swap(tmp); - return *this; - } - - fd_t reset(fd_t f2 = INVALID_FD); - - fd_t get() { - return fd; - } - - void swap(scoped_fd_t &other) { - fd_t tmp = fd; - fd = other.fd; - other.fd = tmp; - } - - MUST_USE fd_t release() { - fd_t f = fd; - fd = INVALID_FD; - return f; - } - -private: - fd_t fd; - DISABLE_COPYING(scoped_fd_t); -}; - -// This completely fills dest_vecs with data from source_vecs (starting at -// offset_into_source). Crashes if source_vecs is too small! -void fill_bufs_from_source(iovec *dest_vecs, const size_t dest_size, - iovec *source_vecs, const size_t source_size, - const size_t offset_into_source); - - -#endif /* ARCH_IO_IO_UTILS_HPP_ */ diff --git a/src/arch/io/network.cc b/src/arch/io/network.cc deleted file mode 100644 index de38a822695..00000000000 --- a/src/arch/io/network.cc +++ /dev/null @@ -1,1824 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. - -#include "arch/io/network.hpp" - -#include -#include -#include -#include - -#ifdef _WIN32 -#include "windows.hpp" -#include // NOLINT -#include // NOLINT -#else -#include -#include -#include -#include -#include -#include -#endif - -#include "arch/runtime/runtime.hpp" -#include "arch/runtime/thread_pool.hpp" -#include "arch/timing.hpp" -#include "arch/types.hpp" -#include "concurrency/auto_drainer.hpp" -#include "concurrency/exponential_backoff.hpp" -#include "concurrency/wait_any.hpp" -#include "containers/printf_buffer.hpp" -#include "logger.hpp" -#include "perfmon/perfmon.hpp" -#include "errors.hpp" - -#ifdef _WIN32 -#include "concurrency/pmap.hpp" -#endif - -#ifdef TRACE_WINSOCK -#define winsock_debugf(...) debugf("winsock: " __VA_ARGS__) -#else -#define winsock_debugf(...) ((void)0) -#endif - -#ifdef _WIN32 -LPFN_CONNECTEX get_ConnectEx(fd_t s) { - static THREAD_LOCAL LPFN_CONNECTEX ConnectEx = nullptr; - if (ConnectEx == nullptr) { - DWORD size = 0; - GUID id = WSAID_CONNECTEX; - DWORD res = WSAIoctl(fd_to_socket(s), SIO_GET_EXTENSION_FUNCTION_POINTER, - &id, sizeof(id), &ConnectEx, sizeof(ConnectEx), - &size, nullptr, nullptr); - guarantee_winerr(res == 0, "WSAIoctl failed"); - } - return ConnectEx; -} - -LPFN_ACCEPTEX get_AcceptEx(fd_t s) { - static THREAD_LOCAL LPFN_ACCEPTEX AcceptEx = nullptr; - if (AcceptEx == nullptr) { - DWORD size = 0; - GUID id = WSAID_ACCEPTEX; - DWORD res = WSAIoctl(fd_to_socket(s), SIO_GET_EXTENSION_FUNCTION_POINTER, - &id, sizeof(id), &AcceptEx, sizeof(AcceptEx), - &size, nullptr, nullptr); - guarantee_winerr(res == 0, "WSAIoctl failed"); - } - return AcceptEx; -} -#endif - -void async_connect(fd_t socket, sockaddr *sa, size_t sa_len, - event_watcher_t *event_watcher, signal_t *interuptor) { -#ifdef _WIN32 - overlapped_operation_t op(event_watcher); - winsock_debugf("connecting socket %x\n", socket); - DWORD bytes_sent; - BOOL res = get_ConnectEx(socket)(fd_to_socket(socket), sa, sa_len, nullptr, 0, &bytes_sent, &op.overlapped); - DWORD error = GetLastError(); - if (!res && error != ERROR_IO_PENDING) { - op.set_cancel(); - winsock_debugf("connect failed: %s", winerr_string(error).c_str()); - throw linux_tcp_conn_t::connect_failed_exc_t(EIO); - } - winsock_debugf("waiting for connection on %x\n", socket); - op.wait_interruptible(interuptor); - if (op.error != NO_ERROR) { - winsock_debugf("ConnectEx failed: %s", winerr_string(op.error).c_str()); - throw linux_tcp_conn_t::connect_failed_exc_t(EIO); - } - winsock_debugf("connected %x\n", socket); -#else - int res; - do { - res = connect(socket, sa, sa_len); - } while (res == -1 && get_errno() == EINTR); - - if (res != 0) { - if (get_errno() == EINPROGRESS) { - linux_event_watcher_t::watch_t watch(event_watcher, poll_event_out); - wait_interruptible(&watch, interuptor); - int error; - socklen_t error_size = sizeof(error); - int getsockoptres = getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, &error_size); - if (getsockoptres != 0) { - throw linux_tcp_conn_t::connect_failed_exc_t(error); - } - if (error != 0) { - throw linux_tcp_conn_t::connect_failed_exc_t(error); - } - } else { - throw linux_tcp_conn_t::connect_failed_exc_t(get_errno()); - } - } -#endif -} - -void connect_ipv4_internal(fd_t socket, int local_port, const in_addr &addr, int port, event_watcher_t *event_watcher, signal_t *interuptor) { - struct sockaddr_in sa; - socklen_t sa_len(sizeof(sa)); - memset(&sa, 0, sa_len); - sa.sin_family = AF_INET; - -#ifdef _WIN32 - sa.sin_port = htons(local_port); - sa.sin_addr.s_addr = INADDR_ANY; - // TODO WINDOWS: can bind block when called like this? - winsock_debugf("binding socket for connect %x\n", socket); - if (bind(fd_to_socket(socket), reinterpret_cast(&sa), sa_len) != 0) { - logWRN("Failed to bind to local port %d: %s", local_port, winerr_string(GetLastError()).c_str()); - } -#else - if (local_port != 0) { - sa.sin_port = htons(local_port); - sa.sin_addr.s_addr = INADDR_ANY; - if (bind(socket, reinterpret_cast(&sa), sa_len) != 0) { - logWRN("Failed to bind to local port %d: %s", local_port, errno_string(get_errno()).c_str()); - } - } -#endif - - sa.sin_port = htons(port); - sa.sin_addr = addr; - - async_connect(socket, reinterpret_cast(&sa), sa_len, event_watcher, interuptor); -} - -void connect_ipv6_internal(fd_t socket, int local_port, const in6_addr &addr, int port, uint32_t scope_id, event_watcher_t *event_watcher, signal_t *interuptor) { - struct sockaddr_in6 sa; - socklen_t sa_len(sizeof(sa)); - memset(&sa, 0, sa_len); - sa.sin6_family = AF_INET6; - -#ifdef _WIN32 - sa.sin6_port = htons(local_port); - sa.sin6_addr = in6addr_any; - winsock_debugf("binding socket for connect %x\n", socket); - if (bind(fd_to_socket(socket), reinterpret_cast(&sa), sa_len) != 0) { - logWRN("Failed to bind to local port %d: %s", local_port, winerr_string(GetLastError()).c_str()); - } -#else - if (local_port != 0) { - sa.sin6_port = htons(local_port); - sa.sin6_addr = in6addr_any; - if (bind(socket, reinterpret_cast(&sa), sa_len) != 0) { - logWRN("Failed to bind to local port %d: %s", local_port, errno_string(get_errno()).c_str()); - } - } -#endif - - sa.sin6_port = htons(port); - sa.sin6_addr = addr; - sa.sin6_scope_id = scope_id; - - async_connect(socket, reinterpret_cast(&sa), sa_len, event_watcher, interuptor); -} - -fd_t create_socket_wrapper(int address_family) { -#ifdef _WIN32 - fd_t res = socket_to_fd(socket(address_family, SOCK_STREAM, IPPROTO_TCP)); - winsock_debugf("new socket %x\n", res); - if (res == INVALID_FD) { - DWORD err = GetLastError(); - logERR("Failed to create socket: %s", winerr_string(err).c_str()); - throw linux_tcp_conn_t::connect_failed_exc_t(EIO); - } - return res; -#else - fd_t res = socket(address_family, SOCK_STREAM, 0); - if (res == INVALID_FD) { - // Let the user know something is wrong - except in the case where - // TCP doesn't support AF_INET6, which may be fairly common and spammy - if (get_errno() != EAFNOSUPPORT || address_family == AF_INET) { - logERR("Failed to create socket: %s", errno_string(get_errno()).c_str()); - } - throw linux_tcp_conn_t::connect_failed_exc_t(get_errno()); - } -#endif - return res; -} - -// Network connection object -linux_tcp_conn_t::linux_tcp_conn_t(const ip_address_t &peer, - int port, - signal_t *interruptor, - int local_port) THROWS_ONLY(connect_failed_exc_t, interrupted_exc_t) : - write_perfmon(nullptr), - sock(create_socket_wrapper(peer.get_address_family())), - event_watcher(new event_watcher_t(sock.get(), this)), - read_in_progress(false), write_in_progress(false), - read_buffer(IO_BUFFER_SIZE), - write_handler(this), - write_queue_limiter(WRITE_QUEUE_MAX_SIZE), - write_coro_pool(1, &write_queue, &write_handler), - current_write_buffer(get_write_buffer()), - drainer(new auto_drainer_t) { - -#ifndef _WIN32 - guarantee_err(fcntl(sock.get(), F_SETFL, O_NONBLOCK) == 0, "Could not make socket non-blocking"); -#endif - - if (local_port != 0) { - // Set the socket to reusable so we don't block out other sockets from this port - int reuse = 1; - if (setsockopt(fd_to_socket(sock.get()), SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&reuse), sizeof(reuse)) != 0) - logWRN("Failed to set socket reuse to true: %s", errno_string(get_errno()).c_str()); - } - { - // Disable Nagle algorithm just as in the listener case - int sockoptval = 1; - int res = setsockopt(fd_to_socket(sock.get()), IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&sockoptval), sizeof(sockoptval)); - if (res == -1 && get_errno() == EINVAL) { - // On OS X, EINVAL means the socket was closed. - throw connect_failed_exc_t(get_errno()); - } - guarantee_err(res != -1, "Could not set TCP_NODELAY option"); - } - - if (peer.is_ipv4()) { - connect_ipv4_internal(sock.get(), local_port, peer.get_ipv4_addr(), port, event_watcher.get(), interruptor); - } else { - connect_ipv6_internal(sock.get(), local_port, peer.get_ipv6_addr(), port, - peer.get_ipv6_scope_id(), event_watcher.get(), interruptor); - } -} - -linux_tcp_conn_t::linux_tcp_conn_t(fd_t s) : - write_perfmon(NULL), - sock(s), - event_watcher(new event_watcher_t(sock.get(), this)), - read_in_progress(false), write_in_progress(false), - read_buffer(IO_BUFFER_SIZE), - write_handler(this), - write_queue_limiter(WRITE_QUEUE_MAX_SIZE), - write_coro_pool(1, &write_queue, &write_handler), - current_write_buffer(get_write_buffer()), - drainer(new auto_drainer_t) { - rassert(sock.get() != INVALID_FD); - -#ifndef _WIN32 - int res = fcntl(sock.get(), F_SETFL, O_NONBLOCK); - guarantee_err(res == 0, "Could not make socket non-blocking"); -#endif -} - -void linux_tcp_conn_t::enable_keepalive() THROWS_ONLY(tcp_conn_write_closed_exc_t) { - int optval = 1; -#ifdef _WIN32 - int res = setsockopt(fd_to_socket(sock.get()), SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast(&optval), sizeof(optval)); -#else - int res = setsockopt(sock.get(), SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)); -#endif - if (res == -1 && get_errno() == EINVAL) { - // On OS X, EINVAL means the socket was closed. - throw tcp_conn_write_closed_exc_t(); - } - guarantee_err(res != -1, "Could not set SO_KEEPALIVE option."); -} - -linux_tcp_conn_t::write_buffer_t * linux_tcp_conn_t::get_write_buffer() { - write_buffer_t *buffer; - - if (unused_write_buffers.empty()) { - buffer = new write_buffer_t; - } else { - buffer = unused_write_buffers.head(); - unused_write_buffers.pop_front(); - } - buffer->size = 0; - return buffer; -} - -linux_tcp_conn_t::write_queue_op_t * linux_tcp_conn_t::get_write_queue_op() { - write_queue_op_t *op; - - if (unused_write_queue_ops.empty()) { - op = new write_queue_op_t; - } else { - op = unused_write_queue_ops.head(); - unused_write_queue_ops.pop_front(); - } - return op; -} - -void linux_tcp_conn_t::release_write_buffer(write_buffer_t *buffer) { - unused_write_buffers.push_front(buffer); -} - -void linux_tcp_conn_t::release_write_queue_op(write_queue_op_t *op) { - op->keepalive = auto_drainer_t::lock_t(); - unused_write_queue_ops.push_front(op); -} - -size_t linux_tcp_conn_t::read_internal(void *buffer, size_t size) THROWS_ONLY(tcp_conn_read_closed_exc_t) { - assert_thread(); - rassert(!read_closed.is_pulsed()); - -#ifdef _WIN32 - overlapped_operation_t op(event_watcher.get()); - winsock_debugf("request read %d bytes on %x\n", size, sock.get()); - // TODO WINDOWS: WSARecv may be more efficient - BOOL res = ReadFile(sock.get(), buffer, size, nullptr, &op.overlapped); - DWORD error = GetLastError(); - if (res || error == ERROR_IO_PENDING) { - op.wait_abortable(&read_closed); - error = op.error; - } else { - op.set_result(0, error); - } - if (read_closed.is_pulsed()) { - throw tcp_conn_read_closed_exc_t(); - } - if (op.nb_bytes == 0 || error == ERROR_HANDLE_EOF) { - on_shutdown_read(); - throw tcp_conn_read_closed_exc_t(); - } else if (error != NO_ERROR) { - logERR("Could not read from socket: %s", winerr_string(error).c_str()); - on_shutdown_read(); - throw tcp_conn_read_closed_exc_t(); - } else { - winsock_debugf("read complete, %d/%d on %x\n", op.nb_bytes, size, sock.get()); - return op.nb_bytes; - } -#else - while (true) { - ssize_t res = ::read(sock.get(), buffer, size); - - if (res == -1 && (get_errno() == EAGAIN || get_errno() == EWOULDBLOCK)) { - /* There's no data available right now, so we must wait for a notification from the - epoll queue, or for an order to shut down. */ - - linux_event_watcher_t::watch_t watch(event_watcher.get(), poll_event_in); - wait_any_t waiter(&watch, &read_closed); - waiter.wait_lazily_unordered(); - - if (read_closed.is_pulsed()) { - /* We were closed for whatever reason. Something else has already called - on_shutdown_read(). In fact, we were probably signalled by on_shutdown_read(). */ - throw tcp_conn_read_closed_exc_t(); - } - - /* Go around the loop and try to read again */ - - } else if (res == 0 || (res == -1 && (get_errno() == ECONNRESET || get_errno() == ENOTCONN))) { - /* We were closed. This is the first notification that the kernel has given us, so we - must call on_shutdown_read(). */ - on_shutdown_read(); - throw tcp_conn_read_closed_exc_t(); - - } else if (res == -1) { - /* Unknown error. This is not expected, but it will probably happen sometime so we - shouldn't crash. */ - logERR("Could not read from socket: %s", errno_string(get_errno()).c_str()); - on_shutdown_read(); - throw tcp_conn_read_closed_exc_t(); - - } else { - /* We read some data, whooo */ - return res; - } - } -#endif -} - -size_t linux_tcp_conn_t::read_some(void *buf, size_t size, signal_t *closer) THROWS_ONLY(tcp_conn_read_closed_exc_t) { - rassert(size > 0); - read_op_wrapper_t sentry(this, closer); - - if (read_buffer.size()) { - /* Return the data from the peek buffer */ - size_t read_buffer_bytes = std::min(read_buffer.size(), size); - memcpy(buf, read_buffer.data(), read_buffer_bytes); - read_buffer.erase_front(read_buffer_bytes); - return read_buffer_bytes; - } else { - /* Go to the kernel _once_. */ - return read_internal(buf, size); - } -} - -void linux_tcp_conn_t::read(void *buf, size_t size, signal_t *closer) THROWS_ONLY(tcp_conn_read_closed_exc_t) { - read_op_wrapper_t sentry(this, closer); - - /* First, consume any data in the peek buffer */ - int read_buffer_bytes = std::min(read_buffer.size(), size); - memcpy(buf, read_buffer.data(), read_buffer_bytes); - read_buffer.erase_front(read_buffer_bytes); - buf = reinterpret_cast(reinterpret_cast(buf) + read_buffer_bytes); - size -= read_buffer_bytes; - - /* Now go to the kernel for any more data that we need */ - while (size > 0) { - size_t delta = read_internal(buf, size); - rassert(delta <= size); - buf = reinterpret_cast(reinterpret_cast(buf) + delta); - size -= delta; - } -} - -void linux_tcp_conn_t::read_buffered(void *buf, size_t size, signal_t *closer) - THROWS_ONLY(tcp_conn_read_closed_exc_t) { - while (size > 0) { - const_charslice read_data = peek(); - if (read_data.end == read_data.beg) { - // We didn't get anything from the read buffer. Get some data from - // the underlying socket... - // For large reads, we read directly into buf to avoid an additional copy - // and additional round trips. - // For smaller reads, we use `read_more_buffered` to read into the - // connection's internal buffer and then copy out whatever we can use - // to satisfy the current request. - if (size >= IO_BUFFER_SIZE) { - return read(buf, size, closer); - } else { - read_more_buffered(closer); - read_data = peek(); - } - } - size_t num_read = read_data.end - read_data.beg; - if (num_read > size) { - num_read = size; - } - rassert(num_read > 0); - memcpy(buf, read_data.beg, num_read); - // Remove the consumed data from the read buffer - pop(num_read, closer); - - size -= num_read; - buf = static_cast(buf) + num_read; - } -} - -void linux_tcp_conn_t::read_more_buffered(signal_t *closer) THROWS_ONLY(tcp_conn_read_closed_exc_t) { - read_op_wrapper_t sentry(this, closer); - - size_t old_size = read_buffer.size(); - read_buffer.resize(old_size + IO_BUFFER_SIZE); - size_t delta = read_internal(read_buffer.data() + old_size, IO_BUFFER_SIZE); - - read_buffer.resize(old_size + delta); -} - -const_charslice linux_tcp_conn_t::peek() const THROWS_ONLY(tcp_conn_read_closed_exc_t) { - assert_thread(); - rassert(!read_in_progress); // Is there a read already in progress? - if (read_closed.is_pulsed()) { - throw tcp_conn_read_closed_exc_t(); - } - - return const_charslice(read_buffer.data(), read_buffer.data() + read_buffer.size()); -} - -const_charslice linux_tcp_conn_t::peek(size_t size, signal_t *closer) THROWS_ONLY(tcp_conn_read_closed_exc_t) { - while (read_buffer.size() < size) { - read_more_buffered(closer); - } - return const_charslice(read_buffer.data(), read_buffer.data() + size); -} - -void linux_tcp_conn_t::pop(size_t len, signal_t *closer) THROWS_ONLY(tcp_conn_read_closed_exc_t) { - assert_thread(); - rassert(!read_in_progress); - if (read_closed.is_pulsed()) { - throw tcp_conn_read_closed_exc_t(); - } - - peek(len, closer); - read_buffer.erase_front(len); -} - -void linux_tcp_conn_t::shutdown_read() { - assert_thread(); -#ifdef _WIN32 - int res = ::shutdown(fd_to_socket(sock.get()), SD_RECEIVE); - if (res != 0 && GetLastError() != WSAENOTCONN) { - logERR("Could not shutdown socket for reading: %s", winerr_string(GetLastError()).c_str()); - } -#else - int res = ::shutdown(sock.get(), SHUT_RD); - if (res != 0 && get_errno() != ENOTCONN) { - logERR("Could not shutdown socket for reading: %s", errno_string(get_errno()).c_str()); - } -#endif - on_shutdown_read(); -} - -void linux_tcp_conn_t::on_shutdown_read() { - assert_thread(); - rassert(!read_closed.is_pulsed()); - read_closed.pulse(); -} - -bool linux_tcp_conn_t::is_read_open() const { - assert_thread(); - return !read_closed.is_pulsed(); -} - -linux_tcp_conn_t::write_handler_t::write_handler_t(linux_tcp_conn_t *_parent) : - parent(_parent) -{ } - -void linux_tcp_conn_t::write_handler_t::coro_pool_callback(write_queue_op_t *operation, UNUSED signal_t *interruptor) { - if (operation->buffer != nullptr) { - parent->perform_write(operation->buffer, operation->size); - if (operation->dealloc != nullptr) { - parent->release_write_buffer(operation->dealloc); - parent->write_queue_limiter.unlock(operation->size); - } - } - - if (operation->cond != nullptr) { - operation->cond->pulse(); - } - if (operation->dealloc != nullptr) { - parent->release_write_queue_op(operation); - } -} - -void linux_tcp_conn_t::internal_flush_write_buffer() { - write_queue_op_t *op = get_write_queue_op(); - assert_thread(); - rassert(write_in_progress); - - /* Swap in a new write buffer, and set up the old write buffer to be - released once the write is over. */ - op->buffer = current_write_buffer->buffer; - op->size = current_write_buffer->size; - op->dealloc = current_write_buffer.release(); - op->cond = nullptr; - op->keepalive = auto_drainer_t::lock_t(drainer.get()); - current_write_buffer.init(get_write_buffer()); - - /* Acquire the write semaphore so the write queue doesn't get too long - to be released once the write is completed by the coroutine pool */ - rassert(op->size <= WRITE_CHUNK_SIZE); - rassert(WRITE_CHUNK_SIZE < WRITE_QUEUE_MAX_SIZE); - write_queue_limiter.co_lock(op->size); - - write_queue.push(op); -} - -void linux_tcp_conn_t::perform_write(const void *buf, size_t size) { - assert_thread(); - - if (write_closed.is_pulsed()) { - /* The write end of the connection was closed, but there are still - operations in the write queue; we are one of those operations. Just - don't do anything. */ - return; - } - -#ifdef _WIN32 - overlapped_operation_t op(event_watcher.get()); - WSABUF wsabuf; - wsabuf.len = size; - wsabuf.buf = const_cast(reinterpret_cast(buf)); - DWORD flags = 0; - winsock_debugf("write on %x\n", sock.get()); - int res = WSASend(fd_to_socket(sock.get()), &wsabuf, 1, nullptr, flags, &op.overlapped, nullptr); - DWORD error = GetLastError(); - if (res == 0 || error == ERROR_IO_PENDING) { - op.wait_abortable(&write_closed); - error = op.error; - } else { - op.set_result(0, error); - } - if (write_closed.is_pulsed()) { - return; - } - if (error != NO_ERROR) { - logERR("Could not write to socket %x: %s", sock.get(), winerr_string(error).c_str()); - on_shutdown_write(); - } else if (op.nb_bytes == 0) { - on_shutdown_write(); - } else { - if (write_perfmon) write_perfmon->record(op.nb_bytes); - rassert(op.nb_bytes == size); // TODO WINDOWS: does windows guarantee this? - } -#else - while (size > 0) { - ssize_t res = ::write(sock.get(), buf, size); - - if (res == -1 && (get_errno() == EAGAIN || get_errno() == EWOULDBLOCK)) { - /* Wait for a notification from the event queue, or for an order to - shut down */ - linux_event_watcher_t::watch_t watch(event_watcher.get(), poll_event_out); - wait_any_t waiter(&watch, &write_closed); - waiter.wait_lazily_unordered(); - - if (write_closed.is_pulsed()) { - /* We were closed for whatever reason. Whatever signalled us has already called - on_shutdown_write(). */ - break; - } - - /* Go around the loop and try to write again */ - - } else if (res == -1 && (get_errno() == EPIPE || get_errno() == ENOTCONN || get_errno() == EHOSTUNREACH || - get_errno() == ENETDOWN || get_errno() == EHOSTDOWN || get_errno() == ECONNRESET)) { - /* These errors are expected to happen at some point in practice */ - on_shutdown_write(); - break; - - } else if (res == -1) { - /* In theory this should never happen, but it probably will. So we write a log message - and then shut down normally. */ - logERR("Could not write to socket: %s", errno_string(get_errno()).c_str()); - on_shutdown_write(); - break; - - } else if (res == 0) { - /* This should never happen either, but it's better to write an error message than to - crash completely. */ - logERR("Didn't expect write() to return 0."); - on_shutdown_write(); - break; - - } else { - rassert(res <= static_cast(size)); - buf = reinterpret_cast(reinterpret_cast(buf) + res); - size -= res; - if (write_perfmon) { - write_perfmon->record(res); - } - } - } -#endif -} - -void linux_tcp_conn_t::write(const void *buf, size_t size, signal_t *closer) THROWS_ONLY(tcp_conn_write_closed_exc_t) { - write_op_wrapper_t sentry(this, closer); - - write_queue_op_t op; - cond_t to_signal_when_done; - - /* Flush out any data that's been buffered, so that things don't get out of order */ - if (current_write_buffer->size > 0) { - internal_flush_write_buffer(); - } - - /* Don't bother acquiring the write semaphore because we're going to block - until the write is done anyway */ - - /* Enqueue the write so it will happen eventually */ - op.buffer = buf; - op.size = size; - op.dealloc = nullptr; - op.cond = &to_signal_when_done; - write_queue.push(&op); - - /* Wait for the write to be done. If the write half of the network connection - is closed before or during our write, then `perform_write()` will turn into a - no-op, so the cond will still get pulsed. */ - to_signal_when_done.wait(); - - if (write_closed.is_pulsed()) { - throw tcp_conn_write_closed_exc_t(); - } -} - -void linux_tcp_conn_t::write_buffered(const void *vbuf, size_t size, signal_t *closer) THROWS_ONLY(tcp_conn_write_closed_exc_t) { - write_op_wrapper_t sentry(this, closer); - - /* Convert to `char` for ease of pointer arithmetic */ - const char *buf = reinterpret_cast(vbuf); - - while (size > 0) { - /* Stop putting more things on the write queue if it's already closed. */ - if (write_closed.is_pulsed()) { - throw tcp_conn_write_closed_exc_t(); - } - - /* Insert the largest chunk that fits in this block */ - size_t chunk = std::min(size, WRITE_CHUNK_SIZE - current_write_buffer->size); - - memcpy(current_write_buffer->buffer + current_write_buffer->size, buf, chunk); - current_write_buffer->size += chunk; - - rassert(current_write_buffer->size <= WRITE_CHUNK_SIZE); - if (current_write_buffer->size == WRITE_CHUNK_SIZE) { - internal_flush_write_buffer(); - } - - buf += chunk; - size -= chunk; - } - - if (write_closed.is_pulsed()) { - throw tcp_conn_write_closed_exc_t(); - } -} - -void linux_tcp_conn_t::writef(signal_t *closer, const char *format, ...) THROWS_ONLY(tcp_conn_write_closed_exc_t) { - va_list ap; - va_start(ap, format); - - printf_buffer_t b(ap, format); - write(b.data(), b.size(), closer); - - va_end(ap); -} - -void linux_tcp_conn_t::flush_buffer(signal_t *closer) THROWS_ONLY(tcp_conn_write_closed_exc_t) { - write_op_wrapper_t sentry(this, closer); - - /* Flush the write buffer; it might be half-full. */ - if (current_write_buffer->size > 0) { - internal_flush_write_buffer(); - } - - /* Wait until we know that the write buffer has gone out over the network. - If the write half of the connection is closed, then the call to - `perform_write()` that `internal_flush_write_buffer()` will turn into a no-op, - but the queue will continue to be pumped and so our cond will still get - pulsed. */ - write_queue_op_t op; - cond_t to_signal_when_done; - op.buffer = nullptr; - op.dealloc = nullptr; - op.cond = &to_signal_when_done; - write_queue.push(&op); - to_signal_when_done.wait(); - - if (write_closed.is_pulsed()) { - throw tcp_conn_write_closed_exc_t(); - } -} - -void linux_tcp_conn_t::flush_buffer_eventually(signal_t *closer) THROWS_ONLY(tcp_conn_write_closed_exc_t) { - write_op_wrapper_t sentry(this, closer); - - /* Flush the write buffer; it might be half-full. */ - if (current_write_buffer->size > 0) { - internal_flush_write_buffer(); - } - - if (write_closed.is_pulsed()) { - throw tcp_conn_write_closed_exc_t(); - } -} - -void linux_tcp_conn_t::shutdown_write() { - assert_thread(); - -#ifdef _WIN32 - int res = ::shutdown(fd_to_socket(sock.get()), SD_SEND); - if (res != 0 && GetLastError() != WSAENOTCONN) { - logERR("Could not shutdown socket for writing: %s", winerr_string(GetLastError()).c_str()); - } -#else - int res = ::shutdown(sock.get(), SHUT_WR); - if (res != 0 && get_errno() != ENOTCONN) { - logERR("Could not shutdown socket for writing: %s", errno_string(get_errno()).c_str()); - } -#endif - - on_shutdown_write(); -} - -void linux_tcp_conn_t::on_shutdown_write() { - assert_thread(); - rassert(!write_closed.is_pulsed()); - write_closed.pulse(); - - /* We don't flush out the write queue or stop the write coro pool explicitly. - But by pulsing `write_closed`, we turn all `perform_write()` operations into - no-ops, so in practice the write queue empties. */ -} - -bool linux_tcp_conn_t::is_write_open() const { - assert_thread(); - return !write_closed.is_pulsed(); -} - -linux_tcp_conn_t::~linux_tcp_conn_t() THROWS_NOTHING { - assert_thread(); - - // Tell the readers and writers to stop. The auto drainer will - // wait for them to stop. - if (is_read_open()) { - shutdown_read(); - } - if (is_write_open()) { - shutdown_write(); - } -} - -void linux_tcp_conn_t::rethread(threadnum_t new_thread) { - if (home_thread() == get_thread_id() && new_thread == INVALID_THREAD) { - rassert(!read_in_progress); - rassert(!write_in_progress); - rassert(event_watcher.has()); -#ifdef _WIN32 - event_watcher->rethread(new_thread); -#else - event_watcher.reset(); -#endif - - } else if (home_thread() == INVALID_THREAD && new_thread == get_thread_id()) { -#ifdef _WIN32 - event_watcher->rethread(new_thread); -#else - rassert(!event_watcher.has()); - event_watcher.init(new event_watcher_t(sock.get(), this)); -#endif - - } else { - crash("linux_tcp_conn_t can be rethread()ed from no thread to the current thread or " - "from the current thread to no thread, but no other combination is legal. The " - "current thread is %" PRIi32 "; the old thread is %" PRIi32 "; the new thread " - "is %" PRIi32 ".\n", - get_thread_id().threadnum, home_thread().threadnum, new_thread.threadnum); - } - - real_home_thread = new_thread; - - read_closed.rethread(new_thread); - write_closed.rethread(new_thread); - write_coro_pool.rethread(new_thread); - - if (drainer.has()) { - drainer->rethread(new_thread); - } -} - -bool linux_tcp_conn_t::getpeername(ip_and_port_t *ip_and_port) { - struct sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); - - int res = ::getpeername(fd_to_socket(sock.get()), reinterpret_cast(&addr), &addr_len); - if (res == 0) { - *ip_and_port = ip_and_port_t(reinterpret_cast(&addr)); - return true; - } - - return false; -} - -void linux_tcp_conn_t::on_event(int /* events */) { - assert_thread(); - - /* This is called by linux_event_watcher_t when error events occur. Ordinary - poll_event_in/poll_event_out events are not sent through this function. */ - - if (is_write_open()) { - shutdown_write(); - } - - if (is_read_open()) { - shutdown_read(); - } - - event_watcher->stop_watching_for_errors(); -} - -#ifdef ENABLE_TLS -tls_conn_wrapper_t::tls_conn_wrapper_t(SSL_CTX *tls_ctx) - THROWS_ONLY(crypto::openssl_error_t) { - ERR_clear_error(); - - conn = SSL_new(tls_ctx); - if (nullptr == conn) { - throw crypto::openssl_error_t(ERR_get_error()); - } - - // Add support for partial writes. - SSL_set_mode(conn, SSL_MODE_ENABLE_PARTIAL_WRITE); -} - -tls_conn_wrapper_t::~tls_conn_wrapper_t() { - SSL_free(conn); -} - -// Set the underlying IO. -void tls_conn_wrapper_t::set_fd(fd_t sock) - THROWS_ONLY(crypto::openssl_error_t) { - if (0 == SSL_set_fd(conn, sock)) { - throw crypto::openssl_error_t(ERR_get_error()); - } -} - -/* This is the client version of the constructor. The base class constructor -will establish a TCP connection to the peer at the given host:port and then we -wrap the tcp connection in TLS using the configuration in the given tls_ctx. */ -linux_secure_tcp_conn_t::linux_secure_tcp_conn_t( - SSL_CTX *tls_ctx, const ip_address_t &host, int port, - signal_t *interruptor, int local_port) - THROWS_ONLY(connect_failed_exc_t, crypto::openssl_error_t, interrupted_exc_t) : - linux_tcp_conn_t(host, port, interruptor, local_port), - conn(tls_ctx) { - - conn.set_fd(sock.get()); - SSL_set_connect_state(conn.get()); - perform_handshake(interruptor); -} - -/* This is the server version of the constructor */ -linux_secure_tcp_conn_t::linux_secure_tcp_conn_t( - SSL_CTX *tls_ctx, fd_t _sock, signal_t *interruptor) - THROWS_ONLY(crypto::openssl_error_t, interrupted_exc_t) : - linux_tcp_conn_t(_sock), - conn(tls_ctx) { - - conn.set_fd(sock.get()); - SSL_set_accept_state(conn.get()); - perform_handshake(interruptor); -} - -linux_secure_tcp_conn_t::~linux_secure_tcp_conn_t() THROWS_NOTHING { - assert_thread(); - - if (is_open()) shutdown(); -} - -void linux_secure_tcp_conn_t::rethread(threadnum_t thread) { - closed.rethread(thread); - - linux_tcp_conn_t::rethread(thread); -} - -void linux_secure_tcp_conn_t::perform_handshake(signal_t *interruptor) - THROWS_ONLY(crypto::openssl_error_t, interrupted_exc_t) { - // Perform TLS handshake. - while (true) { - ERR_clear_error(); - int ret = SSL_do_handshake(conn.get()); - - if (ret > 0) { - return; // Successful TLS handshake. - } - - if (ret == 0) { - // The handshake failed but the connection shut down cleanly. - throw crypto::openssl_error_t(ERR_get_error()); - } - - switch (SSL_get_error(conn.get(), ret)) { - case SSL_ERROR_WANT_READ: - /* The handshake needs to read data, but the underlying I/O has no data - ready to read. Wait for it to be ready or for an interrupt signal. */ - { - linux_event_watcher_t::watch_t watch(get_event_watcher(), poll_event_in); - wait_interruptible(&watch, interruptor); - } - break; - case SSL_ERROR_WANT_WRITE: - /* The handshake needs to write data, but the underlying I/O is not ready - to write. Wait for it to be ready or for an interrupt signal. */ - { - linux_event_watcher_t::watch_t watch(get_event_watcher(), poll_event_out); - wait_interruptible(&watch, interruptor); - } - break; - default: - // Some other error with the underlying I/O. - throw crypto::openssl_error_t(ERR_get_error()); - } - - if (interruptor->is_pulsed()) { - // The handshake cannot continue because we need to shutdown now. - throw interrupted_exc_t(); - } - - /* Go around the loop and try to complete the handshake. */ - } -} - -size_t linux_secure_tcp_conn_t::read_internal(void *buffer, size_t size) - THROWS_ONLY(tcp_conn_read_closed_exc_t) { - assert_thread(); - rassert(!closed.is_pulsed()); - - while(true) { - ERR_clear_error(); - - int ret = SSL_read(conn.get(), buffer, size); - - if (ret > 0) { - return ret; // Operation successful, returns number of bytes read. - } - - switch (SSL_get_error(conn.get(), ret)) { - case SSL_ERROR_ZERO_RETURN: - // Indicates that the peer has sent the "close notify" alert. The - // shutdown state is currently SSL_RECEIVED_SHUTDOWN. We must now - // send our "close notify" alert. - shutdown(); - throw tcp_conn_read_closed_exc_t(); - case SSL_ERROR_WANT_READ: - /* The underlying I/O has no data ready to read. Wait for it to be - ready or for someone to send a close signal. */ - { - linux_event_watcher_t::watch_t watch( - get_event_watcher(), poll_event_in); - wait_any_t waiter(&watch, &closed); - waiter.wait_lazily_unordered(); - } - break; - case SSL_ERROR_WANT_WRITE: - /* Though we are reading, a TLS renegotiation may occur at any time - requiring a write. Wait for the underyling I/O to be ready for a - write, or for someone to send a close signal. */ - { - linux_event_watcher_t::watch_t watch( - get_event_watcher(), poll_event_out); - wait_any_t waiter(&watch, &closed); - waiter.wait_lazily_unordered(); - } - break; - default: - // Some other error. Assume that the connection is unusable. - shutdown_socket(); - throw tcp_conn_read_closed_exc_t(); - } - - if (closed.is_pulsed()) { - /* We were closed for whatever reason. Whatever signalled us has - already called shutdown_socket(). */ - throw tcp_conn_read_closed_exc_t(); - } - - /* Go around the loop and try to read again */ - } -} - -void linux_secure_tcp_conn_t::perform_write(const void *buffer, size_t size) { - assert_thread(); - - if (closed.is_pulsed()) { - /* The connection was closed, but there are still operations in the - write queue; we are one of those operations. Just don't do anything. */ - return; - } - - // Loop for retrying if the underlying socket would block and to retry on - // partial writes. - while (size > 0) { - ERR_clear_error(); - - int ret = SSL_write(conn.get(), buffer, size); - - if (ret > 0) { - // Operation successful, returns number of bytes written. - rassert(static_cast(ret) <= size); - size -= ret; - - // Slide down the buffer. - buffer = reinterpret_cast( - reinterpret_cast(buffer) + ret); - - if (write_perfmon) write_perfmon->record(ret); - - // Go around the loop again if there is more data to write. - continue; - } - - switch (SSL_get_error(conn.get(), ret)) { - case SSL_ERROR_ZERO_RETURN: - // Indicates that the peer has sent the "close notify" alert. The - // shutdown state is currently SSL_RECEIVED_SHUTDOWN. We must now - // send our "close notify" alert. - shutdown(); - return; - case SSL_ERROR_WANT_READ: - /* Though we are writing, a TLS renegotiation may occur at any time - requiring a read. Wait for the underyling I/O to be ready for a - read, or for someone to send a close signal. */ - { - linux_event_watcher_t::watch_t watch(get_event_watcher(), poll_event_in); - wait_any_t waiter(&watch, &closed); - waiter.wait_lazily_unordered(); - } - break; - case SSL_ERROR_WANT_WRITE: - /* The underlying I/O is not ready to accept a write. Wait for it - to be ready or for someone to send a close signal. */ - { - linux_event_watcher_t::watch_t watch( - get_event_watcher(), poll_event_out); - wait_any_t waiter(&watch, &closed); - waiter.wait_lazily_unordered(); - } - break; - default: - // Some other error. Assume that the connection is unusable. - shutdown_socket(); - return; - } - - if (closed.is_pulsed()) { - /* We were closed for whatever reason. Whatever signalled - us has already called shutdown_socket(). */ - return; - } - - /* Go around the loop and try to read again */ - } -} - -/* It is not possible to close only the read or write side of a TLS connection -so we use only a single shutdown method which attempts to shutdown the TLS -before shutting down the underlying tcp connection */ -void linux_secure_tcp_conn_t::shutdown() { - assert_thread(); - - // If something else already shut us down, abort immediately. - if (closed.is_pulsed()) { - return; - } - - // Wait at most 5 seconds for the orderly shutdown. If it doesn't complete by then, - // we simply shutdown the socket. - signal_timer_t shutdown_timeout(5000); - - bool skip_shutdown = false; // Set to true if TLS shutdown encounters an error. - - // If we have not already received a "close notify" alert from the peer, - // then we should send one. If we have received one, this loop will respond - // with our own "close notify" alert. - while (!(skip_shutdown || shutdown_timeout.is_pulsed())) { - ERR_clear_error(); - - int ret = SSL_shutdown(conn.get()); - - if (ret > 0) { - // "close notify" has been sent and received. - break; - } - - if (ret == 0) { - // "close notify" has been sent but not yet received from peer. - continue; - } - - switch(SSL_get_error(conn.get(), ret)) { - case SSL_ERROR_WANT_READ: - { - /* The shutdown needs to read data, but the underlying I/O has no data - ready to read. Wait for it to be ready or for a timeout. */ - linux_event_watcher_t::watch_t watch(get_event_watcher(), poll_event_in); - wait_any_t waiter(&watch, &shutdown_timeout); - waiter.wait_lazily_unordered(); - } - continue; - case SSL_ERROR_WANT_WRITE: - { - /* The handshake needs to write data, but the underlying I/O is not ready - to write. Wait for it to be ready or for a timeout. */ - linux_event_watcher_t::watch_t watch( - get_event_watcher(), poll_event_out); - wait_any_t waiter(&watch, &shutdown_timeout); - waiter.wait_lazily_unordered(); - } - continue; - default: - // Unable to perform clean shutdown. Just skip it. - skip_shutdown = true; - } - } - - shutdown_socket(); -} - -void linux_secure_tcp_conn_t::shutdown_socket() { - assert_thread(); - rassert(!closed.is_pulsed()); - rassert(!read_closed.is_pulsed()); - rassert(!write_closed.is_pulsed()); - - // Shutdown the underlying TCP connection. - int res = ::shutdown(sock.get(), SHUT_RDWR); - if (res != 0 && get_errno() != ENOTCONN) { - logERR( - "Could not shutdown socket for reading and writing: %s", - errno_string(get_errno()).c_str()); - } - - closed.pulse(); - read_closed.pulse(); - write_closed.pulse(); -} - -#endif /* ENABLE_TLS */ - -linux_tcp_conn_descriptor_t::linux_tcp_conn_descriptor_t(fd_t fd) : fd_(fd) { - rassert(fd != INVALID_FD); -} - -linux_tcp_conn_descriptor_t::~linux_tcp_conn_descriptor_t() { - if (fd_ != INVALID_FD) { -#ifdef _WIN32 - int res = closesocket(fd_to_socket(fd_)); - if (res != 0) { - logERR("Could not close socket: %s", winerr_string(GetLastError()).c_str()); - } -#else - int res = ::shutdown(fd_, SHUT_RDWR); - if (res != 0 && get_errno() != ENOTCONN) { - logERR( - "Could not shutdown socket for reading and writing: %s", - errno_string(get_errno()).c_str()); - } -#endif - } -} - -void linux_tcp_conn_descriptor_t::make_server_connection( - tls_ctx_t *tls_ctx, scoped_ptr_t *tcp_conn, signal_t *closer -) THROWS_ONLY(crypto::openssl_error_t, interrupted_exc_t) { - // We pass ownership of `fd_` to the connection. - fd_t sock = fd_; - fd_ = INVALID_FD; -#ifdef ENABLE_TLS - if (tls_ctx != nullptr) { - tcp_conn->init(new linux_secure_tcp_conn_t(tls_ctx, sock, closer)); - return; - } -#endif - tcp_conn->init(new linux_tcp_conn_t(sock)); -} - -void linux_tcp_conn_descriptor_t::make_server_connection( - tls_ctx_t *tls_ctx, linux_tcp_conn_t **tcp_conn_out, signal_t *closer -) THROWS_ONLY(crypto::openssl_error_t, interrupted_exc_t) { - // We pass ownership of `fd_` to the connection. - fd_t sock = fd_; - fd_ = INVALID_FD; -#ifdef ENABLE_TLS - if (tls_ctx != nullptr) { - *tcp_conn_out = new linux_secure_tcp_conn_t(tls_ctx, sock, closer); - return; - } -#endif - *tcp_conn_out = new linux_tcp_conn_t(sock); -} - -/* Network listener object */ -linux_nonthrowing_tcp_listener_t::linux_nonthrowing_tcp_listener_t( - const std::set &bind_addresses, int _port, - const std::function &)> &cb) : - callback(cb), - local_addresses(bind_addresses), - port(_port), - bound(false), - socks(), - last_used_socket_index(0), - event_watchers(), - log_next_error(true) -{ - // If no addresses were supplied, default to 'any' - if (local_addresses.empty()) { - local_addresses.insert(ip_address_t::any(AF_INET6)); -#ifdef _WIN32 - local_addresses.insert(ip_address_t::any(AF_INET)); -#endif - } -} - -bool linux_nonthrowing_tcp_listener_t::begin_listening() { - if (!bound) { - try { - bind_sockets(); - } catch (const tcp_socket_exc_t &) { - return false; - } - } - - const int RDB_LISTEN_BACKLOG = 256; - - // Start listening to connections - for (size_t i = 0; i < socks.size(); ++i) { - winsock_debugf("listening on socket %x\n", socks[i].get()); - int res = listen(fd_to_socket(socks[i].get()), RDB_LISTEN_BACKLOG); -#ifdef _WIN32 - guarantee_winerr(res == 0, "Couldn't listen to the socket"); -#else - guarantee_err(res == 0, "Couldn't listen to the socket"); - res = fcntl(socks[i].get(), F_SETFL, O_NONBLOCK); - guarantee_err(res == 0, "Could not make socket non-blocking"); -#endif - } - - // Start the accept loop - accept_loop_drainer.init(new auto_drainer_t); - coro_t::spawn_sometime(std::bind( - &linux_nonthrowing_tcp_listener_t::accept_loop, this, auto_drainer_t::lock_t(accept_loop_drainer.get()))); - - return true; -} - -bool linux_nonthrowing_tcp_listener_t::is_bound() const { - return bound; -} - -int linux_nonthrowing_tcp_listener_t::get_port() const { - return port; -} - -int linux_nonthrowing_tcp_listener_t::init_sockets() { - event_watchers.reset(); - event_watchers.init(local_addresses.size()); - socks.reset(); - socks.init(local_addresses.size()); - - size_t i = 0; - for (auto addr = local_addresses.begin(); addr != local_addresses.end(); ++addr, ++i) { - if (event_watchers[i].has()) { - event_watchers[i].reset(); - } - -#ifdef _WIN32 - // TODO WINDOWS: maybe use WSASocket instead - socks[i].reset(socket_to_fd(socket(addr->get_address_family(), SOCK_STREAM, IPPROTO_TCP))); - winsock_debugf("new socket for listening: %x\n", socks[i]); - if (socks[i].get() == INVALID_FD) { - logERR("socket failed: %s", winerr_string(GetLastError())); - return EIO; - } -#else - socks[i].reset(socket(addr->get_address_family(), SOCK_STREAM, 0)); - if (socks[i].get() == INVALID_FD) { - return get_errno(); - } -#endif - - event_watchers[i].init(new event_watcher_t(socks[i].get(), this)); - - fd_t sock_fd = socks[i].get(); - guarantee_err(sock_fd != INVALID_FD, "Couldn't create socket"); - - int sockoptval = 1; -#ifdef _WIN32 - // On Windows, we don't set `SO_REUSEADDR`. `SO_REUSEADDR` will cause - // `bind` to happily bind to a port that another process is already - // listening too (similar to `SO_REUSEPORT` on Linux). - // That is bad for security, as well as usability reasons. - // Instead we go even one step further, and set the - // `SO_EXCLUSIVEADDRUSE` option to make sure we catch cases where another - // process was binding with `SO_REUSEADDR` before. - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx - // has a table of what this option means. - int res = setsockopt(fd_to_socket(sock_fd), SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast(&sockoptval), sizeof(sockoptval)); - guarantee_winerr(res != -1, "Could not set EXCLUSIVEADDRUSE option"); -#else - // On Unix-like systems, we set `SO_REUSEADDR` to allow the port - // to be re-bound quickly (e.g. if you restart the server). - int res = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &sockoptval, sizeof(sockoptval)); - guarantee_err(res != -1, "Could not set REUSEADDR option"); -#endif - /* XXX Making our socket NODELAY prevents the problem where responses to - * pipelined requests are delayed, since the TCP Nagle algorithm will - * notice when we send multiple small packets and try to coalesce them. But - * if we are only sending a few of these small packets quickly, like during - * pipeline request responses, then Nagle delays for around 40 ms before - * sending out those coalesced packets if they don't reach the max window - * size. So for latency's sake we want to disable Nagle. - * - * This might decrease our throughput, so perhaps we should add a - * runtime option for it. - */ -#ifdef _WIN32 - res = setsockopt(fd_to_socket(sock_fd), IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&sockoptval), sizeof(sockoptval)); - guarantee_winerr(res != -1, "Could not set TCP_NODELAY option"); -#else - res = setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, &sockoptval, sizeof(sockoptval)); - guarantee_err(res != -1, "Could not set TCP_NODELAY option"); -#endif - } - return 0; -} - -int bind_ipv4_interface(fd_t sock, int *port_out, const struct in_addr &addr) { - sockaddr_in serv_addr; - socklen_t sa_len(sizeof(serv_addr)); - memset(&serv_addr, 0, sa_len); - serv_addr.sin_family = AF_INET; - serv_addr.sin_port = htons(*port_out); - serv_addr.sin_addr = addr; - - int res = bind(fd_to_socket(sock), reinterpret_cast(&serv_addr), sa_len); - winsock_debugf("bound socket %x\n", sock); - if (res != 0) { -#ifdef _WIN32 - logWRN("bind failed: %s", winerr_string(GetLastError()).c_str()); - res = EIO; -#else - res = get_errno(); -#endif - } else if (*port_out == ANY_PORT) { - res = ::getsockname(fd_to_socket(sock), reinterpret_cast(&serv_addr), &sa_len); - guarantee_err(res != -1, "Could not determine socket local port number"); - *port_out = ntohs(serv_addr.sin_port); - } - - return res; -} - -int bind_ipv6_interface(fd_t sock, int *port_out, const ip_address_t &addr) { - sockaddr_in6 serv_addr; - socklen_t sa_len(sizeof(serv_addr)); - memset(&serv_addr, 0, sa_len); - serv_addr.sin6_family = AF_INET6; - serv_addr.sin6_port = htons(*port_out); - serv_addr.sin6_addr = addr.get_ipv6_addr(); - serv_addr.sin6_scope_id = addr.get_ipv6_scope_id(); - - int res = bind(fd_to_socket(sock), reinterpret_cast(&serv_addr), sa_len); - if (res != 0) { -#ifdef _WIN32 - logWRN("bind failed: %s", winerr_string(GetLastError()).c_str()); - res = EIO; -#else - res = get_errno(); -#endif - } else if (*port_out == ANY_PORT) { - res = ::getsockname(fd_to_socket(sock), reinterpret_cast(&serv_addr), &sa_len); - guarantee_err(res != -1, "Could not determine socket local port number"); - *port_out = ntohs(serv_addr.sin6_port); - } - - return res; -} - -void fallback_to_ipv4(std::set *addrs, int err, int port) { - bool ipv6_address_found = false; - - // Fallback to IPv4 only - remove any IPv6 addresses and resize dependant arrays - for (auto it = addrs->begin(); it != addrs->end();) { - if (it->get_address_family() == AF_INET6) { - if (it->is_any()) { - addrs->insert(ip_address_t::any(AF_INET)); - } - addrs->erase(*(it++)); - ipv6_address_found = true; - } else { - ++it; - } - } - - if (!ipv6_address_found) { - throw tcp_socket_exc_t(err, port); - } - logERR("Failed to create sockets for listener on port %d, " - "falling back to IPv4 only", port); -} - -void linux_nonthrowing_tcp_listener_t::bind_sockets() { - // It may take multiple attempts to get all the sockets onto the same port - int local_port = port; - - for (uint32_t attempts = (port == ANY_PORT) ? MAX_BIND_ATTEMPTS : 1; - attempts > 0; --attempts) { - local_port = port; - int res = init_sockets(); - if (res == EAFNOSUPPORT) { - ++attempts; // This attempt doesn't count - fallback_to_ipv4(&local_addresses, res, port); - continue; - } else if (res != 0) { - throw tcp_socket_exc_t(res, port); - } - - size_t i = 0; - for (std::set::iterator addr = local_addresses.begin(); - addr != local_addresses.end(); ++i, ++addr) { - winsock_debugf("binding to %s\n", addr->to_string().c_str()); - switch (addr->get_address_family()) { - case AF_INET: - res = bind_ipv4_interface(socks[i].get(), &local_port, addr->get_ipv4_addr()); - break; - case AF_INET6: - res = bind_ipv6_interface(socks[i].get(), &local_port, *addr); - break; - default: - unreachable(); - } - - if (res == EADDRNOTAVAIL) { - ++attempts; // This attempt doesn't count - fallback_to_ipv4(&local_addresses, res, port); - break; - } else if (res == EADDRINUSE || res == EACCES) { - break; - } else if (res != 0) { - throw tcp_socket_exc_t(res, port); - } - } - - if (i == local_addresses.size()) { - bound = true; - port = local_port; - return; - } - } - throw tcp_socket_exc_t(EADDRINUSE, port); -} - -#ifdef _WIN32 -void linux_nonthrowing_tcp_listener_t::accept_loop_single( - const auto_drainer_t::lock_t &lock, - exponential_backoff_t backoff, - windows_event_watcher_t *event_watcher) { - - static const int ADDRESS_SIZE = INET6_ADDRSTRLEN + 16; - - fd_t listening_sock = event_watcher->handle; - - WSAPROTOCOL_INFO pinfo; - int pinfo_size = sizeof(pinfo); - DWORD res = getsockopt(fd_to_socket(listening_sock), SOL_SOCKET, SO_PROTOCOL_INFO, reinterpret_cast(&pinfo), &pinfo_size); - guarantee_winerr(res == 0, "getsockopt failed"); - int address_family = pinfo.iAddressFamily; - - while (!lock.get_drain_signal()->is_pulsed()) { - overlapped_operation_t op(event_watcher); - fd_t new_sock = socket_to_fd(WSASocket(address_family, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED)); - winsock_debugf("new socket for accepting: %x\n", new_sock); - guarantee_winerr(new_sock != INVALID_FD, "WSASocket failed"); - winsock_debugf("accepting on socket %x\n", listening_sock); - DWORD bytes_received; - char addresses[ADDRESS_SIZE][2]; - BOOL res = get_AcceptEx(listening_sock)(fd_to_socket(listening_sock), fd_to_socket(new_sock), addresses, 0, ADDRESS_SIZE, ADDRESS_SIZE, &bytes_received, &op.overlapped); - - if (res) { - op.set_result(0, NO_ERROR); - } else { - DWORD error = GetLastError(); - if (error != ERROR_IO_PENDING) { - op.set_result(0, error); - } else { - op.wait_abortable(lock.get_drain_signal()); - if (lock.get_drain_signal()->is_pulsed()) { - return; - } - } - } - if (op.error != NO_ERROR) { - logERR("AcceptEx failed: %s", winerr_string(op.error).c_str()); - try { - backoff.failure(lock.get_drain_signal()); - } catch (const interrupted_exc_t &) { - return; - } - } else { - winsock_debugf("accepted %x from %x\n", new_sock, listening_sock); - coro_t::spawn_now_dangerously(std::bind(&linux_nonthrowing_tcp_listener_t::handle, this, new_sock)); - backoff.success(); - } - } -} -#else -fd_t linux_nonthrowing_tcp_listener_t::wait_for_any_socket(const auto_drainer_t::lock_t &lock) { - scoped_array_t > watches(event_watchers.size()); - wait_any_t waiter(lock.get_drain_signal()); - - for (size_t i = 0; i < event_watchers.size(); ++i) { - watches[i].init(new linux_event_watcher_t::watch_t(event_watchers[i].get(), poll_event_in)); - waiter.add(watches[i].get()); - } - - waiter.wait_lazily_unordered(); - - if (lock.get_drain_signal()->is_pulsed()) { - return -1; - } - - for (size_t i = 0; i < watches.size(); ++i) { - // This rather convoluted expression is to make sure we don't starve out higher-indexed interfaces - // because connections are coming in too fast on the lower interfaces, unlikely but valid - size_t index = (last_used_socket_index + i + 1) % watches.size(); - if (watches[index]->is_pulsed()) { - last_used_socket_index = index; - return socks[index].get(); - } - } - - // This should never happen, but it shouldn't be much of a problem - return -1; -} -#endif - -void linux_nonthrowing_tcp_listener_t::accept_loop(auto_drainer_t::lock_t lock) { - exponential_backoff_t backoff(10, 160, 2.0, 0.5); - -#ifdef _WIN32 - - pmap(event_watchers.size(), [this, &lock, &backoff](int i){ - accept_loop_single(lock, backoff, event_watchers[i].get()); - }); - -#else - fd_t active_fd = socks[0].get(); - while(!lock.get_drain_signal()->is_pulsed()) { - fd_t new_sock = accept(active_fd, nullptr, nullptr); - - if (new_sock != INVALID_FD) { - coro_t::spawn_now_dangerously(std::bind(&linux_nonthrowing_tcp_listener_t::handle, this, new_sock)); - backoff.success(); - - /* Assume that if there was a problem before, it's gone now because accept() - is working. */ - log_next_error = true; - - } else if (get_errno() == EAGAIN || get_errno() == EWOULDBLOCK) { - active_fd = wait_for_any_socket(lock); - - } else if (get_errno() == EINTR) { - /* Harmless error; just try again. */ - - } else { - /* Unexpected error. Log it unless it's a repeat error. */ - if (log_next_error) { - logERR("accept() failed: %s.", - errno_string(get_errno()).c_str()); - log_next_error = false; - } - - /* Delay before retrying. */ - try { - backoff.failure(lock.get_drain_signal()); - } catch (const interrupted_exc_t &) { - return; - } - } - } -#endif -} - -void linux_nonthrowing_tcp_listener_t::handle(fd_t socket) { - scoped_ptr_t nconn(new linux_tcp_conn_descriptor_t(socket)); - callback(nconn); -} - -linux_nonthrowing_tcp_listener_t::~linux_nonthrowing_tcp_listener_t() { - /* Interrupt the accept loop */ - accept_loop_drainer.reset(); - - // scoped_fd_t destructor will close() the socket -} - -void linux_nonthrowing_tcp_listener_t::on_event(int) { - /* This is only called in cases of error; normal input events are received - via event_listener.watch(). */ -} - -void noop_fun(UNUSED const scoped_ptr_t &arg) { } - -linux_tcp_bound_socket_t::linux_tcp_bound_socket_t(const std::set &bind_addresses, int port) : - listener(new linux_nonthrowing_tcp_listener_t(bind_addresses, port, noop_fun)) -{ - listener->bind_sockets(); -} - -int linux_tcp_bound_socket_t::get_port() const { - return listener->get_port(); -} - -linux_tcp_listener_t::linux_tcp_listener_t(const std::set &bind_addresses, int port, - const std::function &)> &callback) : - listener(new linux_nonthrowing_tcp_listener_t(bind_addresses, port, callback)) -{ - if (!listener->begin_listening()) { - throw address_in_use_exc_t("localhost", listener->get_port()); - } -} - -linux_tcp_listener_t::linux_tcp_listener_t( - linux_tcp_bound_socket_t *bound_socket, - const std::function &)> &callback) : - listener(bound_socket->listener.release()) -{ - listener->callback = callback; - if (!listener->begin_listening()) { - throw address_in_use_exc_t("localhost", listener->get_port()); - } -} - -int linux_tcp_listener_t::get_port() const { - return listener->get_port(); -} - -linux_repeated_nonthrowing_tcp_listener_t::linux_repeated_nonthrowing_tcp_listener_t( - const std::set &bind_addresses, - int port, - const std::function &)> &callback) : - listener(bind_addresses, port, callback) -{ } - -int linux_repeated_nonthrowing_tcp_listener_t::get_port() const { - return listener.get_port(); -} - -void linux_repeated_nonthrowing_tcp_listener_t::begin_repeated_listening_attempts() { - auto_drainer_t::lock_t lock(&drainer); - coro_t::spawn_sometime( - std::bind(&linux_repeated_nonthrowing_tcp_listener_t::retry_loop, this, lock)); -} - -void linux_repeated_nonthrowing_tcp_listener_t::retry_loop(auto_drainer_t::lock_t lock) { - try { - bool bound = listener.begin_listening(); - - for (int retry_interval = 1; - !bound; - retry_interval = std::min(10, retry_interval + 2)) { - logNTC("Will retry binding to port %d in %d seconds.\n", - listener.get_port(), - retry_interval); - nap(retry_interval * 1000, lock.get_drain_signal()); - bound = listener.begin_listening(); - } - - bound_cond.pulse(); - } catch (const interrupted_exc_t &e) { - // ignore - } -} - -signal_t *linux_repeated_nonthrowing_tcp_listener_t::get_bound_signal() { - return &bound_cond; -} - -std::vector get_ips() { - std::vector ret; - -#ifdef _WIN32 - const ULONG SIZE_INCREMENT = 15000; - scoped_malloc_t addresses_buffer; - UINT res; - for (int i = 1; i < 10; i++) { - ULONG buf_size = i * SIZE_INCREMENT; - addresses_buffer = scoped_malloc_t(buf_size); - res = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, nullptr, addresses_buffer.get(), &buf_size); - if (res != ERROR_BUFFER_OVERFLOW) { - break; - } - } - guarantee_winerr(res == NO_ERROR, "GetAdaptersAddresses failed"); - for (IP_ADAPTER_ADDRESSES *addresses = addresses_buffer.get(); addresses != nullptr; addresses = addresses->Next) { - for (IP_ADAPTER_UNICAST_ADDRESS *unicast = addresses->FirstUnicastAddress; unicast != nullptr; unicast = unicast->Next) { - SOCKADDR *addr = unicast->Address.lpSockaddr; - char buf[INET6_ADDRSTRLEN]; - const char *str; - switch (addr->sa_family) { - case AF_INET: { - auto sa = reinterpret_cast(addr); - str = InetNtop(AF_INET, &sa->sin_addr, buf, sizeof(buf)); - break; - } - case AF_INET6: { - auto sa6 = reinterpret_cast(addr); - str = InetNtop(AF_INET6, &sa6->sin6_addr, buf, sizeof(buf)); - break; - } - default: - continue; - } - guarantee_winerr(str != nullptr, "InetNtop failed"); - ret.emplace_back(str); - } - } -#else - struct ifaddrs *if_addrs = NULL; - int addr_res = getifaddrs(&if_addrs); - guarantee_err(addr_res == 0, "getifaddrs failed, could not determine local ip addresses"); - - for (ifaddrs *p = if_addrs; p != nullptr; p = p->ifa_next) { - if (p->ifa_addr == nullptr) { - continue; - } else if (p->ifa_addr->sa_family == AF_INET) { - if (!(p->ifa_flags & IFF_LOOPBACK)) { - struct sockaddr_in *in_addr = reinterpret_cast(p->ifa_addr); - // I don't think the "+ 1" is necessary, we're being - // paranoid about weak documentation. - const int buflength = INET_ADDRSTRLEN; - char buf[buflength + 1] = { 0 }; - const char *res = inet_ntop(AF_INET, &in_addr->sin_addr, buf, buflength); - - guarantee_err(res != nullptr, "inet_ntop failed"); - - ret.push_back(std::string(buf)); - } - } else if (p->ifa_addr->sa_family == AF_INET6) { - if (!(p->ifa_flags & IFF_LOOPBACK)) { - struct sockaddr_in6 *in6_addr = reinterpret_cast(p->ifa_addr); - - const int buflength = INET6_ADDRSTRLEN; - scoped_array_t buf(buflength + 1); - memset(buf.data(), 0, buf.size()); - const char *res = inet_ntop(AF_INET6, &in6_addr->sin6_addr, buf.data(), buflength); - - guarantee_err(res != nullptr, "inet_ntop failed on an ipv6 address"); - - ret.push_back(std::string(buf.data())); - } - } - } - - freeifaddrs(if_addrs); -#endif - - return ret; -} diff --git a/src/arch/io/network.hpp b/src/arch/io/network.hpp deleted file mode 100644 index 7b962b5d73f..00000000000 --- a/src/arch/io/network.hpp +++ /dev/null @@ -1,562 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_IO_NETWORK_HPP_ -#define ARCH_IO_NETWORK_HPP_ - -#include -#include -#include - -#ifdef ENABLE_TLS -#include -#include -#endif - -#ifdef _WIN32 -#include "windows.hpp" -#else -#include -#include -#include -#endif - -#include -#include -#include -#include -#include - -#include "arch/compiler.hpp" -#include "config/args.hpp" -#include "concurrency/interruptor.hpp" -#include "containers/lazy_erase_vector.hpp" -#include "containers/scoped.hpp" -#include "arch/address.hpp" -#include "arch/io/event_watcher.hpp" -#include "arch/io/io_utils.hpp" -#include "arch/io/openssl.hpp" -#include "arch/runtime/event_queue.hpp" -#include "arch/types.hpp" -#include "concurrency/cond_var.hpp" -#include "concurrency/queue/unlimited_fifo.hpp" -#include "concurrency/semaphore.hpp" -#include "concurrency/coro_pool.hpp" -#include "concurrency/exponential_backoff.hpp" -#include "containers/intrusive_list.hpp" -#include "crypto/error.hpp" -#include "perfmon/types.hpp" - -/* linux_tcp_conn_t provides a disgusting wrapper around a TCP network connection. */ - -class linux_tcp_conn_t : - public home_thread_mixin_t, - private linux_event_callback_t { -public: - friend class linux_tcp_conn_descriptor_t; - - void enable_keepalive() THROWS_ONLY(tcp_conn_write_closed_exc_t); - - class connect_failed_exc_t : public std::exception { - public: - explicit connect_failed_exc_t(int en) : - error(en), - info("Could not make connection: " + errno_string(error)) { } - - const char *what() const throw () { - return info.c_str(); - } - - ~connect_failed_exc_t() throw () { } - - const int error; - const std::string info; - }; - - // NB. interruptor cannot be nullptr. - linux_tcp_conn_t( - const ip_address_t &host, - int port, - signal_t *interruptor, - int local_port = ANY_PORT) - THROWS_ONLY(connect_failed_exc_t, interrupted_exc_t); - - /* Reading */ - - /* If you know beforehand how many bytes you want to read, use read() with a - byte buffer. Returns when the buffer is full, or throws tcp_conn_read_closed_exc_t. - If `closer` is pulsed, throws `read_closed_exc_t` and also closes the read - half of the connection. */ - void read(void *buf, size_t size, signal_t *closer) - THROWS_ONLY(tcp_conn_read_closed_exc_t); - - /* This is a convenience function around `read_more_buffered`, `peek` and `pop` that - behaved like a normal `read`, but uses buffering internally. - In many cases you'll want to use this instead of `read`, especially when reading - a lot of small values. */ - void read_buffered(void *buf, size_t size, signal_t *closer) - THROWS_ONLY(tcp_conn_read_closed_exc_t); - - // If you don't know how many bytes you want to read, but still - // masochistically want to handle buffering yourself. Makes at - // most one call to ::read(), reads some data or throws - // read_closed_exc_t. read_some() is guaranteed to return at least - // one byte of data unless it throws read_closed_exc_t. - size_t read_some(void *buf, size_t size, signal_t *closer) - THROWS_ONLY(tcp_conn_read_closed_exc_t); - - // If you don't know how many bytes you want to read, use peek() - // and then, if you're satisfied, pop what you've read, or if - // you're unsatisfied, read_more_buffered() and then try again. - // Note that you should always call peek() before calling - // read_more_buffered(), because there might be leftover data in - // the peek buffer that might be enough for you. - const_charslice peek() const THROWS_ONLY(tcp_conn_read_closed_exc_t); - - //you can also peek with a specific size (this is really just convenient - //for some things and can in some cases avoid an unneeded copy - const_charslice peek(size_t size, signal_t *closer) - THROWS_ONLY(tcp_conn_read_closed_exc_t); - - void pop(size_t len, signal_t *closer) THROWS_ONLY(tcp_conn_read_closed_exc_t); - - void read_more_buffered(signal_t *closer) THROWS_ONLY(tcp_conn_read_closed_exc_t); - - /* Call shutdown_read() to close the half of the pipe that goes from the peer to us. If there - is an outstanding read() or peek_until() operation, it will throw tcp_conn_read_closed_exc_t. */ - virtual void shutdown_read(); - - /* Returns false if the half of the pipe that goes from the peer to us has been closed. */ - bool is_read_open() const; - - /* Writing */ - - /* write() writes 'size' bytes from 'buf' to the socket and blocks until it - is done. Throws tcp_conn_write_closed_exc_t if the write half of the pipe is closed - before we can finish. If `closer` is pulsed, closes the write half of the - pipe and throws `tcp_conn_write_closed_exc_t`. */ - void write(const void *buf, size_t size, signal_t *closer) - THROWS_ONLY(tcp_conn_write_closed_exc_t); - - /* write_buffered() is like write(), but it might not send the data until - flush_buffer*() or write() is called. Internally, it bundles together the - buffered writes; this may improve performance. */ - void write_buffered(const void *buf, size_t size, signal_t *closer) - THROWS_ONLY(tcp_conn_write_closed_exc_t); - - void writef(signal_t *closer, const char *format, ...) - THROWS_ONLY(tcp_conn_write_closed_exc_t) ATTR_FORMAT(printf, 3, 4); - - // Blocks until flush is done - void flush_buffer(signal_t *closer) - THROWS_ONLY(tcp_conn_write_closed_exc_t); - // Blocks only if the queue is backed up - void flush_buffer_eventually(signal_t *closer) - THROWS_ONLY(tcp_conn_write_closed_exc_t); - - /* Call shutdown_write() to close the half of the pipe that goes from us to the peer. If there - is a write currently happening, it will get tcp_conn_write_closed_exc_t. */ - virtual void shutdown_write(); - - /* Returns false if the half of the pipe that goes from us to the peer has been closed. */ - bool is_write_open() const; - - /* Put a `perfmon_rate_monitor_t` here if you want to record stats on how fast data is being - transmitted over the network. */ - perfmon_rate_monitor_t *write_perfmon; - - virtual ~linux_tcp_conn_t() THROWS_NOTHING; - - virtual void rethread(threadnum_t thread); - - bool getpeername(ip_and_port_t *ip_and_port); - - event_watcher_t *get_event_watcher() { - return event_watcher.get(); - } - -protected: - - void on_shutdown_read(); - void on_shutdown_write(); - - // Used by tcp_listener_t and any derived classes. - explicit linux_tcp_conn_t(fd_t sock); - - // The underlying TCP socket file descriptor. - scoped_fd_t sock; - - /* These are pulsed if and only if the read/write end of the connection has been closed. */ - cond_t read_closed, write_closed; - -private: - - /* `read_op_wrapper_t` and `write_op_wrapper_t` are an attempt to factor out - the boilerplate that would otherwise be at the top of every read- or write- - related method on `linux_tcp_conn_t`. They check that the connection is - open, handle `closer`, etc. */ - - class read_op_wrapper_t : private signal_t::subscription_t { - public: - read_op_wrapper_t(linux_tcp_conn_t *p, signal_t *closer) : parent(p) { - parent->assert_thread(); - rassert(!parent->read_in_progress); - if (closer->is_pulsed()) { - if (parent->is_read_open()) { - parent->shutdown_read(); - } - } else { - this->reset(closer); - } - if (!parent->is_read_open()) { - throw tcp_conn_read_closed_exc_t(); - } - parent->read_in_progress = true; - } - ~read_op_wrapper_t() { - parent->read_in_progress = false; - } - private: - void run() { - if (parent->is_read_open()) { - parent->shutdown_read(); - } - } - linux_tcp_conn_t *parent; - }; - - class write_op_wrapper_t : private signal_t::subscription_t { - public: - write_op_wrapper_t(linux_tcp_conn_t *p, signal_t *closer) : parent(p) { - parent->assert_thread(); - rassert(!parent->write_in_progress); - if (closer->is_pulsed()) { - if (parent->is_write_open()) { - parent->shutdown_write(); - } - } else { - this->reset(closer); - } - if (!parent->is_write_open()) { - throw tcp_conn_write_closed_exc_t(); - } - parent->write_in_progress = true; - } - ~write_op_wrapper_t() { - parent->write_in_progress = false; - } - private: - void run() { - if (parent->is_write_open()) { - parent->shutdown_write(); - } - } - linux_tcp_conn_t *parent; - }; - - /* Note that this only gets called to handle error-events. Read and write - events are handled through the event_watcher_t. */ - void on_event(int events); - - /* Object that we use to watch for events. It's NULL when we are not registered on any - thread, and otherwise is an object that's valid for the current thread. */ - scoped_ptr_t event_watcher; - - /* True if there is a pending read or write */ - bool read_in_progress, write_in_progress; - - /* Holds data that we read from the socket but hasn't been consumed yet */ - lazy_erase_vector_t read_buffer; - - static const size_t WRITE_QUEUE_MAX_SIZE = 128 * KILOBYTE; - static const size_t WRITE_CHUNK_SIZE = 8 * KILOBYTE; - - /* Structs to avoid over-using dynamic allocation */ - struct write_buffer_t : public intrusive_list_node_t { - char buffer[WRITE_CHUNK_SIZE]; - size_t size; - }; - - struct write_queue_op_t : public intrusive_list_node_t { - write_buffer_t *dealloc; - const void *buffer; - size_t size; - cond_t *cond; - auto_drainer_t::lock_t keepalive; - }; - - class write_handler_t : public coro_pool_callback_t { - public: - explicit write_handler_t(linux_tcp_conn_t *_parent); - private: - linux_tcp_conn_t *parent; - void coro_pool_callback(write_queue_op_t *operation, signal_t *interruptor); - } write_handler; - - template - class deleting_intrusive_list_t : public intrusive_list_t { - public: - ~deleting_intrusive_list_t() { - while (T *x = this->head()) { - this->pop_front(); - delete x; - } - } - }; - - /* Lists of unused buffers, new buffers will be put on this list until needed again, reducing - the use of dynamic memory. TODO: decay over time? */ - deleting_intrusive_list_t unused_write_buffers; - deleting_intrusive_list_t unused_write_queue_ops; - - write_buffer_t * get_write_buffer(); - write_queue_op_t * get_write_queue_op(); - void release_write_buffer(write_buffer_t *buffer); - void release_write_queue_op(write_queue_op_t *op); - - - /* Schedules old write buffer's contents to be flushed and swaps in a fresh write buffer. - Blocks until it can acquire the `write_queue_limiter` semaphore, but doesn't wait for - data to be completely written. */ - void internal_flush_write_buffer(); - - /* Used to queue up buffers to write. The functions in `write_queue` will all be - `std::bind()`s of the `perform_write()` function below. */ - unlimited_fifo_queue_t > write_queue; - - /* This semaphore prevents the write queue from getting arbitrarily big. */ - static_semaphore_t write_queue_limiter; - - /* Used to actually perform the writes. Only has one coroutine in it, which will call the - handle_write_queue callback when operations are ready */ - coro_pool_t write_coro_pool; - - /* Buffer we are currently filling up with data that we want to write. When it reaches a - certain size, we push it onto `write_queue`. */ - scoped_ptr_t current_write_buffer; - - scoped_ptr_t drainer; - - /* Reads up to the given number of bytes, but not necessarily that many. Simple - wrapper around ::read(). Returns the number of bytes read or throws - tcp_conn_read_closed_exc_t. Bypasses read_buffer. */ - virtual size_t read_internal( - void *buffer, size_t size - ) THROWS_ONLY(tcp_conn_read_closed_exc_t); - - /* Used to actually perform a write. If the write end of the connection is open, then - writes `size` bytes from `buffer` to the socket. */ - virtual void perform_write(const void *buffer, size_t size); -}; - -#ifdef ENABLE_TLS - -/* tls_conn_wrapper_t wraps a TLS connection. */ -class tls_conn_wrapper_t { -public: - explicit tls_conn_wrapper_t(SSL_CTX *tls_ctx) THROWS_ONLY(crypto::openssl_error_t); - - ~tls_conn_wrapper_t(); - - void set_fd(fd_t sock) THROWS_ONLY(crypto::openssl_error_t); - - SSL *get() { return conn; } - -private: - SSL *conn; - - DISABLE_COPYING(tls_conn_wrapper_t); -}; - -class linux_secure_tcp_conn_t : - public linux_tcp_conn_t { -public: - - friend class linux_tcp_conn_descriptor_t; - - // Client connection constructor. - linux_secure_tcp_conn_t( - SSL_CTX *tls_ctx, const ip_address_t &host, int port, - signal_t *interruptor, int local_port = ANY_PORT - ) THROWS_ONLY(connect_failed_exc_t, crypto::openssl_error_t, interrupted_exc_t); - - ~linux_secure_tcp_conn_t() THROWS_NOTHING; - - /* shutdown_read() and shutdown_write() just close the socket rather than performing - the full TLS shutdown procedure, because they're assumed not to block. - Our usual `shutdown` does block and it also assumes that no other operation is - currently ongoing on the connection, which we can't guarantee here. */ - virtual void shutdown_read() { shutdown_socket(); } - virtual void shutdown_write() { shutdown_socket(); } - - virtual void rethread(threadnum_t thread); - -private: - - // Server connection constructor. - linux_secure_tcp_conn_t( - SSL_CTX *tls_ctx, fd_t _sock, signal_t *interruptor - ) THROWS_ONLY(crypto::openssl_error_t, interrupted_exc_t); - - void perform_handshake(signal_t *interruptor) THROWS_ONLY( - crypto::openssl_error_t, interrupted_exc_t); - - /* Reads up to the given number of bytes, but not necessarily that many. Simple - wrapper around ::read(). Returns the number of bytes read or throws - tcp_conn_read_closed_exc_t. Bypasses read_buffer. */ - virtual size_t read_internal(void *buffer, size_t size) THROWS_ONLY( - tcp_conn_read_closed_exc_t); - - /* Used to actually perform a write. If the write end of the connection is open, then - writes `size` bytes from `buffer` to the socket. */ - virtual void perform_write(const void *buffer, size_t size); - - void shutdown(); - void shutdown_socket(); - - bool is_open() { return !closed.is_pulsed(); } - - tls_conn_wrapper_t conn; - - cond_t closed; -}; - -#endif /* ENABLE_TLS */ - -class linux_tcp_conn_descriptor_t { -public: - ~linux_tcp_conn_descriptor_t(); - - void make_server_connection( - tls_ctx_t *tls_ctx, scoped_ptr_t *tcp_conn, signal_t *closer) - THROWS_ONLY(crypto::openssl_error_t, interrupted_exc_t); - - // Must get called exactly once during lifetime of this object. - // Call it on the thread you'll use the server connection on. - void make_server_connection( - tls_ctx_t *tls_ctx, linux_tcp_conn_t **tcp_conn_out, signal_t *closer) - THROWS_ONLY(crypto::openssl_error_t, interrupted_exc_t); - -private: - friend class linux_nonthrowing_tcp_listener_t; - - explicit linux_tcp_conn_descriptor_t(fd_t fd); - -private: - fd_t fd_; - - DISABLE_COPYING(linux_tcp_conn_descriptor_t); -}; - -/* The linux_nonthrowing_tcp_listener_t is used to listen on a network port for incoming -connections. Create a linux_nonthrowing_tcp_listener_t with some port and then call set_callback(); -the provided callback will be called in a new coroutine every time something connects. */ - -class linux_nonthrowing_tcp_listener_t : private linux_event_callback_t { -public: - linux_nonthrowing_tcp_listener_t(const std::set &bind_addresses, int _port, - const std::function &)> &callback); - - ~linux_nonthrowing_tcp_listener_t(); - - MUST_USE bool begin_listening(); - bool is_bound() const; - int get_port() const; - -protected: - friend class linux_tcp_listener_t; - friend class linux_tcp_bound_socket_t; - - void bind_sockets(); - - // The callback to call when we get a connection - std::function &)> callback; - -private: - static const uint32_t MAX_BIND_ATTEMPTS = 20; - int init_sockets(); - - /* accept_loop() runs in a separate coroutine. It repeatedly tries to accept - new connections; when accept() blocks, then it waits for events from the - event loop. */ - void accept_loop(auto_drainer_t::lock_t lock); - -#ifdef _WIN32 - void accept_loop_single(const auto_drainer_t::lock_t &lock, - exponential_backoff_t backoff, - windows_event_watcher_t *event_watcher); -#else - fd_t wait_for_any_socket(const auto_drainer_t::lock_t &lock); -#endif - scoped_ptr_t accept_loop_drainer; - - void handle(fd_t sock); - - /* event_watcher sends any error conditions to here */ - void on_event(int events); - - // The selected local addresses to listen on, 'any' if empty - std::set local_addresses; - - // The port we're asked to bind to - int port; - - // Inidicates successful binding to a port - bool bound; - - // The sockets to listen for connections on - scoped_array_t socks; - - // The last socket to get a connection, used for round-robining - size_t last_used_socket_index; - - // Sentries representing our registrations with the event loop, one per socket - scoped_array_t > event_watchers; - - bool log_next_error; -}; - -/* Used by the old style tcp listener */ -class linux_tcp_bound_socket_t { -public: - linux_tcp_bound_socket_t(const std::set &bind_addresses, int _port); - int get_port() const; -private: - friend class linux_tcp_listener_t; - - scoped_ptr_t listener; -}; - -/* Replicates old constructor-exception-throwing style for backwards compaitbility */ -class linux_tcp_listener_t { -public: - linux_tcp_listener_t(linux_tcp_bound_socket_t *bound_socket, - const std::function &)> &callback); - linux_tcp_listener_t(const std::set &bind_addresses, int port, - const std::function &)> &callback); - - int get_port() const; - -private: - scoped_ptr_t listener; -}; - -/* Like a linux tcp listener but repeatedly tries to bind to its port until successful */ -class linux_repeated_nonthrowing_tcp_listener_t { -public: - linux_repeated_nonthrowing_tcp_listener_t(const std::set &bind_addresses, int port, - const std::function &)> &callback); - void begin_repeated_listening_attempts(); - - signal_t *get_bound_signal(); - int get_port() const; - -private: - void retry_loop(auto_drainer_t::lock_t lock); - - linux_nonthrowing_tcp_listener_t listener; - cond_t bound_cond; - auto_drainer_t drainer; -}; - -std::vector get_ips(); - -#endif // ARCH_IO_NETWORK_HPP_ diff --git a/src/arch/io/openssl.hpp b/src/arch/io/openssl.hpp deleted file mode 100644 index 961d322d70f..00000000000 --- a/src/arch/io/openssl.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef ARCH_IO_OPENSSL_HPP_ -#define ARCH_IO_OPENSSL_HPP_ - -#ifdef ENABLE_TLS - -// errors.hpp defines an empty final for compilers that don't support it -// That conflicts with -#ifdef final -#define FINAL_WAS_DEFINED -#undef final -#endif - -#include - -#ifdef FINAL_WAS_DEFINED -#define final -#undef FINAL_WAS_DEFINED -#endif - -typedef SSL_CTX tls_ctx_t; - -#else /* ENABLE_TLS */ - -// Some non-TLS code still passes around a null SSL_CTX* -typedef void tls_ctx_t; - -#endif /* ENABLE_TLS */ - - -#endif /* ARCH_IO_OPENSSL_HPP_ */ diff --git a/src/arch/io/timer/timer_kqueue_provider.cc b/src/arch/io/timer/timer_kqueue_provider.cc deleted file mode 100644 index e1623a00058..00000000000 --- a/src/arch/io/timer/timer_kqueue_provider.cc +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "arch/io/timer_provider.hpp" - -#if RDB_TIMER_PROVIDER == RDB_TIMER_PROVIDER_KQUEUE - -#include -#include -#include -#include -#include - -#include "config/args.hpp" -#include "logger.hpp" -#include "utils.hpp" -#include "time.hpp" -#include "containers/printf_buffer.hpp" - -timer_kqueue_provider_t::timer_kqueue_provider_t(linux_event_queue_t *queue) - : queue_(queue), kq_fd_(-1), callback_(NULL) { - const int fd = kqueue(); - guarantee_err(fd != -1, "kqueue() call falied"); - kq_fd_ = fd; - queue_->watch_resource(kq_fd_, poll_event_in, this); -} - -timer_kqueue_provider_t::~timer_kqueue_provider_t() { - queue_->forget_resource(kq_fd_, this); - - guarantee(callback_ == nullptr); - const int res = close(kq_fd_); - guarantee_err(res == 0, "Could not close the kqueue timer."); -} - -void timer_kqueue_provider_t::schedule_oneshot(const int64_t next_time_in_nanos, timer_provider_callback_t *const cb) { - const int64_t time_difference = next_time_in_nanos - static_cast(get_ticks()); - const int64_t wait_nanos = std::max(1, time_difference); - - struct kevent64_s event; - EV_SET64(&event, 99, EVFILT_TIMER, EV_ADD | EV_ONESHOT, NOTE_NSECONDS, wait_nanos, 0, 0, 0); - - // Setting the timeout to zero makes this a non-blocking call. - struct timespec timeout; - timeout.tv_sec = 0; - timeout.tv_nsec = 0; - const int res = kevent64(kq_fd_, &event, 1, nullptr, 0, 0, &timeout); - guarantee_err(res != -1, "kevent64 failed when making timer"); - - callback_ = cb; -} - -void timer_kqueue_provider_t::unschedule_oneshot() { - struct kevent64_s event; - EV_SET64(&event, 99, EVFILT_TIMER, EV_DELETE, 0, 0, 0, 0, 0); - - struct timespec timeout; - timeout.tv_sec = 0; - timeout.tv_nsec = 0; - const int res = kevent64(kq_fd_, &event, 1, nullptr, 0, 0, &timeout); - guarantee_err(res != -1, "kevent64 failed when deleting timer"); - - callback_ = nullptr; -} - -void debug_print(printf_buffer_t *buf, const struct kevent64_s& event) { - buf->appendf("kevent64_s{ident=%" PRIu64 ", filter=%" PRIi16 ", flags=%" PRIu16 - ", fflags=%" PRIu32 ", data=%" PRIi64 ", udata=%" PRIu64 - ", ext[0]=%" PRIu64 ", ext[1]=%" PRIu64, - event.ident, event.filter, event.flags, - event.fflags, event.data, event.udata, - event.ext[0], event.ext[1]); -} - -void timer_kqueue_provider_t::on_event(int eventmask) { - if (eventmask != poll_event_in) { - logERR("Unexpected event mask: %d", eventmask); - } - - const int num_events = 4; - struct kevent64_s events[num_events]; - - // Setting the timeout to 0 makes this a non-blocking call. - struct timespec timeout; - timeout.tv_sec = 0; - timeout.tv_nsec = 0; - - // kevent64 can return more than one EVFILT_TIMER event at a time, even though we only - // registered for it once and we can get an overrun count. We get multiple results for the same - // event, separately. So we have this loop that makes sure we siphon all the possible events - // off the kqueue file descriptor. The only time I've seen it return more than one event was - // when sleeping the computer, and another time when leaving the computer idle for a while. - int64_t expiration_count = 0; - - bool got_short_res = false; - while (!got_short_res) { - int res; - do { - res = kevent64(kq_fd_, nullptr, 0, events, num_events, 0, &timeout); - } while (res == -1 && get_errno() == EINTR); - - guarantee_err(res != -1, "kevent64 failed when reading timer"); - - got_short_res = res < num_events; - - for (int i = 0; i < res; ++i) { - const struct kevent64_s event = events[i]; - guarantee(event.filter == EVFILT_TIMER); - guarantee_xerr(!(event.flags & EV_ERROR), event.data, "The kqueue-based timer returned an error.\n"); - - guarantee(event.ident == 99); - int64_t this_result_expiration_count = events[i].data; - expiration_count += this_result_expiration_count; - } - } - - if (expiration_count > 0 && callback_ != nullptr) { - // Make the callback be NULL before we call it, so that a new callback can be set. - timer_provider_callback_t *local_cb = callback_; - callback_ = nullptr; - local_cb->on_oneshot(); - } -} - -#endif // RDB_TIMER_PROVIDER == RDB_TIMER_PROVIDER_KQUEUE diff --git a/src/arch/io/timer/timer_kqueue_provider.hpp b/src/arch/io/timer/timer_kqueue_provider.hpp deleted file mode 100644 index 906d6fc6159..00000000000 --- a/src/arch/io/timer/timer_kqueue_provider.hpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef ARCH_IO_TIMER_TIMER_KQUEUE_PROVIDER_HPP_ -#define ARCH_IO_TIMER_TIMER_KQUEUE_PROVIDER_HPP_ - -#include "arch/runtime/event_queue.hpp" - -struct timer_provider_callback_t; - -// This uses a kqueue to basically act like a Linux timerfd. This isn't designed to run on kqueue. -struct timer_kqueue_provider_t : public linux_event_callback_t { -public: - explicit timer_kqueue_provider_t(linux_event_queue_t *queue); - ~timer_kqueue_provider_t(); - - void schedule_oneshot(int64_t next_time_in_nanos, timer_provider_callback_t *cb); - void unschedule_oneshot(); - -private: - void on_event(int events); - - linux_event_queue_t *queue_; - fd_t kq_fd_; - timer_provider_callback_t *callback_; - - DISABLE_COPYING(timer_kqueue_provider_t); -}; - - -#endif // ARCH_IO_TIMER_TIMER_KQUEUE_PROVIDER_HPP_ diff --git a/src/arch/io/timer/timer_signal_provider.cc b/src/arch/io/timer/timer_signal_provider.cc deleted file mode 100644 index a37c34f5f03..00000000000 --- a/src/arch/io/timer/timer_signal_provider.cc +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "arch/io/timer_provider.hpp" - -#if RDB_TIMER_PROVIDER == RDB_TIMER_PROVIDER_SIGNAL - -#include "arch/io/timer/timer_signal_provider.hpp" - -#include -#include -#include -#include -#include - -#include "arch/runtime/event_queue.hpp" -#include "arch/io/io_utils.hpp" -#include "utils.hpp" - -// This *should* be a member of sigevent exposed by glibc, who the -// heck knows why it isn't... -#define sigev_notify_thread_id _sigev_un._tid - -void timer_signal_provider_signal_handler(UNUSED int signum, siginfo_t *siginfo, UNUSED void *uctx) { - timer_signal_provider_t *provider = static_cast(siginfo->si_value.sival_ptr); - - timer_provider_callback_t *local_cb = provider->callback; - if (local_cb != nullptr) { - provider->callback = nullptr; - local_cb->on_oneshot(); - } -} - -/* Kernel timer provider based on signals */ -timer_signal_provider_t::timer_signal_provider_t(UNUSED linux_event_queue_t *queue) - : callback(NULL) { - struct sigaction sa = make_sa_sigaction(SA_SIGINFO, &timer_signal_provider_signal_handler); - - // Register the signal. - int res = sigaction(TIMER_NOTIFY_SIGNAL, &sa, nullptr); - guarantee_err(res == 0, "timer signal provider could not register the signal handler"); - - // Initialize the event structure - struct sigevent evp; - bzero(&evp, sizeof(sigevent)); - evp.sigev_signo = TIMER_NOTIFY_SIGNAL; - evp.sigev_notify = SIGEV_THREAD_ID; - evp.sigev_notify_thread_id = _gettid(); - evp.sigev_value.sival_ptr = this; - - // Create the timer - res = timer_create(CLOCK_MONOTONIC, &evp, &timerid); - guarantee_err(res == 0, "Could not create timer"); -} - -timer_signal_provider_t::~timer_signal_provider_t() { - guarantee(callback == nullptr); - - int res = timer_delete(timerid); - guarantee_err(res == 0, "timer_delete failed"); - - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_DFL; - - res = sigaction(TIMER_NOTIFY_SIGNAL, &sa, nullptr); - guarantee_err(res == 0, "timer signal provider could not unregister the signal handler"); -} - -void timer_signal_provider_t::schedule_oneshot(int64_t next_time_in_nanos, timer_provider_callback_t *cb) { - // We could ostensibly use TIMER_ABSTIME in the timer_settime call instead of specifying a - // relative timer, but that would make our code fragilely depend on get_ticks() using - // CLOCK_MONOTONIC. - - const int64_t ticks = get_ticks(); - const int64_t time_diff = next_time_in_nanos - ticks; - const int64_t wait_time = std::max(1, time_diff); - - itimerspec spec; - spec.it_value.tv_sec = wait_time / BILLION; - spec.it_value.tv_nsec = wait_time % BILLION; - spec.it_interval.tv_sec = 0; - spec.it_interval.tv_nsec = 0; - - const int res = timer_settime(timerid, 0, &spec, nullptr); - guarantee_err(res == 0, "Could not arm the timer"); - - callback = cb; -} - -void timer_signal_provider_t::unschedule_oneshot() { - callback = nullptr; - - struct itimerspec spec; - spec.it_interval.tv_sec = 0; - spec.it_interval.tv_nsec = 0; - spec.it_value.tv_sec = 0; - spec.it_value.tv_nsec = 0; - - const int res = timer_settime(timerid, 0, &spec, nullptr); - guarantee_err(res == 0, "Could not disarm the timer."); -} - -#endif // RDB_TIMER_PROVIDER == RDB_TIMER_PROVIDER_SIGNAL diff --git a/src/arch/io/timer/timer_signal_provider.hpp b/src/arch/io/timer/timer_signal_provider.hpp deleted file mode 100644 index 7b94db55df7..00000000000 --- a/src/arch/io/timer/timer_signal_provider.hpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_IO_TIMER_TIMER_SIGNAL_PROVIDER_HPP_ -#define ARCH_IO_TIMER_TIMER_SIGNAL_PROVIDER_HPP_ - -#include "arch/runtime/event_queue.hpp" - -struct timer_provider_callback_t; - -#define TIMER_NOTIFY_SIGNAL (SIGRTMIN + 3) - -/* Kernel timer provider based on signals */ -struct timer_signal_provider_t { -public: - explicit timer_signal_provider_t(linux_event_queue_t *_queue); - ~timer_signal_provider_t(); - - void schedule_oneshot(int64_t next_time_in_nanos, timer_provider_callback_t *cb); - void unschedule_oneshot(); - -private: - friend void timer_signal_provider_signal_handler(UNUSED int signum, siginfo_t *siginfo, UNUSED void *uctx); - - timer_t timerid; - timer_provider_callback_t *callback; - - DISABLE_COPYING(timer_signal_provider_t); -}; - -#endif // ARCH_IO_TIMER_TIMER_SIGNAL_PROVIDER_HPP_ diff --git a/src/arch/io/timer/timer_windows_provider.cc b/src/arch/io/timer/timer_windows_provider.cc deleted file mode 100644 index 5e54087f2cd..00000000000 --- a/src/arch/io/timer/timer_windows_provider.cc +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "arch/io/timer_provider.hpp" - -#if RDB_TIMER_PROVIDER == RDB_TIMER_PROVIDER_WINDOWS - -#include - -#include "logger.hpp" -#include "time.hpp" - -#ifdef TRACE_TIMER -#define debugf_timer(...) debugf("timer: " __VA_ARGS__) -#else -#define debugf_timer(...) ((void)0) -#endif - -timer_windows_provider_t::timer_windows_provider_t(iocp_event_queue_t *event_queue_) : - callback(nullptr), event_queue(event_queue_) { - debugf_timer("[%p] create\n", this); - // TODO WINDOWS: assert that there is no other timer provider for this thread -} - -timer_windows_provider_t::~timer_windows_provider_t() { - debugf_timer("[%p] destroy\n", this); - unschedule_oneshot(); -} - -void timer_windows_provider_t::schedule_oneshot(const int64_t next_time_in_nanos, timer_provider_callback_t *const cb) { - debugf_timer("[%p] scheduled in %" PRIi64 "ns\n", this, next_time_in_nanos - get_ticks()); - event_queue->reset_timer(next_time_in_nanos, cb); -} - -void timer_windows_provider_t::unschedule_oneshot() { - debugf_timer("[%p] unscheduled\n", this); - event_queue->unset_timer(); -} - -#endif // RDB_TIMER_PROVIDER == RDB_TIMER_PROVIDER_WINDOWS diff --git a/src/arch/io/timer/timer_windows_provider.hpp b/src/arch/io/timer/timer_windows_provider.hpp deleted file mode 100644 index d100e2bfccf..00000000000 --- a/src/arch/io/timer/timer_windows_provider.hpp +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_IO_TIMER_TIMER_WINDOWS_PROVIDER_HPP_ -#define ARCH_IO_TIMER_TIMER_WINDOWS_PROVIDER_HPP_ - -#include "arch/runtime/event_queue.hpp" - -struct timer_provider_callback_t; - -// On Windows, the timer's resolution corresponds to -// the resolution of the system clock, typically around 15 ms. - -struct timer_windows_provider_t { -public: - explicit timer_windows_provider_t(iocp_event_queue_t *); - ~timer_windows_provider_t(); - - void schedule_oneshot(const int64_t next_time_in_nanos, timer_provider_callback_t *const cb); - void unschedule_oneshot(); - -private: - timer_provider_callback_t *callback; - iocp_event_queue_t *event_queue; - DISABLE_COPYING(timer_windows_provider_t); -}; - -#endif // ARCH_IO_TIMER_TIMER_WINDOWS_PROVIDER_HPP_ - diff --git a/src/arch/io/timer/timerfd_provider.cc b/src/arch/io/timer/timerfd_provider.cc deleted file mode 100644 index 673b169b6e9..00000000000 --- a/src/arch/io/timer/timerfd_provider.cc +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "arch/io/timer_provider.hpp" - -#if RDB_TIMER_PROVIDER == RDB_TIMER_PROVIDER_TIMERFD - -#include // NOLINT(build/include_order) -#include // NOLINT(build/include_order) -#include // NOLINT(build/include_order) -#include // NOLINT(build/include_order) - -#include "arch/runtime/event_queue.hpp" -#include "logger.hpp" -#include "time.hpp" - -timerfd_provider_t::timerfd_provider_t(linux_event_queue_t *_queue) - : queue(_queue), timer_fd(-1), callback(nullptr) { - const int fd = timerfd_create(CLOCK_MONOTONIC, 0); - guarantee_err(fd != -1, "Could not create timer"); - - const int res = fcntl(fd, F_SETFL, O_NONBLOCK); - guarantee_err(res == 0, "Could not make timer non-blocking"); - - timer_fd = fd; - - // We watch the resource, but so far we haven't set any timings. - queue->watch_resource(timer_fd, poll_event_in, this); -} - -timerfd_provider_t::~timerfd_provider_t() { - queue->forget_resource(timer_fd, this); - - guarantee(callback == nullptr); - const int res = close(timer_fd); - guarantee_err(res == 0 || get_errno() == EINTR, "Could not close the timer."); -} - -void timerfd_provider_t::schedule_oneshot(const int64_t next_time_in_nanos, timer_provider_callback_t *const cb) { - // We could pass TFD_TIMER_ABSTIME to timerfd_settime (thus avoiding the std::max logic below), - // but that would mean this code depends on the fact that get_ticks() is implemented in terms of - // CLOCK_MONOTONIC. - - const int64_t time_difference = next_time_in_nanos - static_cast(get_ticks()); - const int64_t wait_nanos = std::max(1, time_difference); - - struct itimerspec spec; - spec.it_interval.tv_sec = 0; - spec.it_interval.tv_nsec = 0; - spec.it_value.tv_sec = wait_nanos / BILLION; - spec.it_value.tv_nsec = wait_nanos % BILLION; - - const int res = timerfd_settime(timer_fd, 0, &spec, nullptr); - guarantee_err(res == 0, "Could not arm the timer."); - - callback = cb; -} - -void timerfd_provider_t::unschedule_oneshot() { - struct itimerspec spec; - spec.it_interval.tv_sec = 0; - spec.it_interval.tv_nsec = 0; - spec.it_value.tv_sec = 0; - spec.it_value.tv_nsec = 0; - - const int res = timerfd_settime(timer_fd, 0, &spec, nullptr); - guarantee_err(res == 0, "Could not disarm the timer."); - - callback = nullptr; -} - -void timerfd_provider_t::on_event(int events) { - if (events != poll_event_in) { - logERR("Unexpected event mask: %d", events); - } - - eventfd_t nexpirations; - const int res = eventfd_read(timer_fd, &nexpirations); - guarantee_err(res == 0 || get_errno() == EAGAIN, "Could not read timer_fd value"); - if (res == 0 && nexpirations > 0) { - // The callback could be unscheduled but after the timerfd rang, maybe. So we check here. - if (callback != nullptr) { - // Make the callback be NULL before we call it, so that a new callback can be set. - timer_provider_callback_t *local_cb = callback; - callback = nullptr; - local_cb->on_oneshot(); - } - } -} - -#endif // RDB_TIMER_PROVIDER == RDB_TIMER_PROVIDER_TIMERFD diff --git a/src/arch/io/timer/timerfd_provider.hpp b/src/arch/io/timer/timerfd_provider.hpp deleted file mode 100644 index b599f6c8675..00000000000 --- a/src/arch/io/timer/timerfd_provider.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_IO_TIMER_TIMERFD_PROVIDER_HPP_ -#define ARCH_IO_TIMER_TIMERFD_PROVIDER_HPP_ - -#include "arch/runtime/event_queue.hpp" - -struct timer_provider_callback_t; - -struct timerfd_provider_t : public linux_event_callback_t { -public: - explicit timerfd_provider_t(linux_event_queue_t *_queue); - ~timerfd_provider_t(); - - void schedule_oneshot(int64_t next_time_in_nanos, timer_provider_callback_t *cb); - void unschedule_oneshot(); - -private: - void on_event(int events); - - linux_event_queue_t *queue; - fd_t timer_fd; - timer_provider_callback_t *callback; - - DISABLE_COPYING(timerfd_provider_t); -}; - -#endif // ARCH_IO_TIMER_TIMERFD_PROVIDER_HPP_ - diff --git a/src/arch/io/timer_provider.hpp b/src/arch/io/timer_provider.hpp deleted file mode 100644 index 63e8bb2d8b3..00000000000 --- a/src/arch/io/timer_provider.hpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_IO_TIMER_PROVIDER_HPP_ -#define ARCH_IO_TIMER_PROVIDER_HPP_ - -// We pick the right timer provider (that impelements OS level timer interface) depending on which -// system we're on. Some older kernels don't support fdtimers, so we have to resort to signals. OS -// X doesn't support either Linux timer, but kqueue can act like a timerfd. - -// These values are defined such that you can safely check that this header file was included by -// testing whether RDB_TIMER_PROVIDER has been defined, and such that RDB_TIMER_PROVIDER == -// RDB_TIMER_PROVIDER_FOO will be false if the header file was not included. - -#define RDB_TIMER_PROVIDER_TIMERFD 1 -#define RDB_TIMER_PROVIDER_KQUEUE 2 -#define RDB_TIMER_PROVIDER_SIGNAL 3 -#define RDB_TIMER_PROVIDER_WINDOWS 4 - -#if defined(_WIN32) -#define RDB_TIMER_PROVIDER RDB_TIMER_PROVIDER_WINDOWS -#elif defined(LEGACY_LINUX) -#define RDB_TIMER_PROVIDER RDB_TIMER_PROVIDER_SIGNAL -#elif defined(__MACH__) -#define RDB_TIMER_PROVIDER RDB_TIMER_PROVIDER_KQUEUE -#else -#define RDB_TIMER_PROVIDER RDB_TIMER_PROVIDER_TIMERFD -#endif - -#if RDB_TIMER_PROVIDER == RDB_TIMER_PROVIDER_SIGNAL -#include "arch/io/timer/timer_signal_provider.hpp" -typedef timer_signal_provider_t timer_provider_t; -#elif RDB_TIMER_PROVIDER == RDB_TIMER_PROVIDER_KQUEUE -#include "arch/io/timer/timer_kqueue_provider.hpp" -typedef timer_kqueue_provider_t timer_provider_t; -#elif RDB_TIMER_PROVIDER == RDB_TIMER_PROVIDER_TIMERFD -#include "arch/io/timer/timerfd_provider.hpp" -typedef timerfd_provider_t timer_provider_t; -#elif RDB_TIMER_PROVIDER == RDB_TIMER_PROVIDER_WINDOWS -#include "arch/io/timer/timer_windows_provider.hpp" -typedef timer_windows_provider_t timer_provider_t; -#else // RDB_TIMER_PROVIDER == ... -#error "No timer provider define specified." -#endif // RDB_TIMER_PROVIDER == ... - -struct timer_provider_callback_t { - virtual ~timer_provider_callback_t() { } - virtual void on_oneshot() = 0; -}; - -#endif // ARCH_IO_TIMER_PROVIDER_HPP_ diff --git a/src/arch/os_signal.cc b/src/arch/os_signal.cc deleted file mode 100644 index e0f919df792..00000000000 --- a/src/arch/os_signal.cc +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. - -#include "arch/os_signal.hpp" - -#include "arch/runtime/thread_pool.hpp" -#include "arch/types.hpp" -#include "do_on_thread.hpp" - -os_signal_cond_t::os_signal_cond_t() : -#ifdef _WIN32 - source_type(0) -#else - source_pid(-1), - source_uid(-1), - source_signo(0) -#endif -{ - DEBUG_VAR thread_message_t *old = linux_thread_pool_t::get_thread_pool()->exchange_interrupt_message(this); - rassert(old == NULL); -} - -os_signal_cond_t::~os_signal_cond_t() { - if (!is_pulsed()) { - DEBUG_VAR thread_message_t *old = thread_pool_t::get_thread_pool()->exchange_interrupt_message(NULL); - rassert(old == this); - } -} - -void os_signal_cond_t::on_thread_switch() { - // TODO: There's probably an outside chance of a use-after-free bug here. - do_on_thread(home_thread(), std::bind(&os_signal_cond_t::pulse, this)); -} - -std::string os_signal_cond_t::format() { -#ifdef _WIN32 - switch (source_type) { - case CTRL_C_EVENT: - return "Control-C"; - case CTRL_BREAK_EVENT: - return "Control-Break"; - case CTRL_CLOSE_EVENT: - return "Close (End Task) Event"; - case CTRL_LOGOFF_EVENT: - return "Logoff Event"; - case CTRL_SHUTDOWN_EVENT: - return "Shutdown Event"; - default: - return strprintf("console signal %ud", source_type); - } -#else - switch (source_signo) { - case SIGINT: - return strprintf("SIGINT from pid %d, uid %d", - source_pid, source_uid); - case SIGTERM: - return strprintf("SIGTERM from pid %d, uid %d", - source_pid, source_uid); - default: - return strprintf("signal %d from pid %d, uid %d", - source_signo, source_pid, source_uid); - } -#endif -} diff --git a/src/arch/os_signal.hpp b/src/arch/os_signal.hpp deleted file mode 100644 index df82049ebf7..00000000000 --- a/src/arch/os_signal.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_OS_SIGNAL_HPP_ -#define ARCH_OS_SIGNAL_HPP_ - -#include "concurrency/cond_var.hpp" -#include "arch/runtime/runtime_utils.hpp" - -class os_signal_cond_t : public thread_message_t, - public cond_t -{ -public: - os_signal_cond_t(); - ~os_signal_cond_t(); - void on_thread_switch(); - std::string format(); - -private: - friend class linux_thread_pool_t; -#ifdef _WIN32 - DWORD source_type; -#else - pid_t source_pid; - uid_t source_uid; - int source_signo; -#endif -}; - -#endif // ARCH_OS_SIGNAL_HPP_ diff --git a/src/arch/process.hpp b/src/arch/process.hpp deleted file mode 100644 index 37f3c9e230d..00000000000 --- a/src/arch/process.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef ARCH_PROCESS_HPP_ -#define ARCH_PROCESS_HPP_ - -#ifdef _WIN32 - -#include "windows.hpp" - -typedef DWORD process_id_t; - -const process_id_t INVALID_PROCESS_ID = 0; - -inline process_id_t current_process() { - return GetCurrentProcessId(); -} - -#else - -#include "rpc/serialize_macros.hpp" - -typedef pid_t process_id_t; - -const process_id_t INVALID_PROCESS_ID = -1; - -inline process_id_t current_process() { - return getpid(); -} - -#endif - -#endif diff --git a/src/arch/runtime/callable_action.hpp b/src/arch/runtime/callable_action.hpp deleted file mode 100644 index 40f7d32d134..00000000000 --- a/src/arch/runtime/callable_action.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef ARCH_RUNTIME_CALLABLE_ACTION_HPP_ -#define ARCH_RUNTIME_CALLABLE_ACTION_HPP_ - -#include - -#include "arch/compiler.hpp" -#include "errors.hpp" - -/* The below classes may be used to create a generic callable object without - std::function so as to avoid the heap allocation that std::functions use. - Allocate a callable_action_wrapper_t (preferrably on the stack), then assign - any callable object into it. The wrapper will only use the heap if it can't - fit inside the internal pre-allocated buffer. */ - -#define CALLABLE_CUTOFF_SIZE 128 - -class callable_action_t { -public: - virtual void run_action() = 0; - callable_action_t() { } - virtual ~callable_action_t() { } -private: - DISABLE_COPYING(callable_action_t); -}; - -template -class callable_action_instance_t : public callable_action_t { -public: - explicit callable_action_instance_t(Callable &&callable) - : callable_(std::forward(callable)) { } - - void run_action() { callable_(); } - -private: - Callable callable_; -}; - -class callable_action_wrapper_t { -public: - callable_action_wrapper_t(); - ~callable_action_wrapper_t(); - - template - void reset(Callable &&action) - { - rassert(action_ == NULL); - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunreachable-code" -#endif -#if defined(__GNUC__) && (100 * __GNUC__ + __GNUC_MINOR__ >= 600) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wplacement-new=" -#endif - // Allocate the action inside this object, if possible, or the heap otherwise - if (sizeof(callable_action_instance_t) > CALLABLE_CUTOFF_SIZE) { - action_ = new callable_action_instance_t( - std::forward(action)); - action_on_heap = true; - } else { - action_ = new (action_data) callable_action_instance_t( - std::forward(action)); - action_on_heap = false; - } -#if defined(__GNUC__) && (100 * __GNUC__ + __GNUC_MINOR__ >= 600) -#pragma GCC diagnostic pop -#endif -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - } - - void reset(); - - void run(); - -private: - bool action_on_heap; - callable_action_t *action_; - ATTR_ALIGNED(sizeof(void*)) char action_data[CALLABLE_CUTOFF_SIZE]; - - DISABLE_COPYING(callable_action_wrapper_t); -}; - - -#endif // ARCH_RUNTIME_CALLABLE_ACTION_HPP_ diff --git a/src/arch/runtime/context_switching.cc b/src/arch/runtime/context_switching.cc deleted file mode 100644 index 9e254a79574..00000000000 --- a/src/arch/runtime/context_switching.cc +++ /dev/null @@ -1,924 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. - -#ifdef _WIN32 - -#include - -#include "windows.hpp" -#include "errors.hpp" -#include "arch/runtime/context_switching.hpp" -#include "arch/compiler.hpp" -#include "logger.hpp" - -// Some declarations used to measure the stack size -// from http://undocumented.ntinternals.net/ - -#include - -typedef struct { - PVOID UniqueProcess; - PVOID UniqueThread; -} CLIENT_ID; - -typedef LONG KPRIORITY; - -typedef struct { - NTSTATUS ExitStatus; - PVOID TebBaseAddress; - CLIENT_ID ClientId; - KAFFINITY AffinityMask; - KPRIORITY Priority; - KPRIORITY BasePriority; -} THREAD_BASIC_INFORMATION; - -typedef NTSYSAPI NTSTATUS -(NTAPI *NtReadVirtualMemory_t)(IN HANDLE ProcessHandle, - IN PVOID BaseAddress, - OUT PVOID Buffer, - IN ULONG NumberOfBytesToRead, - OUT PULONG NumberOfBytesReaded); - -enum THREADINFOCLASS { ThreadBasicInformation = 0 }; - -typedef NTSTATUS -(WINAPI *NtQueryInformationThread_t)(HANDLE ThreadHandle, - THREADINFOCLASS ThreadInformationClass, - PVOID ThreadInformation, - ULONG ThreadInformationLength, - PULONG ReturnLength); - -fiber_context_ref_t::~fiber_context_ref_t() { - rassert(fiber == nullptr, "leaking a fiber"); -} - -THREAD_LOCAL void* thread_initial_fiber = nullptr; - -void coro_initialize_for_thread() { - if (thread_initial_fiber == nullptr) { - thread_initial_fiber = ConvertThreadToFiber(nullptr); - guarantee_winerr(thread_initial_fiber != nullptr, "ConvertThreadToFiber failed"); - } -} - -void save_stack_info(fiber_stack_t *stack) { - static HMODULE ntdll = LoadLibrary("ntdll.dll"); - static NtQueryInformationThread_t NtQueryInformationThread = - reinterpret_cast( - GetProcAddress(ntdll, "NtQueryInformationThread")); - static NtReadVirtualMemory_t NtReadVirtualMemory = - reinterpret_cast( - GetProcAddress(ntdll, "NtReadVirtualMemory")); - static bool warned = false; - if (NtQueryInformationThread == nullptr || - NtReadVirtualMemory == nullptr) { - if (!warned) { - logWRN("save_stack_info: GetProcAddress failed"); - warned = true; - } - stack->has_stack_info = false; - return; - } - THREAD_BASIC_INFORMATION info; - NTSTATUS res = NtQueryInformationThread(GetCurrentThread(), - ThreadBasicInformation, - &info, - sizeof(info), - nullptr); - if (res != 0) { - logWRN("NtQueryInformationThread failed: %s", - winerr_string(LsaNtStatusToWinError(res)).c_str()); - stack->has_stack_info = false; - return; - } - NT_TIB tib; - res = NtReadVirtualMemory(GetCurrentProcess(), - info.TebBaseAddress, - &tib, - sizeof(tib), - nullptr); - if (res != 0) { - logWRN("NtReadVirtualMemory failed: %s", - winerr_string(LsaNtStatusToWinError(res)).c_str()); - stack->has_stack_info = false; - return; - } - stack->stack_base = reinterpret_cast(tib.StackBase); - stack->stack_limit = reinterpret_cast(tib.StackLimit); - stack->has_stack_info = true; -} - -fiber_stack_t::fiber_stack_t(void(*initial_fun)(void), size_t stack_size) { - auto tuple = std::make_tuple(initial_fun, this, GetCurrentFiber()); - typedef decltype(tuple) data_t; - context.fiber = CreateFiberEx( - stack_size, - stack_size, - 0, // don't switch floating-point state - [](void* data) { - void (*initial_fun)(); - fiber_stack_t *self; - void *parent_fiber; - std::tie(initial_fun, self, parent_fiber) = *reinterpret_cast(data); - save_stack_info(self); - SwitchToFiber(parent_fiber); - initial_fun(); - }, - reinterpret_cast(&tuple)); - guarantee_winerr(context.fiber != nullptr, "CreateFiber failed"); - SwitchToFiber(context.fiber); -} - -fiber_stack_t::~fiber_stack_t() { - DeleteFiber(context.fiber); - context.fiber = nullptr; -} - -bool fiber_stack_t::address_in_stack(const void *addr) const { - if (!has_stack_info) { - rassert(has_stack_info, "could not determine stack limits"); - return true; - } - const char *a = reinterpret_cast(addr); - return stack_limit < a && a <= stack_base; -} - -bool fiber_stack_t::address_is_stack_overflow(const void *addr) const { - if (!has_stack_info) { - rassert(has_stack_info, "could not determine stack limits"); - return false; - } - return reinterpret_cast(addr) <= stack_limit; -} - -size_t fiber_stack_t::free_space_below(const void *addr) const { - guarantee(address_in_stack(addr)); - return reinterpret_cast(addr) - stack_limit; -} - -void context_switch(fiber_context_ref_t *curr_context_out, fiber_context_ref_t *dest_context_in) { - rassert(curr_context_out->fiber == nullptr, "switching from non-null context: %p", curr_context_out->fiber); - rassert(dest_context_in->fiber != nullptr); - curr_context_out->fiber = GetCurrentFiber(); - void *dest_context = dest_context_in->fiber; - dest_context_in->fiber = nullptr; - SwitchToFiber(dest_context); -} - -#else - -#include "arch/runtime/context_switching.hpp" - -#include -#include -#include -#include - -#ifndef NDEBUG -#include // For __cxa_current_exception_type (see below) -#endif - -#ifdef VALGRIND -#include -#endif - -#include "arch/runtime/thread_pool.hpp" -#include "arch/runtime/coroutines.hpp" -#include "arch/io/concurrency.hpp" -#include "containers/scoped.hpp" -#include "errors.hpp" -#include "math.hpp" -#include "utils.hpp" - -/* We have a custom implementation of `swapcontext()` that doesn't swap the -floating-point registers, the SSE registers, or the signal mask. This is for -performance reasons. - -Our custom context-switching code is derived from GLibC, which is covered by the -LGPL. */ - -artificial_stack_context_ref_t::artificial_stack_context_ref_t() : pointer(nullptr) { } - -artificial_stack_context_ref_t::~artificial_stack_context_ref_t() { - rassert(is_nil(), "You're leaking a context."); -} - -bool artificial_stack_context_ref_t::is_nil() { - return pointer == nullptr; -} - -artificial_stack_t::artificial_stack_t(void (*initial_fun)(void), size_t _stack_size) - : stack(_stack_size), stack_size(_stack_size), overflow_protection_enabled(false) { - - // Tell the operating system that it can unmap the stack space - // (except for the first page, which we are definitely going to need). - // This is an optimization to keep memory consumption in check. - guarantee(stack_size >= static_cast(getpagesize())); - madvise(stack.get(), stack_size - getpagesize(), MADV_DONTNEED); - - // Register our stack with Valgrind so that it understands what's going on - // and doesn't create spurious errors. -#ifdef VALGRIND - valgrind_stack_id = VALGRIND_STACK_REGISTER(stack.get(), (intptr_t)stack.get() + stack_size); -#endif - - // Setup the new stack (grows downwards). - // - // +---------------+ <- (1) initial sp. - // | | - // +---------------+ <- (2) sp aligned as needed. - // | | - // +---------------+ <- (3) sp after allocating (fake) caller stack frame; - // | initial_fun | will be the stack pointer at entry to initial_fun. - // +---------------+ <- (4) sp after pushing the return address, initial_fun. - // | | - // | callee-save | - // | registers | - // | | - // +---------------+ <- (5) sp after allocating space for callee-save registers. - // - // When swapcontext is called it will pop the callee-save registers off of the - // stack (may be junk), pop initial_fun off the stack and then jump to initial_fun. - uintptr_t *sp; - - // (1) initialize sp to point at the top of the stack. - sp = reinterpret_cast(uintptr_t(stack.get()) + stack_size); - - // (2) align sp to meet platform ABI requirements. - // Note: not all platforms require 16-byte alignment, but it is easier to do it - // everywhere. - sp = reinterpret_cast(uintptr_t(sp) & static_cast(-16L)); - - // (3) allocate caller stack frame. -#if defined(__i386__) || defined(__x86_64__) - // The x86-64 ABI requires the stack pointer to always be 16-byte-aligned at - // function calls. That is, "(%rsp - 8) is always a multiple of 16 when control - // is transferred to the function entry point". - const size_t min_frame = 1; -#elif defined(__s390x__) - // The s390x ABI requires a 160-byte caller allocated register save area. - const size_t min_frame = 20; -#elif defined(__arm__) - // This slot is used to store r12. - const size_t min_frame = 1; -#elif defined(__arm64__) || defined(__aarch64__) - // The ARM64 ABI requires the stack pointer to always be 16-byte-aligned at - // all registers. - const size_t min_frame = 1; -#elif defined(__powerpc64__) - const size_t min_frame = 4; -#endif - // Zero the caller stack frame. Prevents Valgrind complaining about uninitialized - // value errors when throwing an uncaught exception. - for (size_t i = 0; i < min_frame; i++) { - sp--; - *sp = 0; - } - - // (4) write the return address to the stack. - sp--; - *sp = reinterpret_cast(initial_fun); - - // (5) allocate space for callee-save registers. These will be popped off the - // stack by swapcontext but initial_fun should ignore their contents so we - // don't initialize them to anything. - // See swapcontext for more information about the way registers are restored. -#if defined(__i386__) - sp -= 4; // esi, edi, ebx and ebp. -#elif defined(__x86_64__) - sp -= 6; // r12-r15, rbx and rbp. -#elif defined(__arm__) - // Note: r12 is also stored, in the 'caller frame' slot above the return - // address. - sp -= 8; // r4-r11. -#elif defined(__arm64__) || defined(__aarch64__) - sp -= 20; // d8-d15 + x19-x30 + x30 -#elif defined(__s390x__) - sp -= 16; // r6-r13 and f8-f15. -#elif defined (__powerpc64__) - // Considering the performance, non volatile floating point and vector - // registers are saved and restored for powerpc. There are no failures - // seen during context switch on ubuntu/RHEL powerpc64le systems. - sp -= 20; // r14-r31, toc, cr. -#else -#error "Unsupported architecture." -#endif - - // Save stack pointer. - context.pointer = sp; - - // Our coroutines never return, so we don't put anything else on the stack. -} - -artificial_stack_t::~artificial_stack_t() { - - /* `context` must now point to what it was when we were created. If it - doesn't, that means we're deleting the stack but the corresponding context - is still "out there" somewhere. */ - rassert(!context.is_nil(), "we never got our context back"); - rassert(address_in_stack(context.pointer), "we got the wrong context back"); - - /* Set `context.pointer` to nil to avoid tripping an assertion in its - destructor. */ - context.pointer = nullptr; - - /* Tell Valgrind the stack is no longer in use */ -#ifdef VALGRIND -// Disable GCC diagnostic for VALGRIND_STACK_DEREGISTER. The flag -// exists only in GCC 4.6 and greater. See the "reenable" comment -// after the destructor. -#if defined(__GNUC__) && (100 * __GNUC__ + __GNUC_MINOR__ >= 406) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-but-set-variable" -#endif - VALGRIND_STACK_DEREGISTER(valgrind_stack_id); -// Reenable GCC diagnostic for VALGRIND_STACK_DEREGISTER. -#if defined(__GNUC__) && (100 * __GNUC__ + __GNUC_MINOR__ >= 406) -#pragma GCC diagnostic pop -#endif -#endif - - /* Undo protections changes */ - disable_overflow_protection(); - - /* Return the memory to the operating system right away. This makes - sense because we keep our own cache of coroutine stacks around and - don't need to rely on the allocator to optimize for the case of - us quickly re-allocating an object of the same size. - On OS X we use MADV_FREE. On Linux MADV_FREE is not available, - and we use MADV_DONTNEED instead. */ -#ifdef __MACH__ - madvise(stack.get(), stack_size, MADV_FREE); -#else - madvise(stack.get(), stack_size, MADV_DONTNEED); -#endif -} - -/* Wrapper around `mprotect` that checks the return code. */ -void checked_mprotect_page(void *page_addr, int prot) { - int res = mprotect(page_addr, getpagesize(), prot); - if (res != 0) { -#ifdef __linux__ - if (get_errno() == ENOMEM) { - crash("Failed to (un-)protect a coroutine stack (`mprotect` failed with " - "`ENOMEM`). Try increasing the value of `/proc/sys/vm/max_map_count`."); - } -#endif - crash("Failed to (un-)protect a coroutine stack. `mprotect` failed with error " - "code %d.", get_errno()); - } -} - -void artificial_stack_t::enable_overflow_protection() { - /* Protect the end of the stack so that we crash when we get a stack - overflow instead of corrupting memory. */ - if (overflow_protection_enabled) { - return; - } - /* OS X Instruments hangs when running with mprotect and having object identification - enabled. We don't need it for THREADED_COROUTINES anyway, so don't use it then. */ -#ifndef THREADED_COROUTINES - checked_mprotect_page(stack.get(), PROT_NONE); - overflow_protection_enabled = true; -#endif -} - -void artificial_stack_t::disable_overflow_protection() { - if (!overflow_protection_enabled) { - return; - } -#ifndef THREADED_COROUTINES - checked_mprotect_page(stack.get(), PROT_READ | PROT_WRITE); - overflow_protection_enabled = false; -#endif -} - -bool artificial_stack_t::address_in_stack(const void *addr) const { - return reinterpret_cast(addr) >= - reinterpret_cast(get_stack_bound()) - && reinterpret_cast(addr) < - reinterpret_cast(get_stack_base()); -} - -bool artificial_stack_t::address_is_stack_overflow(const void *addr) const { - void *addr_base = - reinterpret_cast(floor_aligned(reinterpret_cast(addr), - getpagesize())); - return get_stack_bound() == addr_base; -} - -size_t artificial_stack_t::free_space_below(const void *addr) const { - rassert(address_in_stack(addr) && !address_is_stack_overflow(addr)); - - // The bottom page is protected and used to detect stack overflows. Everything - // above that is usable space. - const uintptr_t lowest_valid_address = - reinterpret_cast(get_stack_bound()) + getpagesize(); - - // This check is pretty much equivalent to checking `address_is_stack_overflow`, but - // we avoid the extra call to `getpagesize()` inside of `address_is_stack_overflow` - // in release mode. - guarantee(lowest_valid_address <= reinterpret_cast(addr)); - - return reinterpret_cast(addr) - lowest_valid_address; -} - -extern "C" { -// `lightweight_swapcontext` is defined in assembly further down. If we didn't add the -// asm("_lightweight_swapcontext") here, we'd have to conditionally compile the symbol name in the -// assembly directive to include the underscore on OS X and certain other platforms, or use the -// -fleading-underscore which would be trickier and riskier. -extern void lightweight_swapcontext(void **current_pointer_out, void *dest_pointer) - asm("_lightweight_swapcontext"); -} - -void context_switch(artificial_stack_context_ref_t *current_context_out, artificial_stack_context_ref_t *dest_context_in) { - - /* In the catch-clause of a C++ exception handler, executing a `throw` with - no parameter will re-throw the exception that we caught. This works even if - the parameter-less `throw` is in a function called by the exception handler. - C++ accomplishes this by storing the current exception in a thread-local - variable. We have no way to save and restore the value of this variable when - switching contexts, so instead we just ban context switching from within a - `catch`-block. We use the non-standard GCC-only interface "cxxabi.h" to - figure out if we're in the catch clause of an exception handler. In C++0x we - will be able to use std::current_exception() instead. */ - rassert(!abi::__cxa_current_exception_type(), "It's not safe to switch " - "coroutine stacks in the catch-clause of an exception handler."); - - rassert(current_context_out->is_nil(), "that variable already holds a context"); - rassert(!dest_context_in->is_nil(), "cannot switch to nil context"); - - /* `lightweight_swapcontext()` won't set `dest_context_in->pointer` to nullptr, - so we have to do that ourselves. */ - void *dest_pointer = dest_context_in->pointer; - dest_context_in->pointer = nullptr; - - lightweight_swapcontext(¤t_context_out->pointer, dest_pointer); -} - -asm( -#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__arm64__) || defined(__aarch64__) || defined (__s390x__) || defined (__powerpc64__) -// We keep architecture-specific code interleaved in order to enforce commonality. -#if defined(__x86_64__) -#if defined(__LP64__) || defined(__LLP64__) -// Pointers are of the right size -#else -// Having non-native-sized pointers makes things very messy. -#error "Non-native pointer size." -#endif -#endif // defined(__x86_64__) -".text\n" -"_lightweight_swapcontext:\n" - -#if defined(__i386__) - /* `current_pointer_out` is in `4(%ebp)`. `dest_pointer` is in `8(%ebp)`. */ -#elif defined(__x86_64__) - /* `current_pointer_out` is in `%rdi`. `dest_pointer` is in `%rsi`. */ -#elif defined(__arm__) - /* `current_pointer_out` is in `r0`. `dest_pointer` is in `r1` */ -#elif defined(__s390x_) - /* `current_pointer_out` is in `%r2`. `dest_pointer` is in `%r3`. */ -#elif defined(__powerpc64__) - /* `current_pointer_out` is in `r3`. `dest_pointer` is in `r4` */ -#endif - - // Save preserved registers. -#if defined(__i386__) - // Preserve esi, edi, ebx, and ebp. The return address is already on the stack. - "push %esi\n" - "push %edi\n" - "push %ebx\n" - "push %ebp\n" -#elif defined(__x86_64__) - // Preserve r12-r15, rbx, and rbp. The return address is already on the stack. - "pushq %r12\n" - "pushq %r13\n" - "pushq %r14\n" - "pushq %r15\n" - "pushq %rbx\n" - "pushq %rbp\n" -#elif defined(__arm__) - // Preserve r4-r12 and the return address (r14). For consistency with x86 r12 is - // pushed first, followed by r14 and then r4-r11. - "push {r12}\n" - "push {r14}\n" - "push {r4-r11}\n" -#elif defined(__arm64__) || defined(__aarch64__) - // Preserve d8-d15 + x19-x29 and the return address (x30). - // Note: x30 is stored twice due to alignment requirements - "sub sp, sp, #0xb0\n" - "stp d8, d9, [sp, #0x00]\n" - "stp d10, d11, [sp, #0x10]\n" - "stp d12, d13, [sp, #0x20]\n" - "stp d14, d15, [sp, #0x30]\n" - "stp x19, x20, [sp, #0x40]\n" - "stp x21, x22, [sp, #0x50]\n" - "stp x23, x24, [sp, #0x60]\n" - "stp x25, x26, [sp, #0x70]\n" - "stp x27, x28, [sp, #0x80]\n" - "stp x29, x30, [sp, #0x90]\n" - "str x30, [sp, #0xa0]\n" -#elif defined(__s390x__) - // Preserve r6-r13, the return address (r14), and f8-f15. - "aghi %r15, -136\n" - "stmg %r6, %r14, 64(%r15)\n" - "std %f8, 0(%r15)\n" - "std %f9, 8(%r15)\n" - "std %f10, 16(%r15)\n" - "std %f11, 24(%r15)\n" - "std %f12, 32(%r15)\n" - "std %f13, 40(%r15)\n" - "std %f14, 48(%r15)\n" - "std %f15, 56(%r15)\n" -#elif defined(__powerpc64__) - "addi 1, 1, -(21*8)\n" - "std 2, (8*0)(1)\n" - "std 14, (8*1)(1)\n" - "std 15, (8*2)(1)\n" - "std 16, (8*3)(1)\n" - "std 17, (8*4)(1)\n" - "std 18, (8*5)(1)\n" - "std 19, (8*6)(1)\n" - "std 20, (8*7)(1)\n" - "std 21, (8*8)(1)\n" - "std 22, (8*9)(1)\n" - "std 23, (8*10)(1)\n" - "std 24, (8*11)(1)\n" - "std 25, (8*12)(1)\n" - "std 26, (8*13)(1)\n" - "std 27, (8*14)(1)\n" - "std 28, (8*15)(1)\n" - "std 29, (8*16)(1)\n" - "std 30, (8*17)(1)\n" - "std 31, (8*18)(1)\n" - "mfcr 0\n" - "std 0, (8*19)(1)\n" - "mflr 0\n" - "std 0, (8*20)(1)\n" -#endif - - /* Save old stack pointer. */ -#if defined(__i386__) - /* i386 passes arguments on the stack. We add ((number of things pushed)+1)*(sizeof(void*)) to esp in order to get the first argument. */ - "mov 20(%esp), %ecx\n" - /* We then copy the stack pointer into the space indicated by the first argument. */ - "mov %esp, (%ecx)\n" -#elif defined(__x86_64__) - /* On amd64, the first argument comes from rdi. */ - "movq %rsp, (%rdi)\n" -#elif defined(__arm__) - /* On ARM, the first argument is in `r0`. `r13` is the stack pointer. */ - "str r13, [r0]\n" -#elif defined(__arm64__) || defined(__aarch64__) - /* On ARM64, the first argument is in `x0`. `sp` is the stack pointer and `x4` is a scratch register. */ - "mov x4, sp\n" - "str x4, [x0]\n" -#elif defined(__s390x__) - /* On s390x, the first argument is in r2. r15 is the stack pointer. */ - "stg %r15, 0(%r2)\n" -#elif defined(__powerpc64__) - "std 1, 0(3)\n" -#endif - - /* Load the new stack pointer and the preserved registers. */ -#if defined(__i386__) - /* i386 passes arguments on the stack. We add ((number of things pushed)+1)*(sizeof(void*)) to esp in order to get the first argument. */ - "mov 24(%esp), %esi\n" - /* We then copy the second argument to be the new stack pointer. */ - "mov %esi, %esp\n" -#elif defined(__x86_64__) - /* On amd64, the second argument comes from rsi. */ - "movq %rsi, %rsp\n" -#elif defined(__arm__) - /* On ARM, the second argument is in `r1` */ - "mov r13, r1\n" -#elif defined(__arm64__) || defined(__aarch64__) - /* On ARM64, the second argument is in `x1` */ - "mov sp, x1\n" -#elif defined(__s390x__) - /* On s390x, the second argument is in r3 */ - "lgr %r15, %r3\n" -#elif defined(__powerpc64__) - "mr 1, 4\n" -#endif - -#if defined(__i386__) - "pop %ebp\n" - "pop %ebx\n" - "pop %edi\n" - "pop %esi\n" -#elif defined(__x86_64__) - "popq %rbp\n" - "popq %rbx\n" - "popq %r15\n" - "popq %r14\n" - "popq %r13\n" - "popq %r12\n" -#elif defined(__arm__) - "pop {r4-r11}\n" - "pop {r14}\n" - "pop {r12}\n" -#elif defined(__arm64__) || defined(__aarch64__) - "ldp d8, d9, [sp, #0x00]\n" - "ldp d10, d11, [sp, #0x10]\n" - "ldp d12, d13, [sp, #0x20]\n" - "ldp d14, d15, [sp, #0x30]\n" - "ldp x19, x20, [sp, #0x40]\n" - "ldp x21, x22, [sp, #0x50]\n" - "ldp x23, x24, [sp, #0x60]\n" - "ldp x25, x26, [sp, #0x70]\n" - "ldp x27, x28, [sp, #0x80]\n" - "ldp x29, x30, [sp, #0x90]\n" - "ldr x4, [sp, #0xa0]\n" - "add sp, sp, #0xb0\n" -#elif defined(__s390x__) - "lmg %r6, %r14, 64(%r15)\n" - "ld %f8, 0(%r15)\n" - "ld %f9, 8(%r15)\n" - "ld %f10, 16(%r15)\n" - "ld %f11, 24(%r15)\n" - "ld %f12, 32(%r15)\n" - "ld %f13, 40(%r15)\n" - "ld %f14, 48(%r15)\n" - "ld %f15, 56(%r15)\n" - "aghi %r15, 136\n" -#elif defined(__powerpc64__) - "ld 2, (8*0)(1)\n" - "ld 14, (8*1)(1)\n" - "ld 15, (8*2)(1)\n" - "ld 16, (8*3)(1)\n" - "ld 17, (8*4)(1)\n" - "ld 18, (8*5)(1)\n" - "ld 19, (8*6)(1)\n" - "ld 20, (8*7)(1)\n" - "ld 21, (8*8)(1)\n" - "ld 22, (8*9)(1)\n" - "ld 23, (8*10)(1)\n" - "ld 24, (8*11)(1)\n" - "ld 25, (8*12)(1)\n" - "ld 26, (8*13)(1)\n" - "ld 27, (8*14)(1)\n" - "ld 28, (8*15)(1)\n" - "ld 29, (8*16)(1)\n" - "ld 30, (8*17)(1)\n" - "ld 31, (8*18)(1)\n" - "ld 0, (8*19)(1)\n" - "mtcr 0\n" - "ld 0, (8*20)(1)\n" - "mtlr 0\n" - "addi 1, 1, (8*21)\n" -#endif - -#if defined(__i386__) || defined(__x86_64__) - /* The following ret should return to the address set with - `artificial_stack_t()` or with the previous `lightweight_swapcontext`. The - instruction pointer is saved on the stack from the previous call (or - initialized with `artificial_stack_t()`). */ - "ret\n" -#elif defined(__arm__) - /* Above, we popped `LR` (`r14`) off the stack, so the bx instruction will - jump to the correct return address. */ - "bx r14\n" -#elif defined(__arm64__) || defined(__aarch64__) - /* Above, we stored the `x30` the return address in a variable register `x4` so the ret instruction will - return it to jump. */ - "ret x4\n" -#elif defined(__s390x__) - /* Above, we popped the return address (r14) off the stack. */ - "br %r14\n" -#elif defined(__powerpc64__) - "blr\n" -#endif - -#else -#error "Unsupported architecture." -#endif -); - - - - - - - - -/* Threaded version of context_switching */ - - - -static system_mutex_t virtual_thread_mutexes[MAX_THREADS]; - -void context_switch(threaded_context_ref_t *current_context, - threaded_context_ref_t *dest_context) { - guarantee(current_context != nullptr); - guarantee(dest_context != nullptr); - - bool is_scheduler = false; - if (!current_context->lock.has()) { - // This must be the scheduler. We need to acquire a lock. - is_scheduler = true; - current_context->lock.init(new system_mutex_t::lock_t( - &virtual_thread_mutexes[linux_thread_pool_t::get_thread_id()])); - } - - dest_context->rethread_to_current(); - dest_context->cond.signal(); - // ==== dest_context runs here ==== - current_context->wait(); - // ==== now back on current_context ==== - if (is_scheduler) { - // We must not hold the lock, or we will get deadlocks because the scheduler - // can end up waiting on a system event. If another thread then - // wants to re-thread to our thread, it will be unable to do so. - current_context->lock.reset(); - } -} - -void threaded_context_ref_t::rethread_to_current() { - int32_t old_thread = my_thread_id; - store_virtual_thread(); - if (my_thread_id == old_thread) { - return; - } - - do_rethread = true; -} - -void threaded_context_ref_t::wait() { - cond.wait(&virtual_thread_mutexes[linux_thread_pool_t::get_thread_id()]); - if (do_shutdown) { - lock.reset(); - pthread_exit(nullptr); - } - if (do_rethread) { - restore_virtual_thread(); - // Re-lock to a different thread mutex - lock.reset(); - lock.init(new system_mutex_t::lock_t( - &virtual_thread_mutexes[linux_thread_pool_t::get_thread_id()])); - do_rethread = false; - } -} - -void threaded_context_ref_t::restore_virtual_thread() { - // Fake our thread - linux_thread_pool_t::set_thread_pool(my_thread_pool); - linux_thread_pool_t::set_thread_id(my_thread_id); - linux_thread_pool_t::set_thread(my_thread); -} - -void threaded_context_ref_t::store_virtual_thread() { - my_thread_pool = linux_thread_pool_t::get_thread_pool(); - my_thread_id = linux_thread_pool_t::get_thread_id(); - my_thread = linux_thread_pool_t::get_thread(); -} - -threaded_stack_t::threaded_stack_t(void (*initial_fun_)(void), size_t stack_size) : - initial_fun(initial_fun_), - dummy_stack(initial_fun, stack_size) { - - scoped_ptr_t possible_lock_acq; - if (!coro_t::self()) { - // If we are not in a coroutine, we have to acquire the lock first. - // (coroutines would already hold the lock) - possible_lock_acq.init(new system_mutex_t::lock_t( - &virtual_thread_mutexes[linux_thread_pool_t::get_thread_id()])); - } - - int result = pthread_create(&thread, - nullptr, - threaded_stack_t::internal_run, - reinterpret_cast(this)); - guarantee_xerr(result == 0, result, "Could not create thread: %i", result); - // Wait for the thread to start - launch_cond.wait(&virtual_thread_mutexes[linux_thread_pool_t::get_thread_id()]); -} - -threaded_stack_t::~threaded_stack_t() { - // Our coroutines never terminate. We have to tell the thread to shut down - // before joining it. - - scoped_ptr_t possible_lock_acq; - if (!coro_t::self()) { - // If we are not in a coroutine, we have to acquire the lock first. - // (coroutines would already hold the lock) - possible_lock_acq.init(new system_mutex_t::lock_t( - &virtual_thread_mutexes[linux_thread_pool_t::get_thread_id()])); - } - - // Calling this destructor is safe only if we are on the same virtual thread as - // the thread that we are destroying. This ensures that it is not currently - // active and not holding a lock on the virtual thread mutex. - guarantee(!context.do_rethread - && context.my_thread_id == linux_thread_pool_t::get_thread_id()); - - // Tell the thread to shut down. - context.do_shutdown = true; - context.cond.signal(); - - // We now have to temporarily release our lock to let the thread - // terminate. - if (coro_t::self()) { -#ifdef THREADED_COROUTINES - threaded_context_ref_t &our_context = static_cast( - coro_t::self()->get_stack()->context); - our_context.lock.reset(); -#else - crash("Threaded stack destructed from a coroutine without " - "THREADED_COROUTINES defined."); -#endif - } else { - possible_lock_acq.reset(); - } - - // Wait for the thread to shut down - int result = pthread_join(thread, nullptr); - guarantee_xerr(result == 0, result, "Could not join with thread: %i", result); - - // ... and re-acquire the lock, if we are in a coroutine. - if (coro_t::self()) { -#ifdef THREADED_COROUTINES - threaded_context_ref_t &our_context = static_cast( - coro_t::self()->get_stack()->context); - our_context.lock.init(new system_mutex_t::lock_t( - &virtual_thread_mutexes[linux_thread_pool_t::get_thread_id()])); -#endif - } -} - -void *threaded_stack_t::internal_run(void *p) { - threaded_stack_t *parent = reinterpret_cast(p); - - parent->context.restore_virtual_thread(); - - parent->context.lock.init(new system_mutex_t::lock_t( - &virtual_thread_mutexes[linux_thread_pool_t::get_thread_id()])); - // Notify our parent that we have been created - parent->launch_cond.signal(); - - parent->context.wait(); - parent->initial_fun(); - return nullptr; -} - -bool threaded_stack_t::address_in_stack(const void *addr) const { - return reinterpret_cast(addr) >= - reinterpret_cast(get_stack_bound()) - && reinterpret_cast(addr) < - reinterpret_cast(get_stack_base()); -} - -bool threaded_stack_t::address_is_stack_overflow(const void *addr) const { - void *addr_base = - reinterpret_cast(floor_aligned(reinterpret_cast(addr), - getpagesize())); - return get_stack_bound() == addr_base; -} - -void *threaded_stack_t::get_stack_base() const { - void *stackaddr; - size_t stacksize; - get_stack_addr_size(&stackaddr, &stacksize); - uintptr_t base = reinterpret_cast(stackaddr) - + static_cast(stacksize); - return reinterpret_cast(base); -} - -void *threaded_stack_t::get_stack_bound() const { - void *stackaddr; - size_t stacksize; - get_stack_addr_size(&stackaddr, &stacksize); - return stackaddr; -} - -size_t threaded_stack_t::free_space_below(const void *addr) const { - guarantee(address_in_stack(addr) && !address_is_stack_overflow(addr)); - // The bottom page is protected and used to detect stack overflows. Everything - // above that is usable space. - return reinterpret_cast(addr) - - (reinterpret_cast(get_stack_bound()) + getpagesize()); -} - -void threaded_stack_t::get_stack_addr_size(void **stackaddr_out, - size_t *stacksize_out) const { -#ifdef __MACH__ - // Implementation for OS X - *stacksize_out = pthread_get_stacksize_np(thread); - *stackaddr_out = reinterpret_cast( - reinterpret_cast(pthread_get_stackaddr_np(thread)) - - static_cast(*stacksize_out)); -#else - // Implementation for Linux - pthread_attr_t attr; - int res = pthread_getattr_np(thread, &attr); - guarantee_xerr(res == 0, res, "Unable to get pthread attributes"); - res = pthread_attr_getstack(&attr, stackaddr_out, stacksize_out); - guarantee_xerr(res == 0, res, "Unable to get pthread stack attribute"); - res = pthread_attr_destroy(&attr); - guarantee_xerr(res == 0, res, "Unable to destroy pthread attributes"); -#endif -} - -/* ^^^^ Threaded version of context_switching ^^^^ */ - -#endif // _WIN32 diff --git a/src/arch/runtime/context_switching.hpp b/src/arch/runtime/context_switching.hpp deleted file mode 100644 index 8e5beba5236..00000000000 --- a/src/arch/runtime/context_switching.hpp +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_CONTEXT_SWITCHING_HPP_ -#define ARCH_RUNTIME_CONTEXT_SWITCHING_HPP_ - -#ifdef _WIN32 - -struct fiber_context_ref_t { - void* fiber = nullptr; - bool is_nil() { return fiber == nullptr; } - ~fiber_context_ref_t(); -}; - -void coro_initialize_for_thread(); - -class fiber_stack_t { -public: - fiber_stack_t(void(*initial_fun)(void), size_t stack_size); - ~fiber_stack_t(); - fiber_context_ref_t context; - bool has_stack_info; - char *stack_base; - char *stack_limit; - bool address_in_stack(const void *addr) const; - bool address_is_stack_overflow(const void *addr) const; - size_t free_space_below(const void *addr) const; - - /* These two are currently not implemented for fiber stacks. - I think fibers always have some overflow protection though? */ - void enable_overflow_protection() {} - void disable_overflow_protection() {} -}; - -void context_switch(fiber_context_ref_t *current_context_out, fiber_context_ref_t *dest_context_in); - -typedef fiber_stack_t coro_stack_t; -typedef fiber_context_ref_t coro_context_ref_t; - -#else - -#include - -#include "errors.hpp" - -#include "arch/io/concurrency.hpp" -#include "containers/scoped.hpp" - - -/* Note that `artificial_stack_context_ref_t` is not a POD type. We could make it a POD type, but -at the cost of removing some safety guarantees provided by the constructor and -destructor. */ - -struct artificial_stack_context_ref_t { - - /* Creates a nil `context_ref_t`. */ - artificial_stack_context_ref_t(); - - /* Destroys a `context_ref_t`. It must be nil at the time that this - destructor is called; if you're trying to call the destructor and it's - non-nil, that means you're leaking a stack somewhere. */ - ~artificial_stack_context_ref_t(); - - /* Returns `true` if the `artificial_stack_context_ref_t` is nil, and false otherwise. */ - bool is_nil(); - -private: - friend class artificial_stack_t; - friend void context_switch(artificial_stack_context_ref_t *, - artificial_stack_context_ref_t *); - - /* `pointer` points to a location on the stack of the context in question. - From that pointer, we find the instruction pointer, relevant registers, and - so on. */ - void *pointer; - - DISABLE_COPYING(artificial_stack_context_ref_t); -}; - -class artificial_stack_t { -public: - - /* `artificial_stack_t()` sets up an artificial context. Once it is set up, - you can use `context` to swap into and out of it. When you call - `~artificial_stack_t()`, the original context must have been returned to - `context` again. */ - artificial_stack_t(void (*initial_fun)(void), size_t stack_size); - ~artificial_stack_t(); - - artificial_stack_context_ref_t context; - - /* Returns `true` if the given address is on this stack or in its protection - page. */ - bool address_in_stack(const void *addr) const; - - /* Returns `true` if the given address is in the stack's protection page. */ - bool address_is_stack_overflow(const void *addr) const; - - /* Returns the base of the stack */ - void *get_stack_base() const { return stack.get() + stack_size; } - - /* Returns the end of the stack */ - void *get_stack_bound() const { return stack.get(); } - - /* Returns how many more bytes below the given address can be used */ - size_t free_space_below(const void *addr) const; - - /* Enables stack-smashing protection for this stack, if not already enabled */ - void enable_overflow_protection(); - /* Disables stack-smashing protection for this stack, if currently enabled */ - void disable_overflow_protection(); - -private: - scoped_page_aligned_ptr_t stack; - size_t stack_size; - bool overflow_protection_enabled; -#ifdef VALGRIND - int valgrind_stack_id; -#endif -}; - -/* `context_switch()` switches from one context to another. -`current_context_out` must be nil; it will be filled with the context that we -were in when we called `context_switch()`. `destination_context_in` must be -non-nil; it will be set to nil, and we will switch to it. */ -void context_switch( - artificial_stack_context_ref_t *current_context_out, - artificial_stack_context_ref_t *dest_context_in); - - - -/* ____ Threaded version of context_switching ____ */ - -/* - * This is an alternative implementation where each context runs in a thread of - * its own. The primary use case for this is to allow certain profiling tools - * that would otherwise get confused by our artificial_stack switches to be used - * with RethinkDB. Another use case is to ease porting RethinkDB to new - * architectures in cases where performance doesn't matter much. - * - * Internally, one thread is created per threaded_stack_t. To be compatible - * with the artificial_stack_t implementation, we simulate a fixed number of - * "virtual threads" (up to MAX_THREADS) Each threaded_stack_t has an associated - * virtual thread and only one threaded_stack_t can be active per virtual thread - * at any time, which is enforced through a mutex per virtual thread and a - * condition in each threaded_stack_t. - * - * A context switch then is simply notifying the condition on the threaded_stack_t - * that should be swapped in and then making the previous context wait on its own - * condition (there is some additional logic for handling virtual thread - * switches and switching from/to the global context). - */ - -class linux_thread_pool_t; -class linux_thread_t; - -class threaded_context_ref_t { -public: - threaded_context_ref_t() : do_rethread(false), do_shutdown(false) { - store_virtual_thread(); - } - - /* Every context reference has a condition that can be used to wake it up. - While it is active, it also holds a lock on the virtual_thread_mutexes of - its current virtual thread. */ - system_cond_t cond; - scoped_ptr_t lock; - - /* Returns `true` if the threaded_context_t has not been used yet, - or if it is a reference to the scheduler context and the scheduler is currently - running. - (in other words: if it doesn't currently hold a context reference) */ - bool is_nil() { return !lock.has(); } - - void wait(); - void rethread_to_current(); -private: - friend class threaded_stack_t; - void restore_virtual_thread(); - void store_virtual_thread(); - - bool do_rethread; - bool do_shutdown; - - /* These variables store the values that `restore_virtual_thread()` loads into - linux_thread_pool_t's `thread_pool`, `thread_id` and `thread` variables. - Those variables are thread-local in `linux_thread_pool_t`. Since a threaded - context is actually run in a thread of its own, we have to initialize these - values when the threaded context is activated. - More specifically, we set them to the same values that they had when the - `threaded_context_ref_t` was constructed, so it appears like it ran in - the same thread. Unless the context is re-threaded, in which case - we set them to the values taken from the thread that the context is re-threaded - to. */ - linux_thread_pool_t *my_thread_pool; - int32_t my_thread_id; - linux_thread_t *my_thread; - - DISABLE_COPYING(threaded_context_ref_t); -}; - -void context_switch(threaded_context_ref_t *current_context, - threaded_context_ref_t *dest_context); - -class threaded_stack_t { -public: - - threaded_stack_t(void (*initial_fun_)(void), size_t stack_size); - ~threaded_stack_t(); - - threaded_context_ref_t context; - - /* Returns `true` if the given address is on this stack or in its protection - page. */ - bool address_in_stack(const void *addr) const; - - /* Returns `true` if the given address is in the stack's protection page. */ - bool address_is_stack_overflow(const void *addr) const; - - /* Returns the base of the stack */ - void *get_stack_base() const; - - /* Returns the end of the stack */ - void *get_stack_bound() const; - - /* Returns how many more bytes below the given address can be used */ - size_t free_space_below(const void *addr) const; - - /* These two are currently not implemented for threaded stacks. */ - void enable_overflow_protection() {} - void disable_overflow_protection() {} - -private: - static void *internal_run(void *p); - void get_stack_addr_size(void **stackaddr_out, size_t *stacksize_out) const; - - pthread_t thread; - void (*initial_fun)(void); - system_cond_t launch_cond; - - // To fake the memory overhead of an artificial_stack_t, we actually - // allocate one here and just let it sit around. - // TODO: If we at some point want to use threaded_stack_t for production - // stuff, we will have to remove this. - artificial_stack_t dummy_stack; - - DISABLE_COPYING(threaded_stack_t); -}; - -/* ^^^^ Threaded version of context_switching ^^^^ */ - -#ifdef THREADED_COROUTINES -typedef threaded_stack_t coro_stack_t; -typedef threaded_context_ref_t coro_context_ref_t; -#else -typedef artificial_stack_t coro_stack_t; -typedef artificial_stack_context_ref_t coro_context_ref_t; -#endif - -#endif /* !defined(_WIN32) */ -#endif /* ARCH_RUNTIME_CONTEXT_SWITCHING_HPP_ */ diff --git a/src/arch/runtime/coro_profiler.cc b/src/arch/runtime/coro_profiler.cc deleted file mode 100644 index be53d599da5..00000000000 --- a/src/arch/runtime/coro_profiler.cc +++ /dev/null @@ -1,389 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "arch/runtime/coro_profiler.hpp" - -#ifdef ENABLE_CORO_PROFILER - -#include - -#include -#include -#include - -#include "arch/runtime/coroutines.hpp" -#include "arch/runtime/runtime.hpp" -#include "rethinkdb_backtrace.hpp" -#include "containers/scoped.hpp" -#include "logger.hpp" - -coro_profiler_t &coro_profiler_t::get_global_profiler() { - // Singleton implementation after Scott Meyers. - // Since C++11, this is even thread safe according to here: - // http://stackoverflow.com/questions/1661529/is-meyers-implementation-of-singleton-pattern-thread-safe?lq=1 - static coro_profiler_t profiler; - return profiler; -} - -coro_profiler_t::coro_profiler_t() : reql_output_file(nullptr) { - logINF("Coro profiler activated."); - - const std::string reql_output_filename = - strprintf("coro_profiler_out_%d.py", static_cast(getpid())); - reql_output_file = fopen(reql_output_filename.c_str(), "w"); - if (reql_output_file != nullptr) { - logINF("Writing profiler reports to '%s'", reql_output_filename.c_str()); - write_reql_header(); - } else { - logWRN("Could not open '%s' for writing profiler reports.", reql_output_filename.c_str()); - } -} - -coro_profiler_t::~coro_profiler_t() { - if (reql_output_file != nullptr) { - fclose(reql_output_file); - } -} - -void coro_profiler_t::record_sample(size_t levels_to_strip_from_backtrace) { - if (coro_t::self() == nullptr) return; - - const ticks_t ticks_on_entry = get_ticks(); - - coro_profiler_mixin_t &coro_mixin = static_cast(*coro_t::self()); - bool might_have_to_generate_report = false; - per_thread_samples_t &thread_samples = per_thread_samples[get_thread_id().threadnum].value; - { - const spinlock_acq_t thread_lock(&thread_samples.spinlock); - - // See if we might have to generate a report - if (thread_samples.ticks_at_last_report + CORO_PROFILER_REPORTING_INTERVAL <= ticks_on_entry) { - // There is a chance that we have to generate a report (unless another - // thread is already at it). Let's first release the lock on thread_samples - // before we actually check for that though. That way we can be sure - // not to dead-lock. - might_have_to_generate_report = true; - } - - // Figure out from where we were called. - // We strip ourselves from the backtrace, hence the +1 in the argument to - // `get_current_execution_point().` - const coro_execution_point_key_t execution_point = - get_current_execution_point(levels_to_strip_from_backtrace + 1); - - // Record the sample - per_execution_point_samples_t &execution_point_samples = - thread_samples.per_execution_point_samples[execution_point]; - ++execution_point_samples.num_samples_total; - ticks_t ticks_since_previous = 0; - if (coro_mixin.last_sample_at != 0) { - rassert(coro_mixin.last_sample_at <= ticks_on_entry); - ticks_since_previous = ticks_on_entry - coro_mixin.last_sample_at; - } - rassert(coro_mixin.last_resumed_at <= ticks_on_entry); - rassert(coro_mixin.last_resumed_at > 0); - ticks_t ticks_since_resume = ticks_on_entry - coro_mixin.last_resumed_at; - execution_point_samples.samples.push_back(coro_sample_t(ticks_since_resume, - ticks_since_previous, - coro_t::self()->get_priority())); - coro_mixin.last_sample_at = ticks_on_entry; - } - - if (might_have_to_generate_report) { - const spinlock_acq_t report_interval_lock(&report_interval_spinlock); - if (ticks_at_last_report + CORO_PROFILER_REPORTING_INTERVAL <= ticks_on_entry) { - generate_report(); - } - } - - /* - * Ensign: Captain, we are no longer in warp. - * Captain: What happened? - * Ensign: The profiler is stealing our time and makes us appear slower than we actually are! - * Captain: Can we compensate? - * Ensign: Positive. - * Captain: Do it! - * Ensign: I'm reconfiguring the shield timings er yield timings. That should block its interference. - */ - const ticks_t ticks_on_exit = get_ticks(); - rassert(ticks_on_exit >= ticks_on_entry); - const ticks_t clock_skew = ticks_on_exit - ticks_on_entry; - coro_mixin.last_resumed_at += clock_skew; - coro_mixin.last_sample_at += clock_skew; -} - -void coro_profiler_t::record_coro_resume() { - rassert(coro_t::self()); - - const ticks_t ticks = get_ticks(); - - coro_profiler_mixin_t &coro_mixin = static_cast(*coro_t::self()); - coro_mixin.last_sample_at = ticks; - coro_mixin.last_resumed_at = ticks; -} - -void coro_profiler_t::record_coro_yield(size_t levels_to_strip_from_backtrace) { - rassert(coro_t::self()); - - record_sample(1 + levels_to_strip_from_backtrace); -} - -coro_profiler_t::coro_execution_point_key_t coro_profiler_t::get_current_execution_point( - size_t levels_to_strip_from_backtrace) { - - // Generate call trace - // We strip ourselves, and the frames that are inside `rethinkdb_backtrace()`. - levels_to_strip_from_backtrace += 1 + NUM_FRAMES_INSIDE_RETHINKDB_BACKTRACE; - const size_t max_frames = CORO_PROFILER_BACKTRACE_DEPTH + levels_to_strip_from_backtrace; - void **stack_frames = new void*[max_frames]; - size_t backtrace_size = rethinkdb_backtrace(stack_frames, max_frames); - size_t remaining_size = max_frames - backtrace_size; - if (remaining_size > 0) { - backtrace_size += - coro_t::self()->copy_spawn_backtrace(stack_frames + backtrace_size, remaining_size); - } - small_trace_t trace; - for (size_t i = 0; i < CORO_PROFILER_BACKTRACE_DEPTH; ++i) { - if (i + levels_to_strip_from_backtrace < backtrace_size) { - trace[i] = stack_frames[i + levels_to_strip_from_backtrace]; - } else { - trace[i] = nullptr; - } - } - delete[] stack_frames; - -#ifndef NDEBUG - return coro_execution_point_key_t(coro_t::self()->get_coroutine_type(), trace); -#else - return coro_execution_point_key_t("?", trace); -#endif -} - -void coro_profiler_t::generate_report() { - std::map execution_point_reports; - - // We assume that the global report_interval_spinlock has already been locked by our caller. - { - // Proceed to locking all thread sample structures - std::vector > thread_locks; - for (auto thread_samples = per_thread_samples.begin(); - thread_samples != per_thread_samples.end(); - ++thread_samples) { - thread_locks.push_back( - scoped_ptr_t(new spinlock_acq_t(&thread_samples->value.spinlock))); - } - - // Reset report ticks - const ticks_t ticks = get_ticks(); - ticks_at_last_report = ticks; - for (auto thread_samples = per_thread_samples.begin(); - thread_samples != per_thread_samples.end(); - ++thread_samples) { - thread_samples->value.ticks_at_last_report = ticks; - } - - // Collect data for each trace over all threads - for (auto thread_samples = per_thread_samples.begin(); - thread_samples != per_thread_samples.end(); - ++thread_samples) { - for (auto execution_point_samples = thread_samples->value.per_execution_point_samples.begin(); - execution_point_samples != thread_samples->value.per_execution_point_samples.end(); - ++execution_point_samples) { - // Collect samples - if (!execution_point_samples->second.samples.empty()) { - std::vector &sample_collection = - execution_point_reports[execution_point_samples->first].collected_samples; - sample_collection.insert(sample_collection.end(), - execution_point_samples->second.samples.begin(), - execution_point_samples->second.samples.end()); - - // Reset samples - execution_point_samples->second.samples.clear(); - } - } - } - - // Release per-thread locks - } - - // Compute statistics - for (auto report = execution_point_reports.begin(); - report != execution_point_reports.end(); - ++report) { - report->second.compute_stats(); - } - - if (reql_output_file != nullptr) { - print_to_reql(execution_point_reports); - } -} - -void coro_profiler_t::per_execution_point_collected_report_t::update_min_max( - const double new_sample, - data_distribution_t *current_out) const { - - if (num_samples == 0) { - current_out->min = current_out->max = new_sample; - } else { - current_out->min = std::min(current_out->min, new_sample); - current_out->max = std::max(current_out->max, new_sample); - } -} - -void coro_profiler_t::per_execution_point_collected_report_t::accumulate_sample_pass1( - const double new_sample, - data_distribution_t *current_out) const { - - update_min_max(new_sample, current_out); - current_out->mean += new_sample; -} - -void coro_profiler_t::per_execution_point_collected_report_t::divide_mean( - data_distribution_t *current_out) const { - - current_out->mean /= (num_samples > 0) ? static_cast(num_samples) : 1.0; -} - -void coro_profiler_t::per_execution_point_collected_report_t::accumulate_sample_pass2( - const double new_sample, - data_distribution_t *current_out) const { - - current_out->stddev += std::pow(new_sample - current_out->mean, 2); -} - -void coro_profiler_t::per_execution_point_collected_report_t::divide_stddev( - data_distribution_t *current_out) const { - - // Use the unbiased estimate of the standard deviation, hence division by num_samples-1 - current_out->stddev /= (num_samples > 1) ? static_cast(num_samples-1) : 1.0; - current_out->stddev = std::sqrt(current_out->stddev); -} - -void coro_profiler_t::per_execution_point_collected_report_t::compute_stats() { - rassert(num_samples == 0); // `per_execution_point_collected_report_t` is not re-usable. - - // Pass 1: Compute min, max, mean - for (auto sample = collected_samples.begin(); sample != collected_samples.end(); ++sample) { - accumulate_sample_pass1(ticks_to_secs(sample->ticks_since_previous), - &time_since_previous); - accumulate_sample_pass1(ticks_to_secs(sample->ticks_since_resume), - &time_since_resume); - accumulate_sample_pass1(static_cast(sample->priority), - &priority); - - ++num_samples; - } - divide_mean(&time_since_previous); - divide_mean(&time_since_resume); - divide_mean(&priority); - - // Pass 2: Compute standard deviation - for (auto sample = collected_samples.begin(); sample != collected_samples.end(); ++sample) { - accumulate_sample_pass2(ticks_to_secs(sample->ticks_since_previous), - &time_since_previous); - accumulate_sample_pass2(ticks_to_secs(sample->ticks_since_resume), - &time_since_resume); - accumulate_sample_pass2(static_cast(sample->priority), - &priority); - } - divide_stddev(&time_since_previous); - divide_stddev(&time_since_resume); - divide_stddev(&priority); -} - -void coro_profiler_t::print_to_reql( - const std::map &execution_point_reports) { - guarantee(reql_output_file != nullptr); - - const double time = ticks_to_secs(get_ticks()); - - for (auto report = execution_point_reports.begin(); report != execution_point_reports.end(); ++report) { - fprintf(reql_output_file, - "print t.insert({\n" - "\t\t'time': %.10f,\n", time); - fprintf(reql_output_file, - "\t\t'coro_type': '%s',\n", - report->first.first.c_str()); - fprintf(reql_output_file, - "\t\t'trace': %s,\n", - trace_to_array_str(report->first.second).c_str()); - fprintf(reql_output_file, - "\t\t'num_samples': %zu,\n", - report->second.num_samples); - fprintf(reql_output_file, - "\t\t'since_previous': %s,\n", - distribution_to_object_str(report->second.time_since_previous).c_str()); - fprintf(reql_output_file, - "\t\t'since_resume': %s,\n", - distribution_to_object_str(report->second.time_since_resume).c_str()); - fprintf(reql_output_file, - "\t\t'priority': %s\n", - distribution_to_object_str(report->second.priority).c_str()); - fprintf(reql_output_file, - "\t}).run(conn, durability='soft')\n"); - } -} - -std::string coro_profiler_t::trace_to_array_str(const small_trace_t &trace) { - std::string trace_array_str = "["; - for (size_t i = 0; i < CORO_PROFILER_BACKTRACE_DEPTH; ++i) { - if (trace[i] == nullptr) { - break; - } - if (i > 0) { - trace_array_str += ", "; - } - trace_array_str += "'" + get_frame_description(trace[i]) + "'"; - } - trace_array_str += "]"; - - return trace_array_str; -} - -std::string coro_profiler_t::distribution_to_object_str(const data_distribution_t &distribution) { - return strprintf("{'min': %.10f, 'max': %.10f, 'mean': %.10f, 'stddev': %.10f}", - distribution.min, distribution.max, - distribution.mean, distribution.stddev); -} - -void coro_profiler_t::write_reql_header() { - guarantee(reql_output_file != nullptr); - fprintf(reql_output_file, - "#!/usr/bin/env python\n" - "\n" - "import rethinkdb as r\n" - "conn = r.connect() # Modify this as needed\n" - "t = r.table(\"coro_prof\") # Modify this as needed\n" - "\n"); -} - -const std::string &coro_profiler_t::get_frame_description(void *addr) { - auto cache_it = frame_description_cache.find(addr); - if (cache_it != frame_description_cache.end()) { - return cache_it->second; - } - - backtrace_frame_t frame(addr); - frame.initialize_symbols(); - - -#if CORO_PROFILER_ADDRESS_TO_LINE - std::string line = address_to_line.address_to_line(frame.get_filename(), frame.get_addr()) + " | "; -#else - std::string line = ""; -#endif - std::string demangled_name; - try { - demangled_name = frame.get_demangled_name(); - } catch (const demangle_failed_exc_t &e) { - demangled_name = "?"; - } - std::string description = strprintf("%p\t%s%s", - frame.get_addr(), - line.c_str(), - demangled_name.c_str()); - - return frame_description_cache.insert( - std::pair(addr, description)).first->second; -} - -#endif /* ENABLE_CORO_PROFILER */ diff --git a/src/arch/runtime/coro_profiler.hpp b/src/arch/runtime/coro_profiler.hpp deleted file mode 100644 index 94f5ac86f2e..00000000000 --- a/src/arch/runtime/coro_profiler.hpp +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_CORO_PROFILER_HPP_ -#define ARCH_RUNTIME_CORO_PROFILER_HPP_ - - -#ifdef ENABLE_CORO_PROFILER - -#include - -#include -#include -#include -#include -#include -#include - -#include "backtrace.hpp" -#include "arch/spinlock.hpp" -#include "concurrency/cache_line_padded.hpp" -#include "config/args.hpp" -#include "time.hpp" - - -/* Depth of the stack traces generated by the coro profiler. */ -#define CORO_PROFILER_BACKTRACE_DEPTH 10 - -/* How frequently should the coro profiler aggregate data and generate a report? */ -#define CORO_PROFILER_REPORTING_INTERVAL (secs_to_ticks(1.0) * 5) - -/* If you set CORO_PROFILER_ADDRESS_TO_LINE to 1, the coro profiler prints - * the filename and line number of the source file in/at which a sample was recorded. - * Unfortunately that conversion is pretty slow. */ -#define CORO_PROFILER_ADDRESS_TO_LINE 0 - - -/* - * The `coro_profiler_t` collects information about where coroutines spend time. - * In order to turn it on, `ENABLE_CORO_PROFILER` must be defined at compile time. - * It will only work reliably in debug mode, even though it can provide some - * data in release mode as well. - * You should compile as follows: `make CORO_PROFILING=1 DEBUG=1` - * If you want to use the profiler in release mode, compile with - * `make CORO_PROFILING=1 NO_OMIT_FRAME_POINTER=1` - * Keep in mind though that backtraces can be unreliable in release. - * - * The coro profiler records a sample whenever it encounters a `PROFILER_RECORD_SAMPLE` - * and also every time a coroutine yields. - * - * The following data is aggregated: - * - How often a certain recording point has been reached within the past - * `CORO_PROFILER_REPORTING_INTERVAL`. - * - How much time has passed since the coroutine has resumed running - * (this is useful to identify coroutines that run for long - * periods of time without yielding control) - * - How much time has passed on a coroutine since the previous recording - * point - * - The priority of the coroutine - * - * A combination of coro_type (signature of the function that spawned the coroutine) - * and a limited-depth backtrace (see `CORO_PROFILER_BACKTRACE_DEPTH`) is used to - * identify an "execution point". Data is recorded and reported for each such - * execution point. - * - * The aggregated data is written to the file "coro_profiler_out_PID.py" in the working - * directory. Data is written every `CORO_PROFILER_REPORTING_INTERVAL` ticks. - */ -class coro_profiler_t { -public: - // Should you ever want to make this a true singleton, just make the - // constructor private. - coro_profiler_t(); - ~coro_profiler_t(); - - static coro_profiler_t &get_global_profiler(); - - void record_sample(size_t levels_to_strip_from_backtrace = 0); - - // coroutine execution is resumed - void record_coro_resume(); - // coroutine execution yields - void record_coro_yield(size_t levels_to_strip_from_backtrace); - -private: - typedef std::array small_trace_t; - // We identify an execution point of a coroutine by a pair of - // the coro's coroutine_type (the function which spawned it) and - // a small_trace_t of its current execution point. - typedef std::pair coro_execution_point_key_t; - struct coro_sample_t { - coro_sample_t(ticks_t _ticks_since_resume, ticks_t _ticks_since_previous, int _priority) : - ticks_since_resume(_ticks_since_resume), - ticks_since_previous(_ticks_since_previous), - priority(_priority) { } - ticks_t ticks_since_resume; - ticks_t ticks_since_previous; - int priority; - }; - struct per_execution_point_samples_t { - per_execution_point_samples_t() : num_samples_total(0) { } - int num_samples_total; - std::vector samples; - }; - struct per_thread_samples_t { - per_thread_samples_t() : ticks_at_last_report(get_ticks()) { } - std::map per_execution_point_samples; - spinlock_t spinlock; - // This field is a duplicate of the global `ticks_at_last_report` in - // `coro_profiler_t`. We copy it in each thread in order to avoid having - // to lock and access the global field from different threads. - ticks_t ticks_at_last_report; - }; - - // Represents the distribution of data points as a normal distribution, - // plus minimum and maximum values. - struct data_distribution_t { - data_distribution_t() : min(0.0), max(0.0), mean(0.0), stddev(0.0) { } - double min, max; - - double mean, stddev; - }; - struct per_execution_point_collected_report_t { - per_execution_point_collected_report_t() : num_samples(0) { } - void compute_stats(); - std::vector collected_samples; - size_t num_samples; - data_distribution_t time_since_previous; - data_distribution_t time_since_resume; - data_distribution_t priority; - - private: - // Helper functions for compute_stats - void update_min_max(const double new_sample, data_distribution_t *current_out) const; - void accumulate_sample_pass1(const double new_sample, - data_distribution_t *current_out) const; - void divide_mean(data_distribution_t *current_out) const; - void accumulate_sample_pass2(const double new_sample, - data_distribution_t *current_out) const; - void divide_stddev(data_distribution_t *current_out) const; - }; - - void generate_report(); - void print_to_reql(const std::map &execution_point_reports); - void write_reql_header(); - std::string distribution_to_object_str(const data_distribution_t &distribution); - std::string trace_to_array_str(const small_trace_t &trace); - const std::string &get_frame_description(void *addr); - coro_execution_point_key_t get_current_execution_point(size_t levels_to_strip_from_backtrace); - - // Would be nice if we could use one_per_thread here. However - // that makes the construction order tricky. - std::array, MAX_THREADS> per_thread_samples; - - ticks_t ticks_at_last_report; - /* Locking order is always: - * 1. report_interval_spinlock - * 2. per_thread_samples.spinlock in ascending order of thread num - * You can safely skip some of the locks in this order. - * Acquiring locks in different orders can dead-lock. - */ - spinlock_t report_interval_spinlock; - - std::map frame_description_cache; - address_to_line_t address_to_line; - - // This is NULL if opening the file failed. - FILE *reql_output_file; - - DISABLE_COPYING(coro_profiler_t); -}; - -// Short-cuts -// -// PROFILER_CORO_RESUME and PROFILER_CORO_YIELD are meant to be used in -// the internal coroutine implementation to notify the profiler about when a coroutine -// yields and resumes execution respectively. -// -// PROFILER_RECORD_SAMPLE on the other hand can be used throughout the code to -// increase the granularity of profiling. By default, the coro profiler collects -// data only when a coroutine yields (assuming that PROFILER_CORO_YIELD gets called). -// PROFILER_RECORD_SAMPLE adds an additional point for data collection in between -// such yields and can be used to "trace" execution times through different -// sections of a given piece of code. -#define PROFILER_RECORD_SAMPLE coro_profiler_t::get_global_profiler().record_sample() -#define PROFILER_CORO_RESUME coro_profiler_t::get_global_profiler().record_coro_resume() -#define PROFILER_CORO_YIELD(STRIP_FRAMES) coro_profiler_t::get_global_profiler().record_coro_yield(STRIP_FRAMES) - -#else /* ENABLE_CORO_PROFILER */ - -// Short-cuts (no-ops for disabled coro profiler) -#define PROFILER_RECORD_SAMPLE do {} while(0) -#define PROFILER_CORO_RESUME do {} while(0) -#define PROFILER_CORO_YIELD(STRIP_FRAMES) do {} while(0) - -#endif /* not ENABLE_CORO_PROFILER */ - -#endif /* ARCH_RUNTIME_CORO_PROFILER_HPP_ */ diff --git a/src/arch/runtime/coroutines.cc b/src/arch/runtime/coroutines.cc deleted file mode 100644 index 645fccaf2b8..00000000000 --- a/src/arch/runtime/coroutines.cc +++ /dev/null @@ -1,629 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "arch/runtime/coroutines.hpp" - -#include -#include - -#include -#ifndef NDEBUG -#include -#include -#include -#endif - -#include "arch/runtime/context_switching.hpp" -#include "arch/runtime/coro_profiler.hpp" -#include "arch/runtime/runtime.hpp" -#include "arch/runtime/thread_pool.hpp" -#include "config/args.hpp" -#include "debug.hpp" -#include "do_on_thread.hpp" -#include "logger.hpp" -#include "perfmon/perfmon.hpp" -#include "rethinkdb_backtrace.hpp" -#include "thread_local.hpp" -#include "utils.hpp" - -//Default, can be set through `set_coro_stack_size()` -size_t coro_stack_size = COROUTINE_STACK_SIZE; - -// How many unused coroutine stacks to keep around (at most), before they are -// freed. This value is per thread. -const size_t COROUTINE_FREE_LIST_SIZE = 64; - -// In debug mode, we print a warning if more than this many coroutines have been -// allocated on one thread. -#ifndef NDEBUG -const int COROS_PER_THREAD_WARN_LEVEL = 10000; -#endif - -// The maximum (global) number of stack-protected coroutine stacks. -// On Linux, this number is limited by the setting in `/proc/sys/vm/max_map_count`, -// which is 65536 by default. We use a quarter of that, to leave enough room for the -// memory allocator and other things that might fragment the memory space (note that -// a single protected coroutine consumes two mapped memory regions). -// Exception: debug-mode, where we use a smaller value to exercise the code path more -// often. -#ifdef NDEBUG -const size_t MAX_PROTECTED_COROS = 16384; -#else -const size_t MAX_PROTECTED_COROS = 128; -#endif - - - -/* `coro_globals_t` holds all of the thread-local variables that coroutines need -to operate. There is one per thread; it is constructed by the constructor for -`coro_runtime_t` and destroyed by the destructor. If one exists, you can find -it in `cglobals`. */ - -struct coro_globals_t { - - /* The coroutine we're currently in, if any. NULL if we are in the main context. */ - coro_t *current_coro; - - /* The main context. */ - coro_context_ref_t scheduler; - - /* The previous context. */ - coro_t *prev_coro; - - /* A list of coro_t objects that are not in use. */ - intrusive_list_t free_coros; - - /* A list of coroutines that currently have protected stacks. The least recently - used protected coroutine is always at the front of the list. */ - intrusive_list_t protected_coros_lru; - -#ifndef NDEBUG - - /* An integer counting the number of coros on this thread */ - int coro_count; - /* Have we printed a warning about too many coroutines? We only want to - print it once. */ - bool printed_high_coro_count_warning; - - /* These variables are used in the implementation of - `ASSERT_NO_CORO_WAITING` and `ASSERT_FINITE_CORO_WAITING`. They record the - number of things that are currently preventing us from `wait()`ing or - `notify_now_deprecated()`ing or whatever. */ - int assert_no_coro_waiting_counter; - std::stack > no_waiting_call_sites; - - int assert_finite_coro_waiting_counter; - std::stack > finite_waiting_call_sites; - - std::map running_coroutine_counts; - std::map total_coroutine_counts; - - std::set active_coroutines; - -#endif // NDEBUG - - coro_globals_t() - : current_coro(nullptr) - , prev_coro(nullptr) -#ifndef NDEBUG - , coro_count(0) - , printed_high_coro_count_warning(false) - , assert_no_coro_waiting_counter(0) - , assert_finite_coro_waiting_counter(0) -#endif - { -#ifdef _WIN32 - coro_initialize_for_thread(); -#endif - } - - ~coro_globals_t() { - /* We shouldn't be shutting down from within a coroutine */ - rassert(!current_coro); - - /* Destroy remaining coroutines */ - while (coro_t *s = free_coros.head()) { - free_coros.remove(s); - delete s; - } - } - -}; - -TLS_with_init(coro_globals_t *, cglobals, nullptr); - -// These must be initialized after TLS_cglobals, because perfmon_multi_membership_t -// construction depends on coro_t::coroutines_have_been_initialized() which in turn -// depends on cglobals. -static perfmon_counter_t pm_active_coroutines, pm_allocated_coroutines; -static perfmon_multi_membership_t pm_coroutines_membership(&get_global_perfmon_collection(), - &pm_active_coroutines, "active_coroutines", - &pm_allocated_coroutines, "allocated_coroutines"); - -coro_runtime_t::coro_runtime_t() { - rassert(!TLS_get_cglobals(), "coro runtime initialized twice on this thread"); - TLS_set_cglobals(new coro_globals_t); -} - -coro_runtime_t::~coro_runtime_t() { - rassert(TLS_get_cglobals()); - delete TLS_get_cglobals(); - TLS_set_cglobals(nullptr); -} - -#ifndef NDEBUG -void coro_runtime_t::get_coroutine_counts(std::map *dest) { - dest->clear(); - dest->insert(TLS_get_cglobals()->total_coroutine_counts.begin(), TLS_get_cglobals()->total_coroutine_counts.end()); -} -#endif - -/* coro_t */ - -#ifndef NDEBUG -TLS_with_init(int64_t, coro_selfname_counter, 0); -#endif - -coro_t::coro_t() : - stack(&coro_t::run, coro_stack_size), - current_thread_(linux_thread_pool_t::get_thread_id()), - notified_(false), - waiting_(false), - protected_stack_lru_entry_(this) -#ifndef NDEBUG - , selfname_number(get_thread_id().threadnum + MAX_THREADS * - // The comma here is the comma operator, to implement the semantics - // of `++coro_selfname_counter` - (TLS_set_coro_selfname_counter(TLS_get_coro_selfname_counter()+1), - TLS_get_coro_selfname_counter())) -#endif -#ifdef CROSS_CORO_BACKTRACES - , spawn_backtrace_size(0) -#endif -{ - ++pm_allocated_coroutines; - -#ifndef NDEBUG - TLS_get_cglobals()->coro_count++; - if (TLS_get_cglobals()->coro_count > COROS_PER_THREAD_WARN_LEVEL - && !TLS_get_cglobals()->printed_high_coro_count_warning) { - TLS_get_cglobals()->printed_high_coro_count_warning = true; - debugf("A lot of coroutines are allocated on this thread. This could " - "indicate a misuse of coroutines.\n"); - } -#endif -} - -void coro_t::return_coro_to_free_list(coro_t *coro) { - coro_globals_t *cglobals = TLS_get_cglobals(); - // Note that we must guarantee that `coro` is never evicted immediately. We do so - // by checking the free list size *before* we push `coro` onto it. - // This is important because when we call `return_coro_to_free_list` in - // `coro_t::run`, that coroutine is still active and must not be deleted yet. - static_assert(COROUTINE_FREE_LIST_SIZE > 0, "COROUTINE_FREE_LIST_SIZE cannot be 0"); - if (cglobals->free_coros.size() >= COROUTINE_FREE_LIST_SIZE) { - coro_t *coro_to_delete = cglobals->free_coros.tail(); - cglobals->free_coros.remove(coro_to_delete); - delete coro_to_delete; - } - rassert(cglobals->free_coros.size() < COROUTINE_FREE_LIST_SIZE); - cglobals->free_coros.push_back(coro); -} - -coro_t::~coro_t() { - /* We never move contexts from one thread to another. */ - rassert(get_thread_id() == home_thread()); - -#ifndef NDEBUG - TLS_get_cglobals()->coro_count--; -#endif - --pm_allocated_coroutines; -} - -/* Helper function for switching into a new context and making sure that the new context -is protected against stack overflows. Might also unprotect old contexts to avoid -exhausting kernel limits on memory-mapped regions. */ -void coro_t::switch_to_coro_with_protection(coro_context_ref_t *current_context) { - /* Move ourselves to the back of the `protected_coros_lru` list. */ - coro_globals_t *cglobals = TLS_get_cglobals(); - if (protected_stack_lru_entry_.in_a_list()) { - cglobals->protected_coros_lru.remove(&protected_stack_lru_entry_); - } - cglobals->protected_coros_lru.push_back(&protected_stack_lru_entry_); - - /* If there are too many protected coroutines, unprotect the oldest one and pop it of - the list. */ - size_t max_protected_coros_per_thread = - MAX_PROTECTED_COROS / static_cast(get_num_threads()); - while (cglobals->protected_coros_lru.size() > max_protected_coros_per_thread) { - cglobals->protected_coros_lru.head()->coro->stack.disable_overflow_protection(); - cglobals->protected_coros_lru.pop_front(); - } - - /* Enable protection for us. (if `max_protected_coros_per_thread` was rounded to 0, - we exceed the limit here but that's fine. It's important that the currently active - coroutine is protected.) */ - stack.enable_overflow_protection(); - - /* Now actually perform the context switch. */ - context_switch(current_context, &stack.context); -} - -void switch_to_scheduler( - coro_context_ref_t *current_context, coro_context_ref_t *scheduler) { - context_switch(current_context, scheduler); -} - -void coro_t::run() { - coro_t *coro = TLS_get_cglobals()->current_coro; - -#ifndef NDEBUG - char dummy; - rassert(coro->stack.address_in_stack(&dummy)); -#endif - - while (true) { - rassert(coro == TLS_get_cglobals()->current_coro); - rassert(coro->current_thread_ == get_thread_id()); - rassert(coro->notified_ == false); - rassert(coro->waiting_ == true); - coro->waiting_ = false; - -#ifndef NDEBUG - // Keep track of how many coroutines of each type ran - TLS_get_cglobals()->running_coroutine_counts[coro->coroutine_type]++; - TLS_get_cglobals()->total_coroutine_counts[coro->coroutine_type]++; - TLS_get_cglobals()->active_coroutines.insert(coro); -#endif - PROFILER_CORO_RESUME; - coro->action_wrapper.run(); - PROFILER_CORO_YIELD(0); -#ifndef NDEBUG - TLS_get_cglobals()->running_coroutine_counts[coro->coroutine_type]--; - TLS_get_cglobals()->active_coroutines.erase(coro); -#endif - - rassert(coro->current_thread_ == get_thread_id()); - - // Destroy the Callable object which was either allocated within the coro_t or on the heap - coro->action_wrapper.reset(); - - coro_globals_t *cglobals_on_final_thread = TLS_get_cglobals(); - - /* Remove the coroutine from the `protected_coros_lru` list before we return it - to the free list. We must do this now rather than in `~coro_t` because - `~coro_t` is going to run on the home thread of the coroutine, which is not - necessarily the latest thread on which the coroutine has been executing. - The `protected_coros_lru` entry (if any) will be on the latest thread where it - has been executing, so it must be removed there. - We don't call `disable_stack_protection()` here to increase the efficiency - of the free list. This means that we can slightly exceed the maximum number of - protected coroutines (`MAX_PROTECTED_COROS`), by at most - `COROUTINE_FREE_LIST_SIZE` per thread. */ - if (coro->protected_stack_lru_entry_.in_a_list()) { - cglobals_on_final_thread->protected_coros_lru.remove( - &coro->protected_stack_lru_entry_); - } - - /* Return the context to the free-contexts list we took it from. - - Important: This is ok only because `do_on_thread` and the `linux_message_hub_t` - are *push* based when delivering messages to another thread. That property - guarantees that the message is not executed on another thread before we yield to - the message hub on this one. - If it was executed immediately on another thread, `coro` could get deleted from - the free list before we have performed the next context switch, meaning that we - would free a coroutine that's still running. - `do_on_thread` does execute `return_coro_to_free_list` immediately if the - `coro`'s home thread is the current thread. That too is ok though, because the - implementation of `return_coro_to_free_list` guarantees that `coro` is not going - to be freed immediately. */ - do_on_thread(coro->home_thread(), std::bind(&coro_t::return_coro_to_free_list, coro)); - --pm_active_coroutines; - - if (cglobals_on_final_thread->prev_coro) { - cglobals_on_final_thread->prev_coro->switch_to_coro_with_protection( - &coro->stack.context); - } else { - switch_to_scheduler(&coro->stack.context, &cglobals_on_final_thread->scheduler); - } - } -} - -#ifndef NDEBUG -// This function parses out the type that a coroutine was created with (usually a std::bind), by -// parsing it out of the __PRETTY_FUNCTION__ string of a templated function. This makes some -// assumptions about the format of that string, but if get_and_init_coro is changed, this may -// need to be updated. The only reason we do this is so we don't have to enable RTTI to figure -// out the type of the template, but we can still provide a clean typename. -void coro_t::parse_coroutine_type(const char *coroutine_function) -{ - coroutine_type.assign(coroutine_function); -} -#endif - -coro_t *coro_t::self() { /* class method */ - // Make a local copy because TLS_get_cglobals() can't be inlined, and we don't - // want to call it twice. - coro_globals_t *globals = TLS_get_cglobals(); - return globals == nullptr ? nullptr : globals->current_coro; -} - -void coro_t::wait() { /* class method */ - rassert(self(), "Not in a coroutine context"); - rassert(TLS_get_cglobals()->assert_finite_coro_waiting_counter == 0, - "This code path is not supposed to use coro_t::wait().\nConstraint imposed at: %s:%d", - TLS_get_cglobals()->finite_waiting_call_sites.top().first, TLS_get_cglobals()->finite_waiting_call_sites.top().second); - - rassert(TLS_get_cglobals()->assert_no_coro_waiting_counter == 0, - "This code path is not supposed to use coro_t::wait().\nConstraint imposed at: %s:%d", - TLS_get_cglobals()->no_waiting_call_sites.top().first, TLS_get_cglobals()->no_waiting_call_sites.top().second); - - rassert(!self()->waiting_); - self()->waiting_ = true; - - PROFILER_CORO_YIELD(1); - if (TLS_get_cglobals()->prev_coro) { - TLS_get_cglobals()->prev_coro->switch_to_coro_with_protection( - &self()->stack.context); - } else { - switch_to_scheduler(&self()->stack.context, &TLS_get_cglobals()->scheduler); - } - PROFILER_CORO_RESUME; - - rassert(self()); - rassert(self()->waiting_); - self()->waiting_ = false; -} - -void coro_t::yield() { /* class method */ - rassert(self(), "Not in a coroutine context"); - self()->notify_sometime(); - self()->wait(); -} - -void coro_t::yield_ordered() { /* class method */ - rassert(self(), "Not in a coroutine context"); - self()->notify_later_ordered(); - self()->wait(); -} - -void coro_t::notify_now_deprecated() { - rassert(waiting_); - rassert(!notified_); - rassert(current_thread_.threadnum == linux_thread_pool_t::get_thread_id()); - -#ifndef NDEBUG - rassert(TLS_get_cglobals()->assert_no_coro_waiting_counter == 0, - "This code path is not supposed to use notify_now_deprecated() or spawn_now_dangerously()."); - - /* Record old value of `assert_finite_coro_waiting_counter`. It must be legal to call - `coro_t::wait()` within the coro we are going to jump to, or else we would never jump - back. */ - int old_assert_finite_coro_waiting_counter = TLS_get_cglobals()->assert_finite_coro_waiting_counter; - TLS_get_cglobals()->assert_finite_coro_waiting_counter = 0; -#endif - - if (coro_t::self() != nullptr) { - PROFILER_CORO_YIELD(1); - } - coro_t *prev_prev_coro = TLS_get_cglobals()->prev_coro; - TLS_get_cglobals()->prev_coro = TLS_get_cglobals()->current_coro; - TLS_get_cglobals()->current_coro = this; - - if (TLS_get_cglobals()->prev_coro) { - switch_to_coro_with_protection(&TLS_get_cglobals()->prev_coro->stack.context); - } else { - switch_to_coro_with_protection(&TLS_get_cglobals()->scheduler); - } - - rassert(TLS_get_cglobals()->current_coro == this); - TLS_get_cglobals()->current_coro = TLS_get_cglobals()->prev_coro; - TLS_get_cglobals()->prev_coro = prev_prev_coro; - if (coro_t::self() != nullptr) { - PROFILER_CORO_RESUME; - } - -#ifndef NDEBUG - /* Restore old value of `assert_finite_coro_waiting_counter`. */ - TLS_get_cglobals()->assert_finite_coro_waiting_counter = old_assert_finite_coro_waiting_counter; -#endif -} - -void coro_t::notify_sometime() { - rassert(!notified_); - notified_ = true; - linux_thread_pool_t::get_thread()->message_hub.store_message_sometime( - current_thread_, - this); -} - -void coro_t::notify_later_ordered() { - rassert(!notified_); - notified_ = true; - - /* `current_thread` is the thread that the coroutine lives on, which may or may not be the - same as `get_thread_id()`. (In a call to move_to_thread, it won't be.) */ - linux_thread_pool_t::get_thread()->message_hub.store_message_ordered( - current_thread_, - this); -} - -void coro_t::move_to_thread(threadnum_t thread) { - assert_good_thread_id(thread); - rassert(coro_t::self(), "coro_t::move_to_thread() called when not in a coroutine."); - if (thread.threadnum == linux_thread_pool_t::get_thread_id()) { - // If we're trying to switch to the thread we're currently on, do nothing. - return; - } - /* Remove ourselves from the `protected_coros_lru` list on the old thread, since - those lists are not thread-safe. - We will be added to the one on the new thread once we are woken up there, so there's - no need to add ourselves now even if we're currently protected. - Note that this can lead to us temporarily exceeding the protected coro limit if a lot - of coroutines are in flight between threads at the same time. Hopefully this isn't - going to be too common. */ - if (self()->protected_stack_lru_entry_.in_a_list()) { - TLS_get_cglobals()->protected_coros_lru.remove( - &self()->protected_stack_lru_entry_); - } - self()->current_thread_ = thread; - self()->notify_later_ordered(); - wait(); -} - -void coro_t::on_thread_switch() { - rassert(notified_); - notified_ = false; - - /* TODO: When `notify_now_deprecated()` is finally removed, just fold it - into this function. */ - notify_now_deprecated(); -} - -void coro_t::set_coroutine_stack_size(size_t size) { - coro_stack_size = size; -} - -coro_stack_t* coro_t::get_stack() { - return &stack; -} - -/* Called by SIGSEGV/SIGBUS handler to identify segfaults that come from overflowing -a coroutine's stack. Could also in theory be used by a function to check if it's -about to overflow the stack. */ - -bool is_coroutine_stack_overflow(void *addr) { - return TLS_get_cglobals()->current_coro && TLS_get_cglobals()->current_coro->stack.address_is_stack_overflow(addr); -} - -bool has_n_bytes_free_stack_space(size_t n) { - // We assume that `tester` is going to be allocated on the stack. - // Theoretically this is not guaranteed by the C++ standard, but in practice - // it should work. - char tester; - const coro_t *current_coro = coro_t::self(); - guarantee(current_coro != nullptr); -#ifdef _WIN32 - if (!current_coro->stack.has_stack_info) { - return true; - } -#endif - return current_coro->stack.free_space_below(&tester) >= n; -} - -bool coroutines_have_been_initialized() { - return TLS_get_cglobals() != nullptr; -} - -coro_t * coro_t::get_coro() { - rassert(coroutines_have_been_initialized()); - coro_t *coro; - - if (TLS_get_cglobals()->free_coros.size() == 0) { - coro = new coro_t(); - } else { - coro = TLS_get_cglobals()->free_coros.tail(); - TLS_get_cglobals()->free_coros.remove(coro); - } - - rassert(!coro->intrusive_list_node_t::in_a_list()); - - coro->current_thread_ = get_thread_id(); - coro->notified_ = false; - coro->waiting_ = true; - - ++pm_active_coroutines; - return coro; -} - -void coro_t::grab_spawn_backtrace() { -#ifdef CROSS_CORO_BACKTRACES - // Skip a few constant frames at the beginning of the backtrace. - // This is purely a cosmetic thing, to make the backtraces - // nicer to look at and easier to read. - // - // The +2 comes together as follows: - // +1 for our own stack frame (`grab_spawn_backtrace()`) - // +1 for the fact that we get called from `get_and_init_coro()` - // which is also not interesting at all. - const int num_frames_to_skip = NUM_FRAMES_INSIDE_RETHINKDB_BACKTRACE + 2; - - const size_t buffer_size = static_cast(CROSS_CORO_BACKTRACES_MAX_SIZE - + num_frames_to_skip); - void *buffer[buffer_size]; - spawn_backtrace_size = rethinkdb_backtrace(buffer, buffer_size); - if (spawn_backtrace_size < num_frames_to_skip) { - // Something is fishy if this happens... Probably RethinkDB was compiled - // with some optimizations enabled that inlined some functions or - // optimized away some stack frames, or the value of - // NUM_FRAMES_INSIDE_RETHINKDB_BACKTRACE is wrong, or we were called - // from an unexpected context. - rassert(spawn_backtrace_size >= num_frames_to_skip, - "Got an unexpected backtrace. See comment in coro_t::grab_spawn_backtrace(). " - "If you determine that this is acceptable in your specific case (e.g. " - "you are just debugging something), feel free to temporarily comment " - "this assertion."); - spawn_backtrace_size = num_frames_to_skip; - } - spawn_backtrace_size -= num_frames_to_skip; - rassert(spawn_backtrace_size >= 0); - rassert(spawn_backtrace_size <= CROSS_CORO_BACKTRACES_MAX_SIZE); - memcpy(spawn_backtrace, buffer + num_frames_to_skip, spawn_backtrace_size * sizeof(void *)); - - // If we have space left and were called from inside a coroutine, - // we also append the parent's spawn_backtrace. - int space_remaining = CROSS_CORO_BACKTRACES_MAX_SIZE - spawn_backtrace_size; - if (space_remaining > 0 && self() != nullptr) { - spawn_backtrace_size += self()->copy_spawn_backtrace(spawn_backtrace + spawn_backtrace_size, - space_remaining); - } -#endif -} - -#ifdef CROSS_CORO_BACKTRACES -int coro_t::copy_spawn_backtrace(void **buffer_out, int size) const { -#else -int coro_t::copy_spawn_backtrace(void **, int) const { -#endif -#ifdef CROSS_CORO_BACKTRACES - guarantee(buffer_out != nullptr); - guarantee(size >= 0); - int num_frames_to_store = std::min(size, spawn_backtrace_size); - memcpy(buffer_out, - spawn_backtrace, - static_cast(num_frames_to_store) * sizeof(void *)); - return num_frames_to_store; -#else - return 0; -#endif -} - -#ifndef NDEBUG - -/* These are used in the implementation of `ASSERT_NO_CORO_WAITING` and -`ASSERT_FINITE_CORO_WAITING` */ -assert_no_coro_waiting_t::assert_no_coro_waiting_t(const char *filename, int line_no) { - TLS_get_cglobals()->no_waiting_call_sites.push(std::make_pair(filename, line_no)); - TLS_get_cglobals()->assert_no_coro_waiting_counter++; -} -assert_no_coro_waiting_t::~assert_no_coro_waiting_t() { - TLS_get_cglobals()->no_waiting_call_sites.pop(); - TLS_get_cglobals()->assert_no_coro_waiting_counter--; -} -assert_finite_coro_waiting_t::assert_finite_coro_waiting_t(const char *filename, int line_no) { - TLS_get_cglobals()->finite_waiting_call_sites.push(std::make_pair(filename, line_no)); - TLS_get_cglobals()->assert_finite_coro_waiting_counter++; -} -assert_finite_coro_waiting_t::~assert_finite_coro_waiting_t() { - TLS_get_cglobals()->finite_waiting_call_sites.pop(); - TLS_get_cglobals()->assert_finite_coro_waiting_counter--; -} - -home_coro_mixin_t::home_coro_mixin_t() - : home_coro(coro_t::self()) -{ } - -void home_coro_mixin_t::assert_coro() { - rassert(home_coro == coro_t::self()); -} - -#endif /* NDEBUG */ diff --git a/src/arch/runtime/coroutines.hpp b/src/arch/runtime/coroutines.hpp deleted file mode 100644 index 2bbb826f1e0..00000000000 --- a/src/arch/runtime/coroutines.hpp +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_COROUTINES_HPP_ -#define ARCH_RUNTIME_COROUTINES_HPP_ - -#include -#ifndef NDEBUG -#include -#endif - -#include "arch/compiler.hpp" -#include "arch/runtime/callable_action.hpp" -#include "arch/runtime/context_switching.hpp" -#include "arch/runtime/runtime_utils.hpp" -#include "threading.hpp" -#include "time.hpp" - -// Enable cross-coroutine backtraces in debug mode, or when coro profiling is enabled. -// We don't enable it on ARM, because taking backtraces currently isn't working -// reliably and sometimes causes crashes. -#if (!defined(NDEBUG) && !defined(__MACH__) && !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__)) \ - || defined(ENABLE_CORO_PROFILER) -#define CROSS_CORO_BACKTRACES 1 -#endif -#define CROSS_CORO_BACKTRACES_MAX_SIZE 64 - -threadnum_t get_thread_id(); -struct coro_globals_t; -class coro_t; - - -struct coro_profiler_mixin_t { -#ifdef ENABLE_CORO_PROFILER - coro_profiler_mixin_t() : last_resumed_at(0), last_sample_at(0) { } - ticks_t last_resumed_at; - ticks_t last_sample_at; -#endif -}; - -/* The `coro_lru_entry_t` is used to keep track of coroutines that have protected -stacks and to eventually unprotect them using a least-recently-used strategy. */ -struct coro_lru_entry_t : public intrusive_list_node_t { - explicit coro_lru_entry_t(coro_t *parent) : coro(parent) { } - coro_t *coro; -}; - -/* A coro_t represents a fiber of execution within a thread. Create one with spawn_*(). Within a -coroutine, call wait() to return control to the scheduler; the coroutine will be resumed when -another fiber calls notify_*() on it. - -coro_t objects can switch threads by constructing an `on_thread_t`. */ - -class coro_t : private coro_profiler_mixin_t, - private linux_thread_message_t, - public intrusive_list_node_t, - public home_thread_mixin_t { -public: - friend bool is_coroutine_stack_overflow(void *); - friend bool has_n_bytes_free_stack_space(size_t); - - template - static void spawn_now_dangerously(callable_t &&action) { - coro_t *coro = get_and_init_coro(std::forward(action)); - coro->notify_now_deprecated(); - } - - template - static coro_t *spawn_sometime(callable_t &&action) { - coro_t *coro = get_and_init_coro(std::forward(action)); - coro->notify_sometime(); - return coro; - } - - /* This is an optimization over spawn_sometime() followed by an on_thread_t. - It avoids two thread messages, since it doesn't have to run on the original - thread first, and also doesn't switch back at the end of the coro's lifetime. */ - template - static coro_t *spawn_on_thread(callable_t &&action, threadnum_t thread) { - coro_t *coro = get_and_init_coro(std::forward(action)); - coro->current_thread_ = thread; - coro->notify_sometime(); - return coro; - } - - /* Whenever possible, `spawn_sometime()` should be used instead of - `spawn_later_ordered()` (or `spawn_ordered()`). `spawn_later_ordered()` does not - honor scheduler priorities. */ - template - static coro_t *spawn_later_ordered(callable_t &&action) { - coro_t *coro = get_and_init_coro(std::forward(action)); - coro->notify_later_ordered(); - return coro; - } - - // Use coro_t::spawn_*(std::bind(...)) for spawning with parameters. - - /* Pauses the current coroutine until it is notified */ - static void wait(); - - /* Gives another coroutine a chance to run, but schedules this coroutine to - be run at some point in the future. Might not preserve order; two calls to - `yield()` by different coroutines may return in a different order than they - began in. */ - static void yield(); - /* Like `yield()`, but guarantees that the ordering of coroutines calling - `yield_ordered()` is maintained. */ - static void yield_ordered(); - - /* Returns a pointer to the current coroutine, or `NULL` if we are not in a - coroutine. */ - static coro_t *self(); - - /* Transfers control immediately to the coroutine. Returns when the - coroutine calls `wait()`. - - Note: `notify_now()` may become deprecated eventually. The original purpose - was to provide better performance than could be achieved with - `notify_later_ordered()`, but `notify_sometime()` is now filling that role - instead. */ - void notify_now_deprecated(); - - /* Schedules the coroutine to be woken up eventually. Can be safely called - from any thread. Returns immediately. Does not provide any ordering - guarantees. If you don't need the ordering guarantees that - `notify_later_ordered()` provides, use `notify_sometime()`. */ - void notify_sometime(); - - /* Pushes the coroutine onto the event queue for the thread it's currently - on, such that it will be run. This can safely be called from any thread. - Returns immediately. If you call `notify_later_ordered()` on two coroutines - that are on the same thread, they will run in the same order you call - `notify_later_ordered()` in. */ - // DEPRECATED: Call notify_ordered, its name is shorter. - void notify_later_ordered(); - - void notify_ordered() { - notify_later_ordered(); - } - - -#ifndef NDEBUG - // A unique identifier for this particular instance of coro_t over - // the lifetime of the process. - static int64_t selfname() { - coro_t *running_coro = self(); - return running_coro ? running_coro->selfname_number : 0; - } - - const std::string& get_coroutine_type() { return coroutine_type; } -#endif - - static void set_coroutine_stack_size(size_t size); - - coro_stack_t *get_stack(); - - void set_priority(int _priority) { - linux_thread_message_t::set_priority(_priority); - } - int get_priority() const { - return linux_thread_message_t::get_priority(); - } - - /* Copies the backtrace from the time of spawning the coroutine into - `buffer_out`, which has to be allocated before calling the function. - `size` must contain the maximum number of entries to store. - Returns how many entries have been deposited into `buffer_out`. */ - int copy_spawn_backtrace(void **buffer_out, int size) const; - -private: - /* When called from within a coroutine, schedules the coroutine to be run on - the given thread and then suspends the coroutine until that other thread - picks it up again. Do not call this directly; use `on_thread_t` instead. */ - friend class on_thread_t; - static void move_to_thread(threadnum_t thread); - - // Constructor sets up the stack, get_and_init_coro will load a function to be run - // at which point the coroutine can be notified - coro_t(); - - // Generates a spawn-time backtrace and stores it into `spawn_backtrace`. - void grab_spawn_backtrace(); - - // Performs a context switch from `current_context` to this coroutine. - // Also enables stack-overflow protection on this coroutine. - void switch_to_coro_with_protection(coro_context_ref_t *current_context); - - // If this function footprint ever changes, you may need to update the parse_coroutine_info function - template - static coro_t *get_and_init_coro(callable_t &&action) { - coro_t *coro = get_coro(); -#ifndef NDEBUG - coro->parse_coroutine_type(CURRENT_FUNCTION_PRETTY); -#endif - coro->grab_spawn_backtrace(); - coro->action_wrapper.reset(std::forward(action)); - - // If we were called from a coroutine, the new coroutine inherits our - // caller's priority. - if (self() != nullptr) { - coro->set_priority(self()->get_priority()); - } else { - // Otherwise, just reset to the default. - coro->set_priority(MESSAGE_SCHEDULER_DEFAULT_PRIORITY); - } - - return coro; - } - - static coro_t *get_coro(); - - static void return_coro_to_free_list(coro_t *coro); - - NORETURN static void run(); - - friend class coro_profiler_t; - friend struct coro_globals_t; - ~coro_t(); - - virtual void on_thread_switch(); - - coro_stack_t stack; - - threadnum_t current_thread_; - - // Sanity check variables - bool notified_; - bool waiting_; - - callable_action_wrapper_t action_wrapper; - - /* Used to eventually unprotect the coroutine if it has been inactive for a while. */ - coro_lru_entry_t protected_stack_lru_entry_; - -#ifndef NDEBUG - int64_t selfname_number; - std::string coroutine_type; - void parse_coroutine_type(const char *coroutine_function); -#endif - -#ifdef CROSS_CORO_BACKTRACES - int spawn_backtrace_size; - void *spawn_backtrace[CROSS_CORO_BACKTRACES_MAX_SIZE]; -#endif - - DISABLE_COPYING(coro_t); -}; - -/* Returns true if the given address is in the protection page of the current coroutine. */ -bool is_coroutine_stack_overflow(void *addr); -/* Returns true if at least n bytes are available on the stack of the current coroutine. */ -bool has_n_bytes_free_stack_space(size_t n); -bool coroutines_have_been_initialized(); - -/* Checks that there are at least n bytes of stack space available on the current -coroutine. If that is the case, this function calls `fun` right away. -Otherwise it spawns a new coroutine, runs `fun` in there, waits for it to -finish if necessary, and returns the result of the call.*/ -template -inline result_t call_with_enough_stack(callable_t &&fun, const size_t min_bytes_free) { - coro_t *origin = coro_t::self(); - // This implementation only works if we're in a coroutine. If we're not, - // we just call `fun` right away and hope that the stack size is not an issue - // in those situations. - if (origin == nullptr || has_n_bytes_free_stack_space(min_bytes_free)) { - return fun(); - } else { - result_t res; - std::exception_ptr exception; - bool did_block = false; - bool done_immediately = false; - coro_t::spawn_now_dangerously([&]() { - try { - res = fun(); - } catch (...) { - exception = std::current_exception(); - } - if (did_block) { - origin->notify_sometime(); - } else { - done_immediately = true; - } - }); - // Note that if `fun()` doesn't block, we will get here after the coroutine - // we spawned has already finished, since we're using `spawn_now_dangerously`. - // So ASSERT_FINITE_CORO_WAITING restrictions over `fun()` should remain - // valid with `call_with_enough_stack`. - did_block = true; - if (!done_immediately) { - rassert(origin == coro_t::self()); - origin->wait(); - } - if (exception != nullptr) { - std::rethrow_exception(exception); - } - return res; - } -} - -// Special case for `void` return type -template -inline void call_with_enough_stack(callable_t &&fun, size_t min_bytes_free) { - call_with_enough_stack([&]() -> int { - fun(); - return 0; - }, min_bytes_free); -} - -class home_coro_mixin_t { -private: - coro_t *home_coro; -public: - home_coro_mixin_t(); - void assert_coro(); -}; - -#endif // ARCH_RUNTIME_COROUTINES_HPP_ diff --git a/src/arch/runtime/event_queue.cc b/src/arch/runtime/event_queue.cc deleted file mode 100644 index 332f224c21e..00000000000 --- a/src/arch/runtime/event_queue.cc +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "arch/runtime/event_queue.hpp" - -#include - -#include "arch/runtime/thread_pool.hpp" -#include "concurrency/cond_var.hpp" -#include "utils.hpp" -#include "perfmon/perfmon.hpp" - -perfmon_duration_sampler_t *pm_eventloop_singleton_t::get() { - static perfmon_duration_sampler_t pm_eventloop(secs_to_ticks(1)); - static perfmon_membership_t pm_eventloop_membership( - &get_global_perfmon_collection(), &pm_eventloop, "eventloop"); - return &pm_eventloop; -} - -std::string format_poll_event(int event) { - std::string s; - if (event & poll_event_in) { - s = "in"; - } - if (event & poll_event_out) { - if (s != "") s += " "; - s += "out"; - } - if (event & poll_event_err) { - if (s != "") s += " "; - s += "err"; - } - if (event & poll_event_hup) { - if (s != "") s += " "; - s += "hup"; - } -#ifdef __linux - if (event & poll_event_rdhup) { - if (s != "") s += " "; - s += "rdhup"; - } -#endif - if (s == "") s = "(none)"; - return s; -} diff --git a/src/arch/runtime/event_queue.hpp b/src/arch/runtime/event_queue.hpp deleted file mode 100644 index 830729763fe..00000000000 --- a/src/arch/runtime/event_queue.hpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_EVENT_QUEUE_HPP_ -#define ARCH_RUNTIME_EVENT_QUEUE_HPP_ - -#include - -#include - -#include "perfmon/types.hpp" -#include "arch/runtime/runtime_utils.hpp" -#include "arch/runtime/event_queue_types.hpp" - -std::string format_poll_event(int event); - -// Queue stats (declared here so whichever queue is chosen can access it) -// This is a singleton initialized on first use. -// Note that we cannot put it into a static variable since its initialization -// would race with the initialization of coroutine globals which are accessed -// by the constructor of `perfmon_membership_t`. -struct pm_eventloop_singleton_t { - static perfmon_duration_sampler_t *get(); -}; - -/* Pick the queue now*/ - -#if defined(_WIN32) - -// Use IOCP -#include "arch/runtime/event_queue/iocp.hpp" -typedef iocp_event_queue_t linux_event_queue_t; - -#elif defined(__MACH__) - -// Use kqueue, which is much faster than poll on OS X -#include "arch/runtime/event_queue/kqueue.hpp" -typedef kqueue_event_queue_t linux_event_queue_t; - -#elif !defined(__linux) || defined(NO_EPOLL) - -// Use poll instead of epoll -#include "arch/runtime/event_queue/poll.hpp" -typedef poll_event_queue_t linux_event_queue_t; - -#else - -// Epoll to the rescue -#include "arch/runtime/event_queue/epoll.hpp" -typedef epoll_event_queue_t linux_event_queue_t; - -#endif - -#endif // ARCH_RUNTIME_EVENT_QUEUE_HPP_ diff --git a/src/arch/runtime/event_queue/epoll.cc b/src/arch/runtime/event_queue/epoll.cc deleted file mode 100644 index 1bf5e8b2698..00000000000 --- a/src/arch/runtime/event_queue/epoll.cc +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifdef __linux -#ifndef NO_EPOLL // To make the build system happy. - -#include "arch/runtime/event_queue/epoll.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config/args.hpp" -#include "utils.hpp" -#include "arch/runtime/event_queue.hpp" -#include "arch/runtime/thread_pool.hpp" -#include "perfmon/perfmon.hpp" - -int user_to_epoll(int mode) { - - rassert((mode & (poll_event_in | poll_event_out | poll_event_rdhup)) == mode); - - int out_mode = 0; - if (mode & poll_event_in) out_mode |= EPOLLIN; - if (mode & poll_event_out) out_mode |= EPOLLOUT; - if (mode & poll_event_rdhup) out_mode |= EPOLLRDHUP; - - return out_mode; -} - -int epoll_to_user(int mode) { - - rassert((mode & (EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDHUP)) == (unsigned)mode); - - int out_mode = 0; - if (mode & EPOLLIN) out_mode |= poll_event_in; - if (mode & EPOLLOUT) out_mode |= poll_event_out; - if (mode & EPOLLERR) out_mode |= poll_event_err; - if (mode & EPOLLHUP) out_mode |= poll_event_hup; - if (mode & EPOLLRDHUP) out_mode |= poll_event_rdhup; - - return out_mode; -} - -epoll_event_queue_t::epoll_event_queue_t(linux_queue_parent_t *_parent) - : parent(_parent), nevents(0) { - // Create a poll fd - - epoll_fd = epoll_create1(0); - guarantee_err(epoll_fd >= 0, "Could not create epoll fd"); -} - -void epoll_event_queue_t::run() { - int res; - - // Now, start the loop - while (!parent->should_shut_down()) { - // Grab the events from the kernel! - res = epoll_wait(epoll_fd, events, MAX_IO_EVENT_PROCESSING_BATCH_SIZE, -1); - - // epoll_wait might return with EINTR in some cases (in - // particular under GDB), we just need to retry. - if (res == -1 && get_errno() == EINTR) { - // If the thread has been signalled, we already handled - // the signal in our signal action. - res = 0; - } - - // When epoll_wait returns with EBADF, EFAULT, and EINVAL, - // it probably means that epoll_fd is no longer valid. There's - // no reason to try epoll_wait again, unless we can create a - // new descriptor (which we probably can't at this point). - guarantee_err(res != -1, "Waiting for epoll events failed"); - - // nevents might be used by forget_resource during the loop - nevents = res; - -#ifndef NDEBUG - /* Sanity check: Make sure epoll() didn't give us any events we didn't ask for */ - for (int i = 0; i < nevents; i++) { - int events_gotten = epoll_to_user(events[i].events); - int events_wanted = events_requested[reinterpret_cast(events[i].data.ptr)]; - if (events_gotten & poll_event_in) rassert(events_wanted & poll_event_in); - if (events_gotten & poll_event_out) rassert(events_wanted & poll_event_out); - } -#endif - - // TODO: instead of processing the events immediately, we - // might want to queue them up and then process the queue in - // bursts. This might increase response time but increase - // overall throughput because it will minimize cache faults - // associated with instructions as well as data structures - // (see section 7 [CPU scheduling] in B-tree Indexes and CPU - // Caches by Goetz Graege and Pre-Ake Larson). - - block_pm_duration event_loop_timer(pm_eventloop_singleton_t::get()); - - for (int i = 0; i < nevents; i++) { - if (events[i].data.ptr == nullptr) { - // The event was queued for a resource that's - // been destroyed, so forget_resource is kindly - // notifying us to skip it. - continue; - } else { - linux_event_callback_t *cb = reinterpret_cast(events[i].data.ptr); - int events_gotten = epoll_to_user(events[i].events); -#ifndef NDEBUG - int events_wanted = events_requested[cb]; - if (events_gotten & poll_event_in) rassert(events_wanted & poll_event_in); - if (events_gotten & poll_event_out) rassert(events_wanted & poll_event_out); -#endif - cb->on_event(events_gotten); - } - } - - nevents = 0; - - parent->pump(); - } -} - -epoll_event_queue_t::~epoll_event_queue_t() { - DEBUG_VAR int res = close(epoll_fd); - rassert_err(res == 0, "Could not close epoll_fd"); -} - -void epoll_event_queue_t::watch_resource(fd_t resource, int watch_mode, linux_event_callback_t *cb) { - rassert(cb); - epoll_event event; - - event.events = EPOLLET | user_to_epoll(watch_mode); - event.data.ptr = cb; - - int res = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, resource, &event); - guarantee_err(res == 0, "Could not watch resource\n"); - - DEBUG_ONLY_CODE(events_requested[cb] = watch_mode); -} - -void epoll_event_queue_t::adjust_resource(fd_t resource, int watch_mode, linux_event_callback_t *cb) { - rassert(cb); - epoll_event event; - - event.events = EPOLLET | user_to_epoll(watch_mode); - event.data.ptr = cb; - - int res = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, resource, &event); - guarantee_err(res == 0, "Could not adjust resource"); - - // Go through the queue of messages in the current poll cycle and if any are referring - // to the resource we are adjusting, remove any events that we are no longer requesting. - for (int i = 0; i < nevents; i++) { - if (events[i].data.ptr == cb) { - events[i].events &= EPOLLET | user_to_epoll(watch_mode); - } - } - - DEBUG_ONLY_CODE(events_requested[cb] = watch_mode); -} - -void epoll_event_queue_t::forget_resource(fd_t resource, linux_event_callback_t *cb) { - rassert(cb); - - epoll_event event; - - event.events = EPOLLIN; - event.data.ptr = nullptr; - - int res = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, resource, &event); - guarantee_err(res == 0, "Couldn't remove resource from watching"); - - // Go through the queue of messages in the current poll cycle and - // clean out the ones that are referencing the resource we're - // being asked to forget. - for (int i = 0; i < nevents; i++) { - if (events[i].data.ptr == cb) { - events[i].data.ptr = nullptr; - } - } - - DEBUG_ONLY_CODE(events_requested.erase(events_requested.find(cb))); -} - -void epoll_event_queue_t::watch_event(system_event_t *ev, linux_event_callback_t *cb) { - watch_resource(ev->get_notify_fd(), poll_event_in, cb); -} - -void epoll_event_queue_t::forget_event(system_event_t *ev, linux_event_callback_t *cb) { - forget_resource(ev->get_notify_fd(), cb); -} - -#endif // NO_EPOLL -#endif // __linux diff --git a/src/arch/runtime/event_queue/epoll.hpp b/src/arch/runtime/event_queue/epoll.hpp deleted file mode 100644 index 705cfe497d6..00000000000 --- a/src/arch/runtime/event_queue/epoll.hpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_EVENT_QUEUE_EPOLL_HPP_ -#define ARCH_RUNTIME_EVENT_QUEUE_EPOLL_HPP_ - -#include - -#ifndef NDEBUG -#include -#endif - -#include "arch/runtime/event_queue_types.hpp" -#include "arch/runtime/runtime_utils.hpp" -#include "arch/runtime/system_event.hpp" -#include "config/args.hpp" - -// Event queue structure -struct epoll_event_queue_t { -public: - explicit epoll_event_queue_t(linux_queue_parent_t *parent); - void run(); - ~epoll_event_queue_t(); - - // These should only be called by the event queue itself or by the - // linux_* classes - void watch_resource(fd_t resource, int events, linux_event_callback_t *cb); - void adjust_resource(fd_t resource, int events, linux_event_callback_t *cb); - void forget_resource(fd_t resource, linux_event_callback_t *cb); - - void watch_event(system_event_t *, linux_event_callback_t *cb); - void forget_event(system_event_t *, linux_event_callback_t *cb); - -private: - linux_queue_parent_t *parent; - - fd_t epoll_fd; - - // We store this as a class member because forget_resource needs - // to go through the events and remove queued messages for - // resources that are being destroyed. - epoll_event events[MAX_IO_EVENT_PROCESSING_BATCH_SIZE]; - int nevents; - -#ifndef NDEBUG - /* In debug mode, check to make sure epoll() doesn't give us events that - we didn't ask for. The ints stored here are combinations of poll_event_in - and poll_event_out. */ - std::map events_requested; -#endif - - DISABLE_COPYING(epoll_event_queue_t); -}; - - -#endif // ARCH_RUNTIME_EVENT_QUEUE_EPOLL_HPP_ diff --git a/src/arch/runtime/event_queue/iocp.cc b/src/arch/runtime/event_queue/iocp.cc deleted file mode 100644 index 40f3699e3ec..00000000000 --- a/src/arch/runtime/event_queue/iocp.cc +++ /dev/null @@ -1,191 +0,0 @@ -#if defined (_WIN32) - -#include - -#include "arch/timer.hpp" -#include "arch/runtime/event_queue/iocp.hpp" -#include "arch/runtime/thread_pool.hpp" -#include "arch/io/event_watcher.hpp" - -#ifdef TRACE_IOCP -#include "debug.hpp" -#define debugf_queue(...) debugf("iocp: " __VA_ARGS__) -#else -#define debugf_queue(...) ((void)0) -#endif - -enum class windows_message_type_t : ULONG_PTR { - EVENT_CALLBACK, - OVERLAPPED_OPERATION, - PROXIED_OVERLAPPED_OPERATION -}; - - -struct proxied_overlapped_operation_t { - DWORD error; - DWORD nb_bytes; - overlapped_operation_t *op; - proxied_overlapped_operation_t(DWORD error_, - DWORD nb_bytes_, - overlapped_operation_t *op_) : - error(error_), nb_bytes(nb_bytes_), op(op_) { } -}; - -iocp_event_queue_t::iocp_event_queue_t(linux_thread_t *thread_) - : thread(thread_), timer_cb(nullptr) { - debugf_queue("[%p] create\n", this); - completion_port = CreateIoCompletionPort( - INVALID_HANDLE_VALUE, - NULL, - static_cast(windows_message_type_t::OVERLAPPED_OPERATION), - 1); - guarantee_winerr(completion_port != NULL, "CreateIoCompletionPort failed"); -} - -void iocp_event_queue_t::add_handle(fd_t handle) { - debugf_queue("[%p] add_handle(%x)\n", this, handle); - completion_port = CreateIoCompletionPort( - handle, - completion_port, - static_cast(windows_message_type_t::OVERLAPPED_OPERATION), - 1); - guarantee_winerr(completion_port != NULL, - "CreateIoCompletionPort: failed to add handle"); -} - -void iocp_event_queue_t::watch_event(windows_event_t *event, - linux_event_callback_t *cb) { - debugf_queue("[%p] watch_event\n", this); - rassert(event->event_queue == nullptr && event->callback == nullptr, - "Cannot watch the same event twice"); - event->callback = cb; - event->event_queue = this; -} - -void iocp_event_queue_t::forget_event(windows_event_t *event, - UNUSED linux_event_callback_t *cb) { - debugf_queue("[%p] forget_event\n", this); - event->event_queue = nullptr; - event->callback = nullptr; -} - -void iocp_event_queue_t::post_event(linux_event_callback_t *cb) { - debugf_queue("[%p] post_event\n", this); - rassert(cb != nullptr); - BOOL res = PostQueuedCompletionStatus( - completion_port, - 0, - ULONG_PTR(windows_message_type_t::EVENT_CALLBACK), - reinterpret_cast(cb)); - guarantee_winerr(res, "PostQueuedCompletionStatus failed"); -} - -void iocp_event_queue_t::reset_timer(int64_t next_time_in_nanos_, - timer_provider_callback_t *cb) { - rassert(cb != nullptr); - next_time_in_nanos = next_time_in_nanos_; - timer_cb = cb; -} - -void iocp_event_queue_t::unset_timer() { - timer_cb = nullptr; -} - -void iocp_event_queue_t::run() { - debugf_queue("[%p] run\n", this); - while (!thread->should_shut_down()) { - ULONG nb_bytes; - ULONG_PTR key; - OVERLAPPED *overlapped; - - DWORD wait_ms; - if (timer_cb != nullptr) { - int64_t wait_ns = next_time_in_nanos - get_ticks(); - if (wait_ns <= 0) { - wait_ms = 0; - } else { - wait_ms = 1 + ((wait_ns - 1) / MILLION); // ceil(wait_ns / MILLION) - } - } else { - wait_ms = INFINITE; - } - - debugf_queue("waiting for next event (wait %ums%s)\n", - wait_ms, - wait_ms == INFINITE ? " inf" : ""); - - BOOL res = GetQueuedCompletionStatus(completion_port, - &nb_bytes, - &key, - &overlapped, - wait_ms); - DWORD error = res ? NO_ERROR : GetLastError(); - - if (timer_cb != nullptr && - (error == WAIT_TIMEOUT || next_time_in_nanos < get_ticks())) { - debugf_queue("[%p] trigger timer callback\n", this); - timer_provider_callback_t *cb = timer_cb; - timer_cb = nullptr; - cb->on_oneshot(); - } else if (overlapped == nullptr) { - guarantee_xwinerr(false, error, "GetQueuedCompletionStatus failed"); - } - - if (overlapped != nullptr) { - switch (static_cast(key)) { - case windows_message_type_t::OVERLAPPED_OPERATION: { - debugf_queue("[%p] dequeued overlapped operation %p\n", this, overlapped); - overlapped_operation_t *ao = - reinterpret_cast(overlapped); - threadnum_t target_thread = ao->event_watcher->current_thread(); - if (target_thread != get_thread_id()) { - linux_thread_pool_t *pool = linux_thread_pool_t::get_thread_pool(); - HANDLE target_cp = - pool->threads[target_thread.threadnum]->queue.completion_port; - proxied_overlapped_operation_t *poa = - new proxied_overlapped_operation_t(error, nb_bytes, ao); - BOOL res = PostQueuedCompletionStatus( - target_cp, - 0, - static_cast( - windows_message_type_t::PROXIED_OVERLAPPED_OPERATION), - reinterpret_cast(poa)); - guarantee_winerr(res, "PostQueuedCompletionStatus failed"); - } else { - ao->set_result(nb_bytes, error); - } - break; - } - - case windows_message_type_t::PROXIED_OVERLAPPED_OPERATION: { - debugf_queue("[%p] dequeued proxied overlapped operation\n", this); - guarantee_xwinerr(error == NO_ERROR, - error, - "GetQueuedCompletionStatus failed"); - proxied_overlapped_operation_t *poa = - reinterpret_cast(overlapped); - poa->op->set_result(poa->nb_bytes, poa->error); - delete poa; - break; - } - - case windows_message_type_t::EVENT_CALLBACK: { - debugf_queue("[%p] dequeued event callback\n", this); - guarantee_xwinerr(error == NO_ERROR, - error, - "GetQueuedCompletionStatus failed"); - linux_event_callback_t *cb = - reinterpret_cast(overlapped); - cb->on_event(poll_event_in); - break; - } - - default: - crash("Unknown message type in IOCP queue %lu", key); - } - } - thread->pump(); - } -} - -#endif diff --git a/src/arch/runtime/event_queue/iocp.hpp b/src/arch/runtime/event_queue/iocp.hpp deleted file mode 100644 index 80cda12e618..00000000000 --- a/src/arch/runtime/event_queue/iocp.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef ARCH_RUNTIME_EVENT_QUEUE_IOCP_HPP_ -#define ARCH_RUNTIME_EVENT_QUEUE_IOCP_HPP_ - -#include - -#include -#include - -#include "arch/runtime/system_event.hpp" -#include "arch/runtime/event_queue_types.hpp" -#include "arch/runtime/runtime_utils.hpp" -#include "time.hpp" - -class linux_thread_t; -class timer_provider_callback_t; - -class iocp_event_queue_t { -public: - explicit iocp_event_queue_t(linux_thread_t*); - - void watch_event(windows_event_t*, linux_event_callback_t*); - void forget_event(windows_event_t*, linux_event_callback_t*); - void post_event(linux_event_callback_t *cb); - - void add_handle(fd_t handle); - - void reset_timer(int64_t next_time_in_nanos, timer_provider_callback_t *cb); - void unset_timer(); - - void run(); - -private: - ticks_t next_time_in_nanos; - timer_provider_callback_t *timer_cb; - - linux_thread_t *thread; - HANDLE completion_port; -}; - -#endif diff --git a/src/arch/runtime/event_queue/kqueue.cc b/src/arch/runtime/event_queue/kqueue.cc deleted file mode 100644 index de623007ba2..00000000000 --- a/src/arch/runtime/event_queue/kqueue.cc +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#if defined(__MACH__) - -#include "arch/runtime/event_queue/kqueue.hpp" - -#include -#include -#include -#include - -#include - -#include "arch/runtime/event_queue.hpp" -#include "arch/runtime/thread_pool.hpp" -#include "config/args.hpp" -#include "errors.hpp" -#include "perfmon/perfmon.hpp" -#include "utils.hpp" - -std::set user_to_kevent_filters(int mode) { - rassert((mode & (poll_event_in | poll_event_out)) == mode); - - std::set filters; - if (mode & poll_event_in) filters.insert(EVFILT_READ); - if (mode & poll_event_out) filters.insert(EVFILT_WRITE); - - return filters; -} - -int kevent_filter_to_user(int16_t filter) { - rassert(filter == EVFILT_READ || filter == EVFILT_WRITE); - - if (filter == EVFILT_READ) return poll_event_in; - if (filter == EVFILT_WRITE) return poll_event_out; - unreachable(); -} - -kqueue_event_queue_t::kqueue_event_queue_t(linux_queue_parent_t *_parent) - : parent(_parent) { - - // Create the kqueue - kqueue_fd = kqueue(); - guarantee_err(kqueue_fd >= 0, "Could not create kqueue"); -} - -// Small helper function to call `kevent` with error checking -int call_kevent(int kq, const struct kevent *changelist, int nchanges, - struct kevent *eventlist, int nevents, - const struct timespec *timeout) { - int res; - do { - res = kevent(kq, changelist, nchanges, eventlist, nevents, timeout); - } while (res == -1 && get_errno() == EINTR); - - // Apart from EINTR, it doesn't seem like there's any error code that we can - // really handle here. Terminate instead if the call failed. - guarantee_err(res != -1, "Call to kevent() failed"); - - return res; -} - -void kqueue_event_queue_t::run() { - // Now, start the loop - while (!parent->should_shut_down()) { - // Grab the events from the kqueue! - nevents = call_kevent(kqueue_fd, nullptr, 0, - events, MAX_IO_EVENT_PROCESSING_BATCH_SIZE, nullptr); - - block_pm_duration event_loop_timer(pm_eventloop_singleton_t::get()); - - for (int i = 0; i < nevents; i++) { - if (events[i].udata == nullptr) { - // The event was queued for a resource that's - // been destroyed, so forget_resource is kindly - // notifying us to skip it by setting `udata` to NULL. - continue; - } else { - linux_event_callback_t *cb = - reinterpret_cast(events[i].udata); - cb->on_event(kevent_filter_to_user(events[i].filter)); - } - } - - parent->pump(); - } -} - -kqueue_event_queue_t::~kqueue_event_queue_t() { - int res = close(kqueue_fd); - guarantee_err(res == 0, "Could not close kqueue"); -} - -void kqueue_event_queue_t::add_filters(fd_t resource, const std::set &filters, - linux_event_callback_t *cb) { - rassert(cb != nullptr); - - for (auto filter : filters) { - struct kevent ev; - EV_SET(&ev, static_cast(resource), filter, EV_ADD, 0, 0, cb); - call_kevent(kqueue_fd, &ev, 1, nullptr, 0, nullptr); - } -} - -void kqueue_event_queue_t::del_filters(fd_t resource, const std::set &filters, - linux_event_callback_t *cb) { - rassert(cb != nullptr); - - for (auto filter : filters) { - struct kevent ev; - EV_SET(&ev, static_cast(resource), filter, EV_DELETE, 0, 0, cb); - call_kevent(kqueue_fd, &ev, 1, nullptr, 0, nullptr); - } -} - -void kqueue_event_queue_t::watch_resource(fd_t resource, int event_mask, - linux_event_callback_t *cb) { - rassert(cb != nullptr); - - // Start watching the events on `resource` - std::set filters = user_to_kevent_filters(event_mask); - add_filters(resource, filters, cb); - - guarantee(watched_events.count(resource) == 0); - watched_events[resource] = event_mask; -} - -void kqueue_event_queue_t::adjust_resource(fd_t resource, int event_mask, - linux_event_callback_t *cb) { - rassert(cb); - - auto ev = watched_events.find(resource); - guarantee(ev != watched_events.end()); - - const std::set current_filters = user_to_kevent_filters(ev->second); - const std::set new_filters = user_to_kevent_filters(event_mask); - - // We generate a diff to find out which filters we have to delete from the - // kqueue. - // On the other hand we re-add all events, which will cause kqueue to update the - // `udata` field (the callback) of the existing events in case it has changed. - std::set filters_to_del = current_filters; - for (auto f : new_filters) { - filters_to_del.erase(f); - } - - // Apply the diff - del_filters(resource, filters_to_del, cb); - add_filters(resource, new_filters, cb); - ev->second = event_mask; - - // Go through the queue of messages in the current poll cycle and - // clean out the ones that are referencing the filters that are being - // deleted. - // - // We also update the cb, in case we have changed it - // Note: we don't currently support changing the cb through `adjust_resource` - // in the epoll queue implementation. It is supported in the poll one though, - // and there's no reason for why we should not support it here. - for (int i = 0; i < nevents; i++) { - if (events[i].ident == static_cast(resource)) { - if (filters_to_del.count(events[i].filter) > 0) { - // No longer watching this event - events[i].udata = nullptr; - } else if (events[i].udata != nullptr) { - // Just update the cb, unless we had previously deleted this one. - events[i].udata = cb; - } - } - } -} - -void kqueue_event_queue_t::forget_resource(fd_t resource, linux_event_callback_t *cb) { - rassert(cb); - - // To stop watching all associated events, we have to re-generate the - // corresponding filters. `kqueue` identified an event by its `ident`, `filter`, - // pair so we generate an EV_DELETE kevent for each one. - auto ev = watched_events.find(resource); - guarantee(ev != watched_events.end()); - std::set filters = user_to_kevent_filters(ev->second); - del_filters(resource, filters, cb); - watched_events.erase(ev); - - // Go through the queue of messages in the current poll cycle and - // clean out the ones that are referencing the resource we're - // being asked to forget. - for (int i = 0; i < nevents; i++) { - if (events[i].ident == static_cast(resource)) { - rassert(events[i].udata == cb || events[i].udata == nullptr); - events[i].udata = nullptr; - } - } -} - -void kqueue_event_queue_t::watch_event(system_event_t *ev, linux_event_callback_t *cb) { - watch_resource(ev->get_notify_fd(), poll_event_in, cb); -} - -void kqueue_event_queue_t::forget_event(system_event_t *ev, linux_event_callback_t *cb) { - forget_resource(ev->get_notify_fd(), cb); -} - -#endif // defined(__MACH__) diff --git a/src/arch/runtime/event_queue/kqueue.hpp b/src/arch/runtime/event_queue/kqueue.hpp deleted file mode 100644 index da8414df4a5..00000000000 --- a/src/arch/runtime/event_queue/kqueue.hpp +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_EVENT_QUEUE_KQUEUE_HPP_ -#define ARCH_RUNTIME_EVENT_QUEUE_KQUEUE_HPP_ - -#include - -#include -#include - -#include "arch/runtime/event_queue_types.hpp" -#include "arch/runtime/runtime_utils.hpp" -#include "arch/runtime/system_event.hpp" -#include "config/args.hpp" - -// Event queue using `kqueue`/`kevent` -class kqueue_event_queue_t { -public: - explicit kqueue_event_queue_t(linux_queue_parent_t *parent); - void run(); - ~kqueue_event_queue_t(); - - // These should only be called by the event queue itself or by the linux_* classes - void watch_resource(fd_t resource, int events, linux_event_callback_t *cb); - void adjust_resource(fd_t resource, int events, linux_event_callback_t *cb); - void forget_resource(fd_t resource, linux_event_callback_t *cb); - void watch_event(system_event_t *, linux_event_callback_t *cb); - void forget_event(system_event_t *, linux_event_callback_t *cb); - -private: - // Helper functions - void add_filters(fd_t resource, const std::set &filters, - linux_event_callback_t *cb); - void del_filters(fd_t resource, const std::set &filters, - linux_event_callback_t *cb); - - linux_queue_parent_t *parent; - - fd_t kqueue_fd; - - // Keep track of which event types we are currently watching for a resource, - // so that `adjust_resource` can generate an appropriate diff and - // `forget_resource` knows which events to unwatch. - std::map watched_events; - - // We store this as a class member because forget_resource needs - // to go through the events and remove queued messages for - // resources that are being destroyed. - struct kevent events[MAX_IO_EVENT_PROCESSING_BATCH_SIZE]; - int nevents; - - DISABLE_COPYING(kqueue_event_queue_t); -}; - - -#endif // ARCH_RUNTIME_EVENT_QUEUE_KQUEUE_HPP_ diff --git a/src/arch/runtime/event_queue/poll.cc b/src/arch/runtime/event_queue/poll.cc deleted file mode 100644 index 2e16cc4a18b..00000000000 --- a/src/arch/runtime/event_queue/poll.cc +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. - -#ifndef _WIN32 - -#include "arch/runtime/event_queue/poll.hpp" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - - -#include "config/args.hpp" -#include "utils.hpp" -#include "arch/runtime/event_queue.hpp" -#include "arch/runtime/thread_pool.hpp" -#include "arch/io/timer_provider.hpp" -#include "perfmon/perfmon.hpp" - -int user_to_poll(int mode) { - -#ifndef __linux - DEBUG_VAR -#endif - int allowed_mode_mask = poll_event_in | poll_event_out; - -#ifdef __linux - allowed_mode_mask |= poll_event_rdhup; -#endif - - rassert((mode & allowed_mode_mask) == mode); - - int out_mode = 0; - if (mode & poll_event_in) out_mode |= POLLIN; - if (mode & poll_event_out) out_mode |= POLLOUT; -#ifdef __linux - if (mode & poll_event_rdhup) out_mode |= POLLRDHUP; -#endif - - return out_mode; -} - -int poll_to_user(int mode) { - -#ifndef __linux -DEBUG_VAR -#endif - int allowed_mode_mask = POLLIN | POLLOUT | POLLERR | POLLHUP; - -#ifdef __linux - allowed_mode_mask |= POLLRDHUP; -#endif - - rassert((mode & allowed_mode_mask) == mode); - - int out_mode = 0; - if (mode & POLLIN) out_mode |= poll_event_in; - if (mode & POLLOUT) out_mode |= poll_event_out; - if (mode & POLLERR) out_mode |= poll_event_err; - if (mode & POLLHUP) out_mode |= poll_event_hup; -#ifdef __linux - if (mode & POLLRDHUP) out_mode |= poll_event_rdhup; -#endif - - return out_mode; -} - -poll_event_queue_t::poll_event_queue_t(linux_queue_parent_t *_parent) - : parent(_parent) { -} - -void poll_event_queue_t::run() { - int res; - -#ifndef RDB_TIMER_PROVIDER -#error "RDB_TIMER_PROVIDER not defined." -#elif RDB_TIMER_PROVIDER == RDB_TIMER_PROVIDER_SIGNAL - // Create a restricted sigmask for ppoll: - // In the upcoming loop, we want to continue blocking signals - // (especially SIGINT and SIGTERM, which the main thread - // has interrupt handlers for). However, LEGACY_LINUX - // builds use timer_signal_provider, which requires - // this thread to be able to catch the TIMER_NOTIFY_SIGNAL. - // sigmask_restricted is configured to allow this - // one signal through while blocking the other signals - // for the main thread to handle. - sigset_t sigmask_restricted, sigmask_full; - res = sigfillset(&sigmask_restricted); - guarantee_err(res == 0, "Could not create an full signal mask"); - res = sigdelset(&sigmask_restricted, TIMER_NOTIFY_SIGNAL); - guarantee_err(res == 0, "Could not remove TIMER_NOTIFY_SIGNAL from signal mask"); - - res = sigfillset(&sigmask_full); - guarantee_err(res == 0, "Could not create a full signal mask"); -#endif // RDB_TIMER_PROVIDER - - // Now, start the loop - while (!parent->should_shut_down()) { - // Grab the events from the kernel! -#ifndef RDB_TIMER_PROVIDER -#error "RDB_TIMER_PROVIDER not defined." -#elif RDB_TIMER_PROVIDER == RDB_TIMER_PROVIDER_SIGNAL - res = ppoll(&watched_fds[0], watched_fds.size(), nullptr, &sigmask_restricted); -#else - res = poll(&watched_fds[0], watched_fds.size(), -1); -#endif - // ppoll might return with EINTR in some cases (in particular - // under GDB), we just need to retry. - if (res == -1 && get_errno() == EINTR) { - res = 0; - } - - // The only likely poll error here is ENOMEM, which we - // have no way of handling, and it's probably fatal. - guarantee_err(res != -1, "Waiting for poll events failed"); - - block_pm_duration event_loop_timer(pm_eventloop_singleton_t::get()); - - int count = 0; - for (unsigned int i = 0; i < watched_fds.size(); i++) { - if (watched_fds[i].revents != 0) { - linux_event_callback_t *cb = callbacks[watched_fds[i].fd]; - cb->on_event(poll_to_user(watched_fds[i].revents)); - count++; - } - if (count == res) - break; - } - -#ifndef RDB_TIMER_PROVIDER -#error "RDB_TIMER_PROVIDER not defined." -#elif RDB_TIMER_PROVIDER == RDB_TIMER_PROVIDER_SIGNAL - // If ppoll is busy with file descriptors, the piece of shit - // kernel starves out signals, so we need to unblock them to - // let the signal handlers get called, and then block them - // right back. What a sensible fucking system. - res = pthread_sigmask(SIG_SETMASK, &sigmask_restricted, nullptr); - guarantee_xerr(res == 0, res, "Could not unblock signals"); - res = pthread_sigmask(SIG_SETMASK, &sigmask_full, nullptr); - guarantee_xerr(res == 0, res, "Could not block signals"); -#endif // RDB_TIMER_PROVIDER - - parent->pump(); - } -} - -poll_event_queue_t::~poll_event_queue_t() { -} - -void poll_event_queue_t::watch_resource(fd_t resource, int watch_mode, linux_event_callback_t *cb) { - rassert(cb); - - pollfd pfd; - bzero(&pfd, sizeof(pfd)); - pfd.fd = resource; - pfd.events = user_to_poll(watch_mode); - - watched_fds.push_back(pfd); - callbacks[resource] = cb; -} - -void poll_event_queue_t::adjust_resource(fd_t resource, int events, linux_event_callback_t *cb) { - // Find and adjust the event - callbacks[resource] = cb; - for (unsigned int i = 0; i < watched_fds.size(); i++) { - if (watched_fds[i].fd == resource) { - watched_fds[i].events = user_to_poll(events); - watched_fds[i].revents &= user_to_poll(events); - return; - } - } -} - -void poll_event_queue_t::forget_resource(fd_t resource, DEBUG_VAR linux_event_callback_t *cb) { - rassert(cb); - - // Erase the callback from the map - callbacks.erase(resource); - - // Find and erase the event - for (unsigned int i = 0; i < watched_fds.size(); i++) { - if (watched_fds[i].fd == resource) { - watched_fds.erase(watched_fds.begin() + i); - return; - } - } - -} - -void poll_event_queue_t::watch_event(system_event_t *ev, linux_event_callback_t *cb) { - watch_resource(ev->get_notify_fd(), poll_event_in, cb); -} - -void poll_event_queue_t::forget_event(system_event_t *ev, linux_event_callback_t *cb) { - forget_resource(ev->get_notify_fd(), cb); -} - -#endif diff --git a/src/arch/runtime/event_queue/poll.hpp b/src/arch/runtime/event_queue/poll.hpp deleted file mode 100644 index 9a07d778fb1..00000000000 --- a/src/arch/runtime/event_queue/poll.hpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_EVENT_QUEUE_POLL_HPP_ -#define ARCH_RUNTIME_EVENT_QUEUE_POLL_HPP_ - -#include - -#include -#include - -#include "arch/runtime/event_queue_types.hpp" -#include "arch/runtime/runtime_utils.hpp" -#include "arch/runtime/system_event.hpp" -#include "errors.hpp" - -// Event queue structure -class poll_event_queue_t { -public: - explicit poll_event_queue_t(linux_queue_parent_t *parent); - void run(); - ~poll_event_queue_t(); - - // These should only be called by the event queue itself or by the linux_* classes - void watch_resource(fd_t resource, int events, linux_event_callback_t *cb); - void adjust_resource(fd_t resource, int events, linux_event_callback_t *cb); - void forget_resource(fd_t resource, linux_event_callback_t *cb); - void watch_event(system_event_t *, linux_event_callback_t *cb); - void forget_event(system_event_t *, linux_event_callback_t *cb); - -private: - linux_queue_parent_t *parent; - - std::vector watched_fds; - std::map callbacks; - - DISABLE_COPYING(poll_event_queue_t); -}; - -#endif // ARCH_RUNTIME_EVENT_QUEUE_POLL_HPP_ diff --git a/src/arch/runtime/event_queue_types.hpp b/src/arch/runtime/event_queue_types.hpp deleted file mode 100644 index 70a6e0584ac..00000000000 --- a/src/arch/runtime/event_queue_types.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_EVENT_QUEUE_TYPES_HPP_ -#define ARCH_RUNTIME_EVENT_QUEUE_TYPES_HPP_ - -#include - -// Types that are used, in particular, by poll.hpp and epoll.hpp. - -// Event queue callback -struct linux_event_callback_t { - virtual void on_event(int events) = 0; - virtual ~linux_event_callback_t() {} -}; - -struct linux_queue_parent_t { - virtual void pump() = 0; - virtual bool should_shut_down() = 0; - virtual ~linux_queue_parent_t() {} -}; - -const int poll_event_in = 1; -const int poll_event_out = 2; -const int poll_event_err = 4; -const int poll_event_hup = 8; -const int poll_event_rdhup = 16; - -#endif // ARCH_RUNTIME_EVENT_QUEUE_TYPES_HPP_ - diff --git a/src/arch/runtime/message_hub.cc b/src/arch/runtime/message_hub.cc deleted file mode 100644 index c3f573fb341..00000000000 --- a/src/arch/runtime/message_hub.cc +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "arch/runtime/message_hub.hpp" - -#include -#include - -#include - -#include "config/args.hpp" -#include "arch/runtime/event_queue.hpp" -#include "arch/runtime/thread_pool.hpp" -#include "logger.hpp" -#include "random.hpp" -#include "utils.hpp" - -// Set this to 1 if you would like some "unordered" messages to be unordered. -#ifndef NDEBUG -#define RDB_RELOOP_MESSAGES 0 -#endif - -linux_message_hub_t::linux_message_hub_t(linux_event_queue_t *queue, - linux_thread_pool_t *thread_pool, - threadnum_t current_thread) - : queue_(queue), - thread_pool_(thread_pool), - is_woken_up_(false), - current_thread_(current_thread) { - -#ifndef NDEBUG - if(MESSAGE_SCHEDULER_GRANULARITY < (1 << (NUM_SCHEDULER_PRIORITIES))) { - logWRN("MESSAGE_SCHEDULER_GRANULARITY is too small to honor some of the " - "lower priorities"); - } -#endif - - queue_->watch_event(&event_, this); -} - -linux_message_hub_t::~linux_message_hub_t() { - for (int i = 0; i < thread_pool_->n_threads; i++) { - guarantee(queues_[i].msg_local_list.empty()); - } - for (int p = MESSAGE_SCHEDULER_MIN_PRIORITY; - p <= MESSAGE_SCHEDULER_MAX_PRIORITY; - ++p) { - - guarantee(get_priority_msg_list(p).empty()); - } - - guarantee(incoming_messages_.empty()); -} - -void linux_message_hub_t::do_store_message(threadnum_t nthread, linux_thread_message_t *msg) { - rassert(0 <= nthread.threadnum && nthread.threadnum < thread_pool_->n_threads); - queues_[nthread.threadnum].msg_local_list.push_back(msg); -} - -// Collects a message for a given thread onto a local list. -void linux_message_hub_t::store_message_ordered(threadnum_t nthread, - linux_thread_message_t *msg) { - rassert(!msg->is_ordered); // Each message object can only be enqueued once, - // and once it is removed, is_ordered is reset to false. -#ifndef NDEBUG -#if RDB_RELOOP_MESSAGES - // We default to 1, not zero, to allow store_message_sometime messages to sometimes jump ahead of - // store_message messages. - msg->reloop_count_ = 1; -#else - msg->reloop_count_ = 0; -#endif -#endif // NDEBUG - msg->is_ordered = true; - do_store_message(nthread, msg); -} - -int rand_reloop_count() { - int x; - frexp(randint(10000) / 10000.0, &x); - int ret = -x; - rassert(ret >= 0); - return ret; -} - -void linux_message_hub_t::store_message_sometime(threadnum_t nthread, linux_thread_message_t *msg) { -#ifndef NDEBUG -#if RDB_RELOOP_MESSAGES - msg->reloop_count_ = rand_reloop_count(); -#else - msg->reloop_count_ = 0; -#endif -#endif // NDEBUG - do_store_message(nthread, msg); -} - - -void linux_message_hub_t::insert_external_message(linux_thread_message_t *msg) { - bool do_wake_up; - { - spinlock_acq_t acq(&incoming_messages_lock_); - do_wake_up = !check_and_set_is_woken_up(); - incoming_messages_.push_back(msg); - } - - // Wakey wakey eggs and bakey - if (do_wake_up) { - event_.wakey_wakey(); - } -} - -linux_message_hub_t::msg_list_t &linux_message_hub_t::get_priority_msg_list(int priority) { - rassert(priority >= MESSAGE_SCHEDULER_MIN_PRIORITY); - rassert(priority <= MESSAGE_SCHEDULER_MAX_PRIORITY); - return priority_msg_lists_[priority - MESSAGE_SCHEDULER_MIN_PRIORITY]; -} - -void linux_message_hub_t::on_event(int events) { - if (events != poll_event_in) { - logERR("Unexpected event mask: %d", events); - } - - // You must read wakey-wakeys so that the pipe-based implementation doesn't fill - // up and so that poll-based event triggering doesn't infinite-loop. - event_.consume_wakey_wakeys(); - - // Sort incoming messages into the respective priority_msg_lists_ - sort_incoming_messages_by_priority(); - - // Compute how many messages of MESSAGE_SCHEDULER_MAX_PRIORITY we process - // before we check the incoming queues for new messages. - // We call this the granularity of the message scheduler, and it is - // MESSAGE_SCHEDULER_GRANULARITY or smaller. - size_t total_pending_msgs = 0; - for (int i = 0; i < NUM_SCHEDULER_PRIORITIES; ++i) { - total_pending_msgs += priority_msg_lists_[i].size(); - } - const size_t effective_granularity = std::min(total_pending_msgs, - static_cast(MESSAGE_SCHEDULER_GRANULARITY)); - - // Process a certain number of messages from each priority - for (int current_priority = MESSAGE_SCHEDULER_MAX_PRIORITY; - current_priority >= MESSAGE_SCHEDULER_MIN_PRIORITY; --current_priority) { - - // Compute how many messages of `current_priority` we want to process - // in this pass. - // The priority has an exponential effect on how many messages - // get processed, i.e. if we process 8 messages of priority 1 per pass, - // we are going to process up to 16 messages of priority 2, 32 of - // priority 3 and so on. - // However, we process at least one message of each priority level per - // pass (in case the granularity is too small). - int priority_exponent = MESSAGE_SCHEDULER_MAX_PRIORITY - current_priority; - size_t to_process_from_priority = - std::max(static_cast(1), effective_granularity >> priority_exponent); - - for (linux_thread_message_t *m = get_priority_msg_list(current_priority).head(); - m != nullptr && to_process_from_priority > 0; - m = get_priority_msg_list(current_priority).head()) { - - get_priority_msg_list(current_priority).remove(m); - --to_process_from_priority; - -#ifndef NDEBUG - if (m->reloop_count_ > 0) { - --m->reloop_count_; - do_store_message(current_thread_, m); - continue; - } -#endif - - m->on_thread_switch(); - } - } - - // We might have left some messages unprocessed. - // Check if that is the case, and if yes, make sure we are called again. - for (int i = 0; i < NUM_SCHEDULER_PRIORITIES; ++i) { - if (!priority_msg_lists_[i].empty()) { - // Place wakey_wakey and then yield to the event processing. - // It will wake us up again immediately, but can handle a few - // OS events (such as timers, network messages etc.) in the meantime. - bool do_wake_up; - { - spinlock_acq_t acq(&incoming_messages_lock_); - do_wake_up = !check_and_set_is_woken_up(); - } - if (do_wake_up) { - event_.wakey_wakey(); - } - break; - } - } -} - -void linux_message_hub_t::sort_incoming_messages_by_priority() { - // We do this in two steps to release the spinlock faster. - // append_and_clear is a very cheap operation, while - // assigning each message to a different priority queue - // is more expensive. - - // 1. Pull the messages - msg_list_t new_messages; - { - spinlock_acq_t acq(&incoming_messages_lock_); - new_messages.append_and_clear(&incoming_messages_); - is_woken_up_ = false; - } - - // 2. Sort the messages into their respective priority queues - while (linux_thread_message_t *m = new_messages.head()) { - new_messages.remove(m); - int effective_priority = m->priority; - if (m->is_ordered) { - // Ordered messages are treated as if they had - // priority MESSAGE_SCHEDULER_ORDERED_PRIORITY. - // This ensures that they can never bypass another - // ordered message. - effective_priority = MESSAGE_SCHEDULER_ORDERED_PRIORITY; - m->is_ordered = false; - } - get_priority_msg_list(effective_priority).push_back(m); - } -} - -bool linux_message_hub_t::check_and_set_is_woken_up() { - const bool was_woken_up = is_woken_up_; - is_woken_up_ = true; - return was_woken_up; -} - -// Pushes messages collected locally global lists available to all -// threads. -void linux_message_hub_t::push_messages() { - for (int i = 0; i < thread_pool_->n_threads; i++) { - // Append the local list for ith thread to that thread's global - // message list. - thread_queue_t *queue = &queues_[i]; - if (!queue->msg_local_list.empty()) { - // Transfer messages to the other core - - bool do_wake_up; - { - spinlock_acq_t acq(&thread_pool_->threads[i]->message_hub.incoming_messages_lock_); - - // We only need to do a wake up if we're the first people to do a - // wake up. - do_wake_up = - !thread_pool_->threads[i]->message_hub.check_and_set_is_woken_up(); - - thread_pool_->threads[i]->message_hub.incoming_messages_.append_and_clear(&queue->msg_local_list); - } - - // Wakey wakey, perhaps eggs and bakey - if (do_wake_up) { - thread_pool_->threads[i]->message_hub.event_.wakey_wakey(); - } - } - } -} diff --git a/src/arch/runtime/message_hub.hpp b/src/arch/runtime/message_hub.hpp deleted file mode 100644 index 15ff498b66d..00000000000 --- a/src/arch/runtime/message_hub.hpp +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_MESSAGE_HUB_HPP_ -#define ARCH_RUNTIME_MESSAGE_HUB_HPP_ - -#include - -#include "arch/runtime/event_queue.hpp" -#include "arch/runtime/runtime_utils.hpp" -#include "arch/runtime/system_event.hpp" -#include "arch/spinlock.hpp" -#include "config/args.hpp" -#include "containers/intrusive_list.hpp" -#include "threading.hpp" - - -#define NUM_SCHEDULER_PRIORITIES (MESSAGE_SCHEDULER_MAX_PRIORITY \ - - MESSAGE_SCHEDULER_MIN_PRIORITY \ - + 1) - - -class linux_thread_pool_t; - -/* There is one message hub per thread, NOT one message hub for the entire program. - -Each message hub stores messages that are going from that message hub's home thread to -other threads. It keeps a separate queue for messages destined for each other thread. */ - -class linux_message_hub_t : private linux_event_callback_t { -public: - typedef intrusive_list_t msg_list_t; - - linux_message_hub_t(linux_event_queue_t *queue, linux_thread_pool_t *thread_pool, - threadnum_t current_thread); - - /* For each thread, transfer messages from our msg_local_list for that thread to our - msg_global_list for that thread */ - void push_messages(); - - /* Schedules the given message to be sent to the given thread by pushing it onto our - msg_local_list for that thread */ - void store_message_ordered(threadnum_t nthread, linux_thread_message_t *msg); - - // Schedules the given message to be sent to the given thread. However, these are not - // guaranteed to be called in the same order relative to one another. - void store_message_sometime(threadnum_t nthread, linux_thread_message_t *msg); - - // Called by the thread pool when it needs to deliver a message from the main thread - // (which does not have an event queue) - void insert_external_message(linux_thread_message_t *msg); - - ~linux_message_hub_t(); - -private: - // Does store_message or store_message_sometime, only without setting the reloop_count_ in - // debug mode. - void do_store_message(threadnum_t nthread, linux_thread_message_t *msg); - - // Moves messages from incoming_messages_ into the respective entries of - // priority_msg_lists, depending on the messages' priorities. - void sort_incoming_messages_by_priority(); - - msg_list_t &get_priority_msg_list(int priority); - - linux_event_queue_t *const queue_; - linux_thread_pool_t *const thread_pool_; - - /* Queue for messages going from this->current_thread to other threads */ - struct thread_queue_t { - //TODO this doesn't need to be a class anymore - - /* Messages are cached here before being pushed to the global list so that we don't - have to acquire the spinlock as often */ - msg_list_t msg_local_list; - } queues_[MAX_THREADS]; - - // Must only be used with acquired incoming_messages_lock_ - bool check_and_set_is_woken_up(); - bool is_woken_up_; - msg_list_t incoming_messages_; - spinlock_t incoming_messages_lock_; - - // Use `sort_incoming_messages_by_priority()` to sort incoming_messages_ into - // these lists. - // Use `get_priority_msg_list()` to get the list for a given priority. - // Each list contains messages of the respective priority. - // (except for ordered messages, which go onto the list for - // MESSAGE_SCHEDULER_ORDERED_PRIORITY) - msg_list_t priority_msg_lists_[NUM_SCHEDULER_PRIORITIES]; - - void on_event(int events); - - // The eventfd (or pipe-based alternative) notified after the first incoming - // message is put onto incoming_messages_. - system_event_t event_; - - /* The thread that we queue messages originating from. (Recall that there is one - message_hub_t per thread.) */ - const threadnum_t current_thread_; - - DISABLE_COPYING(linux_message_hub_t); -}; - -#endif // ARCH_RUNTIME_MESSAGE_HUB_HPP_ diff --git a/src/arch/runtime/runtime.cc b/src/arch/runtime/runtime.cc deleted file mode 100644 index 7b34c9f65bd..00000000000 --- a/src/arch/runtime/runtime.cc +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "arch/runtime/runtime.hpp" - -#include - -#include - -#include "arch/runtime/starter.hpp" -#include "arch/runtime/thread_pool.hpp" -#include "do_on_thread.hpp" - -bool in_thread_pool() { - return linux_thread_pool_t::get_thread_pool() != nullptr; -} - -threadnum_t get_thread_id() { - if (i_am_in_blocker_pool_thread()) { - return threadnum_t(-1); - } - return threadnum_t(linux_thread_pool_t::get_thread_id()); -} - -int get_num_threads() { - return linux_thread_pool_t::get_thread_pool()->n_threads; -} - -#ifndef NDEBUG -void assert_good_thread_id(threadnum_t thread) { - if (linux_thread_pool_t::get_thread_pool() == nullptr) { - rassert(thread.threadnum == 0); - } else { - rassert(thread.threadnum >= 0, "(thread = %" PRIi32 ")", thread.threadnum); - rassert(thread.threadnum < get_num_threads(), "(thread = %" PRIi32 ", n_threads = %d)", - thread.threadnum, get_num_threads()); - } -} -#endif - -bool continue_on_thread(threadnum_t thread, linux_thread_message_t *msg) { - assert_good_thread_id(thread); - if (thread.threadnum == linux_thread_pool_t::get_thread_id()) { - // The thread to continue on is the thread we are already on - return true; - } else { - linux_thread_pool_t::get_thread()->message_hub.store_message_ordered(thread, msg); - return false; - } -} - -void call_later_on_this_thread(linux_thread_message_t *msg) { - linux_thread_pool_t::get_thread()->message_hub.store_message_ordered( - threadnum_t(linux_thread_pool_t::get_thread_id()), - msg); -} - -struct starter_t : public thread_message_t { - linux_thread_pool_t *tp; - std::function run; - - starter_t(linux_thread_pool_t *_tp, const std::function &_fun) - : tp(_tp), run(std::bind(&starter_t::run_wrapper, this, _fun)) { } - void on_thread_switch() { - rassert(get_thread_id().threadnum == get_num_threads() - 1); - coro_t::spawn_sometime(run); - } -private: - void run_wrapper(const std::function &fun) { - fun(); - tp->shutdown_thread_pool(); - } -}; - -// Runs the action 'fun()' on thread zero. -void run_in_thread_pool(const std::function &fun, int worker_threads) { - linux_thread_pool_t thread_pool(worker_threads, false); - starter_t starter(&thread_pool, fun); - thread_pool.run_thread_pool(&starter); -} diff --git a/src/arch/runtime/runtime.hpp b/src/arch/runtime/runtime.hpp deleted file mode 100644 index 267c6f7270a..00000000000 --- a/src/arch/runtime/runtime.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_RUNTIME_HPP_ -#define ARCH_RUNTIME_RUNTIME_HPP_ - -#include "threading.hpp" - -class linux_thread_message_t; -typedef linux_thread_message_t thread_message_t; - -threadnum_t get_thread_id(); - -int get_num_threads(); - -#ifndef NDEBUG -bool in_thread_pool(); -void assert_good_thread_id(threadnum_t thread); -#else -inline void assert_good_thread_id(UNUSED threadnum_t thread) { } -#endif - -// TODO: continue_on_thread() and call_later_on_this_thread are mostly obsolete because of -// coroutine-based thread switching. - -// continue_on_thread() is used to send a message to another thread. If the 'thread' parameter is the -// thread that we are already on, then it returns 'true'; otherwise, it will cause the other -// thread's event loop to call msg->on_thread_switch(). - -bool continue_on_thread(threadnum_t thread, linux_thread_message_t *msg); - -// call_later_on_this_thread() will cause msg->on_thread_switch() to be called from the main event loop -// of the thread we are currently on. It's a bit of a hack. - -void call_later_on_this_thread(linux_thread_message_t *msg); - -/* TODO: It is common in the codebase right now to have code like this: - -if (continue_on_thread(thread, msg)) call_later_on_this_thread(msg); - -This is because originally clients would just call store_message_ordered() directly. -When continue_on_thread() was written, the code still assumed that the message's -callback would not be called before continue_on_thread() returned. Using -call_later_on_this_thread() is not ideal because it would be better to just -continue processing immediately if we are already on the correct thread, but -at the time it didn't seem worth rewriting it, so call_later_on_this_thread() -was added to make it easy to simulate the old semantics. */ - - -#endif /* ARCH_RUNTIME_RUNTIME_HPP_ */ diff --git a/src/arch/runtime/runtime_utils.cc b/src/arch/runtime/runtime_utils.cc deleted file mode 100644 index 6dcb3c9a060..00000000000 --- a/src/arch/runtime/runtime_utils.cc +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "arch/runtime/runtime_utils.hpp" - -#include - -#include "arch/runtime/context_switching.hpp" -#include "arch/runtime/coroutines.hpp" -#include "logger.hpp" - -int get_cpu_count() { -#ifdef _WIN32 - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - return sysinfo.dwNumberOfProcessors; -#else - return sysconf(_SC_NPROCESSORS_ONLN); -#endif -} - -callable_action_wrapper_t::callable_action_wrapper_t() : - action_on_heap(false), - action_(nullptr) -{ } - -callable_action_wrapper_t::~callable_action_wrapper_t() -{ - if (action_ != nullptr) { - reset(); - } -} - -void callable_action_wrapper_t::reset() { - rassert(action_ != nullptr); - - if (action_on_heap) { - delete action_; - action_ = nullptr; - action_on_heap = false; - } else { - action_->~callable_action_t(); - action_ = nullptr; - } -} - -void callable_action_wrapper_t::run() { - rassert(action_ != nullptr); - action_->run_action(); -} - -#ifndef _WIN32 - -struct sigaction make_basic_sigaction() { - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - int res = sigfillset(&sa.sa_mask); - guarantee_err(res == 0, "sigfillset failed"); - return sa; -} - -struct sigaction make_sa_handler(int sa_flags, void (*sa_handler_func)(int)) { - guarantee(!(sa_flags & SA_SIGINFO)); - struct sigaction sa = make_basic_sigaction(); - sa.sa_flags = sa_flags; - sa.sa_handler = sa_handler_func; - return sa; -} - -struct sigaction make_sa_sigaction(int sa_flags, void (*sa_sigaction_func)(int, siginfo_t *, void *)) { - guarantee(sa_flags & SA_SIGINFO); - struct sigaction sa = make_basic_sigaction(); - sa.sa_flags = sa_flags; - sa.sa_sigaction = sa_sigaction_func; - return sa; -} - -#endif - diff --git a/src/arch/runtime/runtime_utils.hpp b/src/arch/runtime/runtime_utils.hpp deleted file mode 100644 index eb514864384..00000000000 --- a/src/arch/runtime/runtime_utils.hpp +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_RUNTIME_UTILS_HPP_ -#define ARCH_RUNTIME_RUNTIME_UTILS_HPP_ - -#include -#include -#include - -#include "config/args.hpp" -#include "containers/intrusive_list.hpp" - -#ifdef _WIN32 - -typedef HANDLE fd_t; -const fd_t INVALID_FD = INVALID_HANDLE_VALUE; -const fd_t STDOUT_FD = GetStdHandle(STD_OUTPUT_HANDLE); -const fd_t STDERR_FD = GetStdHandle(STD_ERROR_HANDLE); - -inline SOCKET fd_to_socket(fd_t handle) { - CT_ASSERT(sizeof(SOCKET) == sizeof(HANDLE)); - return reinterpret_cast(handle); -} - -inline fd_t socket_to_fd(SOCKET s) { - return reinterpret_cast(s); -} - -#else - -typedef int fd_t; -const fd_t INVALID_FD = -1; -const fd_t STDOUT_FD = STDOUT_FILENO; -const fd_t STDERR_FD = STDERR_FILENO; - -inline int fd_to_socket(fd_t fd) { - return fd; -} - -inline fd_t socket_to_fd(int fd) { - return fd; -} - -#endif - - -class linux_thread_message_t : public intrusive_list_node_t { -public: - explicit linux_thread_message_t(int _priority) - : priority(_priority), - is_ordered(false) -#ifndef NDEBUG - , reloop_count_(0) -#endif - { } - linux_thread_message_t() - : priority(MESSAGE_SCHEDULER_DEFAULT_PRIORITY), - is_ordered(false) -#ifndef NDEBUG - , reloop_count_(0) -#endif - { } - virtual void on_thread_switch() = 0; - - void set_priority(int _priority) { priority = _priority; } - int get_priority() const { return priority; } -protected: - virtual ~linux_thread_message_t() {} -private: - friend class linux_message_hub_t; - int priority; - bool is_ordered; // Used internally by the message hub -#ifndef NDEBUG - int reloop_count_; -#endif -}; - -typedef linux_thread_message_t thread_message_t; - -int get_cpu_count(); - -// More pollution of runtime_utils.hpp. -#ifndef NDEBUG - -/* If `ASSERT_NO_CORO_WAITING;` appears at the top of a block, then it is illegal -to call `coro_t::wait()`, `coro_t::spawn_now_dangerously()`, or `coro_t::notify_now()` -within that block and any attempt to do so will be a fatal error. */ -#define ASSERT_NO_CORO_WAITING \ - assert_no_coro_waiting_t assert_no_coro_waiting_var(__FILE__, __LINE__) - -/* If `ASSERT_FINITE_CORO_WAITING;` appears at the top of a block, then code -within that block may call `coro_t::spawn_now_dangerously()` or `coro_t::notify_now()` but -not `coro_t::wait()`. This is because `coro_t::spawn_now_dangerously()` and -`coro_t::notify_now()` will return control directly to the coroutine that called -them. */ -#define ASSERT_FINITE_CORO_WAITING assert_finite_coro_waiting_t assert_finite_coro_waiting_var(__FILE__, __LINE__) - -/* Implementation support for `ASSERT_NO_CORO_WAITING` and `ASSERT_FINITE_CORO_WAITING` */ -struct assert_no_coro_waiting_t { - assert_no_coro_waiting_t(const char *, int); - ~assert_no_coro_waiting_t(); -}; -struct assert_finite_coro_waiting_t { - assert_finite_coro_waiting_t(const char *, int); - ~assert_finite_coro_waiting_t(); -}; - -#else // NDEBUG - -/* In release mode, these assertions are no-ops. */ -#define ASSERT_NO_CORO_WAITING do { } while (0) -#define ASSERT_FINITE_CORO_WAITING do { } while (0) - -#endif // NDEBUG - -#ifndef _WIN32 -struct sigaction make_sa_handler(int sa_flags, void (*sa_handler_func)(int)); -struct sigaction make_sa_sigaction(int sa_flags, void (*sa_sigaction_func)(int, siginfo_t *, void *)); -#endif - - -#endif // ARCH_RUNTIME_RUNTIME_UTILS_HPP_ diff --git a/src/arch/runtime/starter.hpp b/src/arch/runtime/starter.hpp deleted file mode 100644 index 48bf6d9cbab..00000000000 --- a/src/arch/runtime/starter.hpp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_STARTER_HPP_ -#define ARCH_RUNTIME_STARTER_HPP_ - -// Implementation in runtime.cc. - -#include - -/* `run_in_thread_pool()` starts a RethinkDB thread pool, runs the given -function in a coroutine inside of it, waits for the function to return, and then -shuts down the thread pool. */ - -void run_in_thread_pool(const std::function &fun, int worker_threads); - -#endif // ARCH_RUNTIME_STARTER_HPP_ diff --git a/src/arch/runtime/system_event.hpp b/src/arch/runtime/system_event.hpp deleted file mode 100644 index 911b814e0a5..00000000000 --- a/src/arch/runtime/system_event.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_SYSTEM_EVENT_HPP_ -#define ARCH_RUNTIME_SYSTEM_EVENT_HPP_ - -#if defined(_WIN32) - -#include "arch/runtime/system_event/windows_event.hpp" -typedef windows_event_t system_event_t; - -#elif defined(NO_EVENTFD) || !defined(__linux) - -#include "arch/runtime/system_event/pipe_event.hpp" -typedef pipe_event_t system_event_t; - -#else - -#include "arch/runtime/system_event/eventfd_event.hpp" -typedef eventfd_event_t system_event_t; - -#endif - -#endif // ARCH_RUNTIME_SYSTEM_EVENT_HPP_ - diff --git a/src/arch/runtime/system_event/eventfd.cc b/src/arch/runtime/system_event/eventfd.cc deleted file mode 100644 index 435908a75fd..00000000000 --- a/src/arch/runtime/system_event/eventfd.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#if defined(LEGACY_LINUX) && !defined(NO_EVENTFD) - -#include -#include - -// Wrappers for eventfd(), since CentOS backported the system -// calls but not the libc wrappers. - -#include "arch/runtime/system_event/eventfd.hpp" -#include "utils.hpp" - -int eventfd(int count, UNUSED int flags) { - rassert(flags == 0); // Legacy kernel doesn't have eventfd2. - return syscall(SYS_eventfd, count); -} - -int eventfd_read(int fd, eventfd_t *value) { - int res = read(fd, value, sizeof(eventfd_t)); - return (res == sizeof(eventfd_t)) ? 0 : -1; -} - -int eventfd_write(int fd, eventfd_t value) { - int res = write(fd, &value, sizeof(eventfd_t)); - return (res == sizeof(eventfd_t)) ? 0 : -1; -} - -#endif // defined(LEGACY_LINUX) && !defined(NO_EVENTFD) diff --git a/src/arch/runtime/system_event/eventfd.hpp b/src/arch/runtime/system_event/eventfd.hpp deleted file mode 100644 index 373e39bffe9..00000000000 --- a/src/arch/runtime/system_event/eventfd.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_SYSTEM_EVENT_EVENTFD_HPP_ -#define ARCH_RUNTIME_SYSTEM_EVENT_EVENTFD_HPP_ - -#ifndef LEGACY_LINUX - -#if defined(__linux) && !defined(NO_EVENTFD) -#include -#endif - -#else - -#include - -typedef uint64_t eventfd_t; - -int eventfd(int count, int flags); -int eventfd_read(int fd, eventfd_t *value); -int eventfd_write(int fd, eventfd_t value); - -#endif /* LEGACY_LINUX */ - -#endif /* ARCH_RUNTIME_SYSTEM_EVENT_EVENTFD_HPP_ */ diff --git a/src/arch/runtime/system_event/eventfd_event.cc b/src/arch/runtime/system_event/eventfd_event.cc deleted file mode 100644 index 78a9fda8569..00000000000 --- a/src/arch/runtime/system_event/eventfd_event.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#if defined(__linux) && !defined(NO_EVENTFD) - -#include "arch/runtime/system_event/eventfd_event.hpp" - -#include -#include -#include - -#include "errors.hpp" -#include "arch/runtime/system_event/eventfd.hpp" - -eventfd_event_t::eventfd_event_t() { - eventfd_ = eventfd(0, 0); - guarantee_err(eventfd_ != -1, "Could not create eventfd"); - - int res = fcntl(eventfd_, F_SETFL, O_NONBLOCK); - guarantee_err(res == 0, "Could not make eventfd non-blocking"); -} - -eventfd_event_t::~eventfd_event_t() { - int res = close(eventfd_); - guarantee_err(res == 0 || get_errno() == EINTR, "Could not close eventfd"); -} - -void eventfd_event_t::consume_wakey_wakeys() { - // We don't care how many pings we consumed from the eventfd. - uint64_t value; - ssize_t res; - do { - res = ::read(eventfd_, &value, sizeof(value)); - } while (res == -1 && get_errno() == EINTR); - - if (res == -1) { - guarantee_err(get_errno() == EAGAIN || get_errno() == EWOULDBLOCK, "Could not read from eventfd."); - } else { - guarantee(res == sizeof(value), "Trouble reading from an eventfd: read %zd bytes", res); - } -} - -void eventfd_event_t::wakey_wakey() { - const uint64_t value = 1; - ssize_t res; - do { - res = ::write(eventfd_, &value, sizeof(value)); - } while (res == -1 && get_errno() == EINTR); - - guarantee_err(res != -1, "Could not write to an eventfd."); - guarantee(res == sizeof(value), "Somehow completed a partial write to an eventfd."); -} - -#endif diff --git a/src/arch/runtime/system_event/eventfd_event.hpp b/src/arch/runtime/system_event/eventfd_event.hpp deleted file mode 100644 index c94381eaf76..00000000000 --- a/src/arch/runtime/system_event/eventfd_event.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_SYSTEM_EVENT_EVENTFD_EVENT_HPP_ -#define ARCH_RUNTIME_SYSTEM_EVENT_EVENTFD_EVENT_HPP_ - -#include -#include "errors.hpp" - -// An event API implemented in terms of eventfd. May not be available -// on older kernels. -struct eventfd_event_t { -public: - eventfd_event_t(); - ~eventfd_event_t(); - - // Consumes all the pings from the eventfd. - void consume_wakey_wakeys(); - - // Pings an eventfd. You may call this from any thread (as long as you know - // nobody will destroy the eventfd_event_t object simultaneously). - void wakey_wakey(); - - int get_notify_fd() { - return eventfd_; - } - -private: - int eventfd_; - DISABLE_COPYING(eventfd_event_t); -}; - -#endif // ARCH_RUNTIME_SYSTEM_EVENT_EVENTFD_EVENT_HPP_ - diff --git a/src/arch/runtime/system_event/pipe_event.cc b/src/arch/runtime/system_event/pipe_event.cc deleted file mode 100644 index 49c3275a57e..00000000000 --- a/src/arch/runtime/system_event/pipe_event.cc +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef _WIN32 - -#include "arch/runtime/system_event/pipe_event.hpp" - -#include -#include -#include - -#include "utils.hpp" -#include "logger.hpp" - -pipe_event_t::pipe_event_t() { - int pipefd[2]; - int res = pipe(pipefd); - guarantee_err(res == 0, "Could not create system event pipe"); - - read_fd_ = pipefd[0]; - write_fd_ = pipefd[1]; - - res = fcntl(read_fd_, F_SETFL, O_NONBLOCK); - guarantee_err(res == 0, "Could not make the notify pipe's read end non-blocking"); - - res = fcntl(write_fd_, F_SETFL, O_NONBLOCK); - guarantee_err(res == 0, "Could not make the notify pipe's write end non-blocking"); -} - -pipe_event_t::~pipe_event_t() { - int res = close(read_fd_); - guarantee_err(res == 0 || get_errno() == EINTR, "Could not close notify pipe's reading end"); - - res = close(write_fd_); - guarantee_err(res == 0 || get_errno() == EINTR, "Could not close notify pipe's writing end"); -} - -void pipe_event_t::consume_wakey_wakeys() { - char buf[512]; - ssize_t res; - do { - do { - res = read(read_fd_, buf, sizeof(buf)); - } while (res == -1 && get_errno() == EINTR); - } while (res == sizeof(buf)); - - if (res == -1) { - // We normally expect a short read count, but if there were an exact multiple of sizeof(buf) - // notifications on the pipe, we would get EAGAIN when we try to read again and there's - // nothing on the pipe. - guarantee_err(get_errno() == EAGAIN || get_errno() == EWOULDBLOCK, "Could not read from notification pipe"); - } -} - -void pipe_event_t::wakey_wakey() { - char buf[1] = { 0 }; - ssize_t res; - do { - res = write(write_fd_, buf, sizeof(buf)); - } while (res == -1 && get_errno() == EINTR); - - if (res == -1) { - // EAGAIN and EWOULDBLOCK are okay because that means the pipe is full, and the recipient - // already has pending notifications anyway. - guarantee_err(get_errno() == EAGAIN || get_errno() == EWOULDBLOCK, "Could not write to notification pipe"); - } -} - -#endif diff --git a/src/arch/runtime/system_event/pipe_event.hpp b/src/arch/runtime/system_event/pipe_event.hpp deleted file mode 100644 index f935ca32578..00000000000 --- a/src/arch/runtime/system_event/pipe_event.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_SYSTEM_EVENT_PIPE_EVENT_HPP_ -#define ARCH_RUNTIME_SYSTEM_EVENT_PIPE_EVENT_HPP_ - -#include - -#include "errors.hpp" - -struct pipe_event_t { -public: - pipe_event_t(); - ~pipe_event_t(); - - void consume_wakey_wakeys(); - - // You may call this from any thread (as long as you know nobody will destroy the - // eventfd_event_t object simultaneously). - void wakey_wakey(); - - int get_notify_fd() { - return read_fd_; - } - -private: - int read_fd_; - int write_fd_; - DISABLE_COPYING(pipe_event_t); -}; - - -#endif // ARCH_RUNTIME_SYSTEM_EVENT_PIPE_EVENT_HPP_ - diff --git a/src/arch/runtime/system_event/windows_event.cc b/src/arch/runtime/system_event/windows_event.cc deleted file mode 100644 index be79232d75c..00000000000 --- a/src/arch/runtime/system_event/windows_event.cc +++ /dev/null @@ -1,15 +0,0 @@ -#ifdef _WIN32 - -#include "arch/runtime/system_event/windows_event.hpp" - -#include "arch/runtime/event_queue/iocp.hpp" -#include "arch/runtime/thread_pool.hpp" - -void windows_event_t::wakey_wakey() { - if (event_queue != nullptr) { - rassert(callback != nullptr); - event_queue->post_event(callback); - } -} - -#endif diff --git a/src/arch/runtime/system_event/windows_event.hpp b/src/arch/runtime/system_event/windows_event.hpp deleted file mode 100644 index 374601411da..00000000000 --- a/src/arch/runtime/system_event/windows_event.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef ARCH_RUNTIME_SYSTEM_EVENT_WINDOWS_EVENT_HPP_ -#define ARCH_RUNTIME_SYSTEM_EVENT_WINDOWS_EVENT_HPP_ - -#include "windows.hpp" -#include "containers/scoped.hpp" -#include "arch/runtime/event_queue_types.hpp" - -class linux_thread_t; -class iocp_event_queue_t; - -class windows_event_t { -public: - windows_event_t() : event_queue(nullptr), callback(nullptr) { } - void wakey_wakey(); - void consume_wakey_wakeys() { } - -private: - friend class iocp_event_queue_t; - - iocp_event_queue_t *event_queue; - linux_event_callback_t *callback; -}; - -#endif diff --git a/src/arch/runtime/thread_pool.cc b/src/arch/runtime/thread_pool.cc deleted file mode 100644 index 238b5b12a17..00000000000 --- a/src/arch/runtime/thread_pool.cc +++ /dev/null @@ -1,523 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "arch/runtime/thread_pool.hpp" - -#include -#include -#include -#include - -#ifndef _WIN32 -#include -#endif - -#include "arch/compiler.hpp" -#include "arch/barrier.hpp" -#include "arch/os_signal.hpp" -#include "arch/io/timer_provider.hpp" -#include "arch/runtime/event_queue.hpp" -#include "arch/runtime/runtime.hpp" -#include "arch/timing.hpp" -#include "errors.hpp" -#include "logger.hpp" -#include "utils.hpp" - -#if !defined(VALGRIND) && !defined(_WIN32) -// This needs to be large enough for abi::__cxa_demangle to not overflow the stack. -// If this is too small, you may see memory corruption and crashes when attempting -// to format a backtrace. -const int SIGNAL_HANDLER_STACK_SIZE = MINSIGSTKSZ + (128 * KILOBYTE); -#endif - -#ifdef _WIN32 -std::atomic linux_thread_pool_t::global_thread_pool = nullptr; - -linux_thread_pool_t *linux_thread_pool_t::get_global_thread_pool() { - return global_thread_pool.load(); -} -#endif - -THREAD_LOCAL linux_thread_pool_t *linux_thread_pool_t::thread_pool = nullptr; -THREAD_LOCAL int linux_thread_pool_t::thread_id = -1; -THREAD_LOCAL linux_thread_t *linux_thread_pool_t::thread = nullptr; - -linux_thread_pool_t *linux_thread_pool_t::get_thread_pool() { - return thread_pool; -} -void linux_thread_pool_t::set_thread_pool(linux_thread_pool_t *val) { - rassert(thread_pool == nullptr || val == nullptr); - thread_pool = val; -} -int linux_thread_pool_t::get_thread_id() { - return thread_id; -} -void linux_thread_pool_t::set_thread_id(int val) { - thread_id = val; -} -linux_thread_t *linux_thread_pool_t::get_thread() { - rassert(thread != nullptr); - return thread; -} -void linux_thread_pool_t::set_thread(linux_thread_t *val) { - rassert(thread == nullptr || val == nullptr); - thread = val; -} - -linux_thread_pool_t::linux_thread_pool_t(int worker_threads, bool _do_set_affinity) : -#ifndef NDEBUG - coroutine_summary(false), -#endif - interrupt_message(nullptr), - generic_blocker_pool(nullptr), - n_threads(worker_threads + 1), // we create an extra utility thread - do_set_affinity(_do_set_affinity) -{ - rassert(n_threads > 1); // we want at least one non-utility thread - rassert(n_threads <= MAX_THREADS); - - int res; - - res = pthread_cond_init(&shutdown_cond, nullptr); - guarantee_xerr(res == 0, res, "Could not create shutdown cond"); - - res = pthread_mutex_init(&shutdown_cond_mutex, nullptr); - guarantee_xerr(res == 0, res, "Could not create shutdown cond mutex"); -} - -os_signal_cond_t *linux_thread_pool_t::exchange_interrupt_message(os_signal_cond_t *m) { - os_signal_cond_t *o; - { - spinlock_acq_t acq(&interrupt_message_lock); - - o = interrupt_message; - - interrupt_message = m; - } - - return o; -} - -struct thread_data_t { - thread_barrier_t *barrier; - linux_thread_pool_t *thread_pool; - int current_thread; - linux_thread_message_t *initial_message; -}; - -void *linux_thread_pool_t::start_thread(void *arg) { -#ifndef _WIN32 - // Block all signals but `SIGSEGV` and `SIGBUS` (will be unblocked by the event - // queue in case of poll). - { - sigset_t sigmask; - int res = sigfillset(&sigmask); - guarantee_err(res == 0, "Could not get a full sigmask"); - - res = sigdelset(&sigmask, SIGSEGV); - guarantee_err(res == 0, "Could not remove SIGSEGV from sigmask"); - res = sigdelset(&sigmask, SIGBUS); - guarantee_err(res == 0, "Could not remove SIGBUS from sigmask"); - - res = pthread_sigmask(SIG_SETMASK, &sigmask, nullptr); - guarantee_xerr(res == 0, res, "Could not block signal"); - } -#endif - - thread_data_t *tdata = reinterpret_cast(arg); - - // Set thread-local variables - set_thread_pool(tdata->thread_pool); - set_thread_id(tdata->current_thread); - - // Use a separate block so that it's very clear how long the thread lives for - // It's not really necessary, but I like it. - { - linux_thread_t local_thread(tdata->thread_pool, tdata->current_thread); - tdata->thread_pool->threads[tdata->current_thread] = &local_thread; - set_thread(&local_thread); - blocker_pool_t *generic_blocker_pool = nullptr; // Will only be instantiated by one thread - - /* Install a handler for segmentation faults that just prints a backtrace. If we're - running under valgrind, we don't install this handler because Valgrind will print the - backtrace for us. */ -#ifndef VALGRIND -#ifndef _WIN32 - scoped_page_aligned_ptr_t stack_base(SIGNAL_HANDLER_STACK_SIZE); - stack_t signal_stack; - signal_stack.ss_sp = stack_base.get(); - signal_stack.ss_flags = 0; - signal_stack.ss_size = SIGNAL_HANDLER_STACK_SIZE; - int res = sigaltstack(&signal_stack, nullptr); - guarantee_err(res == 0, "sigaltstack failed"); - - { - struct sigaction sa = make_sa_sigaction( - SA_SIGINFO | SA_ONSTACK, - &linux_thread_pool_t::fatal_signal_handler); - - res = sigaction(SIGSEGV, &sa, nullptr); - guarantee_err(res == 0, "Could not install SEGV signal handler"); - res = sigaction(SIGBUS, &sa, nullptr); - guarantee_err(res == 0, "Could not install BUS signal handler"); - } -#endif -#endif // VALGRIND - - // First thread should initialize generic_blocker_pool before the start barrier - if (tdata->initial_message) { - rassert(tdata->thread_pool->generic_blocker_pool == nullptr, "generic_blocker_pool already initialized"); - generic_blocker_pool = new blocker_pool_t(GENERIC_BLOCKER_THREAD_COUNT, - &local_thread.queue); - tdata->thread_pool->generic_blocker_pool = generic_blocker_pool; - } - - // If one thread is allowed to run before another one has finished - // starting up, then it might try to access an uninitialized part of the - // unstarted one. - tdata->barrier->wait(); - rassert(tdata->thread_pool->generic_blocker_pool != nullptr, - "Thread passed start barrier while generic_blocker_pool uninitialized"); - - // Prime the pump by calling the initial thread message that was passed to thread_pool::run() - if (tdata->initial_message) { - local_thread.message_hub.insert_external_message(tdata->initial_message); - } - - local_thread.queue.run(); - - // If one thread is allowed to delete itself before another one has - // broken out of its loop, it might delete something that the other thread - // needed to access. - tdata->barrier->wait(); - - // If this thread created the generic blocker pool, clean it up - if (generic_blocker_pool != nullptr) { - delete generic_blocker_pool; - tdata->thread_pool->generic_blocker_pool = nullptr; - } - - tdata->thread_pool->threads[tdata->current_thread] = nullptr; - set_thread(nullptr); - } - - delete tdata; - return nullptr; -} - -#ifndef NDEBUG -void linux_thread_pool_t::enable_coroutine_summary() { - coroutine_summary = true; -} -#endif - -void linux_thread_pool_t::run_thread_pool(linux_thread_message_t *initial_message) { - do_shutdown = false; - - // Start child threads - thread_barrier_t barrier(n_threads + 1); - - for (int i = 0; i < n_threads; i++) { - bool is_utility_thread = (i == n_threads - 1); - thread_data_t *tdata = new thread_data_t(); - tdata->barrier = &barrier; - tdata->thread_pool = this; - tdata->current_thread = i; - // The initial message gets sent to the utility thread. - tdata->initial_message = is_utility_thread ? initial_message : nullptr; - - int res = pthread_create(&pthreads[i], nullptr, &start_thread, tdata); - guarantee_xerr(res == 0, res, "Could not create thread"); - - // Don't set affinity for the utility thread - if (do_set_affinity && !is_utility_thread) { - // On Apple, the thread affinity API has awful documentation, so we don't even bother. -#ifdef _GNU_SOURCE - // Distribute threads evenly among CPUs - int ncpus = get_cpu_count(); - cpu_set_t mask; - CPU_ZERO(&mask); - CPU_SET(i % ncpus, &mask); - res = pthread_setaffinity_np(pthreads[i], sizeof(cpu_set_t), &mask); - guarantee_xerr(res == 0, res, "Could not set thread affinity"); -#endif - } - } - - // Mark the main thread (for use in assertions etc.) - set_thread_id(-1); - - // Set up interrupt handlers - - // Wait for threads to start up so that our interrupt handlers can send messages to the threads. - // TODO: Fix the thread pool so that it isn't awful, fragile, and reliant on thread barriers. - barrier.wait(); - - // TODO: Should we save and restore previous interrupt handlers? This would - // be a good thing to do before distributing the RethinkDB IO layer, but it's - // not really important. - - set_thread_pool(this); // So signal handlers can find us -#ifdef _WIN32 - global_thread_pool.store(this); -#else - { - struct sigaction sa = make_sa_sigaction(SA_SIGINFO, &linux_thread_pool_t::interrupt_handler); - - int res = sigaction(SIGTERM, &sa, nullptr); - guarantee_err(res == 0, "Could not install TERM handler"); - - res = sigaction(SIGINT, &sa, nullptr); - guarantee_err(res == 0, "Could not install INT handler"); - } -#endif - - // Wait for order to shut down - - int res = pthread_mutex_lock(&shutdown_cond_mutex); - guarantee_xerr(res == 0, res, "Could not lock shutdown cond mutex"); - - while (!do_shutdown) { // while loop guards against spurious wakeups - res = pthread_cond_wait(&shutdown_cond, &shutdown_cond_mutex); - guarantee_xerr(res == 0, res, "Could not wait for shutdown cond"); - } - - res = pthread_mutex_unlock(&shutdown_cond_mutex); - guarantee_xerr(res == 0, res, "Could not unlock shutdown cond mutex"); - -#ifdef _WIN32 - global_thread_pool.store(nullptr); -#else - // Remove interrupt handlers - { - struct sigaction sa = make_sa_handler(0, SIG_IGN); - - res = sigaction(SIGTERM, &sa, nullptr); - guarantee_err(res == 0, "Could not remove TERM handler"); - - res = sigaction(SIGINT, &sa, nullptr); - guarantee_err(res == 0, "Could not remove INT handler"); - } -#endif - set_thread_pool(nullptr); - -#ifndef NDEBUG - // Save each thread's coroutine counters before shutting down - std::vector > coroutine_counts(n_threads); -#endif - - // Shut down child threads - for (int i = 0; i < n_threads; i++) { - // Cause child thread to break out of its loop -#ifndef NDEBUG - threads[i]->initiate_shut_down(&coroutine_counts[i]); -#else - threads[i]->initiate_shut_down(); -#endif - } - - // Wait for barrier, because it expects n_threads + 1 things to wait. (Otherwise we'd have to - // have two barriers, which isn't such a problem, but meh.) - barrier.wait(); - - for (int i = 0; i < n_threads; i++) { - // Wait for child thread to actually exit - - res = pthread_join(pthreads[i], nullptr); - guarantee_xerr(res == 0, res, "Could not join thread"); - } - -#ifndef NDEBUG - if (coroutine_summary) - { - // Combine coroutine counts from each thread, and log the totals - std::map total_coroutine_counts; - for (int i = 0; i < n_threads; ++i) { - for (std::map::iterator j = coroutine_counts[i].begin(); - j != coroutine_counts[i].end(); ++j) { - total_coroutine_counts[j->first] += j->second; - } - } - - for (std::map::iterator i = total_coroutine_counts.begin(); - i != total_coroutine_counts.end(); ++i) { - logDBG("%zu coroutines ran with type %s", i->second, i->first.c_str()); - } - } -#endif // NDEBUG -} - -#ifdef _WIN32 - -void linux_thread_pool_t::interrupt_handler(DWORD type) { - // The handler should run on a new thread created by the OS - rassert(get_thread_pool() == nullptr, "The interrupt handler was called on the wrong thread."); - - linux_thread_pool_t *self = global_thread_pool.load(); - - if (self == nullptr) { - logNTC("Caught signal. Waiting for initialization to complete before closing..."); - - const int WAIT_FOR_THREAD_POOL_MS = 10000; - const int NAP_TIME = 200; - - for (int napped = 0; ; napped += NAP_TIME) { - Sleep(NAP_TIME); - self = global_thread_pool.load(); - if (self != nullptr) { - break; - } - if (napped >= WAIT_FOR_THREAD_POOL_MS) { - logNTC("Waiting for initilization timed out. Stopping RethinkDB."); - ExitProcess(EXIT_FAILURE); - } - } - } - - os_signal_cond_t *interrupt_signal = self->exchange_interrupt_message(nullptr); - - if (interrupt_signal != nullptr) { - interrupt_signal->source_type = type; - self->threads[self->n_threads - 1]->message_hub.insert_external_message(interrupt_signal); - } -} - -#else - -// Note: Maybe we should use a signalfd instead of a signal handler, and then -// there would be no issues with potential race conditions because the signal -// would just be pulled out in the main poll/epoll loop. But as long as this works, -// there's no real reason to change it. -void linux_thread_pool_t::interrupt_handler(int signo, siginfo_t *siginfo, void *) { - /* The interrupt handler should run on the main thread, the same thread that - run() was called on. */ - rassert(get_thread_id() == -1, "The interrupt handler was called on the wrong thread."); - - linux_thread_pool_t *self = get_thread_pool(); - - /* Set the interrupt message to NULL at the same time as we get it so that - we don't send the same message twice. This is necessary because it's illegal - to send the same thread message twice until it has been received the first time - (because of the intrusive list), and we could hypothetically get two SIGINTs - in quick succession. */ - os_signal_cond_t *interrupt_signal = self->exchange_interrupt_message(NULL); - if (interrupt_signal != NULL) { - interrupt_signal->source_signo = signo; - interrupt_signal->source_pid = siginfo->si_pid; - interrupt_signal->source_uid = siginfo->si_uid; - self->threads[self->n_threads - 1]->message_hub.insert_external_message(interrupt_signal); - } -} - -void linux_thread_pool_t::fatal_signal_handler( - int signum, - siginfo_t *info, - UNUSED void *data) { - // Linux generates a SIGSEGV when accessing a protected page, OS X generates - // SIGBUS. - if ((signum == SIGSEGV || signum == SIGBUS) - && is_coroutine_stack_overflow(info->si_addr)) { - crash("Callstack overflow in a coroutine"); - } else if (signum == SIGSEGV) { - crash("Segmentation fault from reading the address %p.", info->si_addr); - } else if (signum == SIGBUS) { - crash("Bus error from accessing the address %p.", info->si_addr); - } else { - crash("Unexpected signal: %d\n", signum); - } -} -#endif - -void linux_thread_pool_t::shutdown_thread_pool() { - int res; - - // This will tell the main() thread to tell all the child threads to - // shut down. - - res = pthread_mutex_lock(&shutdown_cond_mutex); - guarantee_xerr(res == 0, res, "Could not lock shutdown cond mutex"); - - do_shutdown = true; - - res = pthread_cond_signal(&shutdown_cond); - guarantee_xerr(res == 0, res, "Could not signal shutdown cond"); - - res = pthread_mutex_unlock(&shutdown_cond_mutex); - guarantee_xerr(res == 0, res, "Could not unlock shutdown cond mutex"); -} - -linux_thread_pool_t::~linux_thread_pool_t() { - int res; - - res = pthread_cond_destroy(&shutdown_cond); - guarantee_xerr(res == 0, res, "Could not destroy shutdown cond"); - - res = pthread_mutex_destroy(&shutdown_cond_mutex); - guarantee_xerr(res == 0, res, "Could not destroy shutdown cond mutex"); -} - -linux_thread_t::linux_thread_t(linux_thread_pool_t *parent_pool, int thread_id) - : queue(this), - message_hub(&queue, parent_pool, threadnum_t(thread_id)), - timer_handler(&queue), - do_shutdown(false) -#ifndef NDEBUG - , coroutine_counts_at_shutdown(NULL) -#endif -{ - // Initialize the mutex which synchronizes access to the do_shutdown variable - int res = pthread_mutex_init(&do_shutdown_mutex, nullptr); - guarantee_xerr(res == 0, res, "could not initialize do_shutdown_mutex"); - - // Watch an eventfd for shutdown notifications - queue.watch_event(&shutdown_notify_event, this); -} - -linux_thread_t::~linux_thread_t() { - -#ifndef NDEBUG - // Save the coroutine counts before they're deleted, should be ready at shutdown - rassert(coroutine_counts_at_shutdown != nullptr); - coroutine_counts_at_shutdown->clear(); - coro_runtime.get_coroutine_counts(coroutine_counts_at_shutdown); -#endif - - int res = pthread_mutex_destroy(&do_shutdown_mutex); - guarantee_xerr(res == 0, res, "could not destroy do_shutdown_mutex"); -} - -void linux_thread_t::pump() { - message_hub.push_messages(); -} - -void linux_thread_t::on_event(int events) { - // No-op. This is just to make sure that the event queue wakes up - // so it can shut down. - - if (events != poll_event_in) { - logERR("Unexpected event mask: %d", events); - } -} - -bool linux_thread_t::should_shut_down() { - int res = pthread_mutex_lock(&do_shutdown_mutex); - guarantee_xerr(res == 0, res, "could not lock do_shutdown_mutex"); - bool result = do_shutdown; - res = pthread_mutex_unlock(&do_shutdown_mutex); - guarantee_xerr(res == 0, res, "could not unlock do_shutdown_mutex"); - return result; -} - -#ifndef NDEBUG -void linux_thread_t::initiate_shut_down(std::map *coroutine_counts) { -#else -void linux_thread_t::initiate_shut_down() { -#endif - int res = pthread_mutex_lock(&do_shutdown_mutex); - guarantee_xerr(res == 0, res, "could not lock do_shutdown_mutex"); -#ifndef NDEBUG - coroutine_counts_at_shutdown = coroutine_counts; -#endif - do_shutdown = true; - shutdown_notify_event.wakey_wakey(); - res = pthread_mutex_unlock(&do_shutdown_mutex); - guarantee_xerr(res == 0, res, "could not unlock do_shutdown_mutex"); -} diff --git a/src/arch/runtime/thread_pool.hpp b/src/arch/runtime/thread_pool.hpp deleted file mode 100644 index d0c1d460dc3..00000000000 --- a/src/arch/runtime/thread_pool.hpp +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_RUNTIME_THREAD_POOL_HPP_ -#define ARCH_RUNTIME_THREAD_POOL_HPP_ - -#include - -#include -#include -#include - -#include "arch/compiler.hpp" -#include "config/args.hpp" -#include "arch/runtime/event_queue.hpp" -#include "arch/runtime/system_event.hpp" -#include "arch/runtime/message_hub.hpp" -#include "arch/runtime/coroutines.hpp" -#include "arch/io/blocker_pool.hpp" -#include "arch/io/timer_provider.hpp" -#include "arch/timer.hpp" - -class linux_thread_t; -class os_signal_cond_t; - -/* coro_runtime_t is borrowed from coroutines.hpp. Please only -construct one coro_runtime_t per thread. Coroutines can only be used -when a coro_runtime_t exists. It exists to take advantage of RAII. */ - -struct coro_runtime_t { - coro_runtime_t(); - ~coro_runtime_t(); - -#ifndef NDEBUG - void get_coroutine_counts(std::map *dest); -#endif -}; - - -/* A thread pool represents a group of threads, each of which is associated with an -event queue. There is one thread pool per server. It is responsible for starting up -and shutting down the threads and event queues. */ - -class linux_thread_pool_t { -public: - linux_thread_pool_t(int worker_threads, bool do_set_affinity); - - // When the process receives a SIGINT or SIGTERM, interrupt_message will be delivered to the - // same thread that initial_message was delivered to, and interrupt_message will be set to - // NULL. If you want to receive notification of further SIGINTs or SIGTERMs, you must call - // exchange_interrupt_message() again. Returns the previous value of interrupt_message. - MUST_USE os_signal_cond_t *exchange_interrupt_message(os_signal_cond_t *interrupt_message); - - // Blocks while threads are working. Only returns after shutdown() is called. initial_message - // is a thread message that will be delivered to thread zero after all of the event queues - // have been started; it is used to start the server's activity. - void run_thread_pool(linux_thread_message_t *initial_message); - -#ifndef NDEBUG - void enable_coroutine_summary(); -#endif - - // Shut down all the threads. Can be called from any thread. - void shutdown_thread_pool(); - - ~linux_thread_pool_t(); - -#ifdef _WIN32 - static void interrupt_handler(DWORD type); -#endif - -private: -#ifndef NDEBUG - bool coroutine_summary; -#endif - - static void *start_thread(void*); - -#ifndef _WIN32 - static void interrupt_handler(int signo, siginfo_t *siginfo, void *); - // Currently handles SIGSEGV and SIGBUS signals. - static void fatal_signal_handler(int, siginfo_t *, void *) NORETURN; -#endif - - spinlock_t interrupt_message_lock; - os_signal_cond_t *interrupt_message; - - // Used to signal the main thread for shutdown - volatile bool do_shutdown; - pthread_cond_t shutdown_cond; - pthread_mutex_t shutdown_cond_mutex; - - // The number of threads to allocate for handling blocking calls - static const int GENERIC_BLOCKER_THREAD_COUNT = 2; - blocker_pool_t* generic_blocker_pool; - -public: - pthread_t pthreads[MAX_THREADS]; - linux_thread_t *threads[MAX_THREADS]; - - // Cooperatively run a blocking function call using the generic_blocker_pool - template - static void run_in_blocker_pool(const Callable &); - - int n_threads; - bool do_set_affinity; - -#ifdef _WIN32 - static linux_thread_pool_t *get_global_thread_pool(); -#endif - - // Getters and setters for the thread local variables. - // See thread_local.hpp for an explanation of why these must not be - // inlined. - static NOINLINE linux_thread_pool_t *get_thread_pool(); - static NOINLINE void set_thread_pool(linux_thread_pool_t *val); - static NOINLINE int get_thread_id(); - static NOINLINE void set_thread_id(int val); - static NOINLINE linux_thread_t *get_thread(); - static NOINLINE void set_thread(linux_thread_t *val); - -private: - -#ifdef _WIN32 - static std::atomic global_thread_pool; -#endif - - // The thread_pool that started the thread we are currently in - static THREAD_LOCAL linux_thread_pool_t *thread_pool; - // The ID of the thread we are currently in - static THREAD_LOCAL int thread_id; - // The event queue for the thread we are currently in (same as &thread_pool->threads[thread_id]) - static THREAD_LOCAL linux_thread_t *thread; - - DISABLE_COPYING(linux_thread_pool_t); -}; - -template -struct generic_job_t : - public blocker_pool_t::job_t -{ - void run() { - (*fn)(); - } - - void done() { - // Now that the function is done, resume execution of the suspended task - suspended->notify_sometime(); - } - - const Callable *fn; - coro_t* suspended; -}; - -// Function to handle blocking calls in a separate thread pool -// This should be used for any calls that cannot otherwise be made non-blocking -template -void linux_thread_pool_t::run_in_blocker_pool(const Callable &fn) -{ - if (get_thread_pool() != nullptr) { - generic_job_t job; - job.fn = &fn; - job.suspended = coro_t::self(); - - rassert(get_thread_pool()->generic_blocker_pool != NULL, - "thread_pool_t::run_in_blocker_pool called while generic_thread_pool uninitialized"); - get_thread_pool()->generic_blocker_pool->do_job(&job); - - // Give up execution, to be resumed when the done callback is made - coro_t::wait(); - } else { - // Thread pool has not been created, just block the current thread, since we won't be - // screwing up any coroutines - fn(); - } -} - -class linux_thread_t : - public linux_event_callback_t, - public linux_queue_parent_t { -public: - linux_thread_t(linux_thread_pool_t *parent_pool, int thread_id); - ~linux_thread_t(); - - linux_event_queue_t queue; - linux_message_hub_t message_hub; - timer_handler_t timer_handler; - - /* Never accessed; its constructor and destructor set up and tear down thread-local variables - for coroutines. */ - coro_runtime_t coro_runtime; - - void pump(); // Called by the event queue - bool should_shut_down(); // Called by the event queue -#ifndef NDEBUG - void initiate_shut_down(std::map *coroutine_counts); // Can be called from any thread -#else - void initiate_shut_down(); // Can be called from any thread -#endif - void on_event(int events); - -private: - volatile bool do_shutdown; - pthread_mutex_t do_shutdown_mutex; - system_event_t shutdown_notify_event; - -#ifndef NDEBUG - std::map *coroutine_counts_at_shutdown; -#endif -}; - -#endif /* ARCH_RUNTIME_THREAD_POOL_HPP_ */ diff --git a/src/arch/spinlock.cc b/src/arch/spinlock.cc deleted file mode 100644 index b97be855fe3..00000000000 --- a/src/arch/spinlock.cc +++ /dev/null @@ -1,46 +0,0 @@ -#include "arch/spinlock.hpp" - -spinlock_t::spinlock_t() { -#if defined(SPINLOCK_PTHREAD_SPINLOCK) - int res = pthread_spin_init(&l, PTHREAD_PROCESS_PRIVATE); - guarantee_xerr(res == 0, res, "could not initialize spinlock"); -#elif defined(SPINLOCK_PTHREAD_MUTEX) - int res = pthread_mutex_init(&l, NULL); - guarantee_xerr(res == 0, res, "could not initialize spinlock"); -#else -#error "Unspecified spinlock implementation" -#endif -} - -spinlock_t::~spinlock_t() { -#if defined(SPINLOCK_PTHREAD_SPINLOCK) - int res = pthread_spin_destroy(&l); -#elif defined(SPINLOCK_PTHREAD_MUTEX) - int res = pthread_mutex_destroy(&l); -#else -#error "Unspecified spinlock implementation" -#endif - guarantee_xerr(res == 0, res, "could not destroy spinlock"); -} - -void spinlock_t::lock() { -#if defined(SPINLOCK_PTHREAD_SPINLOCK) - int res = pthread_spin_lock(&l); -#elif defined(SPINLOCK_PTHREAD_MUTEX) - int res = pthread_mutex_lock(&l); -#else -#error "Unspecified spinlock implementation" -#endif - guarantee_xerr(res == 0, res, "could not lock spin lock"); -} - -void spinlock_t::unlock() { -#if defined(SPINLOCK_PTHREAD_SPINLOCK) - int res = pthread_spin_unlock(&l); -#elif defined(SPINLOCK_PTHREAD_MUTEX) - int res = pthread_mutex_unlock(&l); -#else -#error "Unspecified spinlock implementation" -#endif - guarantee_xerr(res == 0, res, "could not unlock spin lock"); -} diff --git a/src/arch/spinlock.hpp b/src/arch/spinlock.hpp deleted file mode 100644 index 4495d73ee43..00000000000 --- a/src/arch/spinlock.hpp +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef ARCH_SPINLOCK_HPP_ -#define ARCH_SPINLOCK_HPP_ - -#include -#include - -#include "errors.hpp" - -// I don't know the "clean" way to detect the availability of the pthread -// spinlock feature. For now we just use __MACH__ to default to mutex (which -// will just work) on Apple systems. If it's ever useful, making our own -// spinlock implementation could be an option. -#if defined(__MACH__) -#define SPINLOCK_PTHREAD_MUTEX -#else -#define SPINLOCK_PTHREAD_SPINLOCK -#endif - -// TODO: we should use regular mutexes on single core CPU -// instead of spinlocks - -class spinlock_t { -public: - friend class spinlock_acq_t; - - spinlock_t(); - ~spinlock_t(); - -private: - void lock(); - void unlock(); - -#if defined(SPINLOCK_PTHREAD_SPINLOCK) - pthread_spinlock_t l; -#elif defined (SPINLOCK_PTHREAD_MUTEX) - pthread_mutex_t l; -#endif - - DISABLE_COPYING(spinlock_t); -}; - -class spinlock_acq_t { -public: - explicit spinlock_acq_t(spinlock_t *the_lock) : the_lock_(the_lock) { - the_lock_->lock(); - } - ~spinlock_acq_t() { - the_lock_->unlock(); - } - -private: - spinlock_t *the_lock_; - - DISABLE_COPYING(spinlock_acq_t); -}; - -#endif /* ARCH_SPINLOCK_HPP_ */ diff --git a/src/arch/timer.cc b/src/arch/timer.cc deleted file mode 100644 index 4bdbdfe1f40..00000000000 --- a/src/arch/timer.cc +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "arch/timer.hpp" - -#include - -#include "arch/runtime/thread_pool.hpp" -#include "time.hpp" -#include "utils.hpp" - -class timer_token_t : public intrusive_priority_queue_node_t { - friend class timer_handler_t; - -private: - timer_token_t() : interval_nanos(-1), next_time_in_nanos(-1), callback(nullptr) { } - - friend bool left_is_higher_priority(const timer_token_t *left, const timer_token_t *right); - - // The time between rings, if a repeating timer, otherwise zero. - int64_t interval_nanos; - - // The time of the next 'ring'. - int64_t next_time_in_nanos; - - // The callback we call upon each 'ring'. - timer_callback_t *callback; - - DISABLE_COPYING(timer_token_t); -}; - -bool left_is_higher_priority(const timer_token_t *left, const timer_token_t *right) { - return left->next_time_in_nanos < right->next_time_in_nanos; -} - -timer_handler_t::timer_handler_t(linux_event_queue_t *queue) - : timer_provider(queue), - expected_oneshot_time_in_nanos(0) { - // Right now, we have no tokens. So we don't ask the timer provider to do anything for us. -} - -timer_handler_t::~timer_handler_t() { - guarantee(token_queue.empty()); -} - -void timer_handler_t::on_oneshot() { - // If the timer_provider tends to return its callback a touch early, we don't want to make a - // bunch of calls to it, returning a tad early over and over again, leading up to a ticks - // threshold. So we bump the real time up to the threshold when processing the priority queue. - int64_t real_ticks = get_ticks(); - int64_t ticks = std::max(real_ticks, expected_oneshot_time_in_nanos); - - while (!token_queue.empty() && token_queue.peek()->next_time_in_nanos <= ticks) { - timer_token_t *token = token_queue.pop(); - - // Put the repeating timer back on the queue before the callback can be called (so that it - // may be canceled). - if (token->interval_nanos != 0) { - token->next_time_in_nanos = real_ticks + token->interval_nanos; - token_queue.push(token); - } - - token->callback->on_timer(); - - // Delete nonrepeating timer tokens. - if (token->interval_nanos == 0) { - delete token; - } - } - - // We've processed young tokens. Now schedule a new one-shot (if necessary). - if (!token_queue.empty()) { - timer_provider.schedule_oneshot(token_queue.peek()->next_time_in_nanos, this); - } -} - -timer_token_t *timer_handler_t::add_timer_internal(const int64_t ms, timer_callback_t *callback, const bool once) { - const int64_t nanos = ms * MILLION; - rassert(nanos > 0); - - const int64_t next_time_in_nanos = get_ticks() + nanos; - - timer_token_t *const token = new timer_token_t; - token->interval_nanos = once ? 0 : nanos; - token->next_time_in_nanos = next_time_in_nanos; - token->callback = callback; - - const timer_token_t *top_entry = token_queue.peek(); - token_queue.push(token); - - if (top_entry == nullptr || next_time_in_nanos < top_entry->next_time_in_nanos) { - timer_provider.schedule_oneshot(next_time_in_nanos, this); - } - - return token; -} - -void timer_handler_t::cancel_timer(timer_token_t *token) { - token_queue.remove(token); - delete token; - - if (token_queue.empty()) { - timer_provider.unschedule_oneshot(); - } -} - - - -timer_token_t *add_timer(int64_t ms, timer_callback_t *callback) { - return linux_thread_pool_t::get_thread()->timer_handler.add_timer_internal(ms, callback, false); -} - -timer_token_t *fire_timer_once(int64_t ms, timer_callback_t *callback) { - return linux_thread_pool_t::get_thread()->timer_handler.add_timer_internal(ms, callback, true); -} - -void cancel_timer(timer_token_t *timer) { - linux_thread_pool_t::get_thread()->timer_handler.cancel_timer(timer); -} diff --git a/src/arch/timer.hpp b/src/arch/timer.hpp deleted file mode 100644 index 3722103c594..00000000000 --- a/src/arch/timer.hpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef ARCH_TIMER_HPP_ -#define ARCH_TIMER_HPP_ - -#include "containers/intrusive_priority_queue.hpp" -#include "arch/io/timer_provider.hpp" - -class timer_token_t; - -struct timer_callback_t { - virtual void on_timer() = 0; - virtual ~timer_callback_t() { } -}; - -/* This timer class uses the underlying OS timer provider to get one-shot timing events. It then - * manages a list of application timers based on that lower level interface. Everyone who needs a - * timer should use this class (through the thread pool). */ -class timer_handler_t : private timer_provider_callback_t { -public: - explicit timer_handler_t(linux_event_queue_t *queue); - ~timer_handler_t(); - - timer_token_t *add_timer_internal(int64_t ms, timer_callback_t *callback, bool once); - void cancel_timer(timer_token_t *timer); - -private: - void on_oneshot(); - - // The timer provider, a platform-dependent typedef for interfacing with the OS. - timer_provider_t timer_provider; - - // The expected time of the next on_oneshot call. If the oneshot arrived earlier than this - // time, we pretend that it had arrived on time. - int64_t expected_oneshot_time_in_nanos; - - // A priority queue of timer tokens, ordered by the soonest. - intrusive_priority_queue_t token_queue; - - DISABLE_COPYING(timer_handler_t); -}; - - -/* Timer functions create (non-)periodic timers, callbacks for which are - * executed on the same thread that they were created on. Thus, non-thread-safe - * (but coroutine-safe) concurrency primitives can be used where appropriate. - */ -timer_token_t *add_timer(int64_t ms, timer_callback_t *callback); -timer_token_t *fire_timer_once(int64_t ms, timer_callback_t *callback); -void cancel_timer(timer_token_t *timer); - - -#endif /* ARCH_TIMER_HPP_ */ diff --git a/src/arch/timing.cc b/src/arch/timing.cc deleted file mode 100644 index da76e9c5f9f..00000000000 --- a/src/arch/timing.cc +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "arch/timing.hpp" - -#include -#include - -#include "arch/arch.hpp" -#include "arch/runtime/coroutines.hpp" -#include "concurrency/wait_any.hpp" - -// nap() - -void nap(int64_t ms) THROWS_NOTHING { - if (ms > 0) { - signal_timer_t timer; - timer.start(ms); - timer.wait_lazily_ordered(); - } -} - -void nap(int64_t ms, const signal_t *interruptor) THROWS_ONLY(interrupted_exc_t) { - signal_timer_t timer(ms); - wait_interruptible(&timer, interruptor); -} - -// signal_timer_t - -signal_timer_t::signal_timer_t() : timer(nullptr) { } -signal_timer_t::signal_timer_t(int64_t ms) : timer(nullptr) { - start(ms); -} - -signal_timer_t::~signal_timer_t() { - if (timer != nullptr) { - cancel_timer(timer); - } -} - -void signal_timer_t::start(int64_t ms) { - guarantee(timer == nullptr); - guarantee(!is_pulsed()); - if (ms == 0) { - pulse(); - } else { - guarantee(ms > 0); - timer = fire_timer_once(ms, this); - } -} - -bool signal_timer_t::cancel() { - if (timer != nullptr) { - cancel_timer(timer); - timer = nullptr; - return true; - } - return false; -} - -bool signal_timer_t::is_running() const { - return is_pulsed() || timer != nullptr; -} - -void signal_timer_t::on_timer() { - timer = nullptr; - pulse(); -} - -// repeating_timer_t - -repeating_timer_t::repeating_timer_t( - int64_t interval_ms, const std::function &_ringee) : - ringee(_ringee) { - rassert(interval_ms > 0); - timer = add_timer(interval_ms, this); -} - -repeating_timer_t::repeating_timer_t( - int64_t interval_ms, repeating_timer_callback_t *_cb) : - ringee([_cb]() { _cb->on_ring(); }) { - rassert(interval_ms > 0); - timer = add_timer(interval_ms, this); -} - -repeating_timer_t::~repeating_timer_t() { - cancel_timer(timer); -} - -void call_ringer(std::function ringee) { - // It would be very easy for a user of repeating_timer_t to have - // object lifetime errors, if their ring function blocks. So we - // have this assertion. - ASSERT_FINITE_CORO_WAITING; - ringee(); -} - -void repeating_timer_t::on_timer() { - // Spawn _now_, otherwise the repeating_timer_t lifetime might end - // before ring gets used. - coro_t::spawn_now_dangerously(std::bind(call_ringer, ringee)); -} diff --git a/src/arch/timing.hpp b/src/arch/timing.hpp deleted file mode 100644 index f73dc01ec5c..00000000000 --- a/src/arch/timing.hpp +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef ARCH_TIMING_HPP_ -#define ARCH_TIMING_HPP_ - -#include - -#include "arch/timer.hpp" -#include "concurrency/interruptor.hpp" -#include "concurrency/signal.hpp" - -/* Coroutine function that delays for some number of milliseconds. */ - -void nap(int64_t ms) THROWS_NOTHING; - -/* This variant takes an interruptor, and throws `interrupted_exc_t` if the -interruptor is pulsed before the timeout is up */ - -void nap(int64_t ms, const signal_t *interruptor) THROWS_ONLY(interrupted_exc_t); - -class timer_token_t; - -/* Construct a `signal_timer_t` to start a one-shot timer. When the timer "rings", -the signal will be pulsed. It is safe to destroy the `signal_timer_t` before the -timer "rings". */ - -class signal_timer_t : public signal_t, private timer_callback_t { -public: - signal_timer_t(); - explicit signal_timer_t(int64_t ms); // Calls `start` for you. - ~signal_timer_t(); - - // Starts the timer, cannot be called if the timer is already running - void start(int64_t ms); - - // Stops the timer from running - // Returns true if the timer was canceled, false if there was no timer to cancel - bool cancel(); - - // Returns true if the timer is running or has been pulsed - bool is_running() const; - -private: - void on_timer(); - timer_token_t *timer; -}; - -/* Construct a repeating_timer_t to start a repeating timer. It will call its function -when the timer "rings". */ - -/* `repeating_timer_callback_t` is deprecated; prefer passing a function to -`repeating_timer_t`. */ -class repeating_timer_callback_t { -public: - virtual void on_ring() = 0; -protected: - virtual ~repeating_timer_callback_t() { } -}; - -class repeating_timer_t : private timer_callback_t { -public: - repeating_timer_t(int64_t interval_ms, const std::function &ringee); - repeating_timer_t(int64_t interval_ms, repeating_timer_callback_t *ringee); - ~repeating_timer_t(); - -private: - void on_timer(); - timer_token_t *timer; - std::function ringee; - - DISABLE_COPYING(repeating_timer_t); -}; - -#endif /* ARCH_TIMING_HPP_ */ diff --git a/src/arch/types.cc b/src/arch/types.cc deleted file mode 100644 index 2b2c7927457..00000000000 --- a/src/arch/types.cc +++ /dev/null @@ -1,37 +0,0 @@ -#include "arch/types.hpp" - -#include "utils.hpp" - -tcp_socket_exc_t::tcp_socket_exc_t(int errsv, int port) { - info = strprintf("TCP socket creation failed for port %d: %s", - port, errno_string(errsv).c_str()); -} - - -address_in_use_exc_t::address_in_use_exc_t(const char* hostname, int port) throw () { - if (port == 0) { - info = strprintf("Could not establish sockets on all selected local addresses using the same port"); - } else { - info = strprintf("The address at %s:%d is reserved or already in use", hostname, port); - } -} - -file_account_t::file_account_t(file_t *par, int pri, int outstanding_requests_limit) : - parent(par), - account(parent->create_account(pri, outstanding_requests_limit)) { } - -file_account_t::~file_account_t() { - parent->destroy_account(account); -} - -void linux_iocallback_t::on_io_failure(int errsv, int64_t offset, int64_t count) { - if (errsv == ENOSPC) { - // fail_due_to_user_error rather than crash because we don't want to - // print a backtrace in this case. - fail_due_to_user_error("Ran out of disk space. (offset = %" PRIi64 - ", count = %" PRIi64 ")", offset, count); - } else { - crash("I/O operation failed. (%s) (offset = %" PRIi64 ", count = %" PRIi64 ")", - errno_string(errsv).c_str(), offset, count); - } -} diff --git a/src/arch/types.hpp b/src/arch/types.hpp deleted file mode 100644 index 44bc5a5ae6a..00000000000 --- a/src/arch/types.hpp +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef ARCH_TYPES_HPP_ -#define ARCH_TYPES_HPP_ - -#include -#include - -#include - -#include "errors.hpp" -#include "arch/runtime/runtime_utils.hpp" - -template class scoped_array_t; -struct iovec; - -#define DEFAULT_DISK_ACCOUNT (static_cast(0)) -#define UNLIMITED_OUTSTANDING_REQUESTS (-1) - -// TODO: Remove this from this header. - -// The linux_tcp_listener_t constructor can throw this exception -class address_in_use_exc_t : public std::exception { -public: - address_in_use_exc_t(const char* hostname, int port) throw (); - explicit address_in_use_exc_t(const std::string &msg) throw () - : info(msg) { } - - ~address_in_use_exc_t() throw () { } - - const char *what() const throw () { - return info.c_str(); - } - -private: - std::string info; -}; - -class tcp_socket_exc_t : public std::exception { -public: - explicit tcp_socket_exc_t(int err, int port); - - ~tcp_socket_exc_t() throw () { } - - const char *what() const throw () { - return info.c_str(); - } - -private: - std::string info; -}; - -class tcp_conn_read_closed_exc_t : public std::exception { - const char *what() const throw () { - return "Network connection read end closed"; - } -}; - -struct tcp_conn_write_closed_exc_t : public std::exception { - const char *what() const throw () { - return "Network connection write end closed"; - } -}; - - -class linux_iocallback_t { -public: - virtual ~linux_iocallback_t() {} - virtual void on_io_complete() = 0; - - //TODO Remove this default implementation and actually handle io errors. - virtual void on_io_failure(int errsv, int64_t offset, int64_t count); -}; - -class linux_thread_pool_t; -typedef linux_thread_pool_t thread_pool_t; - -class file_account_t; - -class linux_iocallback_t; -typedef linux_iocallback_t iocallback_t; - -class linux_tcp_bound_socket_t; -typedef linux_tcp_bound_socket_t tcp_bound_socket_t; - -class linux_nonthrowing_tcp_listener_t; -typedef linux_nonthrowing_tcp_listener_t non_throwing_tcp_listener_t; - -class linux_tcp_listener_t; -typedef linux_tcp_listener_t tcp_listener_t; - -class linux_repeated_nonthrowing_tcp_listener_t; -typedef linux_repeated_nonthrowing_tcp_listener_t repeated_nonthrowing_tcp_listener_t; - -class linux_tcp_conn_descriptor_t; -typedef linux_tcp_conn_descriptor_t tcp_conn_descriptor_t; - -class linux_tcp_conn_t; -typedef linux_tcp_conn_t tcp_conn_t; - -class linux_secure_tcp_conn_t; -typedef linux_secure_tcp_conn_t secure_tcp_conn_t; - -enum class file_direct_io_mode_t { - direct_desired, - buffered_desired -}; - -// A linux file. It expects reads and writes and buffers to have an -// alignment of DEVICE_BLOCK_SIZE. -class file_t { -public: - enum wrap_in_datasyncs_t { NO_DATASYNCS, WRAP_IN_DATASYNCS }; - - file_t() { } - - virtual ~file_t() { } - virtual int64_t get_file_size() = 0; - virtual void set_file_size(int64_t size) = 0; - virtual void set_file_size_at_least(int64_t size, int64_t extent_size) = 0; - - virtual void read_async(int64_t offset, size_t length, void *buf, - file_account_t *account, linux_iocallback_t *cb) = 0; - virtual void write_async(int64_t offset, size_t length, const void *buf, - file_account_t *account, linux_iocallback_t *cb, - wrap_in_datasyncs_t wrap_in_datasyncs) = 0; - // writev_async doesn't provide the atomicity guarantees of writev. - virtual void writev_async(int64_t offset, size_t length, scoped_array_t &&bufs, - file_account_t *account, linux_iocallback_t *cb) = 0; - - virtual void *create_account(int priority, int outstanding_requests_limit) = 0; - virtual void destroy_account(void *account) = 0; - - virtual bool coop_lock_and_check() = 0; - -private: - DISABLE_COPYING(file_t); -}; - -class file_account_t { -public: - file_account_t(file_t *f, int p, int outstanding_requests_limit = UNLIMITED_OUTSTANDING_REQUESTS); - ~file_account_t(); - void *get_account() { return account; } - -private: - file_t *parent; - - // When used with a linux_file_t, account is a `accounting_diskmgr_t::account_t - // *`. When used with a mock_file_t, it's a unused non-null pointer to some kind - // of irrelevant object. - void *account; - - DISABLE_COPYING(file_account_t); -}; - - -#endif // ARCH_TYPES_HPP_ diff --git a/src/arch/windows_stub/pthread.cc b/src/arch/windows_stub/pthread.cc deleted file mode 100644 index 3fbc8419354..00000000000 --- a/src/arch/windows_stub/pthread.cc +++ /dev/null @@ -1,176 +0,0 @@ -#ifdef _WIN32 - -#include - -#include - -#include "errors.hpp" -#include "logger.hpp" - -int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) { - typedef std::tuple data_t; - data_t* data = new data_t(start_routine, arg); - static const auto go = [](void* rawdata) -> DWORD { - data_t* data = static_cast(rawdata); - auto f = std::get<0>(*data); - auto args = std::get<1>(*data); - delete data; - void* res = f(args); - // void* may be bigger than DWORD - rassert(reinterpret_cast(res) | 0xFFFFFFFF == 0xFFFFFFFF); - return reinterpret_cast(res); - }; - HANDLE handle = CreateThread(nullptr, 0, go, static_cast(data), 0, nullptr); - if (handle == nullptr) { - logERR("CreateThread failed: %s", winerr_string(GetLastError())); - return EINVAL; - } else { - *thread = handle; - return 0; - } -} - -int pthread_join(pthread_t other, void** retval) { - DWORD res = WaitForSingleObject(other, INFINITE); - if (res != WAIT_OBJECT_0) { - return EINVAL; // TODO - } else { - if (retval != nullptr) { - DWORD exit_code; - guarantee_winerr(GetExitCodeThread(other, &exit_code)); - *retval = reinterpret_cast(static_cast(exit_code)); - } - return 0; - } -} - -int pthread_spin_init(pthread_spinlock_t* lock, int type) { - rassert(type == PTHREAD_PROCESS_PRIVATE); - static const int spin_count = 40; // TODO WINDOWS: this spin count is arbitrary - InitializeCriticalSectionAndSpinCount(lock, spin_count); - return 0; -} - - -int pthread_spin_destroy(pthread_mutex_t* lock) { - DeleteCriticalSection(lock); - return 0; -} - -int pthread_spin_lock(pthread_mutex_t* lock) { - EnterCriticalSection(lock); - return 0; -} - -int pthread_spin_unlock(pthread_spinlock_t* lock) { - LeaveCriticalSection(lock); - return 0; -} - -int pthread_mutex_init(pthread_mutex_t *mutex, void *opts) { - rassert(opts == nullptr, "this implementation of pthread_mutex_init does not support attributes"); - InitializeCriticalSection(mutex); - return 0; -} - -int pthread_mutex_destroy(pthread_mutex_t* mutex) { - DeleteCriticalSection(mutex); - return 0; -} - -int pthread_mutex_lock(pthread_mutex_t* mutex) { - EnterCriticalSection(mutex); - return 0; -} - -int pthread_mutex_unlock(pthread_mutex_t* mutex) { - LeaveCriticalSection(mutex); - return 0; -} - -int pthread_rwlock_init(pthread_rwlock_t *lock, void *opts) { - rassert(opts == nullptr, "this implementation of pthread_rwlock_t does not support attributes"); - InitializeSRWLock(&lock->lock); - return 0; -} - -int pthread_rwlock_destroy(pthread_rwlock_t *) { - // SRWLocks do not need to be explicitly destroyed - return 0; -} - -int pthread_rwlock_rdlock(pthread_rwlock_t *lock) { - AcquireSRWLockShared(&lock->lock); - lock->current_acq_mode = pthread_rwlock_t::srw_lock_mode_t::SHARED; - return 0; -} - -int pthread_rwlock_wrlock(pthread_rwlock_t *lock) { - AcquireSRWLockExclusive(&lock->lock); - lock->current_acq_mode = pthread_rwlock_t::srw_lock_mode_t::EXCLUSIVE; - return 0; -} - -int pthread_rwlock_unlock(pthread_rwlock_t *lock) { - switch (lock->current_acq_mode) { - case pthread_rwlock_t::srw_lock_mode_t::SHARED: - ReleaseSRWLockShared(&lock->lock); - break; - case pthread_rwlock_t::srw_lock_mode_t::EXCLUSIVE: - ReleaseSRWLockExclusive(&lock->lock); - break; - default: - unreachable(); - } - return 0; -} - -int pthread_attr_init(pthread_attr_t*) { - return 0; -} - -int pthread_attr_setstacksize(pthread_attr_t*, size_t) { - return 0; -} - -int pthread_attr_destroy(pthread_attr_t*) { - return 0; -} - -int pthread_cond_init(pthread_cond_t* cond, void*) { - InitializeConditionVariable(cond); - return 0; -} - -int pthread_cond_destroy(pthread_cond_t* cond) { - return 0; -} - -int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex) { - BOOL res = SleepConditionVariableCS(cond, mutex, INFINITE); - if (res) { - return 0; - } else { - return EINVAL; // TODO - } -} - -int pthread_cond_signal(pthread_cond_t* cond) { - WakeConditionVariable(cond); - return 0; -} - -int pthread_cond_broadcast(pthread_cond_t* cond) { - WakeAllConditionVariable(cond); - return 0; -} - -int pthread_once(bool *complete, void(*init)(void)) { - if (*complete == PTHREAD_ONCE_INIT) { - *complete = PTHREAD_ONCE_COMPLETED; - init(); - } - return 0; -} - -#endif diff --git a/src/arch/windows_stub/pthread.h b/src/arch/windows_stub/pthread.h deleted file mode 100644 index db0fe071c55..00000000000 --- a/src/arch/windows_stub/pthread.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef ARCH_WINDOWS_STUB_PTHREAD_H_ -#define ARCH_WINDOWS_STUB_PTHREAD_H_ - -// An implementation of a subset of pthread for windows - -#include "windows.hpp" - -struct pthread_attr_t { }; -int pthread_attr_init(pthread_attr_t*); -int pthread_attr_setstacksize(pthread_attr_t*, size_t); -int pthread_attr_destroy(pthread_attr_t*); - -typedef HANDLE pthread_t; -int pthread_create(pthread_t *, const pthread_attr_t *, void *(*) (void *), void *); -int pthread_join(pthread_t, void**); - -typedef CRITICAL_SECTION pthread_spinlock_t; -#define PTHREAD_PROCESS_PRIVATE 0 -int pthread_spin_init(pthread_spinlock_t*, int); -int pthread_spin_destroy(pthread_spinlock_t*); -int pthread_spin_lock(pthread_spinlock_t*); -int pthread_spin_unlock(pthread_spinlock_t*); - -typedef CRITICAL_SECTION pthread_mutex_t; -int pthread_mutex_init(pthread_mutex_t*, void*); -int pthread_mutex_destroy(pthread_mutex_t*); -int pthread_mutex_lock(pthread_mutex_t*); -int pthread_mutex_unlock(pthread_mutex_t*); - -struct pthread_rwlock_t { - enum class srw_lock_mode_t { SHARED, EXCLUSIVE }; - srw_lock_mode_t current_acq_mode; - SRWLOCK lock; -}; -int pthread_rwlock_init(pthread_rwlock_t*, void*); -int pthread_rwlock_destroy(pthread_rwlock_t*); -int pthread_rwlock_rdlock(pthread_rwlock_t*); -int pthread_rwlock_wrlock(pthread_rwlock_t*); -int pthread_rwlock_unlock(pthread_rwlock_t*); - -typedef CONDITION_VARIABLE pthread_cond_t; -int pthread_cond_init(pthread_cond_t*, void*); -int pthread_cond_destroy(pthread_cond_t*); -int pthread_cond_wait(pthread_cond_t*, pthread_mutex_t*); -int pthread_cond_signal(pthread_cond_t*); -int pthread_cond_broadcast(pthread_cond_t*); - -#define pthread_once_t thread_local bool -const bool PTHREAD_ONCE_INIT = false; -const bool PTHREAD_ONCE_COMPLETED = true; -int pthread_once(bool *, void(*)(void)); - -#endif diff --git a/src/arch/windows_stub/sys/uio.h b/src/arch/windows_stub/sys/uio.h deleted file mode 100644 index fcd6dc81ba5..00000000000 --- a/src/arch/windows_stub/sys/uio.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef SRC_ARCH_WINDOWS_STUB_SYS_UIO_H_ -#define SRC_ARCH_WINDOWS_STUB_SYS_UIO_H_ - -// A windows stub for - -#include "windows.hpp" - -struct iovec { - void *iov_base; - size_t iov_len; -}; - -#endif // SRC_ARCH_WINDOWS_STUB_SYS_UIO_H_ diff --git a/src/arch/windows_stub/unistd.h b/src/arch/windows_stub/unistd.h deleted file mode 100644 index e01a7775aaf..00000000000 --- a/src/arch/windows_stub/unistd.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef SRC_ARCH_WINDOWS_STUB_UNISTD_H_ -#define SRC_ARCH_WINDOWS_STUB_UNISTD_H_ - -// A stub for on windows - -#include "windows.hpp" -#include -#include -#include -#include -#include - -#include "errors.hpp" -#include "logger.hpp" - -typedef int gid_t; -typedef int uid_t; - -// Posix uses `off_t` for `offset`, but `off_t` is 32 bit on Windows so that's no good. -// We diverge from it and use `int64_t` instead. -inline ssize_t pread(HANDLE h, void* buf, size_t count, int64_t offset) { - OVERLAPPED overlapped; - memset(&overlapped, 0, sizeof(overlapped)); - guarantee(offset >= 0); - uint64_t unsigned_offset = offset; - overlapped.Offset = unsigned_offset & 0xFFFFFFFF; - overlapped.OffsetHigh = unsigned_offset >> 32; - DWORD nb_bytes; - if (ReadFile(h, buf, count, &nb_bytes, &overlapped) == FALSE) { - set_errno(EIO); - logERR("pread: ReadFile failed: %s\n", winerr_string(GetLastError()).c_str()); - return -1; - } - return nb_bytes; -} - -// See comment above about the `int64_t offset`. -inline ssize_t pwrite(HANDLE h, const void *buf, size_t count, int64_t offset) { - OVERLAPPED overlapped; - memset(&overlapped, 0, sizeof(overlapped)); - guarantee(offset >= 0); - uint64_t unsigned_offset = offset; - overlapped.Offset = unsigned_offset & 0xFFFFFFFF; - overlapped.OffsetHigh = unsigned_offset >> 32; - DWORD bytes_written; - if (WriteFile(h, buf, count, &bytes_written, &overlapped) == FALSE) { - set_errno(EIO); - logERR("pwrite: WriteFile failed: %s", winerr_string(GetLastError()).c_str()); - return -1; - } - return bytes_written; -} - -#ifdef _MSC_VER -// for access -#define F_OK 00 -#define W_OK 02 -#define R_OK 04 -#endif - -#define close _close -#define getpid _getpid - -#endif // SRC_ARCH_WINDOWS_STUB_UNISTD_H_ diff --git a/src/assignment_sentry.hpp b/src/assignment_sentry.hpp deleted file mode 100644 index b30f1454f37..00000000000 --- a/src/assignment_sentry.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef ASSIGNMENT_SENTRY_HPP_ -#define ASSIGNMENT_SENTRY_HPP_ - -template -class assignment_sentry_t { -public: - assignment_sentry_t() : var(nullptr), old_value() { } - assignment_sentry_t(T *v, const T &value) : - var(v), old_value(*var) { - *var = value; - } - ~assignment_sentry_t() { - reset(); - } - void reset(T *v, const T &value) { - reset(); - var = v; - old_value = *var; - *var = value; - } - void reset() { - if (var != nullptr) { - *var = old_value; - var = nullptr; - } - } -private: - T *var; - T old_value; -}; - -#endif // ASSIGNMENT_SENTRY_HPP_ diff --git a/src/backtrace.cc b/src/backtrace.cc deleted file mode 100644 index 9ac88eeb29c..00000000000 --- a/src/backtrace.cc +++ /dev/null @@ -1,406 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "backtrace.hpp" - -#ifdef _WIN32 -#define OPTIONAL // This macro is undefined in "windows.hpp" but necessary for -#include -#include -#else -#include -#include -#include -#endif - -#include -#include -#include -#include - -#include - -#include "arch/runtime/coroutines.hpp" -#include "containers/scoped.hpp" -#include "logger.hpp" -#include "rethinkdb_backtrace.hpp" -#include "utils.hpp" - -#ifndef _WIN32 -static bool parse_backtrace_line(char *line, char **filename, char **function, char **offset, char **address) { - /* - backtrace() gives us lines in one of the following two forms: - ./path/to/the/binary(function+offset) [address] - ./path/to/the/binary [address] - */ - - *filename = line; - - // Check if there is a function present - if (char *paren1 = strchr(line, '(')) { - char *paren2 = strchr(line, ')'); - if (!paren2) return false; - *paren1 = *paren2 = '\0'; // Null-terminate the offset and the filename - *function = paren1 + 1; - char *plus = strchr(*function, '+'); - if (!plus) return false; - *plus = '\0'; // Null-terminate the function name - *offset = plus + 1; - line = paren2 + 1; - if (*line != ' ') return false; - line += 1; - } else { - *function = nullptr; - *offset = nullptr; - char *bracket = strchr(line, '['); - if (!bracket) return false; - line = bracket - 1; - if (*line != ' ') return false; - *line = '\0'; // Null-terminate the file name - line += 1; - } - - // We are now at the opening bracket of the address - if (*line != '[') return false; - line += 1; - *address = line; - line = strchr(line, ']'); - if (!line || line[1] != '\0') return false; - *line = '\0'; // Null-terminate the address - - return true; -} - -/* There has been some trouble with abi::__cxa_demangle. - -Originally, demangle_cpp_name() took a pointer to the mangled name, and returned a -buffer that must be free()ed. It did this by calling __cxa_demangle() and passing NULL -and 0 for the buffer and buffer-size arguments. - -There were complaints that print_backtrace() was smashing memory. Shachaf observed that -pieces of the backtrace seemed to be ending up overwriting other structs, and filed -issue #100. - -Daniel Mewes suspected that the memory smashing was related to calling malloc(). -In December 2010, he changed demangle_cpp_name() to take a static buffer, and fill -this static buffer with the demangled name. See 284246bd. - -abi::__cxa_demangle expects a malloc()ed buffer, and if the buffer is too small it -will call realloc() on it. So the static-buffer approach worked except when the name -to be demangled was too large. - -In March 2011, Tim and Ivan got tired of the memory allocator complaining that someone -was trying to realloc() an unallocated buffer, and changed demangle_cpp_name() back -to the way it was originally. - -Please don't change this function without talking to the people who have already -been involved in this. */ - -std::string demangle_cpp_name(const char *mangled_name) { - int res; - char *name_as_c_str = abi::__cxa_demangle(mangled_name, nullptr, nullptr, &res); - if (res == 0) { - std::string name_as_std_string(name_as_c_str); - free(name_as_c_str); - return name_as_std_string; - } else { - throw demangle_failed_exc_t(); - } -} - -int set_o_cloexec(int fd) { - int flags = fcntl(fd, F_GETFD); - if (flags < 0) { - return flags; - } - return fcntl(fd, F_SETFD, flags | FD_CLOEXEC); -} - -address_to_line_t::addr2line_t::addr2line_t(const char *executable) : input(nullptr), output(nullptr), bad(false), pid(-1) { - if (pipe(child_in) || set_o_cloexec(child_in[0]) || set_o_cloexec(child_in[1]) || pipe(child_out) || set_o_cloexec(child_out[0]) || set_o_cloexec(child_out[1])) { - bad = true; - return; - } - - if ((pid = fork())) { - input = fdopen(child_in[1], "w"); - output = fdopen(child_out[0], "r"); - close(child_in[0]); - close(child_out[1]); - } else { - dup2(child_in[0], 0); // stdin - dup2(child_out[1], 1); // stdout - - const char *args[] = {"addr2line", "-s", "-e", executable, nullptr}; - - execvp("addr2line", const_cast(args)); - // A normal `exit` can get stuck here it seems. Maybe some `atexit` handler? - _exit(EXIT_FAILURE); - } -} - -address_to_line_t::addr2line_t::~addr2line_t() { - if (input) { - fclose(input); - } - if (output) { - fclose(output); - } - if (pid != -1) { - waitpid(pid, nullptr, 0); - } -} - -bool address_to_line_t::run_addr2line(const std::string &executable, const void *address, char *line, int line_size) { - addr2line_t *proc; - - std::map >::iterator iter - = procs.find(executable); - - if (iter != procs.end()) { - proc = iter->second.get(); - } else { - proc = new addr2line_t(executable.c_str()); - auto p = procs.insert( - std::make_pair(executable, - make_scoped(executable.c_str()))).first; - proc = p->second.get(); - } - - if (proc->bad) { - return false; - } - - fprintf(proc->input, "%p\n", address); - fflush(proc->input); - - char *result = fgets(line, line_size, proc->output); - - if (result == nullptr) { - proc->bad = true; - return false; - } - - line[line_size - 1] = '\0'; - - int len = strlen(line); - if (line[len - 1] == '\n') { - line[len - 1] = '\0'; - } - - if (!strcmp(line, "??:0")) return false; - - return true; -} - -std::string address_to_line_t::address_to_line(const std::string &executable, const void *address) { - char line[255]; - bool success = run_addr2line(executable, address, line, sizeof(line)); - - if (!success) { - return ""; - } else { - return std::string(line); - } -} -#endif - -std::string format_backtrace(bool use_addr2line) { - lazy_backtrace_formatter_t bt; - return use_addr2line ? bt.lines() : bt.addrs(); -} - -backtrace_t::backtrace_t() { - scoped_array_t stack_frames(new void*[max_frames], max_frames); // Allocate on heap in case stack space is scarce - int size = rethinkdb_backtrace(stack_frames.data(), max_frames); - -#ifdef CROSS_CORO_BACKTRACES - if (coro_t::self() != nullptr) { - int space_remaining = max_frames - size; - rassert(space_remaining >= 0); - size += coro_t::self()->copy_spawn_backtrace(stack_frames.data() + size, - space_remaining); - } -#endif - - frames.reserve(static_cast(size)); - for (int i = 0; i < size; ++i) { - frames.push_back(backtrace_frame_t(stack_frames[i])); - } -} - -backtrace_frame_t::backtrace_frame_t(const void* _addr) : - symbols_initialized(false), - addr(_addr) { - -} - -void backtrace_frame_t::initialize_symbols() { -#ifndef _WIN32 - void *addr_array[1] = {const_cast(addr)}; - char **symbols = backtrace_symbols(addr_array, 1); - if (symbols != nullptr) { - symbols_line = std::string(symbols[0]); - char *c_filename; - char *c_function; - char *c_offset; - char *c_address; - if (parse_backtrace_line(symbols[0], &c_filename, &c_function, &c_offset, &c_address)) { - if (c_filename != nullptr) { - filename = std::string(c_filename); - } - if (c_function != nullptr) { - function = std::string(c_function); - } - if (c_offset != nullptr) { - offset = std::string(c_offset); - } - } - free(symbols); - } -#endif - symbols_initialized = true; -} - -std::string backtrace_frame_t::get_name() const { - rassert(symbols_initialized); - return function; -} - -std::string backtrace_frame_t::get_symbols_line() const { - rassert(symbols_initialized); - return symbols_line; -} - -std::string backtrace_frame_t::get_demangled_name() const { - rassert(symbols_initialized); -#ifdef _WIN32 - return function; -#else - return demangle_cpp_name(function.c_str()); -#endif -} - -std::string backtrace_frame_t::get_filename() const { - rassert(symbols_initialized); - return filename; -} - -std::string backtrace_frame_t::get_offset() const { - rassert(symbols_initialized); - return offset; -} - -const void *backtrace_frame_t::get_addr() const { - return addr; -} - -lazy_backtrace_formatter_t::lazy_backtrace_formatter_t() : - backtrace_t(), - timestamp(time(nullptr)), - timestr(time2str(timestamp)) { -} - -std::string lazy_backtrace_formatter_t::addrs() { - if (cached_addrs == "") { - cached_addrs = timestr + "\n" + print_frames(false); - } - return cached_addrs; -} - -std::string lazy_backtrace_formatter_t::lines() { - if (cached_lines == "") { - cached_lines = timestr + "\n" + print_frames(true); - } - return cached_lines; -} - -#ifdef _WIN32 -void initialize_dbghelp() { - static std::atomic initialised = false; - if (!initialised.exchange(true)) { - DWORD options = SymGetOptions(); - options |= SYMOPT_LOAD_LINES; // Load line information - options |= SYMOPT_DEFERRED_LOADS; // Lazy symbol loading - SymSetOptions(options); - - // Initialize and load the symbol tables - BOOL ret = SymInitialize(GetCurrentProcess(), nullptr, true); - if (!ret) { - logWRN("Unable to load symbols: %s", winerr_string(GetLastError()).c_str()); - } - } -} -#endif - -std::string backtrace_t::print_frames(bool use_addr2line) const { -#ifdef _WIN32 - initialize_dbghelp(); - - std::string output; - for (size_t i = 0; i < get_num_frames(); i++) { - output.append(strprintf("%d: ", static_cast(i+1))); - DWORD64 offset; - const int MAX_SYMBOL_LENGTH = 2048; // An arbitrary number - auto symbol_info = scoped_malloc_t(sizeof(SYMBOL_INFO) - 1 + MAX_SYMBOL_LENGTH); - symbol_info->SizeOfStruct = sizeof(SYMBOL_INFO); - symbol_info->MaxNameLen = MAX_SYMBOL_LENGTH; - BOOL ret = SymFromAddr(GetCurrentProcess(), reinterpret_cast(get_frame(i).get_addr()), &offset, symbol_info.get()); - if (!ret) { - output.append(strprintf("0x%p [%s]", get_frame(i).get_addr(), winerr_string(GetLastError()).c_str())); - } else { - output.append(strprintf("%s", symbol_info->Name)); - if (offset != 0) { - output.append(strprintf("+%llu", offset)); - } - } - DWORD line_offset; - IMAGEHLP_LINE64 line_info; - ret = SymGetLineFromAddr64(GetCurrentProcess(), reinterpret_cast(get_frame(i).get_addr()), &line_offset, &line_info); - if (!ret) { - // output.append(" (no line info)"); - } else { - output.append(strprintf(" at %s:%u", line_info.FileName, line_info.LineNumber)); - } - output.append("\n"); - } - return output; -#else - address_to_line_t address_to_line; - std::string output; - for (size_t i = 0; i < get_num_frames(); i++) { - backtrace_frame_t current_frame = get_frame(i); - current_frame.initialize_symbols(); - - output.append(strprintf("%d [%p]: ", static_cast(i+1), current_frame.get_addr())); - - try { - output.append(current_frame.get_demangled_name()); - } catch (const demangle_failed_exc_t &) { - if (!current_frame.get_name().empty()) { - output.append(current_frame.get_name() + "+" + current_frame.get_offset()); - } else if (!current_frame.get_symbols_line().empty()) { - output.append(current_frame.get_symbols_line()); - } else { - output.append(""); - } - } - - output.append(" at "); - - std::string some_other_line; - if (use_addr2line) { - if (!current_frame.get_filename().empty()) { - some_other_line = address_to_line.address_to_line(current_frame.get_filename(), current_frame.get_addr()); - } - } - if (!some_other_line.empty()) { - output.append(some_other_line); - } else { - output.append(strprintf("%p", current_frame.get_addr()) + " (" + current_frame.get_filename() + ")"); - } - - output.append("\n"); - } - - return output; -#endif -} diff --git a/src/backtrace.hpp b/src/backtrace.hpp deleted file mode 100644 index c74dbcf8ef7..00000000000 --- a/src/backtrace.hpp +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef BACKTRACE_HPP_ -#define BACKTRACE_HPP_ - -#include -#include - -#include -#include -#include - -#include "containers/scoped.hpp" - -/* `demangle_cpp_name()` attempts to de-mangle the given symbol name. If it -succeeds, it returns the result as a `std::string`. If it fails, it throws -`demangle_failed_exc_t`. */ -struct demangle_failed_exc_t : public std::exception { - const char *what() const throw () { - return "Could not demangle C++ name."; - } -}; -std::string demangle_cpp_name(const char *mangled_name); - -#ifndef _WIN32 -class address_to_line_t { -public: - address_to_line_t() { } - - /* Returns an empty string if the line could not be found */ - std::string address_to_line(const std::string &executable, const void *address); - -private: - bool run_addr2line(const std::string &executable, const void *address, char *line, int line_size); - - // Internal helper class: - class addr2line_t { - public: - explicit addr2line_t(const char *executable); - ~addr2line_t(); - - FILE *input, *output; - bool bad; - private: - int child_in[2], child_out[2]; - pid_t pid; - DISABLE_COPYING(addr2line_t); - }; - - // "Cache" to re-use addr2line processes - std::map > procs; - - DISABLE_COPYING(address_to_line_t); -}; -#endif - -std::string format_backtrace(bool use_addr2line = true); - -// An individual backtrace frame -class backtrace_frame_t { -public: - explicit backtrace_frame_t(const void *_addr); - - // Initializes symbols_line, filename, function and offset strings. - void initialize_symbols(); - - std::string get_filename() const; - std::string get_name() const; - std::string get_demangled_name() const; - std::string get_offset() const; - std::string get_symbols_line() const; - const void *get_addr() const; -private: - std::string filename, function, offset, symbols_line; - bool symbols_initialized; - const void *addr; -}; - -// A backtrace, consisting of backtrace_frame_t -class backtrace_t { -public: - backtrace_t(); - size_t get_num_frames() const { return frames.size(); } - const backtrace_frame_t &get_frame(const size_t i) const { return frames[i]; } - std::string print_frames(bool use_addr2line) const; -private: - static const int max_frames = 100; - std::vector frames; -}; - -// Stores the backtrace from when it was constructed for later printing. -class lazy_backtrace_formatter_t : public backtrace_t { -public: - lazy_backtrace_formatter_t(); - std::string addrs(); // A raw backtrace with assembly addresses (fast) - std::string lines(); // A well-formatted backtrace with source lines (slow) -private: - std::string cached_addrs, cached_lines; - - time_t timestamp; - std::string timestr; - - DISABLE_COPYING(lazy_backtrace_formatter_t); -}; - -#endif /* BACKTRACE_HPP_ */ diff --git a/src/btree/backfill.cc b/src/btree/backfill.cc deleted file mode 100644 index b0666754b37..00000000000 --- a/src/btree/backfill.cc +++ /dev/null @@ -1,733 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "btree/backfill.hpp" - -#include "arch/runtime/coroutines.hpp" -#include "btree/backfill_debug.hpp" -#include "btree/depth_first_traversal.hpp" -#include "btree/leaf_node.hpp" -#include "concurrency/pmap.hpp" -#include "containers/archive/optional.hpp" -#include "containers/archive/stl_types.hpp" - -/* `MAX_CONCURRENT_VALUE_LOADS` is the maximum number of coroutines we'll use for loading -values from the leaf nodes. */ -static const int MAX_CONCURRENT_VALUE_LOADS = 16; - -/* `convert_to_right_bound()` is suitable for converting `btree_key_t *`s in the format -of `right_incl` or `left_excl_or_null` into a `key_range_t::right_bound_t`. */ -key_range_t::right_bound_t convert_to_right_bound(const btree_key_t *rightmost_before) { - if (rightmost_before == nullptr) { - return key_range_t::right_bound_t(store_key_t::min()); - } else { - key_range_t::right_bound_t rb; - rb.unbounded = false; - rb.key().assign(rightmost_before); - bool ok = rb.increment(); - guarantee(ok); - return rb; - } -} - -key_range_t convert_to_key_range( - const btree_key_t *left_excl_or_null, const btree_key_t *right_incl) { - return key_range_t( - left_excl_or_null == nullptr - ? key_range_t::bound_t::none : key_range_t::bound_t::open, - left_excl_or_null, - key_range_t::bound_t::closed, - right_incl); -} - -continue_bool_t btree_send_backfill_pre( - superblock_t *superblock, - release_superblock_t release_superblock, - value_sizer_t *sizer, - const key_range_t &range, - repli_timestamp_t reference_timestamp, - btree_backfill_pre_item_consumer_t *pre_item_consumer, - signal_t *interruptor) { - backfill_debug_range(range, strprintf( - "btree_send_backfill_pre %" PRIu64, reference_timestamp.longtime)); - class callback_t : public depth_first_traversal_callback_t { - public: - continue_bool_t filter_range_ts( - UNUSED const btree_key_t *left_excl_or_null, - const btree_key_t *right_incl, - repli_timestamp_t timestamp, - signal_t *, - bool *skip_out) { - *skip_out = timestamp <= reference_timestamp; - if (*skip_out) { - return pre_item_consumer->on_empty_range( - convert_to_right_bound(right_incl)); - } else { - return continue_bool_t::CONTINUE; - } - } - - continue_bool_t handle_pre_leaf( - const counted_t &buf, - const btree_key_t *left_excl_or_null, - const btree_key_t *right_incl, - signal_t *, - bool *skip_out) { - *skip_out = true; - const leaf_node_t *lnode = static_cast( - buf->read->get_data_read()); - repli_timestamp_t min_deletion_timestamp = - leaf::min_deletion_timestamp(sizer, lnode, buf->lock.get_recency()); - if (min_deletion_timestamp > reference_timestamp.next()) { - /* We might be missing deletion entries, so re-transmit the entire node - */ - backfill_pre_item_t pre_item; - pre_item.range = convert_to_key_range(left_excl_or_null, right_incl); - backfill_debug_range(pre_item.range, strprintf( - "pre-item leaf %" PRIu64, min_deletion_timestamp.longtime)); - return pre_item_consumer->on_pre_item(std::move(pre_item)); - } else { - std::vector keys; - leaf::visit_entries( - sizer, lnode, buf->lock.get_recency(), - [&](const btree_key_t *key, repli_timestamp_t timestamp, - const void *) -> continue_bool_t { - if ((left_excl_or_null != nullptr && - btree_key_cmp(key, left_excl_or_null) <= 0) - || btree_key_cmp(key, right_incl) > 0) { - return continue_bool_t::CONTINUE; - } - /* `visit_entries()` calls the callback in order from newest to - oldest timestamps. So once we find one entry that's older than - the threshold we care about, we know that there won't be any more - entries that are newer than the threshold we care about, so we - abort. */ - if (timestamp <= reference_timestamp) { - return continue_bool_t::ABORT; - } - backfill_debug_key(store_key_t(key), strprintf( - "pre-item key %" PRIu64, timestamp.longtime)); - keys.push_back(key); - return continue_bool_t::CONTINUE; - }); - std::sort(keys.begin(), keys.end(), - [](const btree_key_t *k1, const btree_key_t *k2) { - return btree_key_cmp(k1, k2) < 0; - }); - for (const btree_key_t *key : keys) { - backfill_pre_item_t pre_item; - pre_item.range = key_range_t::one_key(key); - if (continue_bool_t::ABORT == - pre_item_consumer->on_pre_item(std::move(pre_item))) { - return continue_bool_t::ABORT; - } - } - return pre_item_consumer->on_empty_range( - convert_to_right_bound(right_incl)); - } - } - - continue_bool_t handle_pair(scoped_key_value_t &&, signal_t *) { - unreachable(); - } - - continue_bool_t handle_empty( - UNUSED const btree_key_t *left_excl_or_null, - const btree_key_t *right_incl, - signal_t *) { - return pre_item_consumer->on_empty_range( - convert_to_right_bound(right_incl)); - } - - btree_backfill_pre_item_consumer_t *pre_item_consumer; - repli_timestamp_t reference_timestamp; - value_sizer_t *sizer; - } callback; - callback.pre_item_consumer = pre_item_consumer; - callback.reference_timestamp = reference_timestamp; - callback.sizer = sizer; - return btree_depth_first_traversal(superblock, range, &callback, access_t::read, - FORWARD, release_superblock, interruptor); -} - -/* The `backfill_item_loader_t` gets backfill items from the `backfill_item_preparer_t`, -but that the actual row values have not been loaded into the items yet. It loads the -values from the cache and then passes the items on to the -`btree_backfill_item_consumer_t`. */ -class backfill_item_loader_t { -public: - backfill_item_loader_t( - btree_backfill_item_consumer_t *_item_consumer, cond_t *_abort_cond) : - item_consumer(_item_consumer), abort_cond(_abort_cond), - semaphore(MAX_CONCURRENT_VALUE_LOADS) { } - - /* `on_item()` and `on_empty_range()` will be called in lexicographical order. They - will always be called from the same coroutine, so if a call blocks the traversal will - be paused until it returns. */ - - /* The item passed to `on_item` is complete except for the `value` field of each - pair. If the pair has a value, then instead of containing that value, `pair->value` - will contain a pointer into `buf_read.get_data_read()` which can be used to actually - load the value. The pointer is stored in the `std::vector` that would normally - be supposed to store the value. This is kind of a hack, but it will work and it's - localized to these two types. */ - void on_item( - backfill_item_t &&item, - const counted_t &buf, - signal_t *interruptor) { - new_semaphore_in_line_t sem_acq(&semaphore, item.pairs.size()); - wait_interruptible(sem_acq.acquisition_signal(), interruptor); - coro_t::spawn_sometime(std::bind( - &backfill_item_loader_t::handle_item, this, - std::move(item), buf, std::move(sem_acq), - fifo_source.enter_write(), drainer.lock())); - } - - void on_empty_range( - const key_range_t::right_bound_t &empty_range, signal_t *interruptor) { - new_semaphore_in_line_t sem_acq(&semaphore, 1); - wait_interruptible(sem_acq.acquisition_signal(), interruptor); - /* Unlike `handle_item()`, `handle_empty_range()` doesn't do any expensive work, - but we spawn it in a coroutine anyway so that it runs in the right order with - respect to `handle_item()`. */ - coro_t::spawn_sometime(std::bind( - &backfill_item_loader_t::handle_empty_range, this, - empty_range, std::move(sem_acq), fifo_source.enter_write(), drainer.lock())); - } - - void finish(signal_t *interruptor) { - fifo_enforcer_sink_t::exit_write_t exit_write( - &fifo_sink, fifo_source.enter_write()); - wait_interruptible(&exit_write, interruptor); - } - - /* Gets the size of a given value in a leaf node. */ - int64_t size_value( - buf_parent_t parent, - const void *value_in_leaf_node) { - return item_consumer->size_value(parent, value_in_leaf_node); - } - -private: - void handle_item( - /* Conceptually this is passed by move, but `std::bind()` isn't smart enough - to handle that. */ - backfill_item_t &item, // NOLINT runtime/references - const counted_t &buf, - const new_semaphore_in_line_t &, - fifo_enforcer_write_token_t token, - auto_drainer_t::lock_t keepalive) { - try { - pmap(item.pairs.size(), [&](size_t i) { - try { - if (!static_cast(item.pairs[i].value)) { - /* It's a deletion; we don't need to load anything. */ - return; - } - rassert(item.pairs[i].value->size() == sizeof(void *)); - void *value_ptr = *reinterpret_cast( - item.pairs[i].value->data()); - item.pairs[i].value->clear(); - item_consumer->copy_value(buf_parent_t(&buf->lock), value_ptr, - keepalive.get_drain_signal(), &*item.pairs[i].value); - } catch (const interrupted_exc_t &) { - /* we'll check this outside the `pmap()` */ - } - }); - if (keepalive.get_drain_signal()->is_pulsed()) { - throw interrupted_exc_t(); - } - fifo_enforcer_sink_t::exit_write_t exit_write(&fifo_sink, token); - wait_interruptible(&exit_write, keepalive.get_drain_signal()); - if (abort_cond->is_pulsed()) { - return; - } - if (continue_bool_t::ABORT == item_consumer->on_item(std::move(item))) { - abort_cond->pulse(); - } - } catch (const interrupted_exc_t &) { - /* ignore */ - } - } - - void handle_empty_range( - const key_range_t::right_bound_t &threshold, - const new_semaphore_in_line_t &, - fifo_enforcer_write_token_t token, - auto_drainer_t::lock_t keepalive) { - try { - fifo_enforcer_sink_t::exit_write_t exit_write(&fifo_sink, token); - wait_interruptible(&exit_write, keepalive.get_drain_signal()); - if (abort_cond->is_pulsed()) { - return; - } - if (continue_bool_t::ABORT == item_consumer->on_empty_range(threshold)) { - abort_cond->pulse(); - } - } catch (const interrupted_exc_t &) { - /* ignore */ - } - } - - btree_backfill_item_consumer_t *item_consumer; - cond_t *abort_cond; - new_semaphore_t semaphore; - fifo_enforcer_source_t fifo_source; - fifo_enforcer_sink_t fifo_sink; - auto_drainer_t drainer; -}; - -/* `backfill_item_preparer_t` visits leaf nodes using callbacks from the -`btree_depth_first_traversal()`. At each leaf node, it constructs a series of -`backfill_item_t`s describing the leaf, but doesn't set their values yet; in place of the -values, it stores a pointer to where the value can be loaded from the leaf. Then it -passes them to the `backfill_item_loader_t` to do the actual loading. */ -class backfill_item_preparer_t : public depth_first_traversal_callback_t { -public: - backfill_item_preparer_t( - value_sizer_t *_sizer, - repli_timestamp_t _reference_timestamp, - btree_backfill_pre_item_producer_t *_pre_item_producer, - signal_t *_abort_cond, - backfill_item_loader_t *_loader, - backfill_item_memory_tracker_t *_memory_tracker) : - sizer(_sizer), reference_timestamp(_reference_timestamp), - pre_item_producer(_pre_item_producer), abort_cond(_abort_cond), loader(_loader), - memory_tracker(_memory_tracker) - { } - -private: - /* Skip B-tree subtrees that haven't changed since the reference timestamp and that - don't overlap with any pre-items' ranges. */ - continue_bool_t filter_range_ts( - const btree_key_t *left_excl_or_null, - const btree_key_t *right_incl, - repli_timestamp_t timestamp, - signal_t *interruptor, - bool *skip_out) { - *skip_out = false; - if (timestamp <= reference_timestamp) { - key_range_t range = convert_to_key_range(left_excl_or_null, right_incl); - if (pre_item_producer->try_consume_empty_range(range)) { - *skip_out = true; - loader->on_empty_range(range.right, interruptor); - } - } - return abort_cond->is_pulsed() - ? continue_bool_t::ABORT : continue_bool_t::CONTINUE; - } - - continue_bool_t handle_pre_leaf( - const counted_t &buf, - const btree_key_t *left_excl_or_null, - const btree_key_t *right_incl, - signal_t *interruptor, - bool *skip_out) { - *skip_out = true; - key_range_t::right_bound_t cursor = convert_to_right_bound(left_excl_or_null); - key_range_t::right_bound_t right_bound = convert_to_right_bound(right_incl); - while (cursor != right_bound) { - key_range_t subrange; - subrange.left = cursor.key(); - std::list items_from_pre; - continue_bool_t cont = pre_item_producer->consume_range( - &cursor, right_bound, - [&](const backfill_pre_item_t &pre_item) { - items_from_pre.push_back(backfill_item_t()); - items_from_pre.back().range = pre_item.range; - }); - if (cont == continue_bool_t::ABORT) { - return continue_bool_t::ABORT; - } - subrange.right = cursor; - rassert(!subrange.is_empty()); - - if (continue_bool_t::ABORT == handle_pre_leaf_subrange( - buf, subrange, std::move(items_from_pre), interruptor)) { - /* The subrange has not been fully processed. We must abort - and try again next time. */ - return continue_bool_t::ABORT; - } - if (abort_cond->is_pulsed()) { - return continue_bool_t::ABORT; - } - } - return continue_bool_t::CONTINUE; - } - - /* `handle_pre_leaf_subrange()` creates backfill items for the given subrange of the - given leaf, for which the pre-items are already available. The pre-items are - delivered in the form of a `std::list` where each item's range is - the same as one of the pre-items, but the other fields of the item are uninitialized. - It returns `continue_bool_t::ABORT` if the memory usage limit has been hit. - */ - continue_bool_t handle_pre_leaf_subrange( - const counted_t &buf, - const key_range_t &subrange, - std::list &&items_from_pre, - signal_t *interruptor) { - const leaf_node_t *lnode = static_cast( - buf->read->get_data_read()); - repli_timestamp_t min_deletion_timestamp = - leaf::min_deletion_timestamp(sizer, lnode, buf->lock.get_recency()); - if (min_deletion_timestamp > reference_timestamp) { - /* We might be missing deletion entries, so re-transmit the entire node as a - single `backfill_item_t` */ - backfill_debug_range(subrange, strprintf( - "item leaf %" PRIu64, min_deletion_timestamp.longtime)); - backfill_item_t item; - item.min_deletion_timestamp = min_deletion_timestamp; - item.range = subrange; - - /* Create a `backfill_item_t::pair_t` for each key-value pair in the leaf - node, but don't load the values yet */ - leaf::visit_entries( - sizer, lnode, buf->lock.get_recency(), - [&](const btree_key_t *key, repli_timestamp_t timestamp, - const void *value_or_null) -> continue_bool_t { - /* Ignore keys from the leaf node that aren't within our sub-range */ - if (!subrange.contains_key(key)) { - return continue_bool_t::CONTINUE; - } - - size_t i = item.pairs.size(); - item.pairs.resize(i + 1); - item.pairs[i].key.assign(key); - item.pairs[i].recency = timestamp; - if (value_or_null != nullptr) { - /* Store `value_or_null` in the `value` field as a sequence of - 8 (or 4 or whatever) `char`s describing its actual pointer value. - */ - item.pairs[i].value.set(std::vector( - reinterpret_cast(&value_or_null), - reinterpret_cast(1 + &value_or_null))); - } - return continue_bool_t::CONTINUE; - }); - - /* `visit_entries()` returns entries in timestamp order; we need to sort - `item.pairs` now to put it in lexicographical order */ - std::sort(item.pairs.begin(), item.pairs.end(), - [](const backfill_item_t::pair_t &p1, - const backfill_item_t::pair_t &p2) { - return p1.key < p2.key; - }); - - /* Cut the item off if it's too large. */ - continue_bool_t cont = - limit_item_memory_usage(buf_parent_t(&buf->lock), &item); - - /* `limit_item_memory_usage()` might have left the item empty. */ - if (!item.get_range().is_empty()) { - /* Note that `on_item()` may block, which will limit the rate at which - we traverse the B-tree. */ - loader->on_item(std::move(item), buf, interruptor); - } - - return cont; - - } else { - /* Attach `min_deletion_timestamp` to `items_from_pre`, because it hasn't - been initialized yet for them. We also need to clip `items_from_pre` because - some of them might fall partially outside of `subrange`. */ - for (backfill_item_t &i : items_from_pre) { - rassert(subrange.overlaps(i.range)); - i.range = i.range.intersection(subrange); - i.min_deletion_timestamp = min_deletion_timestamp; - } - - /* Find each key-value pair or deletion entry that falls within the range of - a pre item or that changed since `reference_timestamp`. If it falls within - the range of a pre item, put it into the corresponding item in - `items_from_pre`; otherwise, make a new item for it in `items_from_time`. */ - std::list items_from_time; - leaf::visit_entries( - sizer, lnode, buf->lock.get_recency(), - [&](const btree_key_t *key, repli_timestamp_t timestamp, - const void *value_or_null) -> continue_bool_t { - /* The leaf node might be partially outside the range of the - backfill, so we might have to skip some keys */ - if (!subrange.contains_key(key)) { - return continue_bool_t::CONTINUE; - } - - /* In the most common case, `items_from_pre` is totally empty. Since - we ignore entries that are at `reference_timestamp` or older unless - they are in a pre item, we can optimize things slightly by aborting - the leaf node iteration early. */ - if (timestamp <= reference_timestamp && items_from_pre.empty()) { - return continue_bool_t::ABORT; - } - - /* We'll set `item` to the `backfill_item_t` where this key-value - pair should be inserted. First we check if there's an item in - `items_from_pre` that contains the key. If not, we'll create a new - item in `items_from_time`. */ - backfill_item_t *item = nullptr; - /* Linear search sucks, but there probably won't be a whole lot of - pre items, so it's OK for now. */ - for (backfill_item_t &a : items_from_pre) { - if (a.range.contains_key(key)) { - backfill_debug_key(store_key_t(key), "item_from_pre"); - item = &a; - break; - } - } - if (item == nullptr) { - /* We didn't find an item in `items_from_pre`. */ - if (timestamp > reference_timestamp) { - /* We should create a new item for this key-value pair */ - backfill_debug_key(store_key_t(key), strprintf( - "item %" PRIu64, timestamp.longtime)); - items_from_time.push_back(backfill_item_t()); - item = &items_from_time.back(); - item->range = key_range_t::one_key(key); - item->min_deletion_timestamp = - repli_timestamp_t::distant_past; - } else { - /* Ignore this key-value pair */ - return continue_bool_t::CONTINUE; - } - } - - rassert(item->range.contains_key(key)); - - /* This is `timestamp.next()` instead of `timestamp` because it's - possible for us to forget a deletion with timestamp equal to the - oldest timestamped entry, so `min_deletion_timestamp` is one time - unit newer than the oldest timestamped entry. */ - rassert(timestamp.next() >= item->min_deletion_timestamp); - - size_t i = item->pairs.size(); - item->pairs.resize(i + 1); - item->pairs[i].key.assign(key); - item->pairs[i].recency = timestamp; - if (value_or_null != nullptr) { - /* Store `value_or_null` in the `value` field as a sequence of - 8 (or 4 or whatever) `char`s describing its actual pointer value. - */ - item->pairs[i].value.set(std::vector( - reinterpret_cast(&value_or_null), - reinterpret_cast(1 + &value_or_null))); - } - return continue_bool_t::CONTINUE; - }); - - /* `leaf::visit_entries` returns entries in timestamp order, but we want - `items_from_time` to be in lexicographical order, so we need to sort it. Same - with the `pairs` field of each item in `items_from_pre`. */ - items_from_time.sort( - [](const backfill_item_t &i1, const backfill_item_t &i2) { - return i1.range.left < i2.range.left; - }); - for (backfill_item_t &i : items_from_pre) { - std::sort(i.pairs.begin(), i.pairs.end(), - [](const backfill_item_t::pair_t &p1, - const backfill_item_t::pair_t &p2) { - return p1.key < p2.key; - }); - } - - /* Merge `items_from_time` into `items_from_pre`, preserving order. */ - items_from_pre.merge( - std::move(items_from_time), - [](const backfill_item_t &i1, const backfill_item_t &i2) { - rassert(!i1.range.overlaps(i2.range)); - return i1.range.left < i2.range.left; - }); - - /* Send the results to the loader */ - for (backfill_item_t &i : items_from_pre) { - /* Cut the item off if it's too large. */ - continue_bool_t cont = - limit_item_memory_usage(buf_parent_t(&buf->lock), &i); - /* `limit_item_memory_usage()` might have left the item empty. */ - if (!i.get_range().is_empty()) { - loader->on_item(std::move(i), buf, interruptor); - } - if (cont == continue_bool_t::ABORT) { - return continue_bool_t::ABORT; - } - } - loader->on_empty_range(subrange.right, interruptor); - - return continue_bool_t::CONTINUE; - } - } - - MUST_USE continue_bool_t limit_item_memory_usage( - const buf_parent_t &parent, - backfill_item_t *item) { - - /* Reserve space for the item structure itself. */ - memory_tracker->reserve_memory(sizeof(backfill_item_t)); - /* ...if that already exceeds the memory limit, make the item empty and - abort now. */ - if (memory_tracker->is_limit_exceeded()) { - item->mask_in_place(key_range_t::empty()); - return continue_bool_t::ABORT; - } - - /* Reserve space for the values and cut the item off once we have reached - the memory limit. */ - for (const auto &pair : item->pairs) { - /* Compute the amount of memory needed for the pair */ - size_t pair_size; - if (static_cast(pair.value)) { - /* It's not a deletion */ - rassert(pair.value->size() == sizeof(void *)); - const void *value_ptr = *reinterpret_cast( - pair.value->data()); - size_t value_size = - static_cast(loader->size_value(parent, value_ptr)); - pair_size = pair.get_mem_size_with_value(value_size); - } else { - pair_size = pair.get_mem_size(); - } - - /* Reserve the memory. */ - memory_tracker->reserve_memory(pair_size); - bool stop_loading = memory_tracker->is_limit_exceeded(); - memory_tracker->note_item(); - - /* If `stop_loading` is set, we cut the current and all subsequent keys - out of the item. */ - if (stop_loading) { - key_range_t mask_range = key_range_t( - key_range_t::none, store_key_t(), - key_range_t::open, pair.key); - item->mask_in_place(mask_range); - /* We must abort. Our caller might already have consumed some - backfill pre item which we now end up not fully covering. So - continuing would make us skip values. */ - return continue_bool_t::ABORT; - } - } - - /* If the item doesn't have any key/value pairs, we will get here - without telling the memory_tracker that we have made progress. Tell it - now. */ - guarantee(!item->get_range().is_empty()); - memory_tracker->note_item(); - - return continue_bool_t::CONTINUE; - } - - continue_bool_t handle_pair(scoped_key_value_t &&, signal_t *) { - unreachable(); - } - - continue_bool_t handle_empty( - const btree_key_t *left_excl_or_null, - const btree_key_t *right_incl, - signal_t *interruptor) { - key_range_t::right_bound_t cursor = convert_to_right_bound(left_excl_or_null); - key_range_t::right_bound_t end = convert_to_right_bound(right_incl); - while (cursor != end) { - std::vector items; - continue_bool_t cont = pre_item_producer->consume_range( - &cursor, end, - [&](const backfill_pre_item_t &pre_item) { - items.resize(items.size() + 1); - items.back().range = pre_item.range; - items.back().min_deletion_timestamp = repli_timestamp_t::distant_past; - }); - if (cont == continue_bool_t::ABORT) { - return continue_bool_t::ABORT; - } - for (backfill_item_t &i : items) { - loader->on_item( - std::move(i), - counted_t(), - interruptor); - } - loader->on_empty_range(cursor, interruptor); - } - return abort_cond->is_pulsed() - ? continue_bool_t::ABORT : continue_bool_t::CONTINUE; - } - - value_sizer_t *sizer; - repli_timestamp_t reference_timestamp; - btree_backfill_pre_item_producer_t *pre_item_producer; - signal_t *abort_cond; - backfill_item_loader_t *loader; - backfill_item_memory_tracker_t *memory_tracker; -}; - -continue_bool_t btree_send_backfill( - superblock_t *superblock, - release_superblock_t release_superblock, - value_sizer_t *sizer, - const key_range_t &range, - repli_timestamp_t reference_timestamp, - btree_backfill_pre_item_producer_t *pre_item_producer, - btree_backfill_item_consumer_t *item_consumer, - backfill_item_memory_tracker_t *memory_tracker, - signal_t *interruptor) { - backfill_debug_range(range, strprintf( - "btree_send_backfill %" PRIu64, reference_timestamp.longtime)); - cond_t abort_cond; - backfill_item_loader_t loader(item_consumer, &abort_cond); - backfill_item_preparer_t preparer( - sizer, reference_timestamp, pre_item_producer, &abort_cond, &loader, - memory_tracker); - continue_bool_t cont = btree_depth_first_traversal( - superblock, range, &preparer, access_t::read, FORWARD, release_superblock, - interruptor); - /* Wait for `loader` to finish what it's doing, even if `btree_pre_item_producer` - aborted. This is important so that we can make progress even if - `btree_pre_item_producer` only gives us a couple of pre-items at a time. */ - loader.finish(interruptor); - return abort_cond.is_pulsed() ? continue_bool_t::ABORT : cont; -} - -class backfill_deletion_timestamp_updater_t : public depth_first_traversal_callback_t { -public: - backfill_deletion_timestamp_updater_t(value_sizer_t *s, repli_timestamp_t mdt) : - sizer(s), min_deletion_timestamp(mdt) { } - continue_bool_t handle_pre_leaf( - const counted_t &buf, - UNUSED const btree_key_t *left_excl_or_null, - UNUSED const btree_key_t *right_incl, - signal_t *, - bool *skip_out) { - *skip_out = true; - buf_write_t buf_write(&buf->lock); - leaf_node_t *lnode = static_cast(buf_write.get_data_write()); - leaf::erase_deletions(sizer, lnode, - make_optional(min_deletion_timestamp)); - buf->lock.set_recency(superceding_recency( - min_deletion_timestamp, buf->lock.get_recency())); - return continue_bool_t::CONTINUE; - } - continue_bool_t handle_pre_internal( - const counted_t &buf, - UNUSED const btree_key_t *left_excl_or_null, - UNUSED const btree_key_t *right_incl, - signal_t *) { - buf->lock.set_recency(superceding_recency( - min_deletion_timestamp, buf->lock.get_recency())); - return continue_bool_t::CONTINUE; - } - continue_bool_t handle_pair(scoped_key_value_t &&, signal_t *) { - unreachable(); - } -private: - value_sizer_t *sizer; - repli_timestamp_t min_deletion_timestamp; -}; - -void btree_receive_backfill_item_update_deletion_timestamps( - superblock_t *superblock, - release_superblock_t release_superblock, - value_sizer_t *sizer, - const backfill_item_t &item, - signal_t *interruptor) { - backfill_debug_range(item.range, strprintf( - "b.r.b.i.u.d.t. %" PRIu64, item.min_deletion_timestamp.longtime)); - backfill_deletion_timestamp_updater_t updater(sizer, item.min_deletion_timestamp); - continue_bool_t res = btree_depth_first_traversal( - superblock, item.range, &updater, access_t::write, FORWARD, release_superblock, - interruptor); - guarantee(res == continue_bool_t::CONTINUE); -} - diff --git a/src/btree/backfill.hpp b/src/btree/backfill.hpp deleted file mode 100644 index 2e2691339b8..00000000000 --- a/src/btree/backfill.hpp +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef BTREE_BACKFILL_HPP_ -#define BTREE_BACKFILL_HPP_ - -#include -#include -#include - -#include "btree/backfill_types.hpp" -#include "btree/keys.hpp" -#include "btree/types.hpp" -#include "buffer_cache/types.hpp" -#include "concurrency/interruptor.hpp" -#include "containers/optional.hpp" -#include "protocol_api.hpp" -#include "repli_timestamp.hpp" -#include "rpc/serialize_macros.hpp" - -class buf_parent_t; -class superblock_t; -class value_sizer_t; - - -/* `btree_send_backfill_pre()` finds all of the keys or ranges of keys that have changed -since `reference_timestamp` and describes them as a sequence of `backfill_pre_item_t`s, -which it passes to `pre_item_consumer->on_pre_item()`. The pre-items will not overlap, -and the calls to `on_pre_item()` are guaranteed to go in lexicographical order from left -to right. `on_empty_range()` indicates that there won't be any pre items between the end -of the last pre item and the threshold passed to `on_empty_range()`. - -If `on_pre_item()` or `on_empty_range()` returns `ABORT`, then no more calls will be -generated and `btree_send_backfill_pre()` will return `ABORT`. Otherwise, -`btree_send_backfill_pre()` will continue until it reaches the end of `range` and then -return `CONTINUE`. The final call to `on_pre_item()` or `on_empty_range()` is guaranteed -to end exactly on `range.right`. */ - -class btree_backfill_pre_item_consumer_t { -public: - /* These callbacks may block, but `btree_send_backfill_pre()` might hold B-tree locks - while it calls them, so they shouldn't block for long. */ - virtual continue_bool_t on_pre_item(backfill_pre_item_t &&item) THROWS_NOTHING = 0; - virtual continue_bool_t on_empty_range(const key_range_t::right_bound_t &threshold) - THROWS_NOTHING = 0; -protected: - virtual ~btree_backfill_pre_item_consumer_t() { } -}; - -continue_bool_t btree_send_backfill_pre( - superblock_t *superblock, - release_superblock_t release_superblock, - value_sizer_t *sizer, - const key_range_t &range, - repli_timestamp_t reference_timestamp, - btree_backfill_pre_item_consumer_t *pre_item_consumer, - signal_t *interruptor); - -/* `btree_backfill_pre_item_producer_t` is responsible for feeding a stream of -`backfill_pre_item_t`s to `btree_send_backfill()`. */ -class btree_backfill_pre_item_producer_t { -public: - /* These callbacks may block, but `btree_send_backfill()` may hold B-tree locks while - it calls them, so they shouldn't block for long. */ - - /* `btree_send_backfill()` calls `consume_range()` to request a batch of pre-items. - The left end of the batch must be `*cursor_inout`; the right end of the batch is - decided by `consume_range()`, but must be no further than `limit`. `consume_range()` - calls `callback()` for each pre-item in the batch and then sets `*cursor_inout` to - the right-hand edge of the batch. Batches must be contiguous, so the next call to - `consume_range()` must have `*cursor_inout` set to whatever the previous call set - `*cursor_inout` to. If a pre-item spans two batches, it must appear in both. If there - are no pre-items available (so that `*cursor_inout` would not be moved) then - `consume_range()` returns `continue_bool_t::ABORT`. */ - virtual continue_bool_t consume_range( - key_range_t::right_bound_t *cursor_inout, - const key_range_t::right_bound_t &limit, - const std::function &callback) = 0; - - /* `try_consume_empty_range()` consumes the range from `left_excl_or_null` to - `right_incl` if there are no pre-items in that range. If this works, it returns - `true`. If the range is not completely empty, or the producer doesn't yet know - whether or not it's completely empty, it returns `false` and nothing is changed. The - left end of the range must be the right-hand edge of the last range that was - consumed. */ - virtual bool try_consume_empty_range( - const key_range_t &range) = 0; - - /* Note that `btree_send_backfill()` is guaranteed to make progress as long as even - one pre-item is available. For example, if it encounters a leaf node whose contents - go from "AA" to "AZ", but `consume_range()` only produces a pre-items up to "AM" and - then returns `continue_bool_t::ABORT`, then `btree_send_backfill()` must generate - backfill items up to "AM". This property is important in a situation where a B-tree - with very little data is backfilling to a B-tree with a lot of data; there can be - arbitrarily many pre-items per leaf node, so we have to be able to make progress even - if we can't collect all the pre-items for the leaf node we're currently on. */ - -protected: - virtual ~btree_backfill_pre_item_producer_t() { } -}; - -/* `btree_send_backfill()` finds all of the keys or key-ranges which have changed since -`reference_timestamp` or which are included in one of the `btree_pre_item_t`s provided -by `pre_item_producer`. It generates a stream of `backfill_item_t`s which describe those -keys and their associated values and timestamps. It passes the `backfill_item_t`s to the -given `btree_backfill_item_consumer_t`. */ - -class btree_backfill_item_consumer_t { -public: - /* The exact semantics for how `btree_send_backfill()` calls `on_item()` and - `on_empty_range()` are the same as for `btree_send_pre_backfill()` and - `btree_backfill_item_consumer_t`. */ - virtual continue_bool_t on_item( - backfill_item_t &&item) = 0; - virtual continue_bool_t on_empty_range( - const key_range_t::right_bound_t &threshold) = 0; - - /* `copy_value()` is responsible for reading a value out of the leaf node, finding - the corresponding blob pages (if it's a large value), and putting the contents into - `value_out`. This is because the exact format of the leaf node values is opaque to - the B-tree logic. `copy_value()` may block. */ - virtual void copy_value( - buf_parent_t buf_parent, - const void *value_in_leaf_node, - signal_t *interruptor, - std::vector *value_out) = 0; - /* Similarly `size_value()` is responsible for retrieving the size of a given - value in the leaf node. */ - virtual int64_t size_value( - buf_parent_t buf_parent, - const void *value_in_leaf_node) = 0; -protected: - virtual ~btree_backfill_item_consumer_t() { } -}; - -continue_bool_t btree_send_backfill( - superblock_t *superblock, - release_superblock_t release_superblock, - value_sizer_t *sizer, - const key_range_t &range, - repli_timestamp_t reference_timestamp, - btree_backfill_pre_item_producer_t *pre_item_producer, - btree_backfill_item_consumer_t *item_consumer, - backfill_item_memory_tracker_t *memory_tracker, - signal_t *interruptor); - -/* There's no such thing as `btree_receive_backfill()`; the RDB protocol code is -responsible for interpreting the `backfill_item_t`s and translating them into a series -of deletions, insertions, etc. to be applied to the B-tree. - -`btree_receive_backfill_item_update_deletion_timestamps()` (what a mouthful!) is a helper -function that the RDB protocol code calls when applying `backfill_item_t`s. When a -`backfill_item_t` only contains deletions going back to some `min_deletion_timestamp`, we -must record in the B-tree that deletions before that point have been forgotten. We do -this by erasing timestamps and deletion entries before that point in the leaf node. This -function visits every leaf node in `item.range` and erases any timestamps or deletion -entries with timestamps earlier than `item.min_deletion_timestamp`. It should be called -for every `backfill_item_t` that's applied to the B-tree, unless `item.is_single_key()` -returns `true`. */ -void btree_receive_backfill_item_update_deletion_timestamps( - superblock_t *superblock, - release_superblock_t release_superblock, - value_sizer_t *sizer, - const backfill_item_t &item, - signal_t *interruptor); - -#endif // BTREE_BACKFILL_HPP_ - diff --git a/src/btree/backfill_debug.cc b/src/btree/backfill_debug.cc deleted file mode 100644 index 828ab0025ce..00000000000 --- a/src/btree/backfill_debug.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "btree/backfill_debug.hpp" - -#include - -#include "arch/spinlock.hpp" - -#ifdef ENABLE_BACKFILL_DEBUG - -class backfill_debug_msg_t { -public: - bool general; - key_range_t keys; - std::string msg; -}; - -std::deque backfill_debug_msgs; -spinlock_t backfill_debug_lock; - -void backfill_debug_clear_log() { - spinlock_acq_t acq(&backfill_debug_lock); - backfill_debug_msgs.clear(); -} - -void backfill_debug_key(const store_key_t &key, const std::string &msg) { - spinlock_acq_t acq(&backfill_debug_lock); - backfill_debug_msgs.push_back(backfill_debug_msg_t { - false, - key_range_t(key.btree_key()), - msg - }); -} - -void backfill_debug_range(const key_range_t &range, const std::string &msg) { - spinlock_acq_t acq(&backfill_debug_lock); - backfill_debug_msgs.push_back(backfill_debug_msg_t { - false, - range, - msg - }); -} - -void backfill_debug_all(const std::string &msg) { - spinlock_acq_t acq(&backfill_debug_lock); - backfill_debug_msgs.push_back(backfill_debug_msg_t { - true, - key_range_t::universe(), - msg - }); -} - -void backfill_debug_dump_log(const store_key_t &key) { - spinlock_acq_t acq(&backfill_debug_lock); - fprintf(stderr, "-------- BACKFILL DEBUG LOG (%s) --------\n", - key_to_debug_str(key).c_str()); - for (const backfill_debug_msg_t &msg : backfill_debug_msgs) { - if (!msg.keys.contains_key(key)) { - continue; - } - if (msg.general) { - fprintf(stderr, ": %s\n", msg.msg.c_str()); - } else if (msg.keys == key_range_t(key.btree_key())) { - fprintf(stderr, "%s: %s\n", key_to_debug_str(key).c_str(), msg.msg.c_str()); - } else { - fprintf(stderr, "%s: %s\n", - key_range_to_string(msg.keys).c_str(), msg.msg.c_str()); - } - } -} - -#endif /* ENABLE_BACKFILL_DEBUG */ - diff --git a/src/btree/backfill_debug.hpp b/src/btree/backfill_debug.hpp deleted file mode 100644 index 28aa8abfc44..00000000000 --- a/src/btree/backfill_debug.hpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef BTREE_BACKFILL_DEBUG_HPP_ -#define BTREE_BACKFILL_DEBUG_HPP_ - -#include - -#include "btree/keys.hpp" - -/* If the `ENABLE_BACKFILL_DEBUG` symbol is defined, then backfilling-related events -will be recorded in a log and indexed by key. This can be useful for diagnosing bugs in -the backfilling logic. Obviously, this should be disabled when not in use. */ -// #define ENABLE_BACKFILL_DEBUG - -#ifdef ENABLE_BACKFILL_DEBUG -void backfill_debug_clear_log(); -void backfill_debug_key(const store_key_t &key, const std::string &msg); -void backfill_debug_range(const key_range_t &range, const std::string &msg); -void backfill_debug_all(const std::string &msg); -void backfill_debug_dump_log(const store_key_t &key); -#else -#define backfill_debug_clear_log() ((void)0) -#define backfill_debug_key(key, msg) ((void)0) -#define backfill_debug_range(range, msg) ((void)0) -#define backfill_debug_all(msg) ((void)0) -#define backfill_debug_dump_log(key) ((void)0) -#endif /* ENABLE_BACKFILL_DEBUG */ - -#endif /* BTREE_BACKFILL_DEBUG_HPP_ */ - diff --git a/src/btree/backfill_types.cc b/src/btree/backfill_types.cc deleted file mode 100644 index 7226aaed44b..00000000000 --- a/src/btree/backfill_types.cc +++ /dev/null @@ -1,21 +0,0 @@ -#include "btree/backfill_types.hpp" - -#include "containers/archive/optional.hpp" -#include "containers/archive/stl_types.hpp" - - -RDB_IMPL_SERIALIZABLE_1_FOR_CLUSTER(backfill_pre_item_t, range); -RDB_IMPL_SERIALIZABLE_3_FOR_CLUSTER(backfill_item_t::pair_t, key, recency, value); -RDB_IMPL_SERIALIZABLE_3_FOR_CLUSTER(backfill_item_t, - range, pairs, min_deletion_timestamp); - -void backfill_item_t::mask_in_place(const key_range_t &m) { - range = range.intersection(m); - std::vector new_pairs; - for (auto &&pair : pairs) { - if (m.contains_key(pair.key)) { - new_pairs.push_back(std::move(pair)); - } - } - pairs = std::move(new_pairs); -} diff --git a/src/btree/backfill_types.hpp b/src/btree/backfill_types.hpp deleted file mode 100644 index 9c60a807754..00000000000 --- a/src/btree/backfill_types.hpp +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef BTREE_BACKFILL_TYPES_HPP_ -#define BTREE_BACKFILL_TYPES_HPP_ - -#include - -#include "btree/keys.hpp" -#include "containers/optional.hpp" -#include "repli_timestamp.hpp" -#include "rpc/serialize_macros.hpp" - -/* `backfill_pre_item_t` describes a range of keys which have changed on the backfill -destination since the source and destination diverged. The backfill destination sends -pre items to the backfill source so that the source knows to re-transmit the values of -those keys. */ -class backfill_pre_item_t { -public: - key_range_t get_range() const { - return range; - } - size_t get_mem_size() const { - return sizeof(backfill_pre_item_t); - } - void mask_in_place(const key_range_t &m) { - range = range.intersection(m); - } - key_range_t range; -}; -RDB_DECLARE_SERIALIZABLE_FOR_CLUSTER(backfill_pre_item_t); - -/* A `backfill_item_t` is a complete description of some sub-range of the B-tree, -containing all the information necessary to bring the corresponding sub-range of any -other B-tree into a state equivalent to that of the B-tree that the `backfill_item_t` -came from. */ -class backfill_item_t { -public: - class pair_t { - public: - store_key_t key; - repli_timestamp_t recency; - optional > value; /* empty indicates deletion */ - size_t get_mem_size() const { - size_t s = sizeof(pair_t); - if (static_cast(value)) { - s += value->size(); - } - return s; - } - /* The size of a `pair_t` assuming its value is set to the given size */ - static size_t get_mem_size_with_value(size_t value_size) { - return sizeof(pair_t) + value_size; - } - }; - - key_range_t get_range() const { - return range; - } - - /* `get_mem_size()` estimates the size the `backfill_item_t` occupies in RAM or when - serialized over the network. Backfill batch sizes are defined in terms of the total - mem sizes of all the backfill items in the batch. */ - size_t get_mem_size() const { - size_t s = sizeof(backfill_item_t); - for (const auto &pair : pairs) { - s += pair.get_mem_size(); - } - return s; - } - - void mask_in_place(const key_range_t &m); - - /* A single-key backfill item consists of a `key_range_t` covering exactly one key, - and a single `pair_t` for that same key. The backfill receiver logic has a separate - code path for single-key items in order to improve performance. */ - bool is_single_key() { - key_range_t::right_bound_t x(range.left); - x.increment(); - return pairs.size() == 1 && x == range.right; - } - - /* The `range` member describes what range of keys this item applies to. `pairs` - contains an entry for every key present in the range on the backfill source. In - addition, for every key that has been deleted from the range with timestamp greater - than or equal to `min_deletion_timestamp`, `pairs` will contain an deletion entry. - Every pair carries a timestamp, which is greater than or equal to the timestamp of - the actual query that last touched the key. */ - key_range_t range; - std::vector pairs; - repli_timestamp_t min_deletion_timestamp; - - /* TODO: For single-key items, this is not very memory-efficient, because we store - the key in three places: in `pairs[0].key`, in `range.left`, and in `range.right.key` - minus one. Consider wrapping `range` in a `scoped_ptr_t`, which is left empty for a - single-key item. This will also reduce data copying when moving an item. */ -}; -RDB_DECLARE_SERIALIZABLE_FOR_CLUSTER(backfill_item_t::pair_t); -RDB_DECLARE_SERIALIZABLE_FOR_CLUSTER(backfill_item_t); - -#endif // BTREE_BACKFILL_TYPES_HPP_ diff --git a/src/btree/concurrent_traversal.cc b/src/btree/concurrent_traversal.cc deleted file mode 100644 index c986dd844b0..00000000000 --- a/src/btree/concurrent_traversal.cc +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "btree/concurrent_traversal.hpp" - -#include - -#include -#include - -#include "arch/runtime/coroutines.hpp" -#include "concurrency/auto_drainer.hpp" -#include "concurrency/semaphore.hpp" -#include "concurrency/fifo_enforcer.hpp" - -class incr_decr_t { -public: - explicit incr_decr_t(size_t *ptr) : ptr_(ptr) { - guarantee(*ptr_ < SIZE_MAX); - ++*ptr_; - } - ~incr_decr_t() { - --*ptr_; - } - -private: - size_t *ptr_; - - DISABLE_COPYING(incr_decr_t); -}; - - -namespace concurrent_traversal { -static const int initial_semaphore_capacity = 3; -static const int min_semaphore_capacity = 2; -static const int max_semaphore_capacity = 30; -static const int yield_interval = 100; -} // namespace concurrent_traversal - -class concurrent_traversal_adapter_t : public depth_first_traversal_callback_t { -public: - - explicit concurrent_traversal_adapter_t(concurrent_traversal_callback_t *cb, - cond_t *failure_cond) - : semaphore_(concurrent_traversal::initial_semaphore_capacity, 0.5), - sink_waiters_(0), - cb_(cb), - failure_cond_(failure_cond), - yield_counter(0) { } - - continue_bool_t filter_range( - const btree_key_t *left_excl_or_null, - const btree_key_t *right_incl, - signal_t *, - bool *skip_out) { - cb_->filter_range(left_excl_or_null, right_incl, skip_out); - return continue_bool_t::CONTINUE; - } - - void handle_pair_coro(scoped_key_value_t *fragile_keyvalue, - semaphore_acq_t *fragile_acq, - fifo_enforcer_write_token_t token, - auto_drainer_t::lock_t) { - // This is called by coro_t::spawn_now_dangerously. We need to get these - // values before the caller's stack frame is destroyed. - scoped_key_value_t keyvalue = std::move(*fragile_keyvalue); - - semaphore_acq_t semaphore_acq(std::move(*fragile_acq)); - - fifo_enforcer_sink_t::exit_write_t exit_write(&sink_, token); - - continue_bool_t done; - try { - done = cb_->handle_pair( - std::move(keyvalue), - concurrent_traversal_fifo_enforcer_signal_t(&exit_write, this)); - } catch (const interrupted_exc_t &) { - done = continue_bool_t::ABORT; - } - - if (done == continue_bool_t::ABORT) { - failure_cond_->pulse_if_not_already_pulsed(); - } - } - - virtual continue_bool_t handle_pair(scoped_key_value_t &&keyvalue, signal_t *) { - // First thing first: Get in line with the token enforcer. - - fifo_enforcer_write_token_t token = source_.enter_write(); - // ... and wait for the semaphore, we don't want too many things loading - // values at once. - semaphore_acq_t acq(&semaphore_); - - // Yield occasionally so we don't hog the thread if everything is in - // memory and `cb->handle_pair()` doesn't block. - if (yield_counter == concurrent_traversal::yield_interval) { - coro_t::yield(); - yield_counter = 0; - } else { - ++yield_counter; - } - - coro_t::spawn_now_dangerously( - std::bind(&concurrent_traversal_adapter_t::handle_pair_coro, - this, &keyvalue, &acq, token, auto_drainer_t::lock_t(&drainer_))); - - // Report if we've failed by the time this handle_pair call is called. - return failure_cond_->is_pulsed() - ? continue_bool_t::ABORT : continue_bool_t::CONTINUE; - } - - virtual profile::trace_t *get_trace() THROWS_NOTHING { - return cb_->get_trace(); - } - -private: - friend class concurrent_traversal_fifo_enforcer_signal_t; - - adjustable_semaphore_t semaphore_; - - fifo_enforcer_source_t source_; - fifo_enforcer_sink_t sink_; - - // The number of coroutines waiting for their turn with sink_. - size_t sink_waiters_; - - concurrent_traversal_callback_t *cb_; - - // Signals when the query has failed, when we should give up all hope in executing - // the query. - cond_t *failure_cond_; - - // Counted up every time handle_pair() runs so we can yield occasionally. - int yield_counter; - - // We don't use the drainer's drain signal, we use failure_cond_ - auto_drainer_t drainer_; - DISABLE_COPYING(concurrent_traversal_adapter_t); -}; - -concurrent_traversal_fifo_enforcer_signal_t:: -concurrent_traversal_fifo_enforcer_signal_t( - signal_t *eval_exclusivity_signal, - concurrent_traversal_adapter_t *parent) - : eval_exclusivity_signal_(eval_exclusivity_signal), - parent_(parent) { } - -void concurrent_traversal_fifo_enforcer_signal_t::wait() THROWS_NOTHING { - cond_t non_interruptor; - wait_with_interruptor(&non_interruptor); -} - -void concurrent_traversal_fifo_enforcer_signal_t::wait_interruptible() - THROWS_ONLY(interrupted_exc_t) { - wait_with_interruptor(parent_->failure_cond_); -} - -void concurrent_traversal_fifo_enforcer_signal_t::wait_with_interruptor( - signal_t *interruptor) THROWS_ONLY(interrupted_exc_t) { - incr_decr_t incr_decr(&parent_->sink_waiters_); - - if (parent_->sink_waiters_ >= 2) { - // If we have two or more things waiting for the signal (including ourselves) - // we're probably looking too far ahead. Lower the capacity by 1. This might - // seem abrupt (especially considering that our semapoher_.get_capacity() - // concurrent reads can finish in any order), but we have the trickle fraction - // set to 0.5, so there's smoothing. - parent_->semaphore_.set_capacity( - std::max(concurrent_traversal::min_semaphore_capacity, - parent_->semaphore_.get_capacity() - 1)); - } else if (parent_->sink_waiters_ == 1) { - // We're the only thing waiting for the signal? We might not be looking far - // ahead enough. - parent_->semaphore_.set_capacity( - std::min(concurrent_traversal::max_semaphore_capacity, - parent_->semaphore_.get_capacity() + 1)); - } - - ::wait_interruptible(eval_exclusivity_signal_, interruptor); -} - -continue_bool_t btree_concurrent_traversal( - superblock_t *superblock, - const key_range_t &range, - concurrent_traversal_callback_t *cb, - direction_t direction, - release_superblock_t release_superblock) { - cond_t failure_cond; - bool failure_seen; - { - concurrent_traversal_adapter_t adapter(cb, &failure_cond); - cond_t non_interruptor; - failure_seen = (continue_bool_t::ABORT == btree_depth_first_traversal( - superblock, range, &adapter, access_t::read, direction, release_superblock, - &non_interruptor)); - } - // Now that adapter is destroyed, the operations that might have failed have all - // drained. (If we fail, we try to report it to btree_depth_first_traversal (to - // kill the traversal), but it's possible for us to fail after - // btree_depth_first_traversal returns.) - guarantee(!(failure_seen && !failure_cond.is_pulsed())); - return failure_cond.is_pulsed() ? continue_bool_t::ABORT : continue_bool_t::CONTINUE; -} - diff --git a/src/btree/concurrent_traversal.hpp b/src/btree/concurrent_traversal.hpp deleted file mode 100644 index 4376287c581..00000000000 --- a/src/btree/concurrent_traversal.hpp +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef BTREE_CONCURRENT_TRAVERSAL_HPP_ -#define BTREE_CONCURRENT_TRAVERSAL_HPP_ - -#include "btree/depth_first_traversal.hpp" -#include "concurrency/interruptor.hpp" - -class concurrent_traversal_adapter_t; - -namespace profile { class trace_t; } - -class concurrent_traversal_fifo_enforcer_signal_t { -public: - // Do not use wait-interruptible and wait in the same traversal handler, - // or ordering will no longer be guaranteed. - void wait_interruptible() THROWS_ONLY(interrupted_exc_t); - void wait() THROWS_NOTHING; - -private: - friend class concurrent_traversal_adapter_t; - - void wait_with_interruptor(signal_t *interruptor) THROWS_ONLY(interrupted_exc_t); - concurrent_traversal_fifo_enforcer_signal_t(signal_t *eval_exclusivity_signal, - concurrent_traversal_adapter_t *parent); - - signal_t *const eval_exclusivity_signal_; - concurrent_traversal_adapter_t *const parent_; -}; - -class concurrent_traversal_callback_t { -public: - concurrent_traversal_callback_t() { } - - /* See `depth_first_traversal_callback_t` for an explanation of this method. Note - that we don't return a `continue_bool_t` here; that's because these are called out of - sync with respect to `handle_pair()`, so allowing `filter_range()` to abort the - traversal would be confusing. The other `depth_first_traversal_callback_t` methods - could be passed through, but we don't simply because there's no immediate use for - them. */ - virtual void filter_range( - UNUSED const btree_key_t *left_excl_or_null, - UNUSED const btree_key_t *right_incl, - bool *skip_out) { - *skip_out = false; - } - - // Passes a keyvalue and a callback. waiter.wait_interruptible() must be called to - // begin the region of "exclusive access", which only handle_pair implementation - // can enters at a time. (This should happen after loading the value from disk - // (which should be done concurrently) and before using ql::env_t to evaluate - // transforms and terminals, or whatever non-reentrant behavior you have in mind.) - virtual continue_bool_t handle_pair( - scoped_key_value_t &&keyvalue, - concurrent_traversal_fifo_enforcer_signal_t waiter) - THROWS_ONLY(interrupted_exc_t) = 0; - - virtual profile::trace_t *get_trace() THROWS_NOTHING { return nullptr; } - -protected: - virtual ~concurrent_traversal_callback_t() { } -private: - DISABLE_COPYING(concurrent_traversal_callback_t); -}; - -continue_bool_t btree_concurrent_traversal( - superblock_t *superblock, - const key_range_t &range, - concurrent_traversal_callback_t *cb, - direction_t direction, - release_superblock_t release_superblock); - -#endif // BTREE_CONCURRENT_TRAVERSAL_HPP_ diff --git a/src/btree/depth_first_traversal.cc b/src/btree/depth_first_traversal.cc deleted file mode 100644 index 260574e6193..00000000000 --- a/src/btree/depth_first_traversal.cc +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "btree/depth_first_traversal.hpp" - -#include "btree/internal_node.hpp" -#include "btree/leaf_node.hpp" -#include "btree/operations.hpp" -#include "concurrency/interruptor.hpp" -#include "rdb_protocol/profile.hpp" - -scoped_key_value_t::scoped_key_value_t(const btree_key_t *_key, - const void *_value, - movable_t &&buf) - : key_(_key), value_(_value), buf_(std::move(buf)) { - guarantee(buf_.has()); -} - -scoped_key_value_t::scoped_key_value_t(scoped_key_value_t &&movee) - : key_(movee.key_), - value_(movee.value_), - buf_(std::move(movee.buf_)) { - movee.key_ = nullptr; - movee.value_ = nullptr; -} - -scoped_key_value_t::~scoped_key_value_t() { } - -buf_parent_t scoped_key_value_t::expose_buf() { - guarantee(buf_.has()); - return buf_parent_t(&buf_->lock); -} - -// Releases the hold on the buf_lock_t, after which key(), value(), and expose_buf() -// may not be used. -void scoped_key_value_t::reset() { - buf_.reset(); -} - - -/* Returns `true` if we reached the end of the subtree or range, and `false` if -`cb->handle_value()` returned `false`. */ -continue_bool_t btree_depth_first_traversal( - counted_t block, - const key_range_t &range, - depth_first_traversal_callback_t *cb, - access_t access, - direction_t direction, - const btree_key_t *left_excl_or_null, - const btree_key_t *right_incl, - signal_t *interruptor); - -continue_bool_t btree_depth_first_traversal( - superblock_t *superblock, - const key_range_t &range, - depth_first_traversal_callback_t *cb, - access_t access, - direction_t direction, - release_superblock_t release_superblock, - signal_t *interruptor) { - if (range.is_empty()) { - return continue_bool_t::CONTINUE; - } - - const btree_key_t *left_excl_or_null; - store_key_t left_excl_buf(range.left); - if (left_excl_buf.decrement()) { - left_excl_or_null = left_excl_buf.btree_key(); - } else { - left_excl_or_null = nullptr; - } - store_key_t right_incl_buf; - if (range.right.unbounded) { - right_incl_buf = store_key_t::max(); - } else { - right_incl_buf = range.right.key(); - bool ok = right_incl_buf.decrement(); - guarantee(ok, "this is impossible because we checked range is not empty"); - } - - block_id_t root_block_id = superblock->get_root_block_id(); - if (root_block_id == NULL_BLOCK_ID) { - if (release_superblock == release_superblock_t::RELEASE) { - superblock->release(); - } - return cb->handle_empty(left_excl_or_null, right_incl_buf.btree_key(), - interruptor); - } else { - counted_t root_block; - { - // We know that `superblock` is already read-acquired because we call - // get_block_id() above -- so `starter` won't measure time waiting for - // the parent to become acquired. - PROFILE_STARTER_IF_ENABLED( - cb->get_trace() != nullptr, - "Acquire block for read.", - cb->get_trace()); - root_block = make_counted( - superblock->expose_buf(), root_block_id, access); - if (release_superblock == release_superblock_t::RELEASE) { - // Release the superblock ASAP because that's good. - superblock->release(); - } - // Wait for read acquisition of the root block, so that `starter`'s - // profiling information is correct. - wait_interruptible(root_block->lock.read_acq_signal(), interruptor); - } - - return btree_depth_first_traversal( - std::move(root_block), range, cb, access, direction, - left_excl_or_null, right_incl_buf.btree_key(), interruptor); - } -} - -void get_child_key_range( - const internal_node_t *inode, - int child_index, - /* `parent_*` describe the intersection of `inode`'s range and the range of keys - that the traversal is interested in */ - const btree_key_t *parent_left_excl_or_null, - const btree_key_t *parent_right_incl, - const btree_key_t **left_excl_or_null_out, - const btree_key_t **right_incl_out) { - const btree_internal_pair *pair = - internal_node::get_pair_by_index(inode, child_index); - if (child_index != inode->npairs - 1) { - rassert(child_index < inode->npairs - 1); - if (btree_key_cmp(&pair->key, parent_right_incl) < 0) { - *right_incl_out = &pair->key; - } else { - *right_incl_out = parent_right_incl; - } - } else { - *right_incl_out = parent_right_incl; - } - - if (child_index > 0) { - const btree_internal_pair *left_neighbor = - internal_node::get_pair_by_index(inode, child_index - 1); - if (parent_left_excl_or_null == nullptr || - btree_key_cmp(&left_neighbor->key, parent_left_excl_or_null) > 0) { - *left_excl_or_null_out = &left_neighbor->key; - } else { - *left_excl_or_null_out = parent_left_excl_or_null; - } - } else { - *left_excl_or_null_out = parent_left_excl_or_null; - } -} - -continue_bool_t btree_depth_first_traversal( - counted_t block, - const key_range_t &range, - depth_first_traversal_callback_t *cb, - access_t access, - direction_t direction, - const btree_key_t *left_excl_or_null, - const btree_key_t *right_incl, - signal_t *interruptor) { - bool skip; - if (continue_bool_t::ABORT == cb->filter_range_ts( - left_excl_or_null, right_incl, block->lock.get_recency(), interruptor, - &skip)) { - return continue_bool_t::ABORT; - } - if (skip) { - return continue_bool_t::CONTINUE; - } - block->read.init(new buf_read_t(&block->lock)); - const node_t *node = static_cast(block->read->get_data_read()); - if (node::is_internal(node)) { - if (continue_bool_t::ABORT == cb->handle_pre_internal( - block, left_excl_or_null, right_incl, interruptor)) { - return continue_bool_t::ABORT; - } - const internal_node_t *inode = reinterpret_cast(node); - int start_index = internal_node::get_offset_index(inode, range.left.btree_key()); - int end_index; - if (range.right.unbounded) { - end_index = inode->npairs; - } else { - store_key_t r = range.right.key(); - r.decrement(); - end_index = internal_node::get_offset_index(inode, r.btree_key()) + 1; - } - for (int i = 0; i < end_index - start_index; ++i) { - int true_index = (direction == FORWARD ? start_index + i : (end_index - 1) - i); - const btree_internal_pair *pair = internal_node::get_pair_by_index(inode, true_index); - - // Get the child key range - const btree_key_t *child_left_excl_or_null; - const btree_key_t *child_right_incl; - get_child_key_range(inode, true_index, - left_excl_or_null, right_incl, - &child_left_excl_or_null, &child_right_incl); - - if (continue_bool_t::ABORT == cb->filter_range( - child_left_excl_or_null, child_right_incl, interruptor, &skip)) { - return continue_bool_t::ABORT; - } - if (!skip) { - counted_t lock; - { - PROFILE_STARTER_IF_ENABLED( - cb->get_trace() != nullptr, - "Acquire block for read.", - cb->get_trace()); - lock = make_counted( - &block->lock, pair->lnode, access); - wait_interruptible(lock->lock.read_acq_signal(), interruptor); - } - if (continue_bool_t::ABORT == btree_depth_first_traversal( - std::move(lock), range, cb, access, direction, - child_left_excl_or_null, child_right_incl, interruptor)) { - return continue_bool_t::ABORT; - } - } - } - return continue_bool_t::CONTINUE; - } else { - if (continue_bool_t::ABORT == cb->handle_pre_leaf( - block, left_excl_or_null, right_incl, interruptor, &skip)) { - return continue_bool_t::ABORT; - } - if (skip) { - return continue_bool_t::CONTINUE; - } - - const leaf_node_t *lnode = reinterpret_cast(node); - const btree_key_t *key; - - if (direction == FORWARD) { - for (auto it = leaf::inclusive_lower_bound(range.left.btree_key(), *lnode); - it != leaf::end(*lnode); ++it) { - key = (*it).first; - // range.right is exclusive - if (!range.right.unbounded && - btree_key_cmp(key, range.right.key().btree_key()) >= 0) { - break; - } - if (continue_bool_t::ABORT == cb->handle_pair( - scoped_key_value_t( - key, (*it).second, - movable_t(block)), - interruptor)) { - return continue_bool_t::ABORT; - } - } - } else { - leaf_node_t::reverse_iterator it; - if (range.right.unbounded) { - it = leaf::rbegin(*lnode); - } else { - it = leaf::exclusive_upper_bound(range.right.key().btree_key(), *lnode); - } - for (/* assignment above */; it != leaf::rend(*lnode); ++it) { - key = (*it).first; - - // range.left is inclusive - if (btree_key_cmp(key, range.left.btree_key()) < 0) { - break; - } - - if (continue_bool_t::ABORT == cb->handle_pair( - scoped_key_value_t( - key, (*it).second, - movable_t(block)), - interruptor)) { - return continue_bool_t::ABORT; - } - } - } - return continue_bool_t::CONTINUE; - } -} - diff --git a/src/btree/depth_first_traversal.hpp b/src/btree/depth_first_traversal.hpp deleted file mode 100644 index baaf1b8bd21..00000000000 --- a/src/btree/depth_first_traversal.hpp +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef BTREE_DEPTH_FIRST_TRAVERSAL_HPP_ -#define BTREE_DEPTH_FIRST_TRAVERSAL_HPP_ - -#include "btree/keys.hpp" -#include "btree/types.hpp" -#include "buffer_cache/alt.hpp" -#include "containers/archive/archive.hpp" -#include "containers/counted.hpp" -#include "repli_timestamp.hpp" - -namespace profile { class trace_t; } - -class buf_parent_t; -class superblock_t; - -class counted_buf_lock_and_read_t : - public single_threaded_countable_t { -public: - template - explicit counted_buf_lock_and_read_t(Args &&... args) - : lock(std::forward(args)...) { } - buf_lock_t lock; - scoped_ptr_t read; -}; - -// A btree leaf key/value pair that also owns a reference to the buf_lock_t that -// contains said key/value pair. -class scoped_key_value_t { -public: - scoped_key_value_t(const btree_key_t *key, - const void *value, - movable_t &&buf); - scoped_key_value_t(scoped_key_value_t &&movee); - ~scoped_key_value_t(); - void operator=(scoped_key_value_t &&) = delete; - - const btree_key_t *key() const { - guarantee(buf_.has()); - return key_; - } - const void *value() const { - guarantee(buf_.has()); - return value_; - } - buf_parent_t expose_buf(); - - // Releases the hold on the buf_lock_t, after which key(), value(), and - // expose_buf() may not be used. - void reset(); - -private: - const btree_key_t *key_; - const void *value_; - movable_t buf_; - - DISABLE_COPYING(scoped_key_value_t); -}; - -class depth_first_traversal_callback_t { -public: - /* Any of these methods can abort the traversal by returning `ABORT`. In this case, - no further method calls will happen and `btree_depth_first_traversal()` will return - `ABORT`. */ - - /* Can be overloaded if you don't want to query a contiguous range of keys, but only - parts of it. Both `filter_range()` and `filter_range_ts()` will be called before - traversing into any child node; if either sets `*skip_out` to `true`, the range - will be ignored. */ - virtual continue_bool_t filter_range( - UNUSED const btree_key_t *left_excl_or_null, - UNUSED const btree_key_t *right_incl, - UNUSED signal_t *interruptor, - bool *skip_out) { - *skip_out = false; - return continue_bool_t::CONTINUE; - } - virtual continue_bool_t filter_range_ts( - UNUSED const btree_key_t *left_excl_or_null, - UNUSED const btree_key_t *right_incl, - UNUSED repli_timestamp_t timestamp, - UNUSED signal_t *interruptor, - bool *skip_out) { - *skip_out = false; - return continue_bool_t::CONTINUE; - } - - /* Called on every leaf node before the calls to `handle_pair()`. If it sets - `*skip_out` to `true`, the leaf will be ignored. */ - virtual continue_bool_t handle_pre_leaf( - UNUSED const counted_t &buf, - UNUSED const btree_key_t *left_excl_or_null, - UNUSED const btree_key_t *right_incl, - UNUSED signal_t *interruptor, - bool *skip_out) { - *skip_out = false; - return continue_bool_t::CONTINUE; - } - - /* Called on every internal node before recursing. */ - virtual continue_bool_t handle_pre_internal( - UNUSED const counted_t &buf, - UNUSED const btree_key_t *left_excl_or_null, - UNUSED const btree_key_t *right_incl, - UNUSED signal_t *interruptor) { - return continue_bool_t::CONTINUE; - } - - /* The guts of the callback; this handles the actual key-value pairs. */ - virtual continue_bool_t handle_pair( - scoped_key_value_t &&keyvalue, - signal_t *interruptor) = 0; - - /* Called if the B-tree is completely empty. */ - virtual continue_bool_t handle_empty( - UNUSED const btree_key_t *left_excl_or_null, - UNUSED const btree_key_t *right_incl, - UNUSED signal_t *interruptor) { - return continue_bool_t::CONTINUE; - } - - /* Note that the depth-first traversal proceeds in lexicographical order. - - If you were to collect all the calls to `handle_pre_leaf()`; calls to - `handle_empty()`; and calls to `filter_*` which set `*skip_out` to `true`, then the - resulting key ranges would be contiguous and non-overlapping, and they would together - cover the full range of the traversal. */ - - virtual profile::trace_t *get_trace() THROWS_NOTHING { return nullptr; } -protected: - virtual ~depth_first_traversal_callback_t() { } -}; - -enum direction_t { - FORWARD, - BACKWARD -}; - -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE(direction_t, int8_t, FORWARD, BACKWARD); - -/* Returns `CONTINUE` if we reached the end of the btree or range, and `ABORT` if -`cb->handle_value()` returned `ABORT`. */ -continue_bool_t btree_depth_first_traversal( - superblock_t *superblock, - const key_range_t &range, - depth_first_traversal_callback_t *cb, - - /* It's possible to run a depth-first traversal with write access to blocks. However, - it won't handle split/merge/level properly. Therefore, most B-tree operations are not - safe in a depth-first traversal in write mode. The only thing we actually use this - for is erasing old deletion entries, which can never trigger a merge/split/level. */ - access_t access, - - direction_t direction, - release_superblock_t release_superblock, - signal_t *interruptor); - -#endif /* BTREE_DEPTH_FIRST_TRAVERSAL_HPP_ */ diff --git a/src/btree/get_distribution.cc b/src/btree/get_distribution.cc deleted file mode 100644 index 15ce131c5c4..00000000000 --- a/src/btree/get_distribution.cc +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "btree/get_distribution.hpp" - -#include "btree/internal_node.hpp" -#include "btree/node.hpp" -#include "btree/leaf_node.hpp" -#include "btree/parallel_traversal.hpp" -#include "buffer_cache/alt.hpp" -#include "utils.hpp" - -class get_distribution_traversal_helper_t : public btree_traversal_helper_t, public home_thread_mixin_debug_only_t { -public: - get_distribution_traversal_helper_t(int _depth_limit, std::vector *_keys) - : depth_limit(_depth_limit), key_count(0), keys(_keys) - { } - - void read_stat_block(buf_lock_t *stat_block) { - guarantee (stat_block != nullptr); - buf_read_t read(stat_block); - uint32_t sb_size; - const btree_statblock_t *sb_data = - static_cast(read.get_data_read(&sb_size)); - guarantee(sb_size == BTREE_STATBLOCK_SIZE); - key_count = sb_data->population; - } - - // This is free to call mark_deleted. - void process_a_leaf(buf_lock_t *leaf_node_buf, - const btree_key_t *, - const btree_key_t *, - signal_t * /*interruptor*/, - int * /*population_change_out*/) THROWS_ONLY(interrupted_exc_t) { - buf_read_t read(leaf_node_buf); - const leaf_node_t *node - = static_cast(read.get_data_read()); - - for (auto it = leaf::begin(*node); it != leaf::end(*node); ++it) { - const btree_key_t *key = (*it).first; - keys->push_back(store_key_t(key->size, key->contents)); - } - } - - void postprocess_internal_node(buf_lock_t *internal_node_buf) { - buf_read_t read(internal_node_buf); - const internal_node_t *node - = static_cast(read.get_data_read()); - - /* Notice, we iterate all but the last pair because the last pair - * doesn't actually have a key and we're looking for the split points. - * */ - for (int i = 0; i < (node->npairs - 1); i++) { - const btree_internal_pair *pair = internal_node::get_pair_by_index(node, i); - keys->push_back(store_key_t(pair->key.size, pair->key.contents)); - } - } - - void filter_interesting_children(buf_parent_t, - ranged_block_ids_t *ids_source, - interesting_children_callback_t *cb) { - if (ids_source->get_level() < depth_limit) { - int num_block_ids = ids_source->num_block_ids(); - for (int i = 0; i < num_block_ids; ++i) { - block_id_t block_id; - const btree_key_t *left, *right; - ids_source->get_block_id_and_bounding_interval(i, &block_id, &left, &right); - - cb->receive_interesting_child(i); - } - } else { - //We're over the depth limit and thus disinterested in all children - } - - cb->no_more_interesting_children(); - } - - access_t btree_superblock_mode() { - return access_t::read; - } - - access_t btree_node_mode() { - return access_t::read; - } - - int depth_limit; - int64_t key_count; - - //TODO this is inefficient since each one is maximum size - std::vector *keys; -}; - -void get_btree_key_distribution(superblock_t *superblock, int depth_limit, - int64_t *key_count_out, - std::vector *keys_out) { - get_distribution_traversal_helper_t helper(depth_limit, keys_out); - rassert(keys_out->empty(), "Why is this output parameter not an empty vector\n"); - - cond_t non_interruptor; - btree_parallel_traversal(superblock, &helper, &non_interruptor); - *key_count_out = helper.key_count; -} diff --git a/src/btree/get_distribution.hpp b/src/btree/get_distribution.hpp deleted file mode 100644 index c4b22a793e6..00000000000 --- a/src/btree/get_distribution.hpp +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef BTREE_GET_DISTRIBUTION_HPP_ -#define BTREE_GET_DISTRIBUTION_HPP_ - -#include - -#include "btree/keys.hpp" -#include "buffer_cache/types.hpp" - -class superblock_t; - -void get_btree_key_distribution(superblock_t *superblock, int depth_limit, - int64_t *key_count_out, - std::vector *keys_out); - -#endif /* BTREE_GET_DISTRIBUTION_HPP_ */ diff --git a/src/btree/internal_node.cc b/src/btree/internal_node.cc deleted file mode 100644 index c9cd2464a36..00000000000 --- a/src/btree/internal_node.cc +++ /dev/null @@ -1,466 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "btree/internal_node.hpp" - -#include - -#include "btree/node.hpp" - -//In this tree, less than or equal takes the left-hand branch and greater than takes the right hand branch - -namespace internal_node { - -class ibuf_t; - -// We can't use "internal" for internal stuff obviously. -namespace impl { -size_t pair_size_with_key(const btree_key_t *key); -size_t pair_size_with_key_size(uint8_t size); - -void delete_pair(internal_node_t *node, uint16_t offset); -uint16_t insert_pair(internal_node_t *node, const btree_internal_pair *pair); -uint16_t insert_pair(internal_node_t *node, block_id_t lnode, const btree_key_t *key); -void delete_offset(internal_node_t *node, int index); -void insert_offset(internal_node_t *node, uint16_t offset, int index); -void make_last_pair_special(internal_node_t *node); -bool is_equal(const btree_key_t *key1, const btree_key_t *key2); -} // namespace impl - -void init(block_size_t block_size, internal_node_t *node) { - node->magic = internal_node_t::expected_magic; - node->npairs = 0; - node->frontmost_offset = block_size.value(); -} - -void init(block_size_t block_size, internal_node_t *node, const internal_node_t *lnode, const uint16_t *offsets, int numpairs) { - init(block_size, node); - rassert(get_pair_by_index(lnode, lnode->npairs-1)->key.size == 0); - for (int i = 0; i < numpairs; i++) { - node->pair_offsets[i] = impl::insert_pair(node, get_pair(lnode, offsets[i])); - } - node->npairs = numpairs; - std::sort(node->pair_offsets, node->pair_offsets+node->npairs-1, internal_key_comp(node)); - rassert(get_pair_by_index(node, node->npairs-1)->key.size == 0); -} - -block_id_t lookup(const internal_node_t *node, const btree_key_t *key) { - int index = get_offset_index(node, key); - return get_pair_by_index(node, index)->lnode; -} - -bool insert(internal_node_t *node, const btree_key_t *key, block_id_t lnode, block_id_t rnode) { - rassert(key->size <= MAX_KEY_SIZE, "key too large"); - if (is_full(node)) return false; - if (node->npairs == 0) { - btree_key_t special; - special.size = 0; - - const uint16_t special_offset = impl::insert_pair(node, rnode, &special); - impl::insert_offset(node, special_offset, 0); - } - - int index = get_offset_index(node, key); - rassert(!impl::is_equal(&get_pair_by_index(node, index)->key, key), - "tried to insert duplicate key into internal node!"); - const uint16_t offset = impl::insert_pair(node, lnode, key); - impl::insert_offset(node, offset, index); - - get_pair_by_index(node, index + 1)->lnode = rnode; - return true; -} - -bool remove(block_size_t block_size, internal_node_t *node, const btree_key_t *key) { - int index = get_offset_index(node, key); - impl::delete_pair(node, node->pair_offsets[index]); - impl::delete_offset(node, index); - - if (index == node->npairs) { - impl::make_last_pair_special(node); - } - - validate(block_size, node); - return true; -} - -void split(block_size_t block_size, internal_node_t *node, internal_node_t *rnode, btree_key_t *median) { - uint16_t total_pairs = block_size.value() - node->frontmost_offset; - uint16_t first_pairs = 0; - int index = 0; - while (first_pairs < total_pairs/2) { // finds the median index - first_pairs += pair_size(get_pair_by_index(node, index)); - index++; - } - int median_index = index; - - // Equality takes the left branch, so the median should be from this node. - const btree_key_t *median_key = &get_pair_by_index(node, median_index-1)->key; - keycpy(median, median_key); - - init(block_size, rnode, node, node->pair_offsets + median_index, node->npairs - median_index); - - // TODO: This is really slow because most pairs will likely be copied - // repeatedly. There should be a better way. - for (index = median_index; index < node->npairs; index++) { - impl::delete_pair(node, node->pair_offsets[index]); - } - - const uint16_t new_npairs = median_index; - node->npairs = new_npairs; - //make last pair special - impl::make_last_pair_special(node); - - validate(block_size, node); - validate(block_size, rnode); -} - -void merge(block_size_t block_size, const internal_node_t *node, internal_node_t *rnode, const internal_node_t *parent) { - validate(block_size, node); - validate(block_size, rnode); - // get the key in parent which points to node - const btree_key_t *key_from_parent = &get_pair_by_index(parent, get_offset_index(parent, &get_pair_by_index(node, 0)->key))->key; - - guarantee(sizeof(internal_node_t) + (node->npairs + rnode->npairs)*sizeof(*node->pair_offsets) + - (block_size.value() - node->frontmost_offset) + (block_size.value() - rnode->frontmost_offset) + key_from_parent->size < block_size.value(), - "internal nodes too full to merge"); - - memmove(rnode->pair_offsets + node->npairs, rnode->pair_offsets, rnode->npairs * sizeof(*rnode->pair_offsets)); - - for (int i = 0; i < node->npairs-1; i++) { // the last pair is special - const uint16_t new_offset = impl::insert_pair(rnode, get_pair_by_index(node, i)); - rnode->pair_offsets[i] = new_offset; - } - const uint16_t new_offset = impl::insert_pair(rnode, get_pair_by_index(node, node->npairs-1)->lnode, key_from_parent); - rnode->pair_offsets[node->npairs - 1] = new_offset; - - const uint16_t new_npairs = rnode->npairs + node->npairs; - rnode->npairs = new_npairs; - - validate(block_size, rnode); -} - -bool level(block_size_t block_size, internal_node_t *node, - internal_node_t *sibling, btree_key_t *replacement_key, - const internal_node_t *parent, - std::vector *moved_children_out) { - validate(block_size, node); - validate(block_size, sibling); - if (moved_children_out != nullptr) { - moved_children_out->reserve(sibling->npairs); - } - - if (nodecmp(node, sibling) < 0) { - const btree_key_t *key_from_parent = &get_pair_by_index(parent, get_offset_index(parent, &get_pair_by_index(node, 0)->key))->key; - if (sizeof(internal_node_t) + (node->npairs + 1) * sizeof(*node->pair_offsets) + impl::pair_size_with_key(key_from_parent) >= node->frontmost_offset) - return false; - uint16_t special_pair_offset = node->pair_offsets[node->npairs-1]; - block_id_t last_offset = get_pair(node, special_pair_offset)->lnode; - uint16_t new_pair_offset = impl::insert_pair(node, last_offset, key_from_parent); - node->pair_offsets[node->npairs - 1] = new_pair_offset; - - uint16_t new_npairs = node->npairs; - // This loop involves repeated memmoves (resulting in ~(n^2) work where n is - // the block size). There should be a way to drastically reduce the number - // and increase efficiency. - for (;;) { - const btree_internal_pair *pair_to_move = get_pair_by_index(sibling, 0); - uint16_t size_change = sizeof(*node->pair_offsets) + pair_size(pair_to_move); - if (new_npairs * sizeof(*node->pair_offsets) + (block_size.value() - node->frontmost_offset) + size_change >= sibling->npairs * sizeof(*sibling->pair_offsets) + (block_size.value() - sibling->frontmost_offset) - size_change) { - break; - } - - const uint16_t new_offset = impl::insert_pair(node, pair_to_move); - node->pair_offsets[new_npairs] = new_offset; - ++new_npairs; - if (moved_children_out != nullptr) { - moved_children_out->push_back(pair_to_move->lnode); - } - - impl::delete_pair(sibling, sibling->pair_offsets[0]); - impl::delete_offset(sibling, 0); - } - - const btree_internal_pair *pair_for_parent = get_pair_by_index(sibling, 0); - - node->pair_offsets[new_npairs] = special_pair_offset; - ++new_npairs; - - node->npairs = new_npairs; - - btree_internal_pair *special_pair = get_pair(node, special_pair_offset); - special_pair->lnode = pair_for_parent->lnode; - - keycpy(replacement_key, &pair_for_parent->key); - - if (moved_children_out != nullptr) { - moved_children_out->push_back(pair_for_parent->lnode); - } - impl::delete_pair(sibling, sibling->pair_offsets[0]); - impl::delete_offset(sibling, 0); - } else { - uint16_t offset; - const btree_key_t *key_from_parent = &get_pair_by_index(parent, get_offset_index(parent, &get_pair_by_index(sibling, 0)->key))->key; - if (sizeof(internal_node_t) + (node->npairs + 1) * sizeof(*node->pair_offsets) + impl::pair_size_with_key(key_from_parent) >= node->frontmost_offset) - return false; - block_id_t first_child = get_pair_by_index(sibling, sibling->npairs-1)->lnode; - offset = impl::insert_pair(node, first_child, key_from_parent); - impl::insert_offset(node, offset, 0); - if (moved_children_out != nullptr) { - moved_children_out->push_back(first_child); - } - impl::delete_pair(sibling, sibling->pair_offsets[sibling->npairs-1]); - impl::delete_offset(sibling, sibling->npairs-1); - - // This loop involves repeated memmoves. There should be a way to - // drastically reduce the number and increase efficiency. - for (;;) { - const btree_internal_pair *pair_to_move = get_pair_by_index(sibling, sibling->npairs-1); - uint16_t size_change = sizeof(*node->pair_offsets) + pair_size(pair_to_move); - if (node->npairs * sizeof(*node->pair_offsets) + (block_size.value() - node->frontmost_offset) + size_change >= sibling->npairs * sizeof(*sibling->pair_offsets) + (block_size.value() - sibling->frontmost_offset) - size_change) { - break; - } - - offset = impl::insert_pair(node, pair_to_move); - impl::insert_offset(node, offset, 0); - if (moved_children_out != nullptr) { - moved_children_out->push_back(pair_to_move->lnode); - } - - impl::delete_pair(sibling, sibling->pair_offsets[sibling->npairs-1]); - impl::delete_offset(sibling, sibling->npairs-1); - } - - keycpy(replacement_key, &get_pair_by_index(sibling, sibling->npairs-1)->key); - - impl::make_last_pair_special(sibling); - } - - validate(block_size, node); - validate(block_size, sibling); - guarantee(!change_unsafe(node), "level made internal node dangerously full"); - return true; -} - -int sibling(const internal_node_t *node, const btree_key_t *key, block_id_t *sib_id, store_key_t *key_in_middle_out) { - int index = get_offset_index(node, key); - const btree_internal_pair *sib_pair; - int cmp; - if (index > 0) { - sib_pair = get_pair_by_index(node, index-1); - key_in_middle_out->assign(&sib_pair->key); - cmp = 1; - } else { - sib_pair = get_pair_by_index(node, index+1); - key_in_middle_out->assign(&get_pair_by_index(node, index)->key); - cmp = -1; - } - - *sib_id = sib_pair->lnode; - return cmp; //equivalent to nodecmp(node, sibling) -} - - -/* `is_sorted()` returns true if the given range is sorted. */ - -template -bool is_sorted(ForwardIterator first, ForwardIterator last, - StrictWeakOrdering comp) { - for (ForwardIterator it = first; it + 1 < last; it++) { - if (!comp(*it, *(it+1))) - return false; - } - return true; -} - -void update_key(internal_node_t *node, const btree_key_t *key_to_replace, const btree_key_t *replacement_key) { - - const int index = get_offset_index(node, key_to_replace); - const block_id_t tmp_lnode = get_pair_by_index(node, index)->lnode; - impl::delete_pair(node, node->pair_offsets[index]); - - guarantee(sizeof(internal_node_t) + (node->npairs) * sizeof(*node->pair_offsets) + impl::pair_size_with_key(replacement_key) < node->frontmost_offset, - "cannot fit updated key in internal node"); - - const uint16_t new_offset = impl::insert_pair(node, tmp_lnode, replacement_key); - node->pair_offsets[index] = new_offset; - - rassert(is_sorted(node->pair_offsets, node->pair_offsets+node->npairs-1, internal_key_comp(node)), - "Invalid key given to update_key: offsets no longer in sorted order"); - rassert(get_pair_by_index(node, node->npairs-1)->key.size == 0); -} - -bool is_full(const internal_node_t *node) { - return sizeof(internal_node_t) + (node->npairs + 1) * sizeof(*node->pair_offsets) + impl::pair_size_with_key_size(MAX_KEY_SIZE) >= node->frontmost_offset; -} - -bool change_unsafe(const internal_node_t *node) { - return sizeof(internal_node_t) + node->npairs * sizeof(*node->pair_offsets) + MAX_KEY_SIZE >= node->frontmost_offset; -} - -void validate(DEBUG_VAR block_size_t block_size, DEBUG_VAR const internal_node_t *node) { -#ifndef NDEBUG - rassert(reinterpret_cast(&(node->pair_offsets[node->npairs])) <= reinterpret_cast(get_pair(node, node->frontmost_offset))); - rassert(node->frontmost_offset > 0); - rassert(node->frontmost_offset <= block_size.value()); - for (int i = 0; i < node->npairs; i++) { - rassert(node->pair_offsets[i] < block_size.value()); - rassert(node->pair_offsets[i] >= node->frontmost_offset); - } - rassert(is_sorted(node->pair_offsets, node->pair_offsets+node->npairs-1, internal_key_comp(node)), - "Offsets no longer in sorted order"); - rassert(get_pair_by_index(node, node->npairs-1)->key.size == 0); -#endif -} - -bool is_underfull(block_size_t block_size, const internal_node_t *node) { - return (sizeof(internal_node_t) + 1) / 2 + - node->npairs*sizeof(*node->pair_offsets) + - (block_size.value() - node->frontmost_offset) + - /* EPSILON TODO this epsilon is too high lower it*/ - INTERNAL_EPSILON * 2 < block_size.value() / 2; -} - -bool is_mergable(block_size_t block_size, const internal_node_t *node, const internal_node_t *sibling, const internal_node_t *parent) { - const btree_key_t *key_from_parent; - if (nodecmp(node, sibling) < 0) { - key_from_parent = &get_pair_by_index(parent, get_offset_index(parent, &get_pair_by_index(node, 0)->key))->key; - } else { - key_from_parent = &get_pair_by_index(parent, get_offset_index(parent, &get_pair_by_index(sibling, 0)->key))->key; - } - return sizeof(internal_node_t) + - (node->npairs + sibling->npairs + 1)*sizeof(*node->pair_offsets) + - (block_size.value() - node->frontmost_offset) + - (block_size.value() - sibling->frontmost_offset) + key_from_parent->size + - impl::pair_size_with_key_size(MAX_KEY_SIZE) + - INTERNAL_EPSILON < block_size.value(); // must still have enough room for an arbitrary key // TODO: we can't be tighter? -} - -bool is_doubleton(const internal_node_t *node) { - return node->npairs == 2; -} - -size_t pair_size(const btree_internal_pair *pair) { - return impl::pair_size_with_key_size(pair->key.size); -} - -const btree_internal_pair *get_pair(const internal_node_t *node, uint16_t offset) { - return reinterpret_cast(reinterpret_cast(node) + offset); -} - -btree_internal_pair *get_pair(internal_node_t *node, uint16_t offset) { - return reinterpret_cast(reinterpret_cast(node) + offset); -} - -const btree_internal_pair *get_pair_by_index(const internal_node_t *node, int index) { - return get_pair(node, node->pair_offsets[index]); -} -btree_internal_pair *get_pair_by_index(internal_node_t *node, int index) { - return get_pair(node, node->pair_offsets[index]); -} - -int get_offset_index(const internal_node_t *node, const btree_key_t *key) { - return std::lower_bound(node->pair_offsets, node->pair_offsets+node->npairs-1, (uint16_t) internal_key_comp::faux_offset, internal_key_comp(node, key)) - node->pair_offsets; -} - -int nodecmp(const internal_node_t *node1, const internal_node_t *node2) { - const btree_key_t *key1 = &get_pair_by_index(node1, 0)->key; - const btree_key_t *key2 = &get_pair_by_index(node2, 0)->key; - - return btree_key_cmp(key1, key2); -} - -namespace impl { - -size_t pair_size_with_key(const btree_key_t *key) { - return pair_size_with_key_size(key->size); -} - -size_t pair_size_with_key_size(uint8_t size) { - return offsetof(btree_internal_pair, key) + offsetof(btree_key_t, contents) + size; -} - -void delete_pair(internal_node_t *node, uint16_t offset) { - btree_internal_pair *pair_to_delete = get_pair(node, offset); - btree_internal_pair *front_pair = get_pair(node, node->frontmost_offset); - const size_t shift = pair_size(pair_to_delete); - const size_t size = offset - node->frontmost_offset; - - rassert(node->magic == internal_node_t::expected_magic); - memmove(reinterpret_cast(front_pair) + shift, front_pair, size); - rassert(node->magic == internal_node_t::expected_magic); - - - node->frontmost_offset = node->frontmost_offset + shift; - - scoped_array_t new_pair_offsets(node->npairs); - memcpy(new_pair_offsets.data(), node->pair_offsets, sizeof(uint16_t) * node->npairs); - - for (int i = 0; i < node->npairs; i++) { - if (new_pair_offsets[i] < offset) - new_pair_offsets[i] += shift; - } - - memcpy(node->pair_offsets, new_pair_offsets.data(), sizeof(uint16_t) * node->npairs); -} - -uint16_t insert_pair(internal_node_t *node, const btree_internal_pair *pair) { - const uint16_t frontmost_offset = node->frontmost_offset - pair_size(pair); - node->frontmost_offset = frontmost_offset; - - // insert contents - memcpy(get_pair(node, frontmost_offset), pair, pair_size(pair)); - return frontmost_offset; -} - -uint16_t insert_pair(internal_node_t *node, block_id_t lnode, const btree_key_t *key) { - const uint16_t frontmost_offset = node->frontmost_offset - pair_size_with_key(key); - node->frontmost_offset = frontmost_offset; - - btree_internal_pair *new_pair = get_pair(node, frontmost_offset); - - // Use a buffer to prepare the key/value pair which we can then use to generate a patch - scoped_array_t pair_buf(pair_size_with_key(key)); - btree_internal_pair *new_buf_pair = reinterpret_cast(pair_buf.data()); - - // insert contents - new_buf_pair->lnode = lnode; - keycpy(&new_buf_pair->key, key); - - // Patch the new pair into node_buf - memcpy(new_pair, new_buf_pair, pair_size_with_key(key)); - - return frontmost_offset; -} - -void delete_offset(internal_node_t *node, int index) { - uint16_t *pair_offsets = node->pair_offsets; - if (node->npairs > 1) { - memmove(pair_offsets + index, pair_offsets + index + 1, (node->npairs - index - 1) * sizeof(uint16_t)); - } - node->npairs -= 1; -} - -void insert_offset(internal_node_t *node, uint16_t offset, int index) { - uint16_t *pair_offsets = node->pair_offsets; - memmove(pair_offsets + index + 1, pair_offsets + index, (node->npairs - index) * sizeof(uint16_t)); - pair_offsets[index] = offset; - node->npairs += 1; -} - -void make_last_pair_special(internal_node_t *node) { - const int index = node->npairs - 1; - const uint16_t old_offset = node->pair_offsets[index]; - btree_key_t tmp; - tmp.size = 0; - const uint16_t new_offset = insert_pair(node, get_pair(node, old_offset)->lnode, &tmp); - node->pair_offsets[index] = new_offset; - delete_pair(node, old_offset); -} - - -bool is_equal(const btree_key_t *key1, const btree_key_t *key2) { - return btree_key_cmp(key1, key2) == 0; -} - -} // namespace impl - -} // namespace internal_node - diff --git a/src/btree/internal_node.hpp b/src/btree/internal_node.hpp deleted file mode 100644 index 2dba41f5377..00000000000 --- a/src/btree/internal_node.hpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef BTREE_INTERNAL_NODE_HPP_ -#define BTREE_INTERNAL_NODE_HPP_ - -#include - -#include "arch/compiler.hpp" -#include "btree/keys.hpp" -#include "serializer/types.hpp" -#include "utils.hpp" - -struct internal_node_t; - -// See internal_node_t in node.hpp - -/* EPSILON used to prevent split then merge */ -#define INTERNAL_EPSILON (sizeof(btree_key_t) + MAX_KEY_SIZE + sizeof(block_id_t)) - -//Note: This struct is stored directly on disk. Changing it invalidates old data. -ATTR_PACKED(struct btree_internal_pair { - block_id_t lnode; - btree_key_t key; -}); - - -class internal_key_comp; - -// In a perfect world, this namespace would be 'branch'. -namespace internal_node { - -void init(block_size_t block_size, internal_node_t *node); -void init(block_size_t block_size, internal_node_t *node, const internal_node_t *lnode, const uint16_t *offsets, int numpairs); - -block_id_t lookup(const internal_node_t *node, const btree_key_t *key); -bool insert(internal_node_t *node, const btree_key_t *key, block_id_t lnode, block_id_t rnode); -bool remove(block_size_t block_size, internal_node_t *node, const btree_key_t *key); -void split(block_size_t block_size, internal_node_t *node, internal_node_t *rnode, btree_key_t *median); -void merge(block_size_t block_size, const internal_node_t *node, internal_node_t *rnode, const internal_node_t *parent); -bool level(block_size_t block_size, internal_node_t *node, internal_node_t *sibling, - btree_key_t *replacement_key, const internal_node_t *parent, - std::vector *moved_children_out); -int sibling(const internal_node_t *node, const btree_key_t *key, block_id_t *sib_id, store_key_t *key_in_middle_out); -void update_key(internal_node_t *node, const btree_key_t *key_to_replace, const btree_key_t *replacement_key); -int nodecmp(const internal_node_t *node1, const internal_node_t *node2); -bool is_full(const internal_node_t *node); -bool is_underfull(block_size_t block_size, const internal_node_t *node); -bool change_unsafe(const internal_node_t *node); -bool is_mergable(block_size_t block_size, const internal_node_t *node, const internal_node_t *sibling, const internal_node_t *parent); -bool is_doubleton(const internal_node_t *node); - -void validate(block_size_t block_size, const internal_node_t *node); - -size_t pair_size(const btree_internal_pair *pair); -const btree_internal_pair *get_pair(const internal_node_t *node, uint16_t offset); -btree_internal_pair *get_pair(internal_node_t *node, uint16_t offset); - -const btree_internal_pair *get_pair_by_index(const internal_node_t *node, int index); -btree_internal_pair *get_pair_by_index(internal_node_t *node, int index); - -int get_offset_index(const internal_node_t *node, const btree_key_t *key); - -} // namespace internal_node - -class internal_key_comp { - const internal_node_t *node; - const btree_key_t *key; -public: - enum { faux_offset = 0 }; - - explicit internal_key_comp(const internal_node_t *_node) : node(_node), key(nullptr) { } - internal_key_comp(const internal_node_t *_node, const btree_key_t *_key) : node(_node), key(_key) { } - bool operator()(const uint16_t offset1, const uint16_t offset2) { - const btree_key_t *key1 = offset1 == faux_offset ? key : &internal_node::get_pair(node, offset1)->key; - const btree_key_t *key2 = offset2 == faux_offset ? key : &internal_node::get_pair(node, offset2)->key; - return compare(key1, key2) < 0; - } - static int compare(const btree_key_t *key1, const btree_key_t *key2) { - return btree_key_cmp(key1, key2); - } -}; - - - -#endif // BTREE_INTERNAL_NODE_HPP_ diff --git a/src/btree/keys.cc b/src/btree/keys.cc deleted file mode 100644 index 5b0c6fc4bf8..00000000000 --- a/src/btree/keys.cc +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "btree/keys.hpp" - -#include - -#include "debug.hpp" -#include "math.hpp" -#include "utils.hpp" - -std::string key_range_t::print() const { - printf_buffer_t buf; - debug_print(&buf, *this); - return buf.c_str(); -} - -// fast-ish non-null terminated string comparison -int sized_strcmp(const uint8_t *str1, int len1, const uint8_t *str2, int len2) { - int min_len = std::min(len1, len2); - int res = memcmp(str1, str2, min_len); - if (res == 0) { - res = len1 - len2; - } - return res; -} - -bool unescaped_str_to_key(const char *str, int len, store_key_t *buf) { - if (len <= MAX_KEY_SIZE) { - memcpy(buf->contents(), str, len); - buf->set_size(uint8_t(len)); - return true; - } else { - return false; - } -} - -std::string key_to_unescaped_str(const store_key_t &key) { - return std::string(reinterpret_cast(key.contents()), key.size()); -} - -std::string key_to_debug_str(const store_key_t &key) { - std::string s; - s.push_back('"'); - for (int i = 0; i < key.size(); i++) { - uint8_t c = key.contents()[i]; - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') { - s.push_back(c); - } else { - s.push_back('\\'); - s.push_back('x'); - s.push_back(int_to_hex((c & 0xf0) >> 4)); - s.push_back(int_to_hex(c & 0x0f)); - } - } - s.push_back('"'); - return s; -} - -std::string key_to_debug_str(const btree_key_t *key) { - return key_to_debug_str(store_key_t(key)); -} - -key_range_t::key_range_t() : - left(), right(store_key_t()) { } - -key_range_t::key_range_t(bound_t lm, const store_key_t& l, bound_t rm, const store_key_t& r) { - init(lm, l.btree_key(), rm, r.btree_key()); -} - -key_range_t::key_range_t(bound_t lm, const btree_key_t *l, bound_t rm, const btree_key_t *r) { - init(lm, l, rm, r); -} - -void key_range_t::init(bound_t lm, const btree_key_t *l, bound_t rm, const btree_key_t *r) { - switch (lm) { - case bound_t::closed: - left.assign(l); - break; - case bound_t::open: - left.assign(l); - if (left.increment()) { - break; - } else { - rassert(rm == bound_t::none); - /* Our left bound is the largest possible key, and we are open - on the left-hand side. So we are empty. */ - *this = key_range_t::empty(); - return; - } - case bound_t::none: - left = store_key_t::min(); - break; - default: - unreachable(); - } - - switch (rm) { - case bound_t::closed: { - right.unbounded = false; - right.key().assign(r); - bool ok = right.increment(); - guarantee(ok); - break; - } - case bound_t::open: - right.unbounded = false; - right.key().assign(r); - break; - case bound_t::none: - right.unbounded = true; - break; - default: - unreachable(); - } - - rassert(right.unbounded || left <= right.key(), - "left_key(%d)=%s, right_key(%d)=%s", - left.size(), - key_to_debug_str(left).c_str(), - right.internal_key.size(), - key_to_debug_str(right.internal_key).c_str()); -} - -bool key_range_t::is_superset(const key_range_t &other) const { - /* Special-case empty ranges */ - if (other.is_empty()) return true; - if (left > other.left) return false; - if (right < other.right) return false; - return true; -} - -bool key_range_t::overlaps(const key_range_t &other) const { - return key_range_t::right_bound_t(left) < other.right && - key_range_t::right_bound_t(other.left) < right && - !is_empty() && !other.is_empty(); -} - -key_range_t key_range_t::intersection(const key_range_t &other) const { - if (!overlaps(other)) { - return key_range_t::empty(); - } - key_range_t ixn; - ixn.left = left < other.left ? other.left : left; - ixn.right = right > other.right ? other.right : right; - return ixn; -} - -void debug_print(printf_buffer_t *buf, const btree_key_t *k) { - if (k != nullptr) { - debug_print_quoted_string(buf, k->contents, k->size); - } else { - buf->appendf("NULL"); - } -} - -void debug_print(printf_buffer_t *buf, const store_key_t &k) { - if (k == store_key_max) { - buf->appendf("MAX_KEY"); - } else { - debug_print(buf, k.btree_key()); - } -} - -void debug_print(printf_buffer_t *buf, const key_range_t::right_bound_t &rb) { - if (rb.unbounded) { - buf->appendf("+inf"); - } else { - debug_print(buf, rb.key()); - } -} - -void debug_print(printf_buffer_t *buf, const key_range_t &kr) { - buf->appendf("["); - debug_print(buf, kr.left); - buf->appendf(", "); - debug_print(buf, kr.right); - buf->appendf(")"); -} - -std::string key_range_to_string(const key_range_t &kr) { - std::string res; - res += "["; - res += key_to_debug_str(kr.left); - res += ", "; - if (kr.right.unbounded) { - res += "+inf"; - } else { - res += key_to_debug_str(kr.right.key()); - } - res += ")"; - return res; -} - -void debug_print(printf_buffer_t *buf, const store_key_t *k) { - if (k) { - debug_print(buf, *k); - } else { - buf->appendf("NULL"); - } -} - - -bool operator==(const key_range_t::right_bound_t &a, const key_range_t::right_bound_t &b) { - return (a.unbounded && b.unbounded) - || (!a.unbounded && !b.unbounded && a.key() == b.key()); -} -bool operator!=(const key_range_t::right_bound_t &a, const key_range_t::right_bound_t &b) { - return !(a == b); -} -bool operator<(const key_range_t::right_bound_t &a, const key_range_t::right_bound_t &b) { - if (a.unbounded) return false; - if (b.unbounded) return true; - return a.key() < b.key(); -} -bool operator<=(const key_range_t::right_bound_t &a, const key_range_t::right_bound_t &b) { - return a == b || a < b; -} -bool operator>(const key_range_t::right_bound_t &a, const key_range_t::right_bound_t &b) { - return b < a; -} -bool operator>=(const key_range_t::right_bound_t &a, const key_range_t::right_bound_t &b) { - return b <= a; -} - -bool operator==(key_range_t a, key_range_t b) THROWS_NOTHING { - return a.left == b.left && a.right == b.right; -} - -bool operator!=(key_range_t a, key_range_t b) THROWS_NOTHING { - return !(a == b); -} - -bool operator<(const key_range_t &a, const key_range_t &b) THROWS_NOTHING { - return (a.left < b.left || (a.left == b.left && a.right < b.right)); -} - -RDB_IMPL_SERIALIZABLE_2_SINCE_v1_13(key_range_t::right_bound_t, unbounded, internal_key); -RDB_IMPL_SERIALIZABLE_2_SINCE_v1_13(key_range_t, left, right); - -void serialize_for_metainfo(write_message_t *wm, const key_range_t &kr) { - kr.left.serialize_for_metainfo(wm); - serialize_universal(wm, kr.right.unbounded); - kr.right.internal_key.serialize_for_metainfo(wm); -} -archive_result_t deserialize_for_metainfo(read_stream_t *s, key_range_t *out) { - archive_result_t res = out->left.deserialize_for_metainfo(s); - if (bad(res)) { return res; } - res = deserialize_universal(s, &out->right.unbounded); - if (bad(res)) { return res; } - return out->right.internal_key.deserialize_for_metainfo(s); -} diff --git a/src/btree/keys.hpp b/src/btree/keys.hpp deleted file mode 100644 index bdf634f0fe5..00000000000 --- a/src/btree/keys.hpp +++ /dev/null @@ -1,390 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef BTREE_KEYS_HPP_ -#define BTREE_KEYS_HPP_ - -#include -#include -#include - -#include - -#include "arch/compiler.hpp" -#include "config/args.hpp" -#include "containers/archive/archive.hpp" -#include "rpc/serialize_macros.hpp" - -#if defined(__GNUC__) && (100 * __GNUC__ + __GNUC_MINOR__ >= 406) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warray-bounds" -#endif - -enum class sorting_t { - UNORDERED, - ASCENDING, - DESCENDING -}; -// UNORDERED sortings aren't reversed -bool reversed(sorting_t sorting); - -template -bool is_better(const T &a, const T &b, sorting_t sorting) { - if (!reversed(sorting)) { - return a < b; - } else { - return b < a; - } -} - -// Fast string compare -int sized_strcmp(const uint8_t *str1, int len1, const uint8_t *str2, int len2); - -// Note: Changing this struct changes the format of the data stored on disk. -// If you change this struct, previous stored data will be misinterpreted. -ATTR_PACKED(struct btree_key_t { - uint8_t size; - uint8_t contents[]; - uint16_t full_size() const { - return size + offsetof(btree_key_t, contents); - } -}); - -inline int btree_key_cmp(const btree_key_t *left, const btree_key_t *right) { - return sized_strcmp(left->contents, left->size, right->contents, right->size); -} - -struct store_key_t { -public: - store_key_t() { - set_size(0); - } - - store_key_t(int sz, const uint8_t *buf) { - assign(sz, buf); - } - - store_key_t(const store_key_t &_key) { - assign(_key.size(), _key.contents()); - } - - store_key_t &operator=(const store_key_t &_key) { - assign(_key.size(), _key.contents()); - return *this; - } - - explicit store_key_t(const btree_key_t *key) { - assign(key->size, key->contents); - } - - explicit store_key_t(const std::string &s) { - assign(s.size(), reinterpret_cast(s.data())); - } - - btree_key_t *btree_key() { return reinterpret_cast(buffer); } - const btree_key_t *btree_key() const { - return reinterpret_cast(buffer); - } - void set_size(int s) { - rassert(s <= MAX_KEY_SIZE); - btree_key()->size = s; - } - int size() const { return btree_key()->size; } - uint8_t *contents() { return btree_key()->contents; } - const uint8_t *contents() const { return btree_key()->contents; } - - void assign(int sz, const uint8_t *buf) { - set_size(sz); - memcpy(contents(), buf, sz); - } - - void assign(const btree_key_t *key) { - assign(key->size, key->contents); - } - - static store_key_t min() { - return store_key_t(); - } - - static store_key_t max() { - uint8_t buf[MAX_KEY_SIZE]; - for (int i = 0; i < MAX_KEY_SIZE; i++) { - buf[i] = 255; - } - return store_key_t(MAX_KEY_SIZE, buf); - } - - bool increment() { - if (size() < MAX_KEY_SIZE) { - contents()[size()] = 0; - set_size(size() + 1); - return true; - } - while (size() > 0 && contents()[size()-1] == 255) { - set_size(size() - 1); - } - if (size() == 0) { - /* We were the largest possible key. Oops. Restore our previous - state and return `false`. */ - *this = store_key_t::max(); - return false; - } - (reinterpret_cast(contents()))[size()-1]++; - return true; - } - - bool decrement() { - if (size() == 0) { - return false; - } else if ((reinterpret_cast(contents()))[size()-1] > 0) { - (reinterpret_cast(contents()))[size()-1]--; - for (int i = size(); i < MAX_KEY_SIZE; i++) { - contents()[i] = 255; - } - set_size(MAX_KEY_SIZE); - return true; - } else { - set_size(size() - 1); - return true; - } - } - - int compare(const store_key_t& k) const { - return sized_strcmp(contents(), size(), k.contents(), k.size()); - } - - // The wire format of serialize_for_metainfo must not change. - void serialize_for_metainfo(write_message_t *wm) const { - uint8_t sz = size(); - serialize_universal(wm, sz); - wm->append(contents(), sz); - } - - archive_result_t deserialize_for_metainfo(read_stream_t *s) { - uint8_t sz; - archive_result_t res = deserialize_universal(s, &sz); - if (bad(res)) { return res; } - int64_t num_read = force_read(s, contents(), sz); - if (num_read == -1) { - return archive_result_t::SOCK_ERROR; - } - if (num_read < sz) { - return archive_result_t::SOCK_EOF; - } - rassert(num_read == sz); - set_size(sz); - return archive_result_t::SUCCESS; - } - - template - friend void serialize(write_message_t *wm, const store_key_t &sk) { - sk.serialize_for_metainfo(wm); - } - - template - friend archive_result_t deserialize(read_stream_t *s, store_key_t *sk) { - return sk->deserialize_for_metainfo(s); - } - -private: - char buffer[sizeof(btree_key_t) + MAX_KEY_SIZE]; -}; - -static const store_key_t store_key_max = store_key_t::max(); -static const store_key_t store_key_min = store_key_t::min(); - -inline bool operator==(const store_key_t &k1, const store_key_t &k2) { - return k1.size() == k2.size() && memcmp(k1.contents(), k2.contents(), k1.size()) == 0; -} - -inline bool operator!=(const store_key_t &k1, const store_key_t &k2) { - return !(k1 == k2); -} - -inline bool operator<(const store_key_t &k1, const store_key_t &k2) { - return k1.compare(k2) < 0; -} - -inline bool operator>(const store_key_t &k1, const store_key_t &k2) { - return k2 < k1; -} - -inline bool operator<=(const store_key_t &k1, const store_key_t &k2) { - return k1.compare(k2) <= 0; -} - -inline bool operator>=(const store_key_t &k1, const store_key_t &k2) { - return k2 <= k1; -} - -bool unescaped_str_to_key(const char *str, int len, store_key_t *buf); -std::string key_to_unescaped_str(const store_key_t &key); - -std::string key_to_debug_str(const store_key_t &key); - -std::string key_to_debug_str(const btree_key_t *key); - -/* `key_range_t` represents a contiguous set of keys. */ -class key_range_t { -public: - /* If `right.unbounded`, then the range contains all keys greater than or - equal to `left`. If `right.bounded`, then the range contains all keys - greater than or equal to `left` and less than `right.key`. */ - struct right_bound_t { - right_bound_t() : unbounded(true) { } - - explicit right_bound_t(store_key_t k) : unbounded(false), internal_key(k) { } - static right_bound_t make_unbounded() { - right_bound_t rb; - rb.unbounded = true; - return rb; - } - - bool increment() { - if (unbounded) { - return false; - } else { - if (!internal_key.increment()) { - unbounded = true; - } - return true; - } - } - - store_key_t &key() { - rassert(!unbounded); - return internal_key; - } - const store_key_t &key() const { - rassert(!unbounded); - return internal_key; - } - const store_key_t &key_or_max() const { - return unbounded ? store_key_max : internal_key; - } - - bool unbounded; - - /* This is meaningless if `unbounded` is true. Usually you should call `key()` - instead of accessing this directly. */ - store_key_t internal_key; - }; - - const store_key_t &right_or_max() const { - return right.key_or_max(); - } - - // This enum class / static constexpr stuff is to silence a warning that "open" - // shadows a global variable named open. This used to be "enum bound_t". Removing - // the static constexpr definitions would be acceptable. - enum class bound_t { - open, - closed, - none - }; - - static constexpr bound_t open = bound_t::open; - static constexpr bound_t closed = bound_t::closed; - static constexpr bound_t none = bound_t::none; - - key_range_t(); /* creates a range containing no keys */ - key_range_t(bound_t lm, const store_key_t &l, - bound_t rm, const store_key_t &r); - key_range_t(bound_t lm, const btree_key_t *l, - bound_t rm, const btree_key_t *r); - - template - static key_range_t one_key(const T &key) { - return key_range_t(closed, key, closed, key); - } - - static key_range_t empty() THROWS_NOTHING { - return key_range_t(); - } - - static key_range_t universe() THROWS_NOTHING { - store_key_t k; - return key_range_t(key_range_t::none, k, key_range_t::none, k); - } - - static key_range_t with_prefix(const store_key_t &prefix) THROWS_NOTHING { - if (prefix.size() == 0) { - return key_range_t::universe(); - } else { - store_key_t right = prefix; - right.set_size(MAX_KEY_SIZE); - for (size_t i = prefix.size(); i < MAX_KEY_SIZE; ++i) { - right.contents()[i] = 0xFF; - } - return key_range_t(closed, prefix, closed, right); - } - } - - bool is_empty() const { - if (right.unbounded) { - return false; - } else { - rassert(left <= right.key()); - return left == right.key(); - } - } - - // TODO: rename these all to `contains` for consistency with other classes. - bool contains_key(const store_key_t& key) const { - bool left_ok = left <= key; - bool right_ok = right.unbounded || key < right.key(); - return left_ok && right_ok; - } - - bool contains_key(const uint8_t *key, uint8_t size) const { - bool left_ok = sized_strcmp(left.contents(), left.size(), key, size) <= 0; - bool right_ok = right.unbounded || - sized_strcmp(key, size, right.key().contents(), right.key().size()) < 0; - return left_ok && right_ok; - } - - bool contains_key(const btree_key_t *key) const { - return contains_key(key->contents, key->size); - } - - std::string print() const; - bool is_superset(const key_range_t &other) const; - bool overlaps(const key_range_t &other) const; - key_range_t intersection(const key_range_t &other) const; - - store_key_t left; - right_bound_t right; - -private: - void init(bound_t lm, const btree_key_t *l, - bound_t rm, const btree_key_t *r); -}; - -RDB_DECLARE_SERIALIZABLE(key_range_t::right_bound_t); -RDB_DECLARE_SERIALIZABLE(key_range_t); - -// Serialization with stable wire format for metainfo blob. -void serialize_for_metainfo(write_message_t *wm, const key_range_t &kr); -MUST_USE archive_result_t deserialize_for_metainfo(read_stream_t *s, key_range_t *out); - -void debug_print(printf_buffer_t *buf, const btree_key_t *k); -void debug_print(printf_buffer_t *buf, const store_key_t &k); -void debug_print(printf_buffer_t *buf, const store_key_t *k); -void debug_print(printf_buffer_t *buf, const key_range_t::right_bound_t &rb); -void debug_print(printf_buffer_t *buf, const key_range_t &kr); -std::string key_range_to_string(const key_range_t &kr); - -bool operator==(const key_range_t::right_bound_t &a, const key_range_t::right_bound_t &b); -bool operator!=(const key_range_t::right_bound_t &a, const key_range_t::right_bound_t &b); -bool operator<(const key_range_t::right_bound_t &a, const key_range_t::right_bound_t &b); -bool operator<=(const key_range_t::right_bound_t &a, const key_range_t::right_bound_t &b); -bool operator>(const key_range_t::right_bound_t &a, const key_range_t::right_bound_t &b); -bool operator>=(const key_range_t::right_bound_t &a, const key_range_t::right_bound_t &b); - -bool operator==(key_range_t, key_range_t) THROWS_NOTHING; -bool operator!=(key_range_t, key_range_t) THROWS_NOTHING; -bool operator<(const key_range_t &, const key_range_t &) THROWS_NOTHING; - -#if defined(__GNUC__) && (100 * __GNUC__ + __GNUC_MINOR__ >= 406) -#pragma GCC diagnostic pop -#endif - -#endif // BTREE_KEYS_HPP_ diff --git a/src/btree/leaf_node.cc b/src/btree/leaf_node.cc deleted file mode 100644 index ffe7609a6d7..00000000000 --- a/src/btree/leaf_node.cc +++ /dev/null @@ -1,1822 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "btree/leaf_node.hpp" - -#include - -#include -#include - -#include "btree/node.hpp" -#include "repli_timestamp.hpp" -#include "utils.hpp" - -namespace leaf { - - -// A leaf node is a set of key/value, key/value/modification_time, and -// key/deletion_time tuples with distinct keys, where modification -// time or deletion time is omitted for sufficiently old entries. A -// key/deletion_time entry says that the key was recently deleted. -// -// In particular, all the modification times and deletions after a -// certain time are recorded, and those before a certain time are -// omitted. You'll note that we don't have deletion entries that omit -// a timestamp. - -// Leaf nodes support efficient key lookup and efficient key-order -// iteration, and efficient iteration in order of descending -// modification time. The details of implementation are described -// later. - - -// These codes can appear as the first byte of a leaf node entry (see -// below) (values 250 or smaller are just key sizes for key/value -// pairs). - -// Means we have a deletion entry. -const int DELETE_ENTRY_CODE = 255; - -// Means we have a skipped entry exactly one byte long. -const int SKIP_ENTRY_CODE_ONE = 254; - -// Means we have a skipped entry exactly two bytes long. -const int SKIP_ENTRY_CODE_TWO = 253; - -// Means we have a skipped entry exactly N bytes long, of form { uint8_t 252; uint16_t N; char garbage[]; } -const int SKIP_ENTRY_CODE_MANY = 252; - -// A reserved meaningless value. -const int SKIP_ENTRY_RESERVED = 251; - - - - - -// Entries are contiguously connected to the end of a btree -// block. Here's what a full leaf node looks like. -// -// [magic][num_pairs][live_size][frontmost][tstamp_cutpoint][off0][off1][off2]...[offN-1]........[tstamp][entry][tstamp][entry][tstamp][entry][tstamp][entry][entry][entry][entry][entry][entry][entry] -// \___________________________/ ^ ^ ^ -// N = num_pairs frontmost tstamp_cutpoint (block size) -// -// [tstamp] in [tstamp][entry] pairs are non-increasing (when you look at them -// from frontmost to tstamp_cutpoint). This is true even of skip entries. -// -// Here's what an "[entry]" may look like: -// -// [btree key][btree value] -- a live entry -// [255][btree key] -- a deletion entry -// [254] -- a skip entry of size one -// [253][byte] -- a skip entry of size two -// [252][uint16_t sz][byte][byte]...[byte] -- a skip entry of size "sz + 3" -// \___________________/ -// sz bytes -// -// The extra byte(s) in a skip entry are filled with `SKIP_ENTRY_RESERVED`. -// -// The reason why we special-case skip entries that are only one or two bytes -// long is that we the header we use for a skip entry of size three or more is -// itself three bytes, so it can't fit in a slot of size one or two. We don't -// expect to actually see many entries of size one or two, but it pays to be -// thorough. - - -struct entry_t; -struct value_t; - -bool entry_is_deletion(const entry_t *p) { - uint8_t x = *reinterpret_cast(p); - rassert(x != SKIP_ENTRY_RESERVED); - return x == DELETE_ENTRY_CODE; -} - -bool entry_is_live(const entry_t *p) { - uint8_t x = *reinterpret_cast(p); - rassert(x != SKIP_ENTRY_RESERVED); - rassert(MAX_KEY_SIZE == 250); - return x <= MAX_KEY_SIZE; -} - -bool entry_is_skip(const entry_t *p) { - return !entry_is_deletion(p) && !entry_is_live(p); -} - -const btree_key_t *entry_key(const entry_t *p) { - if (entry_is_deletion(p)) { - return reinterpret_cast(1 + reinterpret_cast(p)); - } else { - return reinterpret_cast(p); - } -} - -const void *entry_value(const entry_t *p) { - if (entry_is_deletion(p)) { - return nullptr; - } else { - return reinterpret_cast(p) + entry_key(p)->full_size(); - } -} - -int entry_size(value_sizer_t *sizer, const entry_t *p) { - uint8_t code = *reinterpret_cast(p); - switch (code) { - case DELETE_ENTRY_CODE: - return 1 + entry_key(p)->full_size(); - case SKIP_ENTRY_CODE_ONE: - return 1; - case SKIP_ENTRY_CODE_TWO: - return 2; - case SKIP_ENTRY_CODE_MANY: - return 3 + *reinterpret_cast(1 + reinterpret_cast(p)); - default: - rassert(code <= MAX_KEY_SIZE); - return entry_key(p)->full_size() + sizer->size(entry_value(p)); - } -} - -const entry_t *get_entry(const leaf_node_t *node, int offset) { - return reinterpret_cast(reinterpret_cast(node) + offset + (offset < node->tstamp_cutpoint ? sizeof(repli_timestamp_t) : 0)); -} - -entry_t *get_entry(leaf_node_t *node, int offset) { - return reinterpret_cast(reinterpret_cast(node) + offset + (offset < node->tstamp_cutpoint ? sizeof(repli_timestamp_t) : 0)); -} - -char *get_at_offset(leaf_node_t *node, int offset) { - return reinterpret_cast(node) + offset; -} - -repli_timestamp_t get_timestamp(const leaf_node_t *node, int offset) { - return *reinterpret_cast(reinterpret_cast(node) + offset); -} - -struct entry_iter_t { - int offset; - - void step(value_sizer_t *sizer, const leaf_node_t *node) { - rassert(!done(sizer)); - - offset += entry_size(sizer, get_entry(node, offset)) + (offset < node->tstamp_cutpoint ? sizeof(repli_timestamp_t) : 0); - } - - bool done(value_sizer_t *sizer) const { - int bs = sizer->block_size().value(); - guarantee(offset <= bs, "offset=%d, bs=%d", offset, bs); - return offset == bs; - } - - static entry_iter_t make(const leaf_node_t *node) { - entry_iter_t ret; - ret.offset = node->frontmost; - return ret; - } -}; - -void strprint_entry(std::string *out, value_sizer_t *sizer, const entry_t *entry) { - if (entry_is_live(entry)) { - const btree_key_t *key = entry_key(entry); - *out += strprintf("%.*s:", static_cast(key->size), key->contents); - *out += strprintf("[entry size=%d]", entry_size(sizer, entry)); - *out += strprintf("[value size=%d]", sizer->size(entry_value(entry))); - } else if (entry_is_deletion(entry)) { - const btree_key_t *key = entry_key(entry); - *out += strprintf("%.*s:[deletion]", static_cast(key->size), key->contents); - } else if (entry_is_skip(entry)) { - *out += strprintf("[skip %d]", entry_size(sizer, entry)); - } else { - *out += strprintf("[code %d]", *reinterpret_cast(entry)); - } -} - - -std::string strprint_leaf(value_sizer_t *sizer, const leaf_node_t *node) { - std::string out; - out += strprintf("Leaf(magic='%4.4s', num_pairs=%u, live_size=%u, frontmost=%u, tstamp_cutpoint=%u)\n", - node->magic.bytes, node->num_pairs, node->live_size, node->frontmost, node->tstamp_cutpoint); - - out += strprintf(" Offsets:"); - for (int i = 0; i < node->num_pairs; ++i) { - out += strprintf(" %d", node->pair_offsets[i]); - } - out += strprintf("\n"); - - out += strprintf(" By Key:"); - for (int i = 0; i < node->num_pairs; ++i) { - out += strprintf(" %d:", node->pair_offsets[i]); - strprint_entry(&out, sizer, get_entry(node, node->pair_offsets[i])); - } - out += strprintf("\n"); - - out += strprintf(" By Offset:"); - - entry_iter_t iter = entry_iter_t::make(node); - while (out += strprintf(" %d", iter.offset), !iter.done(sizer)) { - out += strprintf(":"); - if (iter.offset < node->tstamp_cutpoint) { - repli_timestamp_t tstamp = get_timestamp(node, iter.offset); - out += strprintf("[t=%" PRIu64 "]", tstamp.longtime); - } - strprint_entry(&out, sizer, get_entry(node, iter.offset)); - iter.step(sizer, node); - } - out += strprintf("\n"); - - return out; -} - - -void print_entry(FILE *fp, value_sizer_t *sizer, const entry_t *entry) { - if (entry_is_live(entry)) { - const btree_key_t *key = entry_key(entry); - fprintf(fp, "%.*s:", static_cast(key->size), key->contents); - fprintf(fp, "[entry size=%d]", entry_size(sizer, entry)); - fprintf(fp, "[value size=%d]", sizer->size(entry_value(entry))); - } else if (entry_is_deletion(entry)) { - const btree_key_t *key = entry_key(entry); - fprintf(fp, "%.*s:[deletion]", static_cast(key->size), key->contents); - } else if (entry_is_skip(entry)) { - fprintf(fp, "[skip %d]", entry_size(sizer, entry)); - } else { - fprintf(fp, "[code %d]", *reinterpret_cast(entry)); - } - fflush(fp); -} - - -void print(FILE *fp, value_sizer_t *sizer, const leaf_node_t *node) { - fprintf(fp, "Leaf(magic='%4.4s', num_pairs=%u, live_size=%u, frontmost=%u, tstamp_cutpoint=%u)\n", - node->magic.bytes, node->num_pairs, node->live_size, node->frontmost, node->tstamp_cutpoint); - - fprintf(fp, " Offsets:"); - for (int i = 0; i < node->num_pairs; ++i) { - fprintf(fp, " %d", node->pair_offsets[i]); - } - fprintf(fp, "\n"); - fflush(fp); - - fprintf(fp, " By Key:"); - for (int i = 0; i < node->num_pairs; ++i) { - fprintf(fp, " %d:", node->pair_offsets[i]); - print_entry(fp, sizer, get_entry(node, node->pair_offsets[i])); - } - fprintf(fp, "\n"); - - fprintf(fp, " By Offset:"); - fflush(fp); - - entry_iter_t iter = entry_iter_t::make(node); - while (fprintf(fp, " %d", iter.offset), fflush(fp), !iter.done(sizer)) { - fprintf(fp, ":"); - fflush(fp); - if (iter.offset < node->tstamp_cutpoint) { - repli_timestamp_t tstamp = get_timestamp(node, iter.offset); - fprintf(fp, "[t=%" PRIu64 "]", tstamp.longtime); - fflush(fp); - } - print_entry(fp, sizer, get_entry(node, iter.offset)); - iter.step(sizer, node); - } - fprintf(fp, "\n"); - fflush(fp); -} - - -class do_nothing_fscker_t : public key_value_fscker_t { - bool fsck(UNUSED value_sizer_t *sizer, UNUSED const btree_key_t *key, - UNUSED const void *value, UNUSED std::string *msg_out) { - return true; - } -}; - - -bool fsck(value_sizer_t *sizer, const btree_key_t *left_exclusive_or_null, const btree_key_t *right_inclusive_or_null, const leaf_node_t *node, key_value_fscker_t *fscker, std::string *msg_out) { - - struct { - std::string *msg_out; - bool operator()(bool test, const char *msg) { - if (!test) *msg_out = msg; - return !test; - } - } failed; - failed.msg_out = msg_out; - - // We check that all offsets are contiguous (with interspersed - // skip entries) between frontmost and block_size, that frontmost - // is the smallest offset, that live_size is correct, that we have - // correct magic, that the keys are in order, that there are no - // deletion entries after tstamp_cutpoint, and that - // tstamp_cutpoint lies on an entry boundary, and that frontmost - // is not before the end of pair_offsets - - // Basic sanity checks on fields' values. - if (failed(node->magic == sizer->btree_leaf_magic(), - "bad leaf magic") - || failed(node->frontmost >= offsetof(leaf_node_t, pair_offsets) + node->num_pairs * sizeof(uint16_t), - "frontmost offset is before the end of pair_offsets") - || failed(node->live_size <= (sizer->block_size().value() - node->frontmost) + sizeof(uint16_t) * node->num_pairs, - "live_size is impossibly large") - || failed(node->tstamp_cutpoint >= node->frontmost, - "timestamp cut offset below frontmost offset") - || failed(node->tstamp_cutpoint <= sizer->block_size().value(), - "timestamp cut offset larger than block size") - ) { - return false; - } - - // sizeof(offs) is guaranteed to be less than the block_size() thanks to assertions above. - scoped_array_t offs(node->num_pairs); - memcpy(offs.data(), node->pair_offsets, node->num_pairs * sizeof(uint16_t)); - - std::sort(offs.data(), offs.data() + node->num_pairs); - - if (failed(node->num_pairs == 0 || node->frontmost <= offs[0], - "smallest pair offset is before frontmost offset") - || failed(node->num_pairs == 0 || offs[node->num_pairs - 1] < sizer->block_size().value(), - "largest pair offset is larger than block size") - ) { - return false; - } - - entry_iter_t iter = entry_iter_t::make(node); - - int observed_live_size = 0; - - int i = 0; - bool seen_tstamp_cutpoint = false; - static_assert(std::is_same::value, - "This code assumes repli_timestamp_t is a uint64_t."); - uint64_t earliest_so_far = UINT64_MAX; - while (!iter.done(sizer)) { - int offset = iter.offset; - - // tstamp_cutpoint is supposed to be on some entry's offset. - if (offset >= node->tstamp_cutpoint && !seen_tstamp_cutpoint) { - if (failed(offset == node->tstamp_cutpoint, "misaligned tstamp_cutpoint")) { - return false; - } - seen_tstamp_cutpoint = true; - } - - if (failed(offset + (offset < node->tstamp_cutpoint ? sizeof(repli_timestamp_t) : 0) < sizer->block_size().value(), - "offset would be past block size after accounting for the timestamp")) { - return false; - } - - if (offset < node->tstamp_cutpoint) { - repli_timestamp_t tstamp = get_timestamp(node, offset); - if (tstamp.longtime > earliest_so_far) { - *msg_out = strprintf("timestamps out of order (%" PRIu64 " after %" PRIu64 ")\n", tstamp.longtime, earliest_so_far); - return false; - } - earliest_so_far = tstamp.longtime; - } - - const entry_t *ent = get_entry(node, offset); - if (entry_is_live(ent)) { - const void *value = entry_value(ent); - int space = sizer->block_size().value() - (reinterpret_cast(value) - reinterpret_cast(node)); - if (!sizer->fits(value, space)) { - *msg_out = strprintf("problem with key %.*s: value does not fit\n", entry_key(ent)->size, entry_key(ent)->contents); - return false; - } - - std::string fscker_msg; - if (!fscker->fsck(sizer, entry_key(ent), value, &fscker_msg)) { - *msg_out = strprintf("Problem with key %.*s: %s\n", entry_key(ent)->size, entry_key(ent)->contents, fscker_msg.c_str()); - return false; - } - - observed_live_size += sizeof(uint16_t) + entry_size(sizer, ent); - if (failed(i < node->num_pairs, "missing entry offsets")) { - return false; - } - if (offset != offs[i]) { - *msg_out = strprintf("missing live entries or entry offsets (next " - "offset in offsets table is %d, but in node is %d)", - offs[i], offset); - return false; - } - - ++i; - } else if (entry_is_deletion(ent)) { - if (failed(!seen_tstamp_cutpoint, "deletion entry after tstamp_cutpoint") - || failed(i < node->num_pairs, "missing entry offsets") - || failed(offset == offs[i], "missing deletion entries or entry offsets")) { - return false; - } - ++i; - } - - // It's safe to step (because we just checked that the entry - // is valid). - iter.step(sizer, node); - } - - if (failed(i == node->num_pairs, "missing entries") - || failed(node->live_size == observed_live_size, "incorrect live_size recorded")) { - return false; - } - - // Entries look valid, check key ordering. - - const btree_key_t *last = left_exclusive_or_null; - for (int k = 0; k < node->num_pairs; ++k) { - const btree_key_t *key = entry_key(get_entry(node, node->pair_offsets[k])); - if (failed(last == nullptr || btree_key_cmp(last, key) < 0, - "keys out of order")) { - return false; - } - last = key; - } - - if (failed(last == nullptr || right_inclusive_or_null == nullptr - || btree_key_cmp(last, right_inclusive_or_null) <= 0, - "keys out of order (with right_inclusive key)")) { - return false; - } - - return true; -} - - -void validate(DEBUG_VAR value_sizer_t *sizer, DEBUG_VAR const leaf_node_t *node) { -#ifndef NDEBUG - do_nothing_fscker_t fits; - std::string msg; - bool fscked_successfully = fsck(sizer, nullptr, nullptr, node, &fits, &msg); - rassert(fscked_successfully, "%s", msg.c_str()); -#endif -} - -void init(value_sizer_t *sizer, leaf_node_t *node) { - node->magic = sizer->btree_leaf_magic(); - node->num_pairs = 0; - node->live_size = 0; - node->frontmost = sizer->block_size().value(); - node->tstamp_cutpoint = node->frontmost; -} - -int free_space(value_sizer_t *sizer) { - return sizer->block_size().value() - offsetof(leaf_node_t, pair_offsets); -} - -// Returns the mandatory storage cost of the node, returning a value -// in the closed interval [0, free_space(sizer)]. Outputs the offset -// of the first entry for which storing a timestamp is not mandatory. -int mandatory_cost(value_sizer_t *sizer, const leaf_node_t *node, int required_timestamps, int *tstamp_back_offset_out) { - int size = node->live_size; - - // node->live_size does not include deletion entries, deletion - // entries' timestamps, and live entries' timestamps. We add that - // to size. - - entry_iter_t iter = entry_iter_t::make(node); - int count = 0; - int deletions_cost = 0; - int max_deletions_cost = free_space(sizer) / DELETION_RESERVE_FRACTION; - while (!(count == required_timestamps || iter.done(sizer) || iter.offset >= node->tstamp_cutpoint)) { - const entry_t *ent = get_entry(node, iter.offset); - if (entry_is_deletion(ent)) { - if (deletions_cost >= max_deletions_cost) { - break; - } - - int this_entry_cost = sizeof(uint16_t) + sizeof(repli_timestamp_t) + entry_size(sizer, ent); - deletions_cost += this_entry_cost; - size += this_entry_cost; - ++count; - } else if (entry_is_live(ent)) { - ++count; - size += sizeof(repli_timestamp_t); - } - - iter.step(sizer, node); - } - - *tstamp_back_offset_out = iter.offset; - - return size; -} - -int mandatory_cost(value_sizer_t *sizer, const leaf_node_t *node, int required_timestamps) { - int ignored; - return mandatory_cost(sizer, node, required_timestamps, &ignored); -} - -int leaf_epsilon(value_sizer_t *sizer) { - // Returns the maximum possible entry size, i.e. the key cost plus - // the value cost plus pair_offsets plus timestamp cost. - - int key_cost = sizeof(uint8_t) + MAX_KEY_SIZE; - - // If the value is always empty, the DELETE_ENTRY_CODE byte needs to be considered. - int n = std::max(sizer->max_possible_size(), 1); - int pair_offsets_cost = sizeof(uint16_t); - int timestamp_cost = sizeof(repli_timestamp_t); - - return key_cost + n + pair_offsets_cost + timestamp_cost; -} - -bool is_empty(const leaf_node_t *node) { - return node->num_pairs == 0; -} - -bool is_full(value_sizer_t *sizer, const leaf_node_t *node, const btree_key_t *key, const void *value) { - - // Upon an insertion, we preserve `MANDATORY_TIMESTAMPS - 1` - // timestamps and add our own (accounted for below) - // - // Notice this is the site of some bullshit XXX, the line below used to - // consider that we would need to preserve MANDATORY_TIMESTAMPS - 1 - // timestamps. However this is incorrect because the key that we're - // inserting may overwrite one of the timestamped keys. This means that a - // key which mandatory cost had assumed was safe to garbage collect won't - // be which allows us to get into a situation where is_full returns false - // but when we call prepare_space_for_new_entry we fail with an insertion - // because it doesn't actually fit. - int size = mandatory_cost(sizer, node, MANDATORY_TIMESTAMPS); - - // Add the space we'll need for the new key/value pair we would - // insert. We conservatively assume the key is not already - // contained in the node. - - size += sizeof(uint16_t) + sizeof(repli_timestamp_t) + key->full_size() + sizer->size(value); - - // The node is full if we can't fit all that data within the free space. - return size > free_space(sizer); -} - -bool is_underfull(value_sizer_t *sizer, const leaf_node_t *node) { - - // An underfull node is one whose mandatory fields' cost - // constitutes significantly less than half the free space, where - // "significantly" is enough to prevent a split-then-merge. - - // (Note that x / y is indeed taken to mean floor(x / y) below.) - - // A split node's size S is always within leaf_epsilon of - // free_space and then is split as evenly as possible. This means - // the two resultant nodes' sizes are both no less than S / 2 - - // leaf_epsilon / 2, which is no less than (free_space - - // leaf_epsilon) / 2 - leaf_epsilon / 2. Which is no less than is - // free_space / 2 - leaf_epsilon. We don't want an immediately - // split node to be underfull, hence the threshold used below. - - return mandatory_cost(sizer, node, MANDATORY_TIMESTAMPS) < free_space(sizer) / 2 - leaf_epsilon(sizer); -} - - -// Compares indices by looking at values in another array. -class indirect_index_comparator_t { -public: - explicit indirect_index_comparator_t(const uint16_t *array) : array_(array) { } - - bool operator()(uint16_t x, uint16_t y) { - return array_[x] < array_[y]; - } - -private: - const uint16_t *array_; -}; - - -void garbage_collect( - value_sizer_t *sizer, - leaf_node_t *node, - int num_tstamped, - int *preserved_index, - optional tstamp_cutoff_upper_bound = optional()) { - scoped_array_t indices(node->num_pairs); - - for (int i = 0; i < node->num_pairs; ++i) { - indices[i] = i; - } - - std::sort(indices.data(), indices.data() + node->num_pairs, indirect_index_comparator_t(node->pair_offsets)); - - int mand_offset; - UNUSED int cost = mandatory_cost(sizer, node, num_tstamped, &mand_offset); - if (tstamp_cutoff_upper_bound) { - /* If we have an upper bound for the timestamp cutoff point, make sure - that we cut off at least that many timestamps. */ - mand_offset = std::min(*tstamp_cutoff_upper_bound, mand_offset); - } - - int w = sizer->block_size().value(); - int i = node->num_pairs - 1; - for (; i >= 0; --i) { - int offset = node->pair_offsets[indices[i]]; - - if (offset < mand_offset) { - break; - } - - entry_t *ent = get_entry(node, offset); - if (entry_is_live(ent)) { - int sz = entry_size(sizer, ent); - w -= sz; - memmove(get_at_offset(node, w), ent, sz); - node->pair_offsets[indices[i]] = w; - } else { - node->pair_offsets[indices[i]] = 0; - } - } - - // Either i < 0 or node->pair_offsets[indices[i]] < mand_offset. - - node->tstamp_cutpoint = w; - - for (; i >= 0; --i) { - int offset = node->pair_offsets[indices[i]]; - entry_t *ent = get_entry(node, offset); - rassert(!entry_is_skip(ent)); - - // Preserve the timestamp. - int sz = sizeof(repli_timestamp_t) + entry_size(sizer, ent); - - w -= sz; - - memmove(get_at_offset(node, w), get_at_offset(node, offset), sz); - node->pair_offsets[indices[i]] = w; - } - - node->frontmost = w; - - // Now squash dead indices. - int j = 0, k = 0; - for (; k < node->num_pairs; ++k) { - if (*preserved_index == k) { - *preserved_index = j; - } - - if (node->pair_offsets[k] != 0) { - node->pair_offsets[j] = node->pair_offsets[k]; - - j += 1; - } - } - if (*preserved_index == node->num_pairs) { - *preserved_index = j; - } - - node->num_pairs = j; - - validate(sizer, node); -} - - -void garbage_collect(value_sizer_t *sizer, leaf_node_t *node, int num_tstamped) { - int ignore = 0; - garbage_collect(sizer, node, num_tstamped, &ignore); - rassert(ignore == 0); -} - -void clean_entry(void *p, int sz) { - rassert(sz > 0); - - uint8_t *q = reinterpret_cast(p); - if (sz == 1) { - q[0] = SKIP_ENTRY_CODE_ONE; - } else if (sz == 2) { - q[0] = SKIP_ENTRY_CODE_TWO; - q[1] = SKIP_ENTRY_RESERVED; - } else if (sz > 2) { - q[0] = SKIP_ENTRY_CODE_MANY; - rassert(sz < 65536); - *reinterpret_cast(q + 1) = sz - 3; - - // Some memset implementations are broken for nonzero values. - for (int i = 3; i < sz; ++i) { - q[i] = SKIP_ENTRY_RESERVED; - } - } -} - -// Moves entries with pair_offsets indices in the clopen range [beg, -// end) from fro to tow. -void move_elements(value_sizer_t *sizer, leaf_node_t *fro, int beg, int end, - int wpoint, leaf_node_t *tow, int fro_copysize, - int fro_mand_offset, - std::vector *moved_values_out) { - rassert(is_underfull(sizer, tow)); - rassert(end >= beg); - - // This assertion is a bit loose. - rassert(fro_copysize + mandatory_cost(sizer, tow, MANDATORY_TIMESTAMPS) <= free_space(sizer)); - - // Make tow have a nice big region we can copy entries to. Also, - // this means we have no "skip" entries in tow. - garbage_collect(sizer, tow, MANDATORY_TIMESTAMPS, &wpoint); - - // Now resize and move tow's pair_offsets. - memmove(tow->pair_offsets + wpoint + (end - beg), tow->pair_offsets + wpoint, sizeof(uint16_t) * (tow->num_pairs - wpoint)); - - tow->num_pairs += end - beg; - - // pos a - - // Now we're going to do something crazy. Fill the new hole in - // the pair offsets with the numbers in [0, end - beg). - for (int i = 0; i < end - beg; ++i) { - tow->pair_offsets[wpoint + i] = i; - } - - // We treat these numbers as indices into [beg, end) in fro, and - // sort them so that we can access [beg, end) in order by - // increasing offset. - std::sort(tow->pair_offsets + wpoint, tow->pair_offsets + wpoint + (end - beg), indirect_index_comparator_t(fro->pair_offsets + beg)); - - int tow_offset = tow->frontmost; - - // The offset we read from (indirectly pointing to fro's [beg, - // end)) in tow->pair_offsets, and the offset at which we stop. - int fro_index = wpoint; - int fro_index_end = wpoint + (end - beg); - - const int new_frontmost = tow->frontmost - fro_copysize; - - int wri_offset = new_frontmost; - - int adjustable_tow_offsets[MANDATORY_TIMESTAMPS]; - int num_adjustable_tow_offsets = 0; - - // To detect bugs in the code - int actually_copied = 0; - - // We will gradually compute the live size. - int livesize = tow->live_size; - - for (int i = 0; i < wpoint; ++i) { - if (tow->pair_offsets[i] < tow->tstamp_cutpoint) { - rassert(num_adjustable_tow_offsets < MANDATORY_TIMESTAMPS); - adjustable_tow_offsets[num_adjustable_tow_offsets] = i; - ++num_adjustable_tow_offsets; - } - } - - for (int i = wpoint + (end - beg); i < tow->num_pairs; ++i) { - if (tow->pair_offsets[i] < tow->tstamp_cutpoint) { - rassert(num_adjustable_tow_offsets < MANDATORY_TIMESTAMPS); - adjustable_tow_offsets[num_adjustable_tow_offsets] = i; - ++num_adjustable_tow_offsets; - } - } - - int fro_live_size_adjustment = 0; - - for (;;) { - rassert(tow_offset <= tow->tstamp_cutpoint); - if (tow_offset == tow->tstamp_cutpoint || fro_index == fro_index_end) { - // We have no more timestamped information to push. - break; - } - - int fro_offset = fro->pair_offsets[beg + tow->pair_offsets[fro_index]]; - - if (fro_offset >= fro_mand_offset) { - // We have no more timestamped information to push. - break; - } - - repli_timestamp_t fro_tstamp = get_timestamp(fro, fro_offset); - repli_timestamp_t tow_tstamp = get_timestamp(tow, tow_offset); - - // Greater timestamps go first. - if (tow_tstamp < fro_tstamp) { - entry_t *ent = get_entry(fro, fro_offset); - int entsz = entry_size(sizer, ent); - int sz = sizeof(repli_timestamp_t) + entsz; - memmove(get_at_offset(tow, wri_offset), get_at_offset(fro, fro_offset), sz); - - if (entry_is_live(ent)) { - livesize += entsz + sizeof(uint16_t); - fro_live_size_adjustment -= entsz + sizeof(uint16_t); - } - - clean_entry(ent, entsz); - - // Update the pair offset in fro to be the offset in tow - // -- we'll never use the old value again and we'll copy - // the newer values to tow later. - fro->pair_offsets[beg + tow->pair_offsets[fro_index]] = wri_offset; - - wri_offset += sz; - actually_copied += sz; - fro_index++; - - } else { - int sz = sizeof(repli_timestamp_t) + entry_size(sizer, get_entry(tow, tow_offset)); - memmove(get_at_offset(tow, wri_offset), get_at_offset(tow, tow_offset), sz); - - // Update the pair offset of the entry we've moved. - int i; - for (i = 0; i < num_adjustable_tow_offsets; ++i) { - int j = adjustable_tow_offsets[i]; - if (tow->pair_offsets[j] == tow_offset) { - tow->pair_offsets[j] = wri_offset; - break; - } - } - - // Make sure we updated something. - rassert(i != num_adjustable_tow_offsets); - - wri_offset += sz; - tow_offset += sz; - } - } - - uint16_t new_tstamp_cutpoint = wri_offset; - - // Now we have some untimestamped entries to write. - for (; fro_index < fro_index_end; ++fro_index) { - int fro_offset = fro->pair_offsets[beg + tow->pair_offsets[fro_index]]; - entry_t *ent = get_entry(fro, fro_offset); - if (entry_is_live(ent)) { - int sz = entry_size(sizer, ent); - memmove(get_at_offset(tow, wri_offset), ent, sz); - clean_entry(ent, sz); - fro_live_size_adjustment -= sz + sizeof(uint16_t); - - fro->pair_offsets[beg + tow->pair_offsets[fro_index]] = wri_offset; - - wri_offset += sz; - livesize += sz + sizeof(uint16_t); - actually_copied += sz; - rassert(wri_offset <= tow_offset); - } else { - rassert(entry_is_deletion(ent)); - - // This is a dead entry. We'll need to squash this dead entry later. - fro->pair_offsets[beg + tow->pair_offsets[fro_index]] = 0; - - int sz = entry_size(sizer, ent); - clean_entry(ent, sz); - } - } - - guarantee(actually_copied <= fro_copysize); - rassert(wri_offset <= tow_offset); - - // tow may have some timestamped entries that need to become - // un-timestamped. - while (tow_offset < tow->tstamp_cutpoint) { - rassert(wri_offset <= tow_offset); - - entry_t *ent = get_entry(tow, tow_offset); - int sz = entry_size(sizer, ent); - if (entry_is_live(ent)) { - memmove(get_at_offset(tow, wri_offset), ent, sz); - - // Update the pair offset of the entry we've moved. - int i; - for (i = 0; i < num_adjustable_tow_offsets; ++i) { - int j = adjustable_tow_offsets[i]; - if (tow->pair_offsets[j] == tow_offset) { - tow->pair_offsets[j] = wri_offset; - break; - } - } - - // Make sure we updated something. - rassert(i != num_adjustable_tow_offsets); - - wri_offset += sz; - } else { - - // Update the pair offset of the entry we've deleted, so - // that it gets squashed later. - int i; - for (i = 0; i < num_adjustable_tow_offsets; ++i) { - int j = adjustable_tow_offsets[i]; - if (tow->pair_offsets[j] == tow_offset) { - tow->pair_offsets[j] = 0; - } - } - } - tow_offset += sizeof(repli_timestamp_t) + sz; - } - - guarantee(wri_offset <= tow_offset); - - // If we needed to untimestamp any tow entries, we'll need a skip - // entry for the open space. - if (wri_offset < tow_offset) { - // printf("wri_offset = %d, tow_offset = %d\n", wri_offset, tow_offset); - clean_entry(get_at_offset(tow, wri_offset), tow_offset - wri_offset); - } - - // We don't need to do anything else for tow entries because we - // did a garbage collection in tow so they are contiguous through - // the end of the buffer (and the rest don't have timestamps). - - // Copy the valid tow offsets from [beg, end) to the wpoint point - // in tow, and move fro entries. - memcpy(tow->pair_offsets + wpoint, fro->pair_offsets + beg, - sizeof(uint16_t) * (end - beg)); - memmove(fro->pair_offsets + beg, fro->pair_offsets + end, sizeof(uint16_t) * (fro->num_pairs - end)); - fro->num_pairs -= end - beg; - - tow->frontmost = new_frontmost; - - tow->live_size = livesize; - - tow->tstamp_cutpoint = new_tstamp_cutpoint; - - fro->live_size += fro_live_size_adjustment; - - if (moved_values_out != nullptr) { - // Collect value pointers of the moved values - moved_values_out->clear(); - moved_values_out->reserve(end - beg); - for (int pair_idx = wpoint; pair_idx < wpoint + (end - beg); ++pair_idx) { - const int offset = tow->pair_offsets[pair_idx]; - // Skip dead entries - if (offset != 0) { - const entry_t *entry = get_entry(tow, offset); - // Skip deletions - if (entry_is_live(entry)) { - moved_values_out->push_back(entry_value(entry)); - } - } - } - } - - { - // Squash dead entries that we copied pair_offsets from fro, - // for, and that we removed from tow, as well. - int j, k; - for (j = 0, k = 0; k < tow->num_pairs; ++k) { - if (tow->pair_offsets[k] != 0) { - tow->pair_offsets[j] = tow->pair_offsets[k]; - - j += 1; - } - } - tow->num_pairs = j; - } - - validate(sizer, fro); - validate(sizer, tow); -} - -void split(value_sizer_t *sizer, leaf_node_t *node, leaf_node_t *rnode, btree_key_t *median_out) { - int tstamp_back_offset; - int mandatory = mandatory_cost(sizer, node, MANDATORY_TIMESTAMPS, &tstamp_back_offset); - - guarantee(mandatory >= free_space(sizer) - leaf_epsilon(sizer)); - - // We shall split the mandatory cost of this node as evenly as possible. - - int num_mandatories = 0; - int i = node->num_pairs - 1; - int prev_rcost = 0; - int rcost = 0; - while (i >= 0 && rcost < mandatory / 2) { - int offset = node->pair_offsets[i]; - entry_t *ent = get_entry(node, offset); - - // We only take mandatory entries' costs into consideration, - // which guarantees correct behavior (in that neither node nor - // can become underfull after a split). If we didn't do this, - // it would be possible to bias one node with a bunch of - // deletions that makes its mandatory_cost artificially small. - - if (entry_is_live(ent)) { - prev_rcost = rcost; - rcost += entry_size(sizer, ent) + sizeof(uint16_t) + (offset < tstamp_back_offset ? sizeof(repli_timestamp_t) : 0); - - ++num_mandatories; - } else { - rassert(entry_is_deletion(ent)); - - if (offset < tstamp_back_offset) { - prev_rcost = rcost; - rcost += entry_size(sizer, ent) + sizeof(uint16_t) + sizeof(repli_timestamp_t); - - ++num_mandatories; - } - } - - --i; - } - - // Since the mandatory_cost is at least free_space - leaf_epsilon there's no way i can equal num_pairs or zero. - guarantee(i < node->num_pairs); - guarantee(i > 0); - - // Now prev_rcost and rcost envelope mandatory / 2. - guarantee(prev_rcost < mandatory / 2); - guarantee(rcost >= mandatory / 2, "rcost = %d, mandatory / 2 = %d, i = %d", rcost, mandatory / 2, i); - - int s; - int end_rcost; - if ((mandatory - prev_rcost) - prev_rcost < rcost - (mandatory - rcost)) { - end_rcost = prev_rcost; - s = i + 2; - --num_mandatories; - } else { - end_rcost = rcost; - s = i + 1; - } - - // If our math was right, neither node can be underfull just - // considering the split of the mandatory costs. - guarantee(end_rcost >= free_space(sizer) / 2 - leaf_epsilon(sizer)); - guarantee(mandatory - end_rcost >= free_space(sizer) / 2 - leaf_epsilon(sizer)); - - // Now we wish to move the elements at indices [s, num_pairs) to rnode. - - init(sizer, rnode); - - int node_copysize = end_rcost - num_mandatories * sizeof(uint16_t); - move_elements(sizer, node, s, node->num_pairs, 0, rnode, node_copysize, - tstamp_back_offset, nullptr); - - keycpy(median_out, entry_key(get_entry(node, node->pair_offsets[s - 1]))); -} - -void merge(value_sizer_t *sizer, leaf_node_t *left, leaf_node_t *right) { - rassert(left != right); - - rassert(is_underfull(sizer, left)); - rassert(is_underfull(sizer, right)); - - int tstamp_back_offset; - int mandatory = mandatory_cost(sizer, left, MANDATORY_TIMESTAMPS, &tstamp_back_offset); - - int left_copysize = mandatory; - // Uncount the uint16_t cost of mandatory entries. Sigh. - // This includes deletion entries *before* the `tstamp_back_offset`, as well - // as all non-deletion entries. - for (int i = 0; i < left->num_pairs; ++i) { - if (left->pair_offsets[i] < tstamp_back_offset - || !entry_is_deletion(get_entry(left, left->pair_offsets[i]))) { - left_copysize -= sizeof(uint16_t); - } - } - - move_elements(sizer, left, 0, left->num_pairs, 0, right, left_copysize, - tstamp_back_offset, nullptr); -} - -// We move keys out of sibling and into node. -bool level(value_sizer_t *sizer, int nodecmp_node_with_sib, - leaf_node_t *node, leaf_node_t *sibling, - btree_key_t *replacement_key_out, - std::vector *moved_values_out) { - rassert(node != sibling); - - // If sibling were underfull, we'd just merge the nodes. - rassert(is_underfull(sizer, node)); - rassert(!is_underfull(sizer, sibling)); - - // First figure out the inclusive range [beg, end] of elements we want to move - // from sibling. - int beg, end, *w, wstep; - - int node_weight = mandatory_cost(sizer, node, MANDATORY_TIMESTAMPS); - int tstamp_back_offset; - int sibling_weight = mandatory_cost(sizer, sibling, MANDATORY_TIMESTAMPS, - &tstamp_back_offset); - - guarantee(node_weight < sibling_weight); - - if (nodecmp_node_with_sib < 0) { - // node is to the left of sibling, so we want to move elements - // [0, k) from sibling. - - beg = 0; - end = 0; - w = &end; - wstep = 1; - } else { - // node is to the right of sibling, so we want to move - // elements [num_pairs - k, num_pairs) from sibling. - - beg = sibling->num_pairs - 1; - end = sibling->num_pairs - 1; - w = &beg; - wstep = -1; - } - - guarantee(end - beg != sibling->num_pairs - 1); - - int prev_weight_movement = 0; - int weight_movement = 0; - int num_mandatories = 0; - int prev_diff = sizer->block_size().value(); // some impossibly large value - for (;;) { - int offset = sibling->pair_offsets[*w]; - entry_t *ent = get_entry(sibling, offset); - - // We only take mandatory entries' costs into consideration. - if (entry_is_live(ent)) { - int sz = entry_size(sizer, ent) + sizeof(uint16_t) + (offset < tstamp_back_offset ? sizeof(repli_timestamp_t) : 0); - prev_diff = sibling_weight - node_weight; - prev_weight_movement = weight_movement; - weight_movement += sz; - node_weight += sz; - sibling_weight -= sz; - - ++num_mandatories; - } else { - rassert(entry_is_deletion(ent)); - - if (offset < tstamp_back_offset) { - int sz = entry_size(sizer, ent) + sizeof(uint16_t) + sizeof(repli_timestamp_t); - prev_diff = sibling_weight - node_weight; - prev_weight_movement = weight_movement; - weight_movement += sz; - node_weight += sz; - sibling_weight -= sz; - - ++num_mandatories; - } - } - - if (end - beg == sibling->num_pairs - 1 || node_weight >= sibling_weight) { - break; - } - - *w += wstep; - } - - guarantee(end - beg < sibling->num_pairs - 1); - - if (prev_diff <= sibling_weight - node_weight) { - *w -= wstep; - --num_mandatories; - weight_movement = prev_weight_movement; - } - - if (end < beg) { - // Alas, there is no actual leveling to do. - guarantee(end + 1 == beg); - return false; - } - - int sib_copysize = weight_movement - num_mandatories * sizeof(uint16_t); - move_elements(sizer, sibling, beg, end + 1, - nodecmp_node_with_sib < 0 ? node->num_pairs : 0, node, - sib_copysize, tstamp_back_offset, moved_values_out); - - guarantee(node->num_pairs > 0); - guarantee(sibling->num_pairs > 0); - - if (nodecmp_node_with_sib < 0) { - keycpy(replacement_key_out, entry_key(get_entry(node, node->pair_offsets[node->num_pairs - 1]))); - } else { - keycpy(replacement_key_out, entry_key(get_entry(sibling, sibling->pair_offsets[sibling->num_pairs - 1]))); - } - - return true; -} - -bool is_mergable(value_sizer_t *sizer, const leaf_node_t *node, const leaf_node_t *sibling) { - return is_underfull(sizer, node) && is_underfull(sizer, sibling); -} - -// Sets *index_out to the index for the live entry or deletion entry -// for the key, or to the index the key would have if it were -// inserted. Returns true if the key at said index is actually equal. -bool find_key(const leaf_node_t *node, const btree_key_t *key, int *index_out) { - int beg = 0; - int end = node->num_pairs; - - // beg == 0 or key > *(beg - 1). - // end == num_pairs or key < *end. - - while (beg < end) { - // when (end - beg) > 0, (end - beg) / 2 is always less than (end - beg). So beg <= test_point < end. - int test_point = beg + (end - beg) / 2; - - const btree_key_t *ek = entry_key(get_entry(node, node->pair_offsets[test_point])); - - int res = btree_key_cmp(key, ek); - - if (res < 0) { - // key < *test_point. - end = test_point; - } else if (res > 0) { - // key > *test_point. Since test_point < end, we have test_point + 1 <= end. - beg = test_point + 1; - } else { - // We found the key! - *index_out = test_point; - return true; - } - } - - // (Since beg == end, then *(beg - 1) < key < *beg (with appropriate - // provisions for beg == 0 or beg == num_pairs) and index_out - // should be set to beg, and false should be returned. - *index_out = beg; - return false; -} - -bool lookup(value_sizer_t *sizer, const leaf_node_t *node, const btree_key_t *key, void *value_out) { - int index; - if (find_key(node, key, &index)) { - const entry_t *ent = get_entry(node, node->pair_offsets[index]); - if (entry_is_live(ent)) { - const void *val = entry_value(ent); - memcpy(value_out, val, sizer->size(val)); - return true; - } - } - - return false; -} - -/* `insert()` and `remove()` call this to insert a new entry into the leaf node. - -First it removes any existing entry for `key`; then it makes room in the leaf -node for a new entry and fills `*space_out` with a pointer to the beginning of -where the new entry should go. `prepare_space_for_new_entry()` takes care of -updating `pair_offsets` and `num_pairs`, moving around other entries as needed -to maintain timestamp ordering, putting a timestamp on the new entry if -appropriate, and maintaining `frontmost` and `tstamp_cutpoint`. The caller is -responsible for writing the actual entry itself (including the key) and for -updating `live_size` if the newly created entry is live. - -`new_entry_size` is the total size of the new entry, including key, value, -and/or code byte, but not including repli timestamp. - -It is an error to put a deletion entry after `tstamp_cutpoint`. If the caller -intends to insert a deletion entry, it should pass `false` for -`allow_after_tstamp_cutpoint`. If the entry would go after `tstamp_cutpoint` -and `allow_after_tstamp_cutpoint` is false, then instead of making room for the -entry, `prepare_space_for_new_entry()` will return false. It will still remove -any preexisting entry that was in the leaf node. If the entry would go before -`tstamp_cutpoint` or `allow_after_tstamp_cutpoint` is true, then the return -value will be true. */ -MUST_USE bool prepare_space_for_new_entry( - value_sizer_t *sizer, - leaf_node_t *node, - const btree_key_t *key, - int new_entry_size, - repli_timestamp_t tstamp, - /* Used to derive the highest possible timestamp that non-timestamped - entries might have. Usually the recency of the buf_t that node is in. */ - repli_timestamp_t maximum_existing_tstamp, - bool allow_after_tstamp_cutpoint, - char **space_out) { - - /* Get an upper bound for the timestamp cutoff point in case we later call - `garbage_collect()`. We want to make sure that garbage collection cleans up - *at least* as much space as it would clean up now. This is crucial, because - our caller (in particular `insert()`) might have used `is_full()` in order to - check that the leaf node has enough space for a given entry. - If the key already exists, we are going to call `clear_entry()` on it below, - before we call `garbage_collect()`. However this can have the unwanted side - effect that `mandatory_cost` in `garbage_collect()` no longer exceeds the - `max_deletions_cost`, or exceeds it only at a later offset - (this is true in particular if the existing entry is a deletion entry). - As a consequence of that, `garbage_collect()` might clean up slightly less space - than was originally assumed. - We avoid this problem by getting the cutoff point now, and having `garbage_collect()` - pick the lower of the two. */ - int gc_tstamp_cutoff_upper_bound; - mandatory_cost(sizer, node, MANDATORY_TIMESTAMPS - 1, &gc_tstamp_cutoff_upper_bound); - - /* Figure out where in `pair_offsets` to put the offset of the new entry, - and simultaneously check for an existing entry for this key. If the entry - already exists, clean it. */ - - int index; - bool found = find_key(node, key, &index); - - if (found) { - int offset = node->pair_offsets[index]; - entry_t *ent = get_entry(node, offset); - - int sz = entry_size(sizer, ent); - - if (entry_is_live(ent)) { - node->live_size -= sizeof(uint16_t) + sz; - } - - clean_entry(ent, sz); - - /* We'll re-use the now open slot in `pair_offsets`. If it turns out - that we aren't actually creating a new entry, like if we're deleting a - key which has no timestamp, then we'll close up this space in - `pair_offsets` later. */ - - } else { - /* Later, once we've confirmed that we actually want to create an entry - in the leaf node, we'll open up some space in `pair_offsets`. */ - } - - /* Garbage collect if appropriate. - We do it after cleaning up any existing entry so that adding a deletion entry - *almost* always works no matter how full the node is. - If the existing entry is smaller than the new deletion entry that we're trying - to add, we might not have enough space for it even after garbage collection. - This can happen if the previous entry didn't have a timestamp and had a very - small value size (in practice this should be really rare). - We check for this condition further down, and recover from it by dropping - all existing timestamps and discarding the delete entry by returning `false`. */ - - if (offsetof(leaf_node_t, pair_offsets) + - sizeof(uint16_t) * (node->num_pairs + (found ? 0 : 1)) + - sizeof(repli_timestamp_t) + - new_entry_size > - node->frontmost) { - - if (found) { - /* We can't re-use an existing index if we're garbage collecting. */ - found = false; - memmove( - node->pair_offsets + index, - node->pair_offsets + index + 1, - sizeof(uint16_t) * (node->num_pairs - index - 1)); - --node->num_pairs; - } - - /* Passing `&index` as a parameter to `garbage_collect()` - guarantees that it will remain valid even as `pair_offsets` entries are - moved around. */ - garbage_collect(sizer, node, MANDATORY_TIMESTAMPS - 1, &index, - make_optional(gc_tstamp_cutoff_upper_bound)); - - /* Make sure that `index` still refers to where the new key should be - inserted. */ - DEBUG_VAR int index2; - rassert(!find_key(node, key, &index2)); - rassert(index == index2, "garbage_collect() failed to preserve index"); - } - - /* Compute where in the node to put the new entry */ - - uint16_t end_of_where_new_entry_should_go; - bool new_entry_should_have_timestamp; - - if (node->frontmost == sizer->block_size().value() || - (node->frontmost < node->tstamp_cutpoint && get_timestamp(node, node->frontmost) <= tstamp)) { - /* In the most common case, the new value will go right at - `node->frontmost` and will get a timestamp. For performance reasons, we - check for this case specially and short-circuit. If a cosmic ray were to - strike the computer and make us take the second branch of this `if` when - we should have taken the first branch, the result should be the same. */ - end_of_where_new_entry_should_go = node->frontmost; - new_entry_should_have_timestamp = true; - - } else { - entry_iter_t iter = entry_iter_t::make(node); - while (!iter.done(sizer) && iter.offset < node->tstamp_cutpoint && get_timestamp(node, iter.offset) > tstamp) { - iter.step(sizer, node); - } - end_of_where_new_entry_should_go = iter.offset; - - if (end_of_where_new_entry_should_go == node->tstamp_cutpoint && - node->tstamp_cutpoint != sizer->block_size().value()) { - /* We are after all of the timestamped entries, but before at least - one non-timestamped entry. We know that the non-timestamped entries - have a timestamp of at most maximum_existing_tstamp. If our own timestamp - is higher than that, we can put a timestamp on ourself. Otherwise, we - don't know what the timestamp would have been on the non-timestamped entry, - so we mustn't put a timestamp on ourself. */ - new_entry_should_have_timestamp = tstamp > maximum_existing_tstamp; - } else { - new_entry_should_have_timestamp = true; - } - } - - /* If a deletion would go after `tstamp_cutpoint`, instead we want to just - discard it entirely (deletions have `allow_after_tstamp_cutpoint == false`). */ - bool actually_create_entry = new_entry_should_have_timestamp || allow_after_tstamp_cutpoint; - - /* As mentioned above, it can theoretically happen that we're trying to replace - an entry by a deletion entry and we don't have enough space to write the deletion - entry. - We check for this here, and shift the timestamp cutoff point in order to - maintain correctness. - This will be very rare in practice, since values are usually larger than - deletion entries. */ - bool drop_timestamps = false; - if (actually_create_entry - && !allow_after_tstamp_cutpoint - && offsetof(leaf_node_t, pair_offsets) - + sizeof(uint16_t) * (node->num_pairs + (found ? 0 : 1)) - + new_entry_size - + sizeof(repli_timestamp_t) - > node->frontmost) { - actually_create_entry = false; - drop_timestamps = true; - } - - if (!actually_create_entry) { - if (found) { - /* We're deleting the previous entry for this key, but not inserting - a new one; close the gap in `pair_offsets`. `index` is the location - of the open slot. */ - memmove( - node->pair_offsets + index, - node->pair_offsets + index + 1, - sizeof(uint16_t) * (node->num_pairs - index - 1)); - --node->num_pairs; - } - - if (drop_timestamps) { - erase_deletions(sizer, node, optional()); - } - - return false; - } - - /* There was no previous entry for this key, but we're creating a new entry; - make some room in `pair_offsets`. `index` is where the open slot should go. - We didn't do this before because we weren't sure if we were actually gonna - create a new entry or not. */ - - if (!found) { - memmove( - node->pair_offsets + index + 1, - node->pair_offsets + index, - sizeof(uint16_t) * (node->num_pairs - index)); - ++node->num_pairs; - } - - /* Now that we know where in the leaf node to write our entry, make space if - necessary. */ - - int total_space_for_new_entry = new_entry_size + (new_entry_should_have_timestamp ? sizeof(repli_timestamp_t) : 0); - - if (end_of_where_new_entry_should_go == node->frontmost) { - /* This is the common case. Just like before, we check for this case - specially and short-circuit, even though the algorithm in the `else` - branch is a no-op if `end_of_where_new_entry_should_go` is - `node->frontmost`. */ - } else { - /* Move the entries themselves */ - memmove( - get_at_offset(node, node->frontmost - total_space_for_new_entry), - get_at_offset(node, node->frontmost), - end_of_where_new_entry_should_go - node->frontmost); - /* Update the `pair_offsets` table so it points to the new locations of - the entries */ - for (int i = 0; i < node->num_pairs; ++i) { - if (i == index) continue; - if (node->pair_offsets[i] < end_of_where_new_entry_should_go) { - node->pair_offsets[i] -= total_space_for_new_entry; - } - } - } - - node->frontmost -= total_space_for_new_entry; - guarantee(offsetof(leaf_node_t, pair_offsets) - + sizeof(uint16_t) * node->num_pairs <= node->frontmost); - - /* Write the timestamp if we need one, and update `node->tstamp_cutpoint` if - we don't. */ - - uint16_t start_of_where_new_entry_should_go = end_of_where_new_entry_should_go - total_space_for_new_entry; - if (new_entry_should_have_timestamp) { - *reinterpret_cast(get_at_offset(node, start_of_where_new_entry_should_go)) = tstamp; - } else { - rassert(end_of_where_new_entry_should_go == node->tstamp_cutpoint); - node->tstamp_cutpoint = start_of_where_new_entry_should_go; - } - - /* Record the offset in `pair_offsets` */ - - node->pair_offsets[index] = start_of_where_new_entry_should_go; - - /* Fill output variable */ - - if (new_entry_should_have_timestamp) { - *space_out = get_at_offset(node, start_of_where_new_entry_should_go + sizeof(repli_timestamp_t)); - } else { - *space_out = get_at_offset(node, start_of_where_new_entry_should_go); - } - guarantee(end_of_where_new_entry_should_go <= sizer->block_size().value()); - - return true; -} - -// Inserts a key/value pair into the node. Hopefully you've already -// cleaned up the old value, if there is one. -void insert( - value_sizer_t *sizer, - leaf_node_t *node, - const btree_key_t *key, - const void *value, - repli_timestamp_t tstamp, - repli_timestamp_t maximum_existing_tstamp) { - rassert(!is_full(sizer, node, key, value)); - - /* Make space for the entry itself */ - - char *location_to_write_data; - bool should_write = prepare_space_for_new_entry(sizer, node, - key, key->full_size() + sizer->size(value), tstamp, maximum_existing_tstamp, - true, - &location_to_write_data); - guarantee(should_write); - - /* Now copy the data into the node itself */ - - memcpy(location_to_write_data, key, key->full_size()); - location_to_write_data += key->full_size(); - memcpy(location_to_write_data, value, sizer->size(value)); - - node->live_size += sizeof(uint16_t) + key->full_size() + sizer->size(value); - - validate(sizer, node); -} - -void remove( - value_sizer_t *sizer, - leaf_node_t *node, - const btree_key_t *key, - repli_timestamp_t tstamp, - repli_timestamp_t maximum_existing_tstamp) { - /* If the deletion entry would fall after `tstamp_cutpoint`, then it - shouldn't be written at all. If that's the case, then - `prepare_space_for_new_entry()` will return false because we pass false for - `allow_after_tstamp_cutpoint`. */ - - char *location_to_write_data; - if (prepare_space_for_new_entry(sizer, node, - key, - 1 + key->full_size(), /* 1 for `DELETE_ENTRY_CODE` */ - tstamp, - maximum_existing_tstamp, - false, - &location_to_write_data)) { - *location_to_write_data = static_cast(DELETE_ENTRY_CODE); - ++location_to_write_data; - memcpy(location_to_write_data, key, key->full_size()); - } - - validate(sizer, node); -} - -// Erases the entry for the given key, leaving behind no trace. -void erase_presence(value_sizer_t *sizer, leaf_node_t *node, const btree_key_t *key) { - int index; - bool found = find_key(node, key, &index); - if (found) { - int offset = node->pair_offsets[index]; - entry_t *ent = get_entry(node, offset); - - int sz = entry_size(sizer, ent); - if (entry_is_live(ent)) { - node->live_size -= sizeof(uint16_t) + sz; - } - - clean_entry(ent, sz); - - memmove(node->pair_offsets + index, node->pair_offsets + index + 1, (node->num_pairs - (index + 1)) * sizeof(uint16_t)); - node->num_pairs -= 1; - } - - validate(sizer, node); -} - -repli_timestamp_t min_deletion_timestamp( - value_sizer_t *sizer, - const leaf_node_t *node, - repli_timestamp_t maximum_existing_timestamp) { - repli_timestamp_t earliest_so_far = maximum_existing_timestamp; - entry_iter_t iter = entry_iter_t::make(node); - while (!iter.done(sizer) && iter.offset < node->tstamp_cutpoint) { - repli_timestamp_t tstamp = get_timestamp(node, iter.offset); - rassert(earliest_so_far >= tstamp, - "asserted earliest_so_far (%" PRIu64 ") >= tstamp (%" PRIu64 ")", - earliest_so_far.longtime, tstamp.longtime); - earliest_so_far = tstamp; - iter.step(sizer, node); - } - /* It's possible for us to forget a deletion with timestamp equal to the oldest - timestamped entry, so the min deletion timestamp is one time unit newer than the - oldest timestamped entry. */ - return earliest_so_far.next(); -} - -void erase_deletions( - value_sizer_t *sizer, leaf_node_t *node, - optional min_del_timestamp) { - int old_tstamp_cutpoint = node->tstamp_cutpoint; - entry_iter_t iter = entry_iter_t::make(node); - - if (min_del_timestamp.has_value()) { - /* Advance `iter` to the first entry with a timestamp that's lower than - `min_del_timestamp - 1`. */ - while (true) { - if (iter.done(sizer) || iter.offset >= old_tstamp_cutpoint) { - return; - } - if (get_timestamp(node, iter.offset).next() < *min_del_timestamp) { - break; - } - iter.step(sizer, node); - } - } - - /* We'll remove timestamps from all of the entries between `old_tstamp_cutpoint` and - `new_tstamp_cutpoint`. But we won't update `leaf->tstamp_cutpoint` until the end of - the function because we want `iter` to continue iterating according to the old value. - */ - int new_tstamp_cutpoint = iter.offset; - - /* Step until we reach `old_tstamp_cutpoint`, erasing timestamps and deletions as we - go. Make a note of each deletion's offset so we can remove them from the - `pair_offsets` array later. */ - std::set deletion_offsets; - while (!iter.done(sizer) && iter.offset != old_tstamp_cutpoint) { - int off = iter.offset; - guarantee(off >= new_tstamp_cutpoint && off < old_tstamp_cutpoint); - const entry_t *ent = get_entry(node, off); - /* Move `iter` before we modify `ent`, to avoid confusion */ - iter.step(sizer, node); - if (entry_is_deletion(ent)) { - clean_entry( - get_at_offset(node, off), - sizeof(repli_timestamp_t) + entry_size(sizer, ent)); - deletion_offsets.insert(off); - } else { - /* This is the code path for both skip entries and live entries, because skip - entries have timestamps too. */ - clean_entry(get_at_offset(node, off), sizeof(repli_timestamp_t)); - } - } - - /* If a pointer in `pair_offsets` was pointing to a timestamp that's been erased on - a key-value pair, we need to move it forward by `sizeof(repli_timestamp_t)`. If it - was pointing to a deletion entry that's been erased, we need to remove that entry - from `pair_offsets`. */ - int src = 0, dst = 0; - int num_deleted = deletion_offsets.size(); - for (; src < node->num_pairs; ++src) { - uint16_t off = node->pair_offsets[src]; - auto it = deletion_offsets.find(off); - if (it == deletion_offsets.end()) { - if (off >= new_tstamp_cutpoint && off < old_tstamp_cutpoint) { - off += sizeof(repli_timestamp_t); - } - node->pair_offsets[dst++] = off; - } else { - guarantee(off >= new_tstamp_cutpoint && off < old_tstamp_cutpoint); - deletion_offsets.erase(it); - } - } - guarantee(deletion_offsets.empty()); - node->num_pairs -= num_deleted; - - /* Finally, update `node->tstamp_cutpoint` */ - node->tstamp_cutpoint = new_tstamp_cutpoint; -} - -/* Calls `cb` on every entry in the node, whether a real entry or a deletion. The calls -will be in order from most recent to least recent. For entries with no timestamp, the -callback will get `min_deletion_timestamp() - 1`. */ -continue_bool_t visit_entries( - value_sizer_t *sizer, - const leaf_node_t *node, - repli_timestamp_t maximum_existing_timestamp, - const std::function &cb) { - repli_timestamp_t earliest_so_far = maximum_existing_timestamp; - for (entry_iter_t iter = entry_iter_t::make(node); - !iter.done(sizer); iter.step(sizer, node)) { - repli_timestamp_t tstamp; - if (iter.offset < node->tstamp_cutpoint) { - tstamp = get_timestamp(node, iter.offset); - } else { - tstamp = earliest_so_far; - } - earliest_so_far = tstamp; - - const entry_t *ent = get_entry(node, iter.offset); - - if (entry_is_skip(ent)) { - continue; - } - - if (continue_bool_t::ABORT == cb(entry_key(ent), tstamp, entry_value(ent))) { - return continue_bool_t::ABORT; - } - } - return continue_bool_t::CONTINUE; -} - -iterator::iterator() - : node_(nullptr), index_(-1) { } - -iterator::iterator(const leaf_node_t *node, int index) - : node_(node), index_(index) { } - -std::pair iterator::operator*() const { - guarantee(index_ < static_cast(node_->num_pairs)); - guarantee(index_ >= 0); - const entry_t *entree = get_entry(node_, node_->pair_offsets[index_]); - return std::make_pair(entry_key(entree), entry_value(entree)); -} - -iterator &iterator::operator++() { - guarantee(index_ < static_cast(node_->num_pairs), - "Trying to increment past the end of an iterator."); - do { - ++index_; - } while (index_ < node_->num_pairs && !entry_is_live(get_entry(node_, node_->pair_offsets[index_]))); - return *this; -} - -iterator &iterator::operator--() { - guarantee(index_ > -1, "Trying to decrement past the beginning of an iterator."); - do { - --index_; - } while (index_ >= 0 && !entry_is_live(get_entry(node_, node_->pair_offsets[index_]))); - return *this; -} - -bool iterator::operator==(const iterator &other) const { return cmp(other) == 0; } -bool iterator::operator!=(const iterator &other) const { return cmp(other) != 0; } -bool iterator::operator<(const iterator &other) const { return cmp(other) < 0; } -bool iterator::operator>(const iterator &other) const { return cmp(other) > 0; } -bool iterator::operator<=(const iterator &other) const { return cmp(other) <= 0; } -bool iterator::operator>=(const iterator &other) const { return cmp(other) >= 0; } - -int iterator::cmp(const iterator &other) const { - guarantee(node_ == other.node_); - return index_ - other.index_; -} - -reverse_iterator::reverse_iterator() { } - -reverse_iterator::reverse_iterator(const leaf_node_t *node, int index) - : inner_(node, index) { } - -std::pair reverse_iterator::operator*() const { - return *inner_; -} - -reverse_iterator &reverse_iterator::operator++() { - --inner_; - return *this; -} - -reverse_iterator &reverse_iterator::operator--() { - ++inner_; - return *this; -} - -bool reverse_iterator::operator==(const reverse_iterator &other) const { return inner_ == other.inner_; } -bool reverse_iterator::operator!=(const reverse_iterator &other) const { return inner_ != other.inner_; } -bool reverse_iterator::operator<(const reverse_iterator &other) const { return inner_ > other.inner_; } -bool reverse_iterator::operator>(const reverse_iterator &other) const { return inner_ < other.inner_; } -bool reverse_iterator::operator<=(const reverse_iterator &other) const { return inner_ >= other.inner_; } -bool reverse_iterator::operator>=(const reverse_iterator &other) const { return inner_ <= other.inner_; } - - -leaf_node_t::iterator begin(const leaf_node_t &leaf_node) { - return ++leaf_node_t::iterator(&leaf_node, -1); -} - -leaf_node_t::iterator end(const leaf_node_t &leaf_node) { - return leaf_node_t::iterator(&leaf_node, leaf_node.num_pairs); -} - -leaf_node_t::reverse_iterator rbegin(const leaf_node_t &leaf_node) { - return ++leaf_node_t::reverse_iterator(&leaf_node, leaf_node.num_pairs); -} - -leaf_node_t::reverse_iterator rend(const leaf_node_t &leaf_node) { - return leaf_node_t::reverse_iterator(&leaf_node, -1); -} - -leaf::iterator inclusive_lower_bound(const btree_key_t *key, const leaf_node_t &leaf_node) { - int index; - leaf::find_key(&leaf_node, key, &index); - if (index == leaf_node.num_pairs || - entry_is_live(leaf::get_entry(&leaf_node, leaf_node.pair_offsets[index]))) { - return leaf_node_t::iterator(&leaf_node, index); - } else { - return ++leaf_node_t::iterator(&leaf_node, index); - } -} - -leaf::reverse_iterator exclusive_upper_bound(const btree_key_t *key, const leaf_node_t &leaf_node) { - int index; - leaf::find_key(&leaf_node, key, &index); - if (index < leaf_node.num_pairs) { - const leaf::entry_t *entry = leaf::get_entry(&leaf_node, leaf_node.pair_offsets[index]); - const btree_key_t *ekey = leaf::entry_key(entry); - if (entry_is_live(entry) && - btree_key_cmp(ekey, key) == 0) { - // We have to skip this entry to make the iterator exclusive, - // hence the ++. - return ++leaf_node_t::reverse_iterator(&leaf_node, index); - } - } - - return ++leaf_node_t::reverse_iterator(&leaf_node, index); -} - -} // namespace leaf diff --git a/src/btree/leaf_node.hpp b/src/btree/leaf_node.hpp deleted file mode 100644 index ada2b2235a6..00000000000 --- a/src/btree/leaf_node.hpp +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef BTREE_LEAF_NODE_HPP_ -#define BTREE_LEAF_NODE_HPP_ - -#include -#include -#include - -#include "arch/compiler.hpp" -#include "btree/types.hpp" -#include "buffer_cache/types.hpp" -#include "containers/optional.hpp" - -class value_sizer_t; -struct btree_key_t; -class repli_timestamp_t; - -namespace leaf { -class iterator; -class reverse_iterator; -} //namespace leaf - -// The leaf node begins with the following struct layout. -ATTR_PACKED(struct leaf_node_t { - // The value-type-specific magic value. It's a bit of a hack, but - // it's possible to construct a value_sizer_t based on this value. - block_magic_t magic; - - // The size of pair_offsets. - uint16_t num_pairs; - - // The total size (in bytes) of the live entries and their 2-byte - // pair offsets in pair_offsets. (Does not include the size of - // the live entries' timestamps.) - uint16_t live_size; - - // The frontmost offset. - uint16_t frontmost; - - // The first offset whose entry is not accompanied by a timestamp. - uint16_t tstamp_cutpoint; - - // The pair offsets. - uint16_t pair_offsets[]; - - //Iteration - typedef leaf::iterator iterator; - typedef leaf::reverse_iterator reverse_iterator; -}); - -namespace leaf { - -leaf_node_t::iterator begin(const leaf_node_t &leaf_node); -leaf_node_t::iterator end(const leaf_node_t &leaf_node); - -leaf_node_t::reverse_iterator rbegin(const leaf_node_t &leaf_node); -leaf_node_t::reverse_iterator rend(const leaf_node_t &leaf_node); - -leaf_node_t::iterator inclusive_lower_bound(const btree_key_t *key, const leaf_node_t &leaf_node); -leaf_node_t::reverse_iterator exclusive_upper_bound(const btree_key_t *key, const leaf_node_t &leaf_node); - - - -// We must maintain timestamps and deletion entries as best we can, -// with the following limitations. The number of timestamps stored -// need not be more than the most `MANDATORY_TIMESTAMPS` recent -// timestamps. The deletions stored need not be any more than what is -// necessary to fill `(block_size - offsetof(leaf_node_t, -// pair_offsets)) / DELETION_RESERVE_FRACTION` bytes. For example, -// with a 4084 block size, if the five most recent operations were -// deletions of 250-byte keys, we would only be required to store the -// 2 most recent deletions and the 2 most recent timestamps. -// -// These parameters are in the header because some unit tests are -// based on them. -const int MANDATORY_TIMESTAMPS = 5; -const int DELETION_RESERVE_FRACTION = 10; - - - - - - -std::string strprint_leaf(value_sizer_t *sizer, const leaf_node_t *node); - -void print(FILE *fp, value_sizer_t *sizer, const leaf_node_t *node); - -class key_value_fscker_t { -public: - key_value_fscker_t() { } - - // Returns true if there are no problems. - virtual bool fsck(value_sizer_t *sizer, const btree_key_t *key, - const void *value, std::string *msg_out) = 0; - -protected: - virtual ~key_value_fscker_t() { } - - DISABLE_COPYING(key_value_fscker_t); -}; - -bool fsck(value_sizer_t *sizer, const btree_key_t *left_exclusive_or_null, const btree_key_t *right_inclusive_or_null, const leaf_node_t *node, key_value_fscker_t *fscker, std::string *msg_out); - -void validate(value_sizer_t *sizer, const leaf_node_t *node); - -void init(value_sizer_t *sizer, leaf_node_t *node); - -bool is_empty(const leaf_node_t *node); - -bool is_full(value_sizer_t *sizer, const leaf_node_t *node, const btree_key_t *key, const void *value); - -bool is_underfull(value_sizer_t *sizer, const leaf_node_t *node); - -void split(value_sizer_t *sizer, leaf_node_t *node, leaf_node_t *sibling, - btree_key_t *median_out); - -void merge(value_sizer_t *sizer, leaf_node_t *left, leaf_node_t *right); - -// The pointers in `moved_values_out` point to positions in `node` and -// will be valid as long as `node` remains unchanged. -bool level(value_sizer_t *sizer, int nodecmp_node_with_sib, leaf_node_t *node, - leaf_node_t *sibling, btree_key_t *replacement_key_out, - std::vector *moved_values_out); - -bool is_mergable(value_sizer_t *sizer, const leaf_node_t *node, const leaf_node_t *sibling); - -bool find_key(const leaf_node_t *node, const btree_key_t *key, int *index_out); - -bool lookup(value_sizer_t *sizer, const leaf_node_t *node, const btree_key_t *key, void *value_out); - -void insert( - value_sizer_t *sizer, - leaf_node_t *node, - const btree_key_t *key, - const void *value, - repli_timestamp_t tstamp, - /* the recency of the buf_t that `node` comes from */ - repli_timestamp_t maximum_existing_tstamp); - -/* `remove()` removes any preexisting value or tombstone, then creates a tombstone unless -`tstamp` is before `tstamp_cutpoint`. */ -void remove( - value_sizer_t *sizer, - leaf_node_t *node, - const btree_key_t *key, - repli_timestamp_t tstamp, - /* the recency of the buf_t that `node` comes from */ - repli_timestamp_t maximum_existing_tstamp); - -/* `erase_presence()` removes any preexisting value or tombstone, but does not create a -new tombstone. */ -void erase_presence(value_sizer_t *sizer, leaf_node_t *node, const btree_key_t *key); - -/* Returns the smallest timestamp such that if a deletion had occurred with that -timestamp, the node would still have a record of it. */ -repli_timestamp_t min_deletion_timestamp( - value_sizer_t *sizer, - const leaf_node_t *node, - // the recency of the buf that `node` came from - repli_timestamp_t maximum_existing_timestamp); - -/* Removes all timestamps and deletions. Optionally: specify a `min_timestamp` -so that only timestamps and deletions earlier than the given timestamp are removed. */ -void erase_deletions( - value_sizer_t *sizer, leaf_node_t *node, - optional min_timestamp); - -/* Calls `cb` on every entry in the node, whether a real entry or a deletion. The calls -will be in order from most recent to least recent. For entries with no timestamp, the -callback will get `min_deletion_timestamp() - 1`. */ -continue_bool_t visit_entries( - value_sizer_t *sizer, - const leaf_node_t *node, - // the recency of the buf that `node` came from - repli_timestamp_t maximum_existing_timestamp, - const std::function &cb); - -class iterator { -public: - iterator(); - iterator(const leaf_node_t *node, int index); - std::pair operator*() const; - iterator &operator++(); - iterator &operator--(); - bool operator==(const iterator &other) const; - bool operator!=(const iterator &other) const; - bool operator<(const iterator &other) const; - bool operator>(const iterator &other) const; - bool operator<=(const iterator &other) const; - bool operator>=(const iterator &other) const; -private: - /* negative return => this < other */ - int cmp(const iterator &other) const; - const leaf_node_t *node_; - int index_; -}; - -class reverse_iterator { -public: - reverse_iterator(); - reverse_iterator(const leaf_node_t *node, int index); - std::pair operator*() const; - reverse_iterator &operator++(); - reverse_iterator &operator--(); - bool operator==(const reverse_iterator &other) const; - bool operator!=(const reverse_iterator &other) const; - bool operator<(const reverse_iterator &other) const; - bool operator>(const reverse_iterator &other) const; - bool operator<=(const reverse_iterator &other) const; - bool operator>=(const reverse_iterator &other) const; -private: - iterator inner_; -}; - -} // namespace leaf - - -#endif // BTREE_LEAF_NODE_HPP_ diff --git a/src/btree/node.cc b/src/btree/node.cc deleted file mode 100644 index d7c7c0c76a7..00000000000 --- a/src/btree/node.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "btree/node.hpp" - -#include "btree/leaf_node.hpp" -#include "btree/internal_node.hpp" - -const block_magic_t internal_node_t::expected_magic = { { 'i', 'n', 't', 'e' } }; - -namespace node { - -bool is_underfull(value_sizer_t *sizer, const node_t *node) { - if (node->magic == sizer->btree_leaf_magic()) { - return leaf::is_underfull(sizer, reinterpret_cast(node)); - } else { - rassert(is_internal(node)); - return internal_node::is_underfull(sizer->block_size(), reinterpret_cast(node)); - } -} - -bool is_mergable(value_sizer_t *sizer, const node_t *node, const node_t *sibling, const internal_node_t *parent) { - if (sizer->btree_leaf_magic() == node->magic) { - return leaf::is_mergable(sizer, reinterpret_cast(node), reinterpret_cast(sibling)); - } else { - rassert(is_internal(node)); - return internal_node::is_mergable(sizer->block_size(), reinterpret_cast(node), reinterpret_cast(sibling), parent); - } -} - - -void split(value_sizer_t *sizer, node_t *node, node_t *rnode, btree_key_t *median) { - if (is_leaf(node)) { - leaf::split(sizer, reinterpret_cast(node), - reinterpret_cast(rnode), median); - } else { - internal_node::split(sizer->block_size(), reinterpret_cast(node), - reinterpret_cast(rnode), median); - } -} - -void merge(value_sizer_t *sizer, node_t *node, node_t *rnode, const internal_node_t *parent) { - if (is_leaf(node)) { - leaf::merge(sizer, reinterpret_cast(node), reinterpret_cast(rnode)); - } else { - internal_node::merge(sizer->block_size(), reinterpret_cast(node), reinterpret_cast(rnode), parent); - } -} - -void validate(DEBUG_VAR value_sizer_t *sizer, DEBUG_VAR const node_t *node) { -#ifndef NDEBUG - if (node->magic == sizer->btree_leaf_magic()) { - leaf::validate(sizer, reinterpret_cast(node)); - } else if (node->magic == internal_node_t::expected_magic) { - internal_node::validate(sizer->block_size(), reinterpret_cast(node)); - } else { - unreachable("Invalid leaf node type."); - } -#endif -} - -} // namespace node diff --git a/src/btree/node.hpp b/src/btree/node.hpp deleted file mode 100644 index 9f78768a15d..00000000000 --- a/src/btree/node.hpp +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef BTREE_NODE_HPP_ -#define BTREE_NODE_HPP_ - -#include -#include -#include -#include - -#include - -#include "arch/compiler.hpp" -#include "btree/keys.hpp" -#include "buffer_cache/types.hpp" -#include "config/args.hpp" -#include "serializer/types.hpp" -#include "version.hpp" - -class value_sizer_t { -public: - value_sizer_t() { } - virtual ~value_sizer_t() { } - - virtual int size(const void *value) const = 0; - virtual bool fits(const void *value, int length_available) const = 0; - virtual int max_possible_size() const = 0; - virtual block_magic_t btree_leaf_magic() const = 0; - virtual max_block_size_t block_size() const = 0; - -private: - DISABLE_COPYING(value_sizer_t); -}; - -ATTR_PACKED(struct btree_statblock_t { - //The total number of keys in the btree - int64_t population; - - btree_statblock_t() - : population(0) - { } -}); -static const uint32_t BTREE_STATBLOCK_SIZE = sizeof(btree_statblock_t); - - -//Note: This struct is stored directly on disk. Changing it invalidates old data. -ATTR_PACKED(struct internal_node_t { - block_magic_t magic; - uint16_t npairs; - uint16_t frontmost_offset; - uint16_t pair_offsets[0]; - - static const block_magic_t expected_magic; -}); - -// A node_t is either a btree_internal_node or a btree_leaf_node. -ATTR_PACKED(struct node_t { - block_magic_t magic; -}); - -namespace node { - -inline bool is_internal(const node_t *node) { - if (node->magic == internal_node_t::expected_magic) { - return true; - } - return false; -} - -inline bool is_leaf(const node_t *node) { - // We assume that a node is a leaf whenever it's not internal. - // Unfortunately we cannot check the magic directly, because it differs - // for different value types. - return !is_internal(node); -} - -bool is_mergable(value_sizer_t *sizer, const node_t *node, const node_t *sibling, const internal_node_t *parent); - -bool is_underfull(value_sizer_t *sizer, const node_t *node); - -void split(value_sizer_t *sizer, node_t *node, node_t *rnode, btree_key_t *median); - -void merge(value_sizer_t *sizer, node_t *node, node_t *rnode, const internal_node_t *parent); - -void validate(value_sizer_t *sizer, const node_t *node); - -} // namespace node - -inline void keycpy(btree_key_t *dest, const btree_key_t *src) { - memcpy(dest, src, sizeof(btree_key_t) + src->size); -} - -#endif // BTREE_NODE_HPP_ diff --git a/src/btree/operations.cc b/src/btree/operations.cc deleted file mode 100644 index 124193f478d..00000000000 --- a/src/btree/operations.cc +++ /dev/null @@ -1,665 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "btree/operations.hpp" - -#include - -#include "btree/internal_node.hpp" -#include "btree/leaf_node.hpp" -#include "buffer_cache/alt.hpp" -#include "buffer_cache/blob.hpp" -#include "containers/archive/vector_stream.hpp" -#include "rdb_protocol/profile.hpp" -#include "rdb_protocol/store.hpp" - -void insert_root(block_id_t root_id, superblock_t* sb) { - sb->set_root_block_id(root_id); -} - -block_id_t create_stat_block(buf_parent_t parent) { - buf_lock_t stats_block(parent, alt_create_t::create); - buf_write_t write(&stats_block); - // Make the stat block be the default constructed stats block. - *static_cast(write.get_data_write(BTREE_STATBLOCK_SIZE)) - = btree_statblock_t(); - return stats_block.block_id(); -} - -buf_lock_t get_root(value_sizer_t *sizer, superblock_t *sb) { - const block_id_t node_id = sb->get_root_block_id(); - - if (node_id != NULL_BLOCK_ID) { - return buf_lock_t(sb->expose_buf(), node_id, access_t::write); - } else { - buf_lock_t lock(sb->expose_buf(), alt_create_t::create); - { - buf_write_t write(&lock); - leaf::init(sizer, static_cast(write.get_data_write())); - } - insert_root(lock.block_id(), sb); - return lock; - } -} - -// Helper function for `check_and_handle_split()` and `check_and_handle_underfull()`. -// Detaches all values in the given node if it's an internal node, and calls -// `detacher` on each value if it's a leaf node. -void detach_all_children(const node_t *node, buf_parent_t parent, - const value_deleter_t *detacher) { - if (node::is_leaf(node)) { - const leaf_node_t *leaf = reinterpret_cast(node); - // Detach the values that are now in `rbuf` with `buf` as their parent. - for (auto it = leaf::begin(*leaf); it != leaf::end(*leaf); ++it) { - detacher->delete_value(parent, (*it).second); - } - } else { - const internal_node_t *internal = - reinterpret_cast(node); - // Detach the values that are now in `rbuf` with `buf` as their parent. - for (int pair_idx = 0; pair_idx < internal->npairs; ++pair_idx) { - block_id_t child_id = - internal_node::get_pair_by_index(internal, pair_idx)->lnode; - parent.detach_child(child_id); - } - } -} - -// Split the node if necessary. If the node is a leaf_node, provide the new -// value that will be inserted; if it's an internal node, provide NULL (we -// split internal nodes proactively). -// `detacher` is used to detach any values that are removed from `buf`, in -// case `buf` is a leaf. -void check_and_handle_split(value_sizer_t *sizer, - buf_lock_t *buf, - buf_lock_t *last_buf, - superblock_t *sb, - const btree_key_t *key, void *new_value, - const value_deleter_t *detacher) { - { - buf_read_t buf_read(buf); - const node_t *node = static_cast(buf_read.get_data_read()); - - // If the node isn't full, we don't need to split, so we're done. - if (!node::is_internal(node)) { // This should only be called when update_needed. - rassert(new_value); - if (!leaf::is_full(sizer, reinterpret_cast(node), - key, new_value)) { - return; - } - } else { - rassert(!new_value); - if (!internal_node::is_full(reinterpret_cast(node))) { - return; - } - } - } - - // If we are splitting the root, we must detach it from sb first. - // It will later be attached to a newly created root, together with its - // newly created sibling. - if (last_buf->empty()) { - sb->expose_buf().detach_child(buf->block_id()); - } - - // Allocate a new node to split into, and some temporary memory to keep - // track of the median key in the split; then actually split. - buf_lock_t rbuf(last_buf->empty() ? sb->expose_buf() : buf_parent_t(last_buf), - alt_create_t::create); - store_key_t median_buffer; - btree_key_t *median = median_buffer.btree_key(); - - { - buf_write_t buf_write(buf); - buf_write_t rbuf_write(&rbuf); - node::split(sizer, - static_cast(buf_write.get_data_write()), - static_cast(rbuf_write.get_data_write()), - median); - - // We must detach all entries that we have removed from `buf`. - buf_read_t rbuf_read(&rbuf); - const node_t *node = static_cast(rbuf_read.get_data_read()); - // The parent of the entries used to be `buf`, even though they are now in - // `rbuf`... - detach_all_children(node, buf_parent_t(buf), detacher); - } - - // Since we moved subtrees from `buf` to `rbuf`, we need to set `rbuf`'s recency - // greater than that of any of its subtrees. We know that `buf`'s recency is greater - // than that of any of the subtrees that were moved, so we can just copy `buf`'s - // recency onto `rbuf`. This is more conservative than it needs to be. - rbuf.set_recency(buf->get_recency()); - - // Insert the key that sets the two nodes apart into the parent. - if (last_buf->empty()) { - // We're splitting what was previously the root, so create a new root to use as the parent. - *last_buf = buf_lock_t(sb->expose_buf(), alt_create_t::create); - { - buf_write_t last_write(last_buf); - internal_node::init(sizer->block_size(), - static_cast(last_write.get_data_write())); - } - // We set the recency of the new root block to the recency of its two sub-trees. - last_buf->set_recency(buf->get_recency()); - - insert_root(last_buf->block_id(), sb); - } - - { - buf_write_t last_write(last_buf); - DEBUG_VAR bool success - = internal_node::insert(static_cast(last_write.get_data_write()), - median, - buf->block_id(), rbuf.block_id()); - rassert(success, "could not insert internal btree node"); - } - - // We've split the node; now figure out where the key goes and release the other buf (since we're done with it). - if (0 >= btree_key_cmp(key, median)) { - // The key goes in the old buf (the left one). - - // Do nothing. - - } else { - // The key goes in the new buf (the right one). - buf->swap(rbuf); - } -} - -// Merge or level the node if necessary. -// `detacher` is used to detach any values that are removed from `buf` or its -// sibling, in case `buf` is a leaf. -void check_and_handle_underfull(value_sizer_t *sizer, - buf_lock_t *buf, - buf_lock_t *last_buf, - superblock_t *sb, - const btree_key_t *key, - const value_deleter_t *detacher) { - bool node_is_underfull; - { - if (last_buf->empty()) { - // The root node is never underfull. - node_is_underfull = false; - } else { - buf_read_t buf_read(buf); - const node_t *const node = static_cast(buf_read.get_data_read()); - node_is_underfull = node::is_underfull(sizer, node); - } - } - if (node_is_underfull) { - // Acquire a sibling to merge or level with. - store_key_t key_in_middle; - block_id_t sib_node_id; - int nodecmp_node_with_sib; - - { - buf_read_t last_buf_read(last_buf); - const internal_node_t *parent_node - = static_cast(last_buf_read.get_data_read()); - nodecmp_node_with_sib = internal_node::sibling(parent_node, key, - &sib_node_id, - &key_in_middle); - } - - // Now decide whether to merge or level. - buf_lock_t sib_buf(last_buf, sib_node_id, access_t::write); - - bool node_is_mergable; - { - buf_read_t sib_buf_read(&sib_buf); - const node_t *sib_node - = static_cast(sib_buf_read.get_data_read()); - -#ifndef NDEBUG - node::validate(sizer, sib_node); -#endif - - buf_read_t buf_read(buf); - const node_t *const node - = static_cast(buf_read.get_data_read()); - buf_read_t last_buf_read(last_buf); - const internal_node_t *parent_node - = static_cast(last_buf_read.get_data_read()); - - node_is_mergable = node::is_mergable(sizer, node, sib_node, parent_node); - } - - if (node_is_mergable) { - // Merge. - - const repli_timestamp_t buf_recency = buf->get_recency(); - const repli_timestamp_t sib_buf_recency = sib_buf.get_recency(); - - // Nodes must be passed to merge in ascending order. - // Make it such that we always merge from sib_buf into buf. It - // simplifies the code below. - if (nodecmp_node_with_sib < 0) { - buf->swap(sib_buf); - } - - bool parent_was_doubleton; - { - buf_read_t last_buf_read(last_buf); - const internal_node_t *parent_node - = static_cast(last_buf_read.get_data_read()); - parent_was_doubleton = internal_node::is_doubleton(parent_node); - } - if (parent_was_doubleton) { - // `buf` will get a new parent below. Detach it from its old one. - // We can't detach it later because its value will already have - // been changed by then. - // And I guess that would be bad, wouldn't it? - last_buf->detach_child(buf->block_id()); - } - - { - buf_write_t sib_buf_write(&sib_buf); - buf_write_t buf_write(buf); - buf_read_t last_buf_read(last_buf); - - // Detach all values / children in `sib_buf` - buf_read_t sib_buf_read(&sib_buf); - const node_t *node = - static_cast(sib_buf_read.get_data_read()); - detach_all_children(node, buf_parent_t(&sib_buf), detacher); - - const internal_node_t *parent_node - = static_cast(last_buf_read.get_data_read()); - node::merge(sizer, - static_cast(sib_buf_write.get_data_write()), - static_cast(buf_write.get_data_write()), - parent_node); - } - sib_buf.mark_deleted(); - sib_buf.reset_buf_lock(); - - /* `buf` now has sub-trees that came from both `buf` and `sib_buf`. We need - to set its recency greater than or equal to any of its new sub-trees. We know - that `buf`'s and `sib_buf`'s old recencies were greater than or equal to - those of their old sub-trees, so the greater of their recencies must be - greater than or equal to that of any sub-tree now in `buf`. This is more - conservative than it needs to be. */ - buf->set_recency(superceding_recency(buf_recency, sib_buf_recency)); - - if (!parent_was_doubleton) { - buf_write_t last_buf_write(last_buf); - internal_node::remove(sizer->block_size(), - static_cast(last_buf_write.get_data_write()), - key_in_middle.btree_key()); - } else { - // The parent has only 1 key after the merge (which means that - // it's the root and our node is its only child). Insert our - // node as the new root. - // This is why we had detached `buf` from `last_buf` earlier. - last_buf->mark_deleted(); - insert_root(buf->block_id(), sb); - } - } else { - // Level. - store_key_t replacement_key_buffer; - btree_key_t *replacement_key = replacement_key_buffer.btree_key(); - - bool leveled; - { - bool is_internal; - { - buf_read_t buf_read(buf); - const node_t *node = - static_cast(buf_read.get_data_read()); - is_internal = node::is_internal(node); - } - buf_write_t buf_write(buf); - buf_write_t sib_buf_write(&sib_buf); - buf_read_t last_buf_read(last_buf); - const internal_node_t *parent_node - = static_cast(last_buf_read.get_data_read()); - // We handle internal and leaf nodes separately because of the - // different ways their children have to be detached. - // (for internal nodes: call detach_child directly vs. for leaf - // nodes: use the supplied value_deleter_t (which might either - // detach the children as well or do nothing)) - if (is_internal) { - std::vector moved_children; - leveled = internal_node::level(sizer->block_size(), - static_cast(buf_write.get_data_write()), - static_cast(sib_buf_write.get_data_write()), - replacement_key, parent_node, &moved_children); - // Detach children that have been removed from `sib_buf`: - for (size_t i = 0; i < moved_children.size(); ++i) { - sib_buf.detach_child(moved_children[i]); - } - } else { - std::vector moved_values; - leveled = leaf::level(sizer, nodecmp_node_with_sib, - static_cast(buf_write.get_data_write()), - static_cast(sib_buf_write.get_data_write()), - replacement_key, &moved_values); - // Detach values that have been removed from `sib_buf`: - for (size_t i = 0; i < moved_values.size(); ++i) { - detacher->delete_value(buf_parent_t(&sib_buf), - moved_values[i]); - } - } - } - - // We moved new subtrees or values into `buf`, so its recency may need to - // be increased. Conservatively update it to the max of its old recency and - // `sib_buf`'s recency, because `sib_buf`'s recency is known to be greater - // than or equal to the recency of any of the moved subtrees or values. - buf->set_recency(superceding_recency( - sib_buf.get_recency(), buf->get_recency())); - - if (leveled) { - buf_write_t last_buf_write(last_buf); - internal_node::update_key(static_cast(last_buf_write.get_data_write()), - key_in_middle.btree_key(), - replacement_key); - } - } - } -} - -/* Passing in a pass_back_superblock parameter will cause this function to - * return the superblock after it's no longer needed (rather than releasing - * it). Notice the superblock is not guaranteed to be returned until the - * keyvalue_location_t that's passed in (keyvalue_location_out) is destroyed. - * This is because it may need to use the superblock for some of its methods. - * */ -void find_keyvalue_location_for_write( - value_sizer_t *sizer, - superblock_t *superblock, const btree_key_t *key, - repli_timestamp_t timestamp, - const value_deleter_t *balancing_detacher, - keyvalue_location_t *keyvalue_location_out, - profile::trace_t *trace, - promise_t *pass_back_superblock) THROWS_NOTHING { - keyvalue_location_out->superblock = superblock; - keyvalue_location_out->pass_back_superblock = pass_back_superblock; - - keyvalue_location_out->stat_block = keyvalue_location_out->superblock->get_stat_block_id(); - - buf_lock_t last_buf; - buf_lock_t buf; - { - // KSI: We can't acquire the block for write here -- we could, but it would - // worsen the performance of the program -- sometimes we only end up using - // this block for read. So the profiling information is not very good. - PROFILE_STARTER_IF_ENABLED( - trace != nullptr, "Acquiring block for write.", trace); - buf = get_root(sizer, superblock); - } - - // Walk down the tree to the leaf. - for (;;) { - { - buf_read_t read(&buf); - if (!node::is_internal(static_cast(read.get_data_read()))) { - break; - } - } - // Check if the node is overfull and proactively split it if it is (since this is - // an internal node). - { - PROFILE_STARTER_IF_ENABLED( - trace != nullptr, "Perhaps split node.", trace); - check_and_handle_split( - sizer, &buf, &last_buf, superblock, key, nullptr, balancing_detacher); - } - - // Check if the node is underfull, and merge/level if it is. - { - PROFILE_STARTER_IF_ENABLED( - trace != nullptr, "Perhaps merge nodes.", trace); - check_and_handle_underfull( - sizer, &buf, &last_buf, superblock, key, balancing_detacher); - } - - // Release the superblock, if we've gone past the root (and haven't - // already released it). If we're still at the root or at one of - // its direct children, we might still want to replace the root, so - // we can't release the superblock yet. - if (!last_buf.empty() && keyvalue_location_out->superblock) { - if (pass_back_superblock != nullptr) { - pass_back_superblock->pulse(superblock); - keyvalue_location_out->superblock = nullptr; - } else { - keyvalue_location_out->superblock->release(); - keyvalue_location_out->superblock = nullptr; - } - } - - // Release the old previous node (unless we're at the root), and set - // the next previous node (which is the current node). - last_buf.reset_buf_lock(); - - // As we traverse the path, update the recency of each node to maintain the - // invariant that each node's recency is greater than or equal to that of any - // value in it. This will allow the backfill logic to efficiently find recently - // changed nodes. Note that we do this after the split/merge/level logic; this - // isn't strictly necessary, but it makes the timestamps slightly tighter. - buf.set_recency(superceding_recency(buf.get_recency(), timestamp)); - - // Look up and acquire the next node. - block_id_t node_id; - { - buf_read_t read(&buf); - auto node = static_cast(read.get_data_read()); - node_id = internal_node::lookup(node, key); - } - rassert(node_id != NULL_BLOCK_ID && node_id != SUPERBLOCK_ID); - - { - PROFILE_STARTER_IF_ENABLED( - trace != nullptr, "Acquire a block for write.", trace); - buf_lock_t tmp(&buf, node_id, access_t::write); - last_buf = std::move(buf); - buf = std::move(tmp); - } - } - - { - scoped_malloc_t tmp(sizer->max_possible_size()); - - // We've gone down the tree and gotten to a leaf. Now look up the key. - buf_read_t read(&buf); - auto node = static_cast(read.get_data_read()); - bool key_found = leaf::lookup(sizer, node, key, tmp.get()); - - if (key_found) { - keyvalue_location_out->there_originally_was_value = true; - keyvalue_location_out->value = std::move(tmp); - } - } - - keyvalue_location_out->last_buf.swap(last_buf); - keyvalue_location_out->buf.swap(buf); -} - -void find_keyvalue_location_for_read( - value_sizer_t *sizer, - superblock_t *superblock, const btree_key_t *key, - keyvalue_location_t *keyvalue_location_out, - btree_stats_t *stats, profile::trace_t *trace) { - stats->pm_keys_read.record(); - stats->pm_total_keys_read += 1; - - const block_id_t root_id = superblock->get_root_block_id(); - rassert(root_id != SUPERBLOCK_ID); - - if (root_id == NULL_BLOCK_ID) { - // There is no root, so the tree is empty. - superblock->release(); - return; - } - - buf_lock_t buf; - { - PROFILE_STARTER_IF_ENABLED( - trace != nullptr, "Acquire a block for read.", trace);; - buf_lock_t tmp(superblock->expose_buf(), root_id, access_t::read); - superblock->release(); - buf = std::move(tmp); - } - -#ifndef NDEBUG - { - buf_read_t read(&buf); - node::validate(sizer, static_cast(read.get_data_read())); - } -#endif // NDEBUG - - for (;;) { - block_id_t node_id; - { - buf_read_t read(&buf); - const void *data = read.get_data_read(); - if (!node::is_internal(static_cast(data))) { - break; - } - - node_id = internal_node::lookup(static_cast(data), - key); - } - rassert(node_id != NULL_BLOCK_ID && node_id != SUPERBLOCK_ID); - - { - PROFILE_STARTER_IF_ENABLED( - trace != nullptr, "Acquire a block for read.", trace); - buf_lock_t tmp(&buf, node_id, access_t::read); - buf.reset_buf_lock(); - buf = std::move(tmp); - } - -#ifndef NDEBUG - { - buf_read_t read(&buf); - node::validate(sizer, static_cast(read.get_data_read())); - } -#endif // NDEBUG - } - - // Got down to the leaf, now probe it. - scoped_malloc_t value(sizer->max_possible_size()); - bool value_found; - { - buf_read_t read(&buf); - const leaf_node_t *leaf - = static_cast(read.get_data_read()); - value_found = leaf::lookup(sizer, leaf, key, value.get()); - } - if (value_found) { - keyvalue_location_out->buf = std::move(buf); - keyvalue_location_out->there_originally_was_value = true; - keyvalue_location_out->value = std::move(value); - } -} - -void apply_keyvalue_change( - value_sizer_t *sizer, - keyvalue_location_t *kv_loc, - const btree_key_t *key, repli_timestamp_t tstamp, - const value_deleter_t *balancing_detacher, - delete_mode_t delete_mode) { - /* how much this keyvalue change affects the total population of the btree - * (should be -1, 0 or 1) */ - int population_change; - - if (kv_loc->value.has()) { - // We have a value to insert. - - // Split the node if necessary, to make sure that we have room - // for the value. Not necessary when deleting, because the - // node won't grow. - - check_and_handle_split(sizer, &kv_loc->buf, &kv_loc->last_buf, - kv_loc->superblock, key, kv_loc->value.get(), - balancing_detacher); - - { -#ifndef NDEBUG - buf_read_t read(&kv_loc->buf); - auto leaf_node = static_cast(read.get_data_read()); - rassert(!leaf::is_full(sizer, leaf_node, key, kv_loc->value.get())); -#endif - } - - if (kv_loc->there_originally_was_value) { - population_change = 0; - } else { - population_change = 1; - } - - /* Update the leaf node's recency to the greater of its previous recency and the - newly-inserted value's recency, to maintain the invariant that its recency is - greater than or equal to that of any entry pair in it. */ - const repli_timestamp_t previous_leaf_recency = kv_loc->buf.get_recency(); - kv_loc->buf.set_recency(superceding_recency(tstamp, kv_loc->buf.get_recency())); - - { - buf_write_t write(&kv_loc->buf); - auto leaf_node = static_cast(write.get_data_write()); - leaf::insert(sizer, - leaf_node, - key, - kv_loc->value.get(), - tstamp, - previous_leaf_recency); - } - } else { - // Delete the value if it's there. - if (kv_loc->there_originally_was_value || - delete_mode != delete_mode_t::REGULAR_QUERY) { - const repli_timestamp_t previous_leaf_recency = kv_loc->buf.get_recency(); - if (delete_mode != delete_mode_t::ERASE) { - /* Update the leaf node's recency to the greater of its previous recency - and the deletion's recency, to maintain the invariant that its recency is - greater than or equal to that of any entry pair in it. */ - kv_loc->buf.set_recency(superceding_recency( - tstamp, kv_loc->buf.get_recency())); - } - - { - buf_write_t write(&kv_loc->buf); - auto leaf_node = static_cast(write.get_data_write()); - switch (delete_mode) { - case delete_mode_t::REGULAR_QUERY: /* fall through */ - case delete_mode_t::MAKE_TOMBSTONE: { - leaf::remove(sizer, - leaf_node, - key, - tstamp, - previous_leaf_recency); - } break; - case delete_mode_t::ERASE: { - leaf::erase_presence(sizer, - leaf_node, - key); - } break; - default: unreachable(); - } - - } - } - if (kv_loc->there_originally_was_value) { - population_change = -1; - } else { - population_change = 0; - } - } - - // Check to see if the leaf is underfull (following a change in - // size or a deletion, and merge/level if it is. - check_and_handle_underfull(sizer, &kv_loc->buf, &kv_loc->last_buf, - kv_loc->superblock, key, balancing_detacher); - - // Modify the stats block. The stats block is detached from the rest of the - // btree, we don't keep a consistent view of it, so we pass the txn as its - // parent. - if (kv_loc->stat_block != NULL_BLOCK_ID) { - buf_lock_t stat_block(buf_parent_t(kv_loc->buf.txn()), - kv_loc->stat_block, access_t::write); - buf_write_t stat_block_write(&stat_block); - auto stat_block_buf = static_cast( - stat_block_write.get_data_write(BTREE_STATBLOCK_SIZE)); - stat_block_buf->population += population_change; - } -} diff --git a/src/btree/operations.hpp b/src/btree/operations.hpp deleted file mode 100644 index f5fb02b4431..00000000000 --- a/src/btree/operations.hpp +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef BTREE_OPERATIONS_HPP_ -#define BTREE_OPERATIONS_HPP_ - -#include -#include -#include - -#include "btree/node.hpp" -#include "btree/stats.hpp" -#include "buffer_cache/alt.hpp" -#include "concurrency/fifo_enforcer.hpp" -#include "concurrency/new_semaphore.hpp" -#include "concurrency/promise.hpp" -#include "containers/archive/stl_types.hpp" -#include "containers/scoped.hpp" -#include "perfmon/perfmon.hpp" -#include "repli_timestamp.hpp" -#include "utils.hpp" - -/* This is the main entry point for performing B-tree operations. */ - -namespace profile { -class trace_t; -} - -class buf_parent_t; -class cache_t; -class value_deleter_t; - -enum cache_snapshotted_t { CACHE_SNAPSHOTTED_NO, CACHE_SNAPSHOTTED_YES }; - -/* An abstract superblock provides the starting point for performing B-tree operations. -This makes it so that the B-tree code doesn't actually have to know about the format of -the superblock, or about anything else that might be contained in the superblock besides -the root block ID and the stat block ID. */ -class superblock_t { -public: - superblock_t() { } - virtual ~superblock_t() { } - // Release the superblock if possible (otherwise do nothing) - virtual void release() = 0; - - virtual block_id_t get_root_block_id() = 0; - virtual void set_root_block_id(block_id_t new_root_block) = 0; - - /* If stats collection is desired, create a stat block with `create_stat_block()` and - store its ID on the superblock, then return it from `get_stat_block_id()`. If stats - collection is not desired, `get_stat_block_id()` can always return `NULL_BLOCK_ID`. - */ - virtual block_id_t get_stat_block_id() = 0; - - virtual buf_parent_t expose_buf() = 0; - - cache_t *cache() { return expose_buf().cache(); } - -private: - DISABLE_COPYING(superblock_t); -}; - -class keyvalue_location_t { -public: - keyvalue_location_t() - : superblock(nullptr), pass_back_superblock(nullptr), - there_originally_was_value(false), stat_block(NULL_BLOCK_ID) { } - - ~keyvalue_location_t() { - if (superblock != nullptr) { - if (pass_back_superblock != nullptr) { - pass_back_superblock->pulse(superblock); - } else { - superblock->release(); - } - } - } - - superblock_t *superblock; - - promise_t *pass_back_superblock; - - // The parent buf of buf, if buf is not the root node. This is hacky. - buf_lock_t last_buf; - - // The buf owning the leaf node which contains the value. - buf_lock_t buf; - - bool there_originally_was_value; - // If the key/value pair was found, a pointer to a copy of the - // value, otherwise NULL. - scoped_malloc_t value; - - template - T *value_as() { return static_cast(value.get()); } - - // Stat block when modifications are made using this class the statblock is - // update. - block_id_t stat_block; -private: - - DISABLE_COPYING(keyvalue_location_t); -}; - -buf_lock_t get_root(value_sizer_t *sizer, superblock_t *sb); - -void check_and_handle_split(value_sizer_t *sizer, - buf_lock_t *buf, - buf_lock_t *last_buf, - superblock_t *sb, - const btree_key_t *key, void *new_value, - const value_deleter_t *detacher); - -void check_and_handle_underfull(value_sizer_t *sizer, - buf_lock_t *buf, - buf_lock_t *last_buf, - superblock_t *sb, - const btree_key_t *key, - const value_deleter_t *detacher); - -/* Set sb to have root id as its root block and release sb */ -void insert_root(block_id_t root_id, superblock_t *sb); - -/* Create a stat block suitable for storing in a superblock and returning from -`get_stat_block_id()`. */ -block_id_t create_stat_block(buf_parent_t parent); - -/* Note that there's no guarantee that `pass_back_superblock` will have been - * pulsed by the time `find_keyvalue_location_for_write` returns. In some cases, - * the superblock is returned only when `*keyvalue_location_out` gets destructed. */ -void find_keyvalue_location_for_write( - value_sizer_t *sizer, - superblock_t *superblock, - const btree_key_t *key, - repli_timestamp_t timestamp, - const value_deleter_t *balancing_detacher, - keyvalue_location_t *keyvalue_location_out, - profile::trace_t *trace, - promise_t *pass_back_superblock = nullptr) THROWS_NOTHING; - -void find_keyvalue_location_for_read( - value_sizer_t *sizer, - superblock_t *superblock, - const btree_key_t *key, - keyvalue_location_t *keyvalue_location_out, - btree_stats_t *stats, - profile::trace_t *trace); - -/* `delete_mode_t` controls how `apply_keyvalue_change()` acts when `kv_loc->value` is -empty. */ -enum class delete_mode_t { - /* If there was a value before, remove it and add a tombstone. (If `tstamp` is less - than the cutpoint, no tombstone will be added.) Otherwise, do nothing. This mode is - used for regular delete queries. */ - REGULAR_QUERY, - /* If there was a value or tombstone before, remove it. This mode is used for erasing - ranges of the database (e.g. during resharding) and also sometimes in backfilling. */ - ERASE, - /* If there was a value or tombstone before, remove it. Then add a tombstone, - regardless of what was present before, unless `tstamp` is less than the cutpoint. - This mode is used for transferring tombstones from other servers in backfilling. */ - MAKE_TOMBSTONE -}; - -void apply_keyvalue_change( - value_sizer_t *sizer, - keyvalue_location_t *kv_loc, - const btree_key_t *key, - repli_timestamp_t tstamp, - const value_deleter_t *balancing_detacher, - delete_mode_t delete_mode); - -#endif // BTREE_OPERATIONS_HPP_ diff --git a/src/btree/parallel_traversal.cc b/src/btree/parallel_traversal.cc deleted file mode 100644 index 8fded40916b..00000000000 --- a/src/btree/parallel_traversal.cc +++ /dev/null @@ -1,691 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "btree/parallel_traversal.hpp" - -#include "arch/runtime/runtime.hpp" -#include "btree/internal_node.hpp" -#include "btree/node.hpp" -#include "btree/operations.hpp" -#include "concurrency/coro_pool.hpp" -#include "concurrency/queue/unlimited_fifo.hpp" - -// By the way, this code all sucks, the design of this code sucks, and the entire -// premise upon which this code was designed was retarded. -// -// - he/she that wrote this code - -// Traversal - -// We want a traversal operation to follow a few simple rules. -// -// 1. Get as far away from the root as possible. -// -// 2. Avoid using more than K + O(1) blocks, for some user-selected -// constant K. -// -// 3. Prefetch efficiently. -// -// This code hopefully will be nice to genericize; you could -// reimplement rget if you genericized this. - -// The Lifecycle of a block_id_t -// -// Every time we deal with a block_id_t, it goes through these states... -// -// 1. Knowledge of the block_id_t. This is where we know about the -// block_id_t, and haven't done anything about it yet. -// -// 2. Acquiring its subtree_recency value from the serializer. The -// block_id_t is grouped with a bunch of others in an array, and we've -// sent a request to the serializer to respond with all these -// subtree_recency values (and the original array). -// -// 3. Acquired the subtree_recency value. The block_id_t's -// subtree_recency is known, but we still have not attempted to -// acquire the block. (If the recency is insufficiently recent, we -// stop here.) -// -// 4. Block acquisition pending. We have sent a request to acquire -// the block. It has not yet successfully completed. -// -// 5I. Block acquisition complete, it's an internal node, partly -// processed children. We hold the lock on the block, and the -// children blocks are currently being processed and have not reached -// stage 4. -// -// 6I. Live children all reached stage 4. We can now release ownership -// of the block. We stop here. -// -// 5L. Block acquisition complete, it's a leaf node, we may have to -// handle large values. -// -// 6L. Large values all pending or better, so we can release ownership -// of the block. We stop here. -// -// (6L might not be implemented, or 5L and 6L might depend on whether -// the btree_traversal_helper_t implementation is interested in -// values.) - -class parent_releaser_t; - -struct acquisition_waiter_callback_t { - virtual void you_may_acquire() = 0; - virtual void cancel() = 0; -protected: - virtual ~acquisition_waiter_callback_t() { } -}; - -class traversal_state_t : public coro_pool_callback_t { -public: - traversal_state_t(block_size_t _max_block_size, btree_traversal_helper_t *_helper, - signal_t *_interruptor) - : max_block_size(_max_block_size), - stat_block(NULL_BLOCK_ID), - helper(_helper), - interruptor(_interruptor), - interrupted(false), - coro_pool(4, &pending_acquires, this) - { - interruptor_watcher.parent = this; - interruptor_watcher.reset(_interruptor); - } - - ~traversal_state_t() { - if (!interrupted) { - for (size_t i = 0; i < acquisition_waiter_stacks.size(); ++i) { - for (size_t j = 0; j < acquisition_waiter_stacks[i].size(); ++j) { - acquisition_waiter_stacks[i][j]->cancel(); - } - } - } - } - - const block_size_t max_block_size; - - /* The block id where we can find the stat block, we need this at the end - * to update population counts. */ - block_id_t stat_block; - - // The helper. - btree_traversal_helper_t *helper; - - signal_t *interruptor; - bool interrupted; - cond_t finished_cond; - - // The number of pending + acquired blocks, by level. - std::vector level_counts; - - class interruptor_watcher_t : public signal_t::subscription_t { - public: - void run() { - parent->interrupt(); - } - traversal_state_t *parent; - } interruptor_watcher; - - int64_t& level_count(int level) { - rassert(level >= 0); - if (size_t(level) >= level_counts.size()) { - rassert(size_t(level) == level_counts.size(), "Somehow we skipped a level! (level = %d)", level); - level_counts.resize(level + 1, 0); - } - return level_counts[level]; - } - - static int64_t level_max(UNUSED int level) { - // level = 1 is the root level. These numbers are - // ridiculously small because we have to spawn a coroutine - // because the buffer cache is broken. - // Also we tend to interfere with other i/o for a weird reason. - // (potential explanation: on the higher levels of the btree, we - // trigger the load of a significant fraction of all blocks. Sooner or - // later, queries end up waiting for the same blocks. But at that point - // they effectively get queued into our i/o queue (assuming we have an - // i/o account of ourselves). If this number is high and disks are slow, - // the latency of that i/o queue can be in the area of seconds though, - // and the query blocks for that time). - return 16; - } - - void consider_pulsing() { - rassert(coro_t::self()); - - // We try to do as many pulses as we can (thus getting - // behavior equivalent to calling consider_pulsing) but we - // only actually do one pulse because this function gets - // called immediately after every single block deallocation, - // which only decrements counters by 1. - - // Right now we don't actually do more than one pulse at a - // time, but we should try. - - rassert(level_counts.size() <= acquisition_waiter_stacks.size()); - level_counts.resize(acquisition_waiter_stacks.size(), 0); - - // If we're trying to stop, don't spawn any new jobs. - if (!interrupted) { - for (int i = level_counts.size() - 1; i >= 0; --i) { - if (level_counts[i] < level_max(i)) { - int diff = level_max(i) - level_counts[i]; - - while (diff > 0 && !acquisition_waiter_stacks[i].empty()) { - acquisition_waiter_callback_t *waiter_cb = acquisition_waiter_stacks[i].back(); - acquisition_waiter_stacks[i].pop_back(); - - // Spawn a coroutine so that it's safe to acquire - // blocks. - level_count(i) += 1; - pending_acquires.push(waiter_cb); - diff -= 1; - } - } - } - } - - if (total_level_count() == 0) { - finished_cond.pulse(); - } - } - - void coro_pool_callback(acquisition_waiter_callback_t *waiter_cb, UNUSED signal_t *pool_interruptor) { - waiter_cb->you_may_acquire(); - } - - void interrupt() { - rassert(interrupted == false); - interrupted = true; - for (size_t i = 0; i < acquisition_waiter_stacks.size(); ++i) { - for (size_t j = 0; j < acquisition_waiter_stacks[i].size(); ++j) { - acquisition_waiter_stacks[i][j]->cancel(); - } - } - } - - int64_t total_level_count() { - int64_t sum = 0; - for (size_t i = 0; i < level_counts.size(); ++i) { - sum += level_counts[i]; - } - return sum; - } - - std::vector< std::vector > acquisition_waiter_stacks; - - std::vector& acquisition_waiter_stack(int level) { - rassert(level >= 0); - if (level >= static_cast(acquisition_waiter_stacks.size())) { - rassert(level == static_cast(acquisition_waiter_stacks.size()), - "Somehow we skipped a level! (level = %d, stacks.size() = %d)", - level, static_cast(acquisition_waiter_stacks.size())); - acquisition_waiter_stacks.resize(level + 1); - } - return acquisition_waiter_stacks[level]; - } - - void wait() { - finished_cond.wait(); - } - -private: - unlimited_fifo_queue_t pending_acquires; - coro_pool_t coro_pool; - DISABLE_COPYING(traversal_state_t); -}; - -void subtrees_traverse(traversal_state_t *state, - parent_releaser_t *releaser, int level, - const counted_t &ids_source); -void do_a_subtree_traversal(traversal_state_t *state, int level, - buf_parent_t parent, block_id_t block_id, - btree_key_t *left_exclusive_or_null, - btree_key_t *right_inclusive_or_null, - lock_in_line_callback_t *acq_start_cb); - -void process_a_leaf_node(traversal_state_t *state, - buf_lock_t buf, int level, - const btree_key_t *left_exclusive_or_null, - const btree_key_t *right_inclusive_or_null); -void process_a_internal_node(traversal_state_t *state, - buf_lock_t buf, int level, - const btree_key_t *left_exclusive_or_null, - const btree_key_t *right_inclusive_or_null); - -struct node_ready_callback_t { - virtual void on_node_ready(buf_lock_t buf) = 0; - virtual void on_cancel() = 0; -protected: - virtual ~node_ready_callback_t() { } -}; - -struct acquire_a_node_fsm_t : public acquisition_waiter_callback_t { - // Not much of an fsm. - traversal_state_t *state; - buf_parent_t parent; - int level; - block_id_t block_id; - lock_in_line_callback_t *acq_start_cb; - node_ready_callback_t *node_ready_cb; - - explicit acquire_a_node_fsm_t(buf_parent_t _parent) : parent(_parent) { } - - void you_may_acquire() { - rassert(coro_t::self()); - - buf_lock_t block(parent, block_id, state->helper->btree_node_mode()); - acq_start_cb->on_in_line(); - - node_ready_callback_t *local_cb = node_ready_cb; - delete this; - local_cb->on_node_ready(std::move(block)); - } - - void cancel() { - /* This is safe because `on_in_line()` is used only to know when it's OK - to release the parent, and now that we're not gonna ever acquire the - child it's safe to release the parent. */ - acq_start_cb->on_in_line(); - - node_ready_callback_t *local_cb = node_ready_cb; - delete this; - local_cb->on_cancel(); - } -}; - - -void acquire_a_node(traversal_state_t *state, int level, - buf_parent_t parent, block_id_t block_id, - lock_in_line_callback_t *acq_start_cb, - node_ready_callback_t *node_ready_cb) { - rassert(coro_t::self()); - acquire_a_node_fsm_t *fsm = new acquire_a_node_fsm_t(parent); - fsm->state = state; - fsm->level = level; - fsm->block_id = block_id; - fsm->acq_start_cb = acq_start_cb; - fsm->node_ready_cb = node_ready_cb; - - state->acquisition_waiter_stack(level).push_back(fsm); - - if (state->interrupted) { - fsm->cancel(); - } - - state->consider_pulsing(); -} - -class parent_releaser_t { -public: - virtual buf_parent_t expose_parent() = 0; - virtual void release() = 0; -protected: - virtual ~parent_releaser_t() { } -}; - -struct internal_node_releaser_t : public parent_releaser_t { - buf_lock_t buf_; - traversal_state_t *state_; - buf_parent_t expose_parent() { - return buf_parent_t(&buf_); - } - virtual void release() { - state_->helper->postprocess_internal_node(&buf_); - delete this; - } - internal_node_releaser_t(buf_lock_t &&buf, - traversal_state_t *state) - : buf_(std::move(buf)), - state_(state) { } - - virtual ~internal_node_releaser_t() { } -}; - -void btree_parallel_traversal(superblock_t *superblock, - btree_traversal_helper_t *helper, - signal_t *interruptor, - release_superblock_t release_superblock) - THROWS_ONLY(interrupted_exc_t) { - traversal_state_t state(superblock->cache()->max_block_size(), - helper, interruptor); - - /* Record the stat block for updating populations later */ - state.stat_block = superblock->get_stat_block_id(); - - if (state.stat_block != NULL_BLOCK_ID) { - /* Give the helper a look at the stat block */ - buf_lock_t stat_block(buf_parent_t(superblock->expose_buf().txn()), - state.stat_block, access_t::read); - helper->read_stat_block(&stat_block); - } else { - helper->read_stat_block(nullptr); - } - - if (helper->progress) { - helper->progress->inform(0, parallel_traversal_progress_t::LEARN, parallel_traversal_progress_t::INTERNAL); - helper->progress->inform(0, parallel_traversal_progress_t::ACQUIRE, parallel_traversal_progress_t::INTERNAL); - } - - block_id_t root_id = superblock->get_root_block_id(); - rassert(root_id != SUPERBLOCK_ID); - - struct : public parent_releaser_t { - superblock_t *superblock; - bool release_superblock; - buf_parent_t expose_parent() { - return superblock->expose_buf(); - } - void release() { - if (release_superblock) { - superblock->release(); - } - } - } superblock_releaser; - superblock_releaser.superblock = superblock; - superblock_releaser.release_superblock = - release_superblock == release_superblock_t::RELEASE; - - if (root_id == NULL_BLOCK_ID) { - superblock_releaser.release(); - // No root, so no keys in this entire shard. - } else { - state.level_count(0) += 1; - state.acquisition_waiter_stacks.resize(1); - counted_t ids_source(new ranged_block_ids_t(root_id, nullptr, nullptr, 0)); - subtrees_traverse(&state, - &superblock_releaser, 1, ids_source); - state.wait(); - /* Wait for `state.wait()` to succeed even if `interruptor` is pulsed, - so that we don't abandon running coroutines or FSMs */ - if (interruptor->is_pulsed()) { - throw interrupted_exc_t(); - } - } -} - - -void subtrees_traverse(traversal_state_t *state, - parent_releaser_t *releaser, - int level, - const counted_t& ids_source) { - rassert(coro_t::self()); - interesting_children_callback_t *fsm - = new interesting_children_callback_t(state, releaser, level, - ids_source); - state->helper->filter_interesting_children(releaser->expose_parent(), - ids_source.get(), fsm); -} - -struct do_a_subtree_traversal_fsm_t : public node_ready_callback_t { - traversal_state_t *state; - int level; - store_key_t left_exclusive; - bool left_unbounded; - store_key_t right_inclusive; - bool right_unbounded; - - void on_node_ready(buf_lock_t buf) { - rassert(coro_t::self()); - bool is_leaf; - { - buf_read_t read(&buf); - const node_t *node = static_cast(read.get_data_read()); - is_leaf = node::is_leaf(node); - } - - const btree_key_t *left_exclusive_or_null = left_unbounded ? nullptr : left_exclusive.btree_key(); - const btree_key_t *right_inclusive_or_null = right_unbounded ? nullptr : right_inclusive.btree_key(); - - if (is_leaf) { - if (state->helper->progress) { - state->helper->progress->inform(level, parallel_traversal_progress_t::ACQUIRE, parallel_traversal_progress_t::LEAF); - } - process_a_leaf_node(state, std::move(buf), level, - left_exclusive_or_null, right_inclusive_or_null); - } else { - // The node is internal - if (state->helper->progress) { - state->helper->progress->inform(level, parallel_traversal_progress_t::ACQUIRE, parallel_traversal_progress_t::INTERNAL); - } - process_a_internal_node(state, std::move(buf), level, - left_exclusive_or_null, right_inclusive_or_null); - } - - delete this; - } - - void on_cancel() { - delete this; - } -}; - -void do_a_subtree_traversal(traversal_state_t *state, int level, - buf_parent_t parent, block_id_t block_id, - const btree_key_t *left_exclusive_or_null, - const btree_key_t *right_inclusive_or_null, - lock_in_line_callback_t *acq_start_cb) { - do_a_subtree_traversal_fsm_t *fsm = new do_a_subtree_traversal_fsm_t; - fsm->state = state; - fsm->level = level; - - if (left_exclusive_or_null) { - fsm->left_exclusive.assign(left_exclusive_or_null); - fsm->left_unbounded = false; - } else { - fsm->left_unbounded = true; - } - if (right_inclusive_or_null) { - fsm->right_inclusive.assign(right_inclusive_or_null); - fsm->right_unbounded = false; - } else { - fsm->right_unbounded = true; - } - - acquire_a_node(state, level, parent, block_id, acq_start_cb, fsm); -} - -// This releases its buf_lock_t parameter. -void process_a_internal_node(traversal_state_t *state, - buf_lock_t buf, - int level, - const btree_key_t *left_exclusive_or_null, - const btree_key_t *right_inclusive_or_null) { - counted_t ids_source; - { - buf_read_t read(&buf); - const internal_node_t *node - = static_cast(read.get_data_read()); - - ids_source = make_counted( - state->max_block_size, node, - left_exclusive_or_null, right_inclusive_or_null, level); - } - - subtrees_traverse(state, new internal_node_releaser_t(std::move(buf), state), - level + 1, ids_source); -} - -void process_a_leaf_node(traversal_state_t *state, buf_lock_t buf, - int level, const btree_key_t *left_exclusive_or_null, - const btree_key_t *right_inclusive_or_null) { - rassert(coro_t::self()); - int population_change = 0; - - try { - state->helper->process_a_leaf(&buf, left_exclusive_or_null, - right_inclusive_or_null, state->interruptor, &population_change); - } catch (const interrupted_exc_t &) { - rassert(state->interruptor->is_pulsed()); - /* ignore it; the backfill will come to a stop on its own now that - `interruptor` has been pulsed */ - } - - txn_t *txn = buf.txn(); - buf.reset_buf_lock(); - - if (state->helper->btree_node_mode() != access_t::write) { - rassert(population_change == 0, "A read only operation claims it change the population of a leaf.\n"); - } else if (population_change != 0 && state->stat_block != NULL_BLOCK_ID) { - // The stat block has no parent, our changes to it are commutative and - // readers expect out-of-date values. - buf_lock_t stat_block(buf_parent_t(txn), state->stat_block, access_t::write); - buf.reset_buf_lock(); - buf_write_t stat_block_write(&stat_block); - auto stat_block_buf = static_cast( - stat_block_write.get_data_write(BTREE_STATBLOCK_SIZE)); - stat_block_buf->population += population_change; - } else { - // Don't acquire the block to not change the value. - } - - if (state->helper->progress) { - state->helper->progress->inform(level, parallel_traversal_progress_t::RELEASE, parallel_traversal_progress_t::LEAF); - } - state->level_count(level) -= 1; - state->consider_pulsing(); -} - -void interesting_children_callback_t::receive_interesting_child(int child_index) { - rassert(child_index >= 0 && child_index < ids_source->num_block_ids()); - - if (state->helper->progress) { - state->helper->progress->inform(level, parallel_traversal_progress_t::LEARN, parallel_traversal_progress_t::UNKNOWN); - } - block_id_t block_id; - const btree_key_t *left_excl_or_null; - const btree_key_t *right_incl_or_null; - ids_source->get_block_id_and_bounding_interval(child_index, &block_id, &left_excl_or_null, &right_incl_or_null); - - ++acquisition_countdown; - do_a_subtree_traversal(state, level, releaser->expose_parent(), - block_id, left_excl_or_null, right_incl_or_null, this); -} - -void interesting_children_callback_t::no_more_interesting_children() { - decr_acquisition_countdown(); -} - -void interesting_children_callback_t::on_in_line() { - decr_acquisition_countdown(); -} - -void interesting_children_callback_t::decr_acquisition_countdown() { - rassert(coro_t::self()); - rassert(acquisition_countdown > 0); - --acquisition_countdown; - if (acquisition_countdown == 0) { - releaser->release(); - state->level_count(level - 1) -= 1; - if (state->helper->progress) { - state->helper->progress->inform(level - 1, parallel_traversal_progress_t::RELEASE, parallel_traversal_progress_t::INTERNAL); - } - state->consider_pulsing(); - delete this; - } -} - - -int ranged_block_ids_t::num_block_ids() const { - if (node_.has()) { - return node_->npairs; - } else { - return 1; - } -} - -void ranged_block_ids_t::get_block_id_and_bounding_interval(int index, - block_id_t *block_id_out, - const btree_key_t **left_excl_bound_out, - const btree_key_t **right_incl_bound_out) const { - if (node_.has()) { - rassert(index >= 0); - rassert(index < node_->npairs); - - const btree_internal_pair *pair = internal_node::get_pair_by_index(node_.get(), index); - *block_id_out = pair->lnode; - *right_incl_bound_out = (index == node_->npairs - 1 ? right_inclusive_or_null_ : &pair->key); - - if (index == 0) { - *left_excl_bound_out = left_exclusive_or_null_; - } else { - const btree_internal_pair *left_neighbor = internal_node::get_pair_by_index(node_.get(), index - 1); - *left_excl_bound_out = &left_neighbor->key; - } - } else { - *block_id_out = forced_block_id_; - *left_excl_bound_out = left_exclusive_or_null_; - *right_incl_bound_out = right_inclusive_or_null_; - } -} - -int ranged_block_ids_t::get_level() { - return level; -} - -void parallel_traversal_progress_t::inform(int level, action_t action, node_type_t type) { - assert_thread(); - rassert(learned.size() == acquired.size() && acquired.size() == released.size()); - rassert(level >= 0); - if (size_t(level) >= learned.size()) { - learned.resize(level + 1, 0); - acquired.resize(level + 1, 0); - released.resize(level + 1, 0); - } - - if (type == LEAF) { - if (height == -1) { - height = level; - } - rassert(height == level); - } - - switch (action) { - case LEARN: - learned[level]++; - break; - case ACQUIRE: - acquired[level]++; - break; - case RELEASE: - released[level]++; - break; - default: - unreachable(); - break; - } - rassert(learned.size() == acquired.size() && acquired.size() == released.size()); -} - -progress_completion_fraction_t parallel_traversal_progress_t::guess_completion() const { - assert_thread(); - rassert(learned.size() == acquired.size() && acquired.size() == released.size()); - - if (height == -1) { - return progress_completion_fraction_t(); - } - - /* First we compute the ratio at each stage of the acquired nodes to the - * learned nodes. This gives us a rough estimate of the branch factor at - * each level. */ - - std::vector released_to_acquired_ratios; - for (unsigned i = 0; i + 1 < learned.size(); ++i) { - released_to_acquired_ratios.push_back(static_cast(acquired[i + 1]) / std::max(1.0, released[i])); - } - - std::vector population_by_level_guesses; - population_by_level_guesses.push_back(learned[0]); - - rassert(!learned.empty()); - for (unsigned i = 0; i + 1 < learned.size(); i++) { - population_by_level_guesses.push_back(static_cast(released_to_acquired_ratios[i] * population_by_level_guesses[i])); - } - - int estimate_of_total_nodes = 0; - int total_released_nodes = 0; - for (unsigned i = 0; i < population_by_level_guesses.size(); i++) { - estimate_of_total_nodes += population_by_level_guesses[i]; - total_released_nodes += released[i]; - } - - return progress_completion_fraction_t(total_released_nodes, estimate_of_total_nodes); -} - diff --git a/src/btree/parallel_traversal.hpp b/src/btree/parallel_traversal.hpp deleted file mode 100644 index e38d7f6afa1..00000000000 --- a/src/btree/parallel_traversal.hpp +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef BTREE_PARALLEL_TRAVERSAL_HPP_ -#define BTREE_PARALLEL_TRAVERSAL_HPP_ - -#include -#include - -#include "concurrency/access.hpp" -#include "btree/traversal_progress.hpp" -#include "btree/types.hpp" -#include "buffer_cache/types.hpp" -#include "concurrency/interruptor.hpp" -#include "concurrency/signal.hpp" -#include "containers/scoped.hpp" -#include "btree/node.hpp" - -class buf_lock_t; -class buf_parent_t; -struct btree_superblock_t; -class traversal_state_t; -class parent_releaser_t; -class btree_slice_t; -struct btree_key_t; -struct internal_node_t; -class superblock_t; - -struct lock_in_line_callback_t { -public: - lock_in_line_callback_t() { } - virtual void on_in_line() = 0; -protected: - virtual ~lock_in_line_callback_t() { } -private: - DISABLE_COPYING(lock_in_line_callback_t); -}; - - -// HEY: Make this an abstract class, have two separate implementation -// classes for the "forced block id" case and the internal node case. -// (Hint: I don't really care.) -class ranged_block_ids_t : public single_threaded_countable_t { -public: - ranged_block_ids_t(block_size_t bs, const internal_node_t *node, - const btree_key_t *left_exclusive_or_null, - const btree_key_t *right_inclusive_or_null, - int _level) - : node_(bs.value()), - left_exclusive_or_null_(left_exclusive_or_null), - right_inclusive_or_null_(right_inclusive_or_null), - level(_level) - { - memcpy(node_.get(), node, bs.value()); - } - ranged_block_ids_t(block_id_t forced_block_id, - const btree_key_t *left_exclusive_or_null, - const btree_key_t *right_inclusive_or_null, - int _level) - : node_(), - forced_block_id_(forced_block_id), - left_exclusive_or_null_(left_exclusive_or_null), - right_inclusive_or_null_(right_inclusive_or_null), - level(_level) - { } - - int num_block_ids() const; - void get_block_id_and_bounding_interval(int index, - block_id_t *block_id_out, - const btree_key_t **left_excl_bound_out, - const btree_key_t **right_incl_bound_out) const; - - int get_level(); - -private: - scoped_malloc_t node_; - block_id_t forced_block_id_; - const btree_key_t *left_exclusive_or_null_; - const btree_key_t *right_inclusive_or_null_; - int level; - - DISABLE_COPYING(ranged_block_ids_t); -}; - -class interesting_children_callback_t : public lock_in_line_callback_t { -public: - // Call these function in filter_interesting_children. - void receive_interesting_child(int child_index); - void no_more_interesting_children(); - - interesting_children_callback_t( - traversal_state_t *_state, - parent_releaser_t *_releaser, - int _level, - const counted_t &_ids_source) - : state(_state), releaser(_releaser), - level(_level), acquisition_countdown(1), ids_source(_ids_source) { } - -private: - void on_in_line(); - void decr_acquisition_countdown(); - - traversal_state_t *state; - parent_releaser_t *releaser; - int level; - int acquisition_countdown; - counted_t ids_source; - - DISABLE_COPYING(interesting_children_callback_t); -}; - -class parallel_traversal_progress_t; - -struct btree_traversal_helper_t { - btree_traversal_helper_t() - : progress(nullptr) - { } - - //Before any of these other functions are called the helper gets a chance - //to look at the stat block and possibly record some values of interest - //Notice the values in the stat block do not reflect changes which are - //still traversing the tree at the time this is called. Also notice that - //this value may be null if the stat block has not been allocated yet and - //this traversal is read only (which prohibits us from allocating it) - virtual void read_stat_block(buf_lock_t *) { } - - // This is free to call mark_deleted. - virtual void process_a_leaf(buf_lock_t *leaf_node_buf, - const btree_key_t *left_exclusive_or_null, - const btree_key_t *right_inclusive_or_null, - signal_t *interruptor, - int *population_change_out) THROWS_ONLY(interrupted_exc_t) = 0; - - virtual void postprocess_internal_node(buf_lock_t *internal_node_buf) = 0; - - virtual void filter_interesting_children( - buf_parent_t parent, - ranged_block_ids_t *ids_source, - interesting_children_callback_t *cb) = 0; - - virtual access_t btree_superblock_mode() = 0; - virtual access_t btree_node_mode() = 0; - - - virtual ~btree_traversal_helper_t() { } - - parallel_traversal_progress_t *progress; -}; - -void btree_parallel_traversal( - superblock_t *superblock, - btree_traversal_helper_t *helper, - signal_t *interruptor, - release_superblock_t release_superblock = release_superblock_t::RELEASE) - THROWS_ONLY(interrupted_exc_t); - - -class parallel_traversal_progress_t : public traversal_progress_t { -public: - parallel_traversal_progress_t() : height(-1) { } - - enum action_t { - LEARN, - ACQUIRE, - RELEASE - }; - - enum node_type_t { - UNKNOWN, - INTERNAL, - LEAF - }; - - void inform(int level, action_t, node_type_t); - - progress_completion_fraction_t guess_completion() const; - -private: - std::vector learned; //How many nodes at each level we believe exist - std::vector acquired; //How many nodes at each level we've acquired - std::vector released; //How many nodes at each level we've released - - int height; //The height we've learned the tree has. Or -1 if we're still unsure; - - DISABLE_COPYING(parallel_traversal_progress_t); -}; - - -#endif // BTREE_PARALLEL_TRAVERSAL_HPP_ diff --git a/src/btree/reql_specific.cc b/src/btree/reql_specific.cc deleted file mode 100644 index 0fdfda958f7..00000000000 --- a/src/btree/reql_specific.cc +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "btree/reql_specific.hpp" - -#include "btree/secondary_operations.hpp" -#include "buffer_cache/blob.hpp" -#include "containers/binary_blob.hpp" - -/* This is the actual structure stored on disk for the superblock of a table's primary or -sindex B-tree. Both of them use the exact same format, but the sindex B-trees don't make -use of the `sindex_block` or `metainfo_blob` fields. */ -ATTR_PACKED(struct reql_btree_superblock_t { - block_magic_t magic; - block_id_t root_block; - block_id_t stat_block; - block_id_t sindex_block; - - static const int METAINFO_BLOB_MAXREFLEN - = from_ser_block_size_t::cache_size - sizeof(block_magic_t) - - 3 * sizeof(block_id_t); - - char metainfo_blob[METAINFO_BLOB_MAXREFLEN]; -}); - -static const uint32_t REQL_BTREE_SUPERBLOCK_SIZE = sizeof(reql_btree_superblock_t); - -template -struct reql_btree_version_magic_t { - static const block_magic_t value; -}; - -// All pre-2.1 values are the same, so we treat them all as v2.0.x -// Versions pre-2.1 store version_range_ts in their metainfo -template <> -const block_magic_t - reql_btree_version_magic_t::value = - { { 's', 'u', 'p', 'e' } }; - -// Versions post-2.1 store version_ts in their metainfo -template <> -const block_magic_t - reql_btree_version_magic_t::value = - { { 's', 'u', 'p', 'f' } }; - -void btree_superblock_ct_asserts() { - // Just some place to put the CT_ASSERTs - CT_ASSERT(reql_btree_superblock_t::METAINFO_BLOB_MAXREFLEN > 0); - CT_ASSERT(from_cache_block_size_t::ser_size - == DEVICE_BLOCK_SIZE); -} - -real_superblock_t::real_superblock_t(buf_lock_t &&sb_buf) - : sb_buf_(std::move(sb_buf)) {} - -real_superblock_t::real_superblock_t( - buf_lock_t &&sb_buf, - new_semaphore_in_line_t &&write_semaphore_acq) - : write_semaphore_acq_(std::move(write_semaphore_acq)), - sb_buf_(std::move(sb_buf)) {} - -void real_superblock_t::release() { - sb_buf_.reset_buf_lock(); - write_semaphore_acq_.reset(); -} - -block_id_t real_superblock_t::get_root_block_id() { - buf_read_t read(&sb_buf_); - uint32_t sb_size; - const reql_btree_superblock_t *sb_data - = static_cast(read.get_data_read(&sb_size)); - guarantee(sb_size == REQL_BTREE_SUPERBLOCK_SIZE); - return sb_data->root_block; -} - -void real_superblock_t::set_root_block_id(const block_id_t new_root_block) { - buf_write_t write(&sb_buf_); - reql_btree_superblock_t *sb_data = static_cast( - write.get_data_write(REQL_BTREE_SUPERBLOCK_SIZE)); - sb_data->root_block = new_root_block; -} - -block_id_t real_superblock_t::get_stat_block_id() { - buf_read_t read(&sb_buf_); - uint32_t sb_size; - const reql_btree_superblock_t *sb_data = - static_cast(read.get_data_read(&sb_size)); - guarantee(sb_size == REQL_BTREE_SUPERBLOCK_SIZE); - return sb_data->stat_block; -} - -block_id_t real_superblock_t::get_sindex_block_id() { - buf_read_t read(&sb_buf_); - uint32_t sb_size; - const reql_btree_superblock_t *sb_data = - static_cast(read.get_data_read(&sb_size)); - guarantee(sb_size == REQL_BTREE_SUPERBLOCK_SIZE); - return sb_data->sindex_block; -} - -sindex_superblock_t::sindex_superblock_t(buf_lock_t &&sb_buf) - : sb_buf_(std::move(sb_buf)) {} - -void sindex_superblock_t::release() { - sb_buf_.reset_buf_lock(); -} - -block_id_t sindex_superblock_t::get_root_block_id() { - buf_read_t read(&sb_buf_); - uint32_t sb_size; - const reql_btree_superblock_t *sb_data - = static_cast(read.get_data_read(&sb_size)); - guarantee(sb_size == REQL_BTREE_SUPERBLOCK_SIZE); - return sb_data->root_block; -} - -void sindex_superblock_t::set_root_block_id(const block_id_t new_root_block) { - buf_write_t write(&sb_buf_); - reql_btree_superblock_t *sb_data = static_cast( - write.get_data_write(REQL_BTREE_SUPERBLOCK_SIZE)); - sb_data->root_block = new_root_block; -} - -block_id_t sindex_superblock_t::get_stat_block_id() { - buf_read_t read(&sb_buf_); - uint32_t sb_size; - const reql_btree_superblock_t *sb_data = - static_cast(read.get_data_read(&sb_size)); - guarantee(sb_size == REQL_BTREE_SUPERBLOCK_SIZE); - return sb_data->stat_block; -} - -block_id_t sindex_superblock_t::get_sindex_block_id() { - buf_read_t read(&sb_buf_); - uint32_t sb_size; - const reql_btree_superblock_t *sb_data = - static_cast(read.get_data_read(&sb_size)); - guarantee(sb_size == REQL_BTREE_SUPERBLOCK_SIZE); - return sb_data->sindex_block; -} - -// Run backfilling at a reduced priority -#define BACKFILL_CACHE_PRIORITY 10 - -void btree_slice_t::init_real_superblock(real_superblock_t *superblock, - const std::vector &metainfo_key, - const binary_blob_t &metainfo_value) { - buf_write_t sb_write(superblock->get()); - auto sb = static_cast( - sb_write.get_data_write(REQL_BTREE_SUPERBLOCK_SIZE)); - - // Properly zero the superblock, zeroing sb->metainfo_blob, in particular. - memset(sb, 0, REQL_BTREE_SUPERBLOCK_SIZE); - - sb->magic = reql_btree_version_magic_t::value; - sb->root_block = NULL_BLOCK_ID; - sb->stat_block = create_stat_block(buf_parent_t(superblock->get()->txn())); - sb->sindex_block = NULL_BLOCK_ID; - - set_superblock_metainfo(superblock, metainfo_key, metainfo_value, - cluster_version_t::v2_1); - - buf_lock_t sindex_block(superblock->get(), alt_create_t::create); - initialize_secondary_indexes(&sindex_block); - sb->sindex_block = sindex_block.block_id(); -} - -void btree_slice_t::init_sindex_superblock(sindex_superblock_t *superblock) { - buf_write_t sb_write(superblock->get()); - auto sb = static_cast( - sb_write.get_data_write(REQL_BTREE_SUPERBLOCK_SIZE)); - - // Properly zero the superblock, zeroing sb->metainfo_blob, in particular. - memset(sb, 0, REQL_BTREE_SUPERBLOCK_SIZE); - - sb->magic = reql_btree_version_magic_t::value; - sb->root_block = NULL_BLOCK_ID; - sb->stat_block = NULL_BLOCK_ID; - sb->sindex_block = NULL_BLOCK_ID; -} - -btree_slice_t::btree_slice_t(cache_t *c, perfmon_collection_t *parent, - const std::string &identifier, - index_type_t index_type) - : stats(parent, - (index_type == index_type_t::SECONDARY ? "index-" : "") + identifier), - cache_(c), - backfill_account_(cache()->create_cache_account(BACKFILL_CACHE_PRIORITY)) { } - -btree_slice_t::~btree_slice_t() { } - -void superblock_metainfo_iterator_t::advance(char * p) { - char* cur = p; - if (cur == end) { - goto check_failed; - } - rassert(end - cur >= static_cast(sizeof(sz_t)), "Superblock metainfo data is corrupted: walked past the end off the buffer"); - if (end - cur < static_cast(sizeof(sz_t))) { - goto check_failed; - } - key_size = *reinterpret_cast(cur); - cur += sizeof(sz_t); - - rassert(end - cur >= static_cast(key_size), "Superblock metainfo data is corrupted: walked past the end off the buffer"); - if (end - cur < static_cast(key_size)) { - goto check_failed; - } - key_ptr = cur; - cur += key_size; - - rassert(end - cur >= static_cast(sizeof(sz_t)), "Superblock metainfo data is corrupted: walked past the end off the buffer"); - if (end - cur < static_cast(sizeof(sz_t))) { - goto check_failed; - } - value_size = *reinterpret_cast(cur); - cur += sizeof(sz_t); - - rassert(end - cur >= static_cast(value_size), "Superblock metainfo data is corrupted: walked past the end off the buffer"); - if (end - cur < static_cast(value_size)) { - goto check_failed; - } - value_ptr = cur; - cur += value_size; - - pos = p; - next_pos = cur; - - return; - -check_failed: - pos = next_pos = end; - key_size = value_size = 0; - key_ptr = value_ptr = nullptr; -} - -void superblock_metainfo_iterator_t::operator++() { - if (!is_end()) { - advance(next_pos); - } -} - -void get_superblock_metainfo( - real_superblock_t *superblock, - std::vector, std::vector > > *kv_pairs_out, - cluster_version_t *version_out) { - std::vector metainfo; - { - buf_read_t read(superblock->get()); - uint32_t sb_size; - const reql_btree_superblock_t *data - = static_cast(read.get_data_read(&sb_size)); - guarantee(sb_size == REQL_BTREE_SUPERBLOCK_SIZE); - - if (data->magic == reql_btree_version_magic_t::value) { - *version_out = cluster_version_t::v2_1; - } else if (data->magic == reql_btree_version_magic_t::value) { - *version_out = cluster_version_t::v2_0; - } else { - crash("Unrecognized reql_btree_superblock_t::magic found."); - } - - // The const cast is okay because we access the data with access_t::read - // and don't write to the blob. - blob_t blob(superblock->get()->cache()->max_block_size(), - const_cast(data->metainfo_blob), - reql_btree_superblock_t::METAINFO_BLOB_MAXREFLEN); - blob_acq_t acq; - buffer_group_t group; - blob.expose_all(buf_parent_t(superblock->get()), access_t::read, - &group, &acq); - - const int64_t group_size = group.get_size(); - metainfo.resize(group_size); - - buffer_group_t group_cpy; - group_cpy.add_buffer(group_size, metainfo.data()); - - buffer_group_copy_data(&group_cpy, const_view(&group)); - } - - for (superblock_metainfo_iterator_t kv_iter(metainfo.data(), metainfo.data() + metainfo.size()); !kv_iter.is_end(); ++kv_iter) { - superblock_metainfo_iterator_t::key_t key = kv_iter.key(); - superblock_metainfo_iterator_t::value_t value = kv_iter.value(); - kv_pairs_out->push_back(std::make_pair(std::vector(key.second, key.second + key.first), std::vector(value.second, value.second + value.first))); - } -} - -void set_superblock_metainfo(real_superblock_t *superblock, - const std::vector &key, - const binary_blob_t &value, - cluster_version_t version) { - std::vector > keys = {key}; - std::vector values = {value}; - set_superblock_metainfo(superblock, keys, values, version); -} - -void set_superblock_metainfo(real_superblock_t *superblock, - const std::vector > &keys, - const std::vector &values, - cluster_version_t version) { - buf_write_t write(superblock->get()); - reql_btree_superblock_t *data - = static_cast( - write.get_data_write(REQL_BTREE_SUPERBLOCK_SIZE)); - - if (version == cluster_version_t::v2_0) { - data->magic = reql_btree_version_magic_t::value; - } else if (version == cluster_version_t::v2_1) { - data->magic = reql_btree_version_magic_t::value; - } else { - crash("Unsupported version when writing metainfo."); - } - - blob_t blob(superblock->get()->cache()->max_block_size(), - data->metainfo_blob, reql_btree_superblock_t::METAINFO_BLOB_MAXREFLEN); - blob.clear(buf_parent_t(superblock->get())); - - std::vector metainfo; - - rassert(keys.size() == values.size()); - auto value_it = values.begin(); - for (auto key_it = keys.begin(); key_it != keys.end(); ++key_it, ++value_it) { - union { - char x[sizeof(uint32_t)]; - uint32_t y; - } u; - rassert(key_it->size() < UINT32_MAX); - rassert(value_it->size() < UINT32_MAX); - - u.y = key_it->size(); - metainfo.insert(metainfo.end(), u.x, u.x + sizeof(uint32_t)); - metainfo.insert(metainfo.end(), key_it->begin(), key_it->end()); - - u.y = value_it->size(); - metainfo.insert(metainfo.end(), u.x, u.x + sizeof(uint32_t)); - metainfo.insert( - metainfo.end(), - static_cast(value_it->data()), - static_cast(value_it->data()) + value_it->size()); - } - - blob.append_region(buf_parent_t(superblock->get()), metainfo.size()); - - { - blob_acq_t acq; - buffer_group_t write_group; - blob.expose_all(buf_parent_t(superblock->get()), access_t::write, - &write_group, &acq); - - buffer_group_t group_cpy; - group_cpy.add_buffer(metainfo.size(), metainfo.data()); - - buffer_group_copy_data(&write_group, const_view(&group_cpy)); - } -} - -void get_btree_superblock( - txn_t *txn, - access_t access, - scoped_ptr_t *got_superblock_out) { - buf_lock_t tmp_buf(buf_parent_t(txn), SUPERBLOCK_ID, access); - scoped_ptr_t tmp_sb(new real_superblock_t(std::move(tmp_buf))); - *got_superblock_out = std::move(tmp_sb); -} - -/* Variant for writes that go through a superblock write semaphore */ -void get_btree_superblock( - txn_t *txn, - UNUSED write_access_t access, - new_semaphore_in_line_t &&write_sem_acq, - scoped_ptr_t *got_superblock_out) { - buf_lock_t tmp_buf(buf_parent_t(txn), SUPERBLOCK_ID, access_t::write); - scoped_ptr_t tmp_sb( - new real_superblock_t(std::move(tmp_buf), std::move(write_sem_acq))); - *got_superblock_out = std::move(tmp_sb); -} - -void get_btree_superblock_and_txn_for_writing( - cache_conn_t *cache_conn, - new_semaphore_t *superblock_write_semaphore, - UNUSED write_access_t superblock_access, - int expected_change_count, - write_durability_t durability, - scoped_ptr_t *got_superblock_out, - scoped_ptr_t *txn_out) { - txn_t *txn = new txn_t(cache_conn, durability, expected_change_count); - - txn_out->init(txn); - - /* Acquire a ticket from the superblock_write_semaphore */ - new_semaphore_in_line_t sem_acq; - if(superblock_write_semaphore != nullptr) { - sem_acq.init(superblock_write_semaphore, 1); - sem_acq.acquisition_signal()->wait(); - } - - get_btree_superblock(txn, write_access_t::write, std::move(sem_acq), got_superblock_out); -} - -void get_btree_superblock_and_txn_for_backfilling( - cache_conn_t *cache_conn, - cache_account_t *backfill_account, - scoped_ptr_t *got_superblock_out, - scoped_ptr_t *txn_out) { - txn_t *txn = new txn_t(cache_conn, read_access_t::read); - txn_out->init(txn); - txn->set_account(backfill_account); - - get_btree_superblock(txn, access_t::read, got_superblock_out); - (*got_superblock_out)->get()->snapshot_subdag(); -} - -// KSI: This function is possibly stupid: it's nonsensical to talk about the entire -// cache being snapshotted -- we want some subtree to be snapshotted, at least. -// However, if you quickly release the superblock, you'll release any snapshotting of -// secondary index nodes that you could not possibly access. -void get_btree_superblock_and_txn_for_reading( - cache_conn_t *cache_conn, - cache_snapshotted_t snapshotted, - scoped_ptr_t *got_superblock_out, - scoped_ptr_t *txn_out) { - txn_t *txn = new txn_t(cache_conn, read_access_t::read); - txn_out->init(txn); - - get_btree_superblock(txn, access_t::read, got_superblock_out); - - if (snapshotted == CACHE_SNAPSHOTTED_YES) { - (*got_superblock_out)->get()->snapshot_subdag(); - } -} - diff --git a/src/btree/reql_specific.hpp b/src/btree/reql_specific.hpp deleted file mode 100644 index 0ed73a78076..00000000000 --- a/src/btree/reql_specific.hpp +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef BTREE_REQL_SPECIFIC_HPP_ -#define BTREE_REQL_SPECIFIC_HPP_ - -#include "btree/operations.hpp" - -/* Most of the code in the `btree/` directory doesn't "know" about the format of the -superblock; instead it manipulates the superblock using the abstract `superblock_t`. This -file provides the concrete superblock implementation used for ReQL primary and sindex -B-trees. It also provides functions for working with the secondary index block and the -metainfo, which are unrelated to the B-tree but stored on the ReQL primary superblock. - -`btree/secondary_operations.*` and `btree/reql_specific.*` are the only files in the -`btree/` directory that know about ReQL-specific concepts such as metainfo and sindexes. -They should probably be moved out of the `btree/` directory. */ - -class binary_blob_t; - -/* `real_superblock_t` represents the superblock for the primary B-tree of a table. */ -class real_superblock_t : public superblock_t { -public: - explicit real_superblock_t(buf_lock_t &&sb_buf); - real_superblock_t(buf_lock_t &&sb_buf, new_semaphore_in_line_t &&write_semaphore_acq); - - void release(); - buf_lock_t *get() { return &sb_buf_; } - - block_id_t get_root_block_id(); - void set_root_block_id(block_id_t new_root_block); - - block_id_t get_stat_block_id(); - - block_id_t get_sindex_block_id(); - - buf_parent_t expose_buf() { return buf_parent_t(&sb_buf_); } - -private: - /* The write_semaphore_acq_ is empty for reads. - For writes it locks the write superblock acquisition semaphore until the - sb_buf_ is released. - Note that this is used to throttle writes compared to reads, but not required - for correctness. */ - new_semaphore_in_line_t write_semaphore_acq_; - - buf_lock_t sb_buf_; -}; - -/* `sindex_superblock_t` represents the superblock for a sindex B-tree. */ -class sindex_superblock_t : public superblock_t { -public: - explicit sindex_superblock_t(buf_lock_t &&sb_buf); - - void release(); - buf_lock_t *get() { return &sb_buf_; } - - block_id_t get_root_block_id(); - void set_root_block_id(block_id_t new_root_block); - - block_id_t get_stat_block_id(); - - /* sindex superblocks shouldn't have a sindex block of their own. But in previous - versions of RethinkDB they would have an empty sindex block. This is exposed so that - we can delete such a sindex block when deleting the sindex superblock. */ - block_id_t get_sindex_block_id(); - - buf_parent_t expose_buf() { return buf_parent_t(&sb_buf_); } - -private: - buf_lock_t sb_buf_; -}; - -enum class index_type_t { - PRIMARY, - SECONDARY -}; - -/* btree_slice_t is a thin wrapper around cache_t that handles initializing the buffer -cache for the purpose of storing a B-tree. It is specific to ReQL primary and secondary -index B-trees. */ - -class btree_slice_t : public home_thread_mixin_debug_only_t { -public: - // Initializes a superblock (presumably, a buf_lock_t constructed with - // alt_create_t::create) for use with btrees, setting the initial value of the - // metainfo (with a single key/value pair). Not for use with sindex superblocks. - static void init_real_superblock(real_superblock_t *superblock, - const std::vector &metainfo_key, - const binary_blob_t &metainfo_value); - static void init_sindex_superblock(sindex_superblock_t *superblock); - - btree_slice_t(cache_t *cache, - perfmon_collection_t *parent, - const std::string &identifier, - index_type_t index_type); - - ~btree_slice_t(); - - cache_t *cache() { return cache_; } - cache_account_t *get_backfill_account() { return &backfill_account_; } - - btree_stats_t stats; - -private: - cache_t *cache_; - - // Cache account to be used when backfilling. - cache_account_t backfill_account_; - - DISABLE_COPYING(btree_slice_t); -}; - -/* This iterator encapsulates most of the metainfo data layout. Unfortunately, - * functions set_superblock_metainfo and delete_superblock_metainfo also know a - * lot about the data layout, so if it's changed, these functions must be - * changed as well. - * - * Data layout is dead simple right now, it's an array of the following - * (unaligned, unpadded) contents: - * - * sz_t key_size; - * char key[key_size]; - * sz_t value_size; - * char value[value_size]; - */ -struct superblock_metainfo_iterator_t { - typedef uint32_t sz_t; // be careful: the values of this type get casted to int64_t in checks, so it must fit - typedef std::pair key_t; - typedef std::pair value_t; - - superblock_metainfo_iterator_t(char *metainfo, char *metainfo_end) : end(metainfo_end) { advance(metainfo); } - - bool is_end() { return pos == end; } - - void operator++(); - - std::pair operator*() { - return std::make_pair(key(), value()); - } - key_t key() { return std::make_pair(key_size, key_ptr); } - value_t value() { return std::make_pair(value_size, value_ptr); } - - char *record_ptr() { return pos; } - char *next_record_ptr() { return next_pos; } - char *end_ptr() { return end; } - sz_t *value_size_ptr() { return reinterpret_cast(value_ptr) - 1; } -private: - void advance(char *p); - - char *pos; - char *next_pos; - char *end; - sz_t key_size; - char *key_ptr; - sz_t value_size; - char *value_ptr; -}; - - -// Metainfo functions -void get_superblock_metainfo( - real_superblock_t *superblock, - std::vector< std::pair, std::vector > > *kv_pairs_out, - cluster_version_t *version_out); - -void set_superblock_metainfo(real_superblock_t *superblock, - const std::vector &key, - const binary_blob_t &value, - cluster_version_t version); - -void set_superblock_metainfo(real_superblock_t *superblock, - const std::vector > &keys, - const std::vector &values, - cluster_version_t version); - -// Convenience functions for accessing the superblock -void get_btree_superblock( - txn_t *txn, - access_t access, - scoped_ptr_t *got_superblock_out); - -/* Variant for writes that go through a superblock write semaphore */ -void get_btree_superblock( - txn_t *txn, - write_access_t access, - new_semaphore_in_line_t &&write_sem_acq, - scoped_ptr_t *got_superblock_out); - -void get_btree_superblock_and_txn_for_writing( - cache_conn_t *cache_conn, - new_semaphore_t *superblock_write_semaphore, - write_access_t superblock_access, - int expected_change_count, - write_durability_t durability, - scoped_ptr_t *got_superblock_out, - scoped_ptr_t *txn_out); - -void get_btree_superblock_and_txn_for_backfilling( - cache_conn_t *cache_conn, - cache_account_t *backfill_account, - scoped_ptr_t *got_superblock_out, - scoped_ptr_t *txn_out); - -void get_btree_superblock_and_txn_for_reading( - cache_conn_t *cache_conn, - cache_snapshotted_t snapshotted, - scoped_ptr_t *got_superblock_out, - scoped_ptr_t *txn_out); - -#endif /* BTREE_REQL_SPECIFIC_HPP_ */ - diff --git a/src/btree/secondary_operations.cc b/src/btree/secondary_operations.cc deleted file mode 100644 index 311f78235fb..00000000000 --- a/src/btree/secondary_operations.cc +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "btree/secondary_operations.hpp" - -#include "btree/operations.hpp" -#include "buffer_cache/alt.hpp" -#include "buffer_cache/blob.hpp" -#include "buffer_cache/serialize_onto_blob.hpp" -#include "containers/archive/vector_stream.hpp" -#include "containers/archive/versioned.hpp" - -RDB_IMPL_SERIALIZABLE_5_SINCE_v2_2( - secondary_index_t, superblock, opaque_definition, - needs_post_construction_range, being_deleted, id); - -// Pre 2.2 we didn't have the `needs_post_construction_range` field, but instead had -// a boolean `post_construction_complete`. -// We need to specify a custom deserialization function for that: -template -archive_result_t pre_2_2_deserialize( - read_stream_t *s, secondary_index_t *sindex) { - archive_result_t res = archive_result_t::SUCCESS; - res = deserialize(s, deserialize_deref(sindex->superblock)); - if (bad(res)) { return res; } - res = deserialize(s, deserialize_deref(sindex->opaque_definition)); - if (bad(res)) { return res; } - - bool post_construction_complete = false; - res = deserialize(s, &post_construction_complete); - if (bad(res)) { return res; } - sindex->needs_post_construction_range = - post_construction_complete - ? key_range_t::empty() - : key_range_t::universe(); - - res = deserialize(s, deserialize_deref(sindex->being_deleted)); - if (bad(res)) { return res; } - res = deserialize(s, deserialize_deref(sindex->id)); - if (bad(res)) { return res; } - return res; -} -template <> archive_result_t deserialize( - read_stream_t *s, secondary_index_t *sindex) { - return pre_2_2_deserialize(s, sindex); -} -template <> archive_result_t deserialize( - read_stream_t *s, secondary_index_t *sindex) { - return pre_2_2_deserialize(s, sindex); -} -template <> archive_result_t deserialize( - read_stream_t *s, secondary_index_t *sindex) { - return pre_2_2_deserialize(s, sindex); -} -template <> archive_result_t deserialize( - read_stream_t *s, secondary_index_t *sindex) { - return pre_2_2_deserialize(s, sindex); -} -template <> archive_result_t deserialize( - read_stream_t *s, secondary_index_t *sindex) { - return pre_2_2_deserialize(s, sindex); -} - -RDB_IMPL_SERIALIZABLE_2_SINCE_v1_13(sindex_name_t, name, being_deleted); - -ATTR_PACKED(struct btree_sindex_block_t { - static const int SINDEX_BLOB_MAXREFLEN = 4076; - - block_magic_t magic; - char sindex_blob[SINDEX_BLOB_MAXREFLEN]; -}); - -template -struct btree_sindex_block_magic_t { - static const block_magic_t value; -}; - -block_magic_t v1_13_sindex_block_magic = { { 's', 'i', 'n', 'd' } }; -template <> -const block_magic_t -btree_sindex_block_magic_t::value - = { { 's', 'i', 'n', 'e' } }; -template <> -const block_magic_t -btree_sindex_block_magic_t::value - = { { 's', 'i', 'n', 'f' } }; -template <> -const block_magic_t -btree_sindex_block_magic_t::value - = { { 's', 'i', 'n', 'g' } }; -template <> -const block_magic_t -btree_sindex_block_magic_t::value - = { { 's', 'i', 'n', 'h' } }; -template <> -const block_magic_t -btree_sindex_block_magic_t::value - = { { 's', 'i', 'n', 'i' } }; -template <> -const block_magic_t -btree_sindex_block_magic_t::value - = { { 's', 'i', 'n', 'j' } }; -template <> -const block_magic_t -btree_sindex_block_magic_t::value - = { { 's', 'i', 'n', 'k' } }; -template <> -const block_magic_t -btree_sindex_block_magic_t::value - = { { 's', 'i', 'n', 'l' } }; - -cluster_version_t sindex_block_version(const btree_sindex_block_t *data) { - if (data->magic == v1_13_sindex_block_magic) { - fail_due_to_user_error( - "Found a secondary index from unsupported RethinkDB version 1.13. " - "You can migrate this secondary index using RethinkDB 2.0."); - } else if (data->magic - == btree_sindex_block_magic_t::value) { - return cluster_version_t::v1_14; - } else if (data->magic - == btree_sindex_block_magic_t::value) { - return cluster_version_t::v1_15; - } else if (data->magic - == btree_sindex_block_magic_t::value) { - return cluster_version_t::v1_16; - } else if (data->magic - == btree_sindex_block_magic_t::value) { - return cluster_version_t::v2_0; - } else if (data->magic - == btree_sindex_block_magic_t::value) { - return cluster_version_t::v2_1; - } else if (data->magic - == btree_sindex_block_magic_t::value) { - return cluster_version_t::v2_2; - } else if (data->magic - == btree_sindex_block_magic_t< - cluster_version_t::v2_3>::value) { - return cluster_version_t::v2_3; - } else if (data->magic - == btree_sindex_block_magic_t< - cluster_version_t::v2_4_is_latest_disk>::value) { - return cluster_version_t::v2_4_is_latest_disk; - } else { - crash("Unexpected magic in btree_sindex_block_t."); - } -} - -void get_secondary_indexes_internal( - buf_lock_t *sindex_block, - std::map *sindexes_out) { - buf_read_t read(sindex_block); - const btree_sindex_block_t *data - = static_cast(read.get_data_read()); - - blob_t sindex_blob(sindex_block->cache()->max_block_size(), - const_cast(data->sindex_blob), - btree_sindex_block_t::SINDEX_BLOB_MAXREFLEN); - deserialize_for_version_from_blob(sindex_block_version(data), - buf_parent_t(sindex_block), &sindex_blob, sindexes_out); -} - -void set_secondary_indexes_internal( - buf_lock_t *sindex_block, - const std::map &sindexes) { - buf_write_t write(sindex_block); - btree_sindex_block_t *data - = static_cast(write.get_data_write()); - - blob_t sindex_blob(sindex_block->cache()->max_block_size(), - data->sindex_blob, - btree_sindex_block_t::SINDEX_BLOB_MAXREFLEN); - // There's just one field in btree_sindex_block_t, sindex_blob. So we set - // the magic to the latest value and serialize with the latest version. - data->magic = btree_sindex_block_magic_t::value; - serialize_onto_blob( - buf_parent_t(sindex_block), &sindex_blob, sindexes); -} - -void initialize_secondary_indexes(buf_lock_t *sindex_block) { - buf_write_t write(sindex_block); - btree_sindex_block_t *data - = static_cast(write.get_data_write()); - data->magic = btree_sindex_block_magic_t::value; - memset(data->sindex_blob, 0, btree_sindex_block_t::SINDEX_BLOB_MAXREFLEN); - - set_secondary_indexes_internal(sindex_block, - std::map()); -} - -bool get_secondary_index(buf_lock_t *sindex_block, const sindex_name_t &name, - secondary_index_t *sindex_out) { - std::map sindex_map; - - get_secondary_indexes_internal(sindex_block, &sindex_map); - - auto it = sindex_map.find(name); - if (it != sindex_map.end()) { - *sindex_out = it->second; - return true; - } else { - return false; - } -} - -bool get_secondary_index(buf_lock_t *sindex_block, uuid_u id, - secondary_index_t *sindex_out) { - std::map sindex_map; - - get_secondary_indexes_internal(sindex_block, &sindex_map); - for (auto it = sindex_map.begin(); it != sindex_map.end(); ++it) { - if (it->second.id == id) { - *sindex_out = it->second; - return true; - } - } - return false; -} - -void get_secondary_indexes(buf_lock_t *sindex_block, - std::map *sindexes_out) { - get_secondary_indexes_internal(sindex_block, sindexes_out); -} - -void migrate_secondary_index_block(buf_lock_t *sindex_block) { - cluster_version_t block_version; - { - buf_read_t read(sindex_block); - const btree_sindex_block_t *data - = static_cast(read.get_data_read()); - block_version = sindex_block_version(data); - } - - std::map sindexes; - get_secondary_indexes_internal(sindex_block, &sindexes); - if (block_version != cluster_version_t::LATEST_DISK) { - set_secondary_indexes_internal(sindex_block, sindexes); - } -} - -void set_secondary_index(buf_lock_t *sindex_block, const sindex_name_t &name, - const secondary_index_t &sindex) { - std::map sindex_map; - get_secondary_indexes_internal(sindex_block, &sindex_map); - - /* We insert even if it already exists overwriting the old value. */ - sindex_map[name] = sindex; - set_secondary_indexes_internal(sindex_block, sindex_map); -} - -void set_secondary_index(buf_lock_t *sindex_block, uuid_u id, - const secondary_index_t &sindex) { - std::map sindex_map; - get_secondary_indexes_internal(sindex_block, &sindex_map); - - for (auto it = sindex_map.begin(); it != sindex_map.end(); ++it) { - if (it->second.id == id) { - guarantee(sindex.id == id, "This shouldn't change the id."); - it->second = sindex; - } - } - set_secondary_indexes_internal(sindex_block, sindex_map); -} - -bool delete_secondary_index(buf_lock_t *sindex_block, const sindex_name_t &name) { - std::map sindex_map; - get_secondary_indexes_internal(sindex_block, &sindex_map); - - if (sindex_map.erase(name) == 1) { - set_secondary_indexes_internal(sindex_block, sindex_map); - return true; - } else { - return false; - } -} - diff --git a/src/btree/secondary_operations.hpp b/src/btree/secondary_operations.hpp deleted file mode 100644 index 6afbf713bc7..00000000000 --- a/src/btree/secondary_operations.hpp +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef BTREE_SECONDARY_OPERATIONS_HPP_ -#define BTREE_SECONDARY_OPERATIONS_HPP_ - -#include -#include -#include - -#include "btree/keys.hpp" -#include "buffer_cache/types.hpp" -#include "containers/archive/archive.hpp" -#include "containers/uuid.hpp" -#include "rpc/serialize_macros.hpp" -#include "serializer/types.hpp" - -/* This file contains code for working with the sindex block, which is a child of the -table's primary superblock. - -`btree/secondary_operations.*` and `btree/reql_specific.*` are the only files in the -`btree/` directory that know about ReQL-specific concepts such as metainfo and sindexes. -They should probably be moved out of the `btree/` directory. */ - -class buf_lock_t; - -struct secondary_index_t { - secondary_index_t() - : superblock(NULL_BLOCK_ID), - needs_post_construction_range(key_range_t::universe()), - being_deleted(false), - id(generate_uuid()) { } - - /* A virtual superblock. */ - block_id_t superblock; - - /* Whether the index still needs to be post constructed, and/or is being deleted. - Note that an index can be in any combination of those states. */ - key_range_t needs_post_construction_range; - bool being_deleted; - - /* Note that this is even still relevant if the index is being deleted. In that case - it tells us whether the index had completed post constructing before it got deleted - or not. That is relevant because once an index got post-constructed, there can be - snapshotted read queries that are still accessing it, and we must detach any - values that we are deleting from the index. - If on the other hand the index never finished post-construction, we must not detach - values because they might be pointing to blocks that no longer exist (in general a - not fully constructed index can be in an inconsistent state). */ - bool post_construction_complete() const { - return needs_post_construction_range.is_empty(); - } - - /* Determines whether it's ok to query the index. */ - bool is_ready() const { - return post_construction_complete() && !being_deleted; - } - - /* An opaque blob that describes the index. See serialize_sindex_info and - deserialize_sindex_info. At one point it contained a serialized map_wire_func_t - and a sindex_multi_bool_t. Now it also contains reql version info. (This being - a std::vector is a holdover from when we had multiple protocols.) */ - std::vector opaque_definition; - - /* Sindexes contain a uuid_u to prevent a rapid deletion and recreation of - * a sindex with the same name from tricking a post construction in to - * switching targets. This issue is described in more detail here: - * https://github.com/rethinkdb/rethinkdb/issues/657 */ - uuid_u id; -}; - -RDB_DECLARE_SERIALIZABLE(secondary_index_t); - -struct sindex_name_t { - sindex_name_t() - : name(""), being_deleted(false) { } - explicit sindex_name_t(const std::string &n) - : name(n), being_deleted(false) { } - - bool operator<(const sindex_name_t &other) const { - return (being_deleted && !other.being_deleted) || - (being_deleted == other.being_deleted && name < other.name); - } - bool operator==(const sindex_name_t &other) const { - return being_deleted == other.being_deleted && name == other.name; - } - - std::string name; - // This additional bool in `sindex_name_t` makes sure that the name - // of an index that's being deleted can never conflict with any newly created - // index. - bool being_deleted; -}; - -RDB_DECLARE_SERIALIZABLE(sindex_name_t); - -//Secondary Index functions - -/* Note if this function is called after secondary indexes have been added it - * will leak blocks (and also make those secondary indexes unusable.) There's - * no reason to ever do this. */ -void initialize_secondary_indexes(buf_lock_t *superblock); - -bool get_secondary_index(buf_lock_t *sindex_block, - const sindex_name_t &name, - secondary_index_t *sindex_out); - -bool get_secondary_index(buf_lock_t *sindex_block, uuid_u id, - secondary_index_t *sindex_out); - -void get_secondary_indexes(buf_lock_t *sindex_block, - std::map *sindexes_out); - -/* Rewrites the secondary index block with up-to-date serialization */ -void migrate_secondary_index_block(buf_lock_t *sindex_block); - -/* Overwrites existing values with the same id. */ -void set_secondary_index(buf_lock_t *sindex_block, - const sindex_name_t &name, const secondary_index_t &sindex); - -/* Must be used to overwrite an already existing sindex. */ -void set_secondary_index(buf_lock_t *sindex_block, uuid_u id, - const secondary_index_t &sindex); - -// XXX note this just drops the entry. It doesn't cleanup the btree that it points -// to. `drop_sindex` Does both and should be used publicly. -bool delete_secondary_index(buf_lock_t *sindex_block, const sindex_name_t &name); - -#endif /* BTREE_SECONDARY_OPERATIONS_HPP_ */ diff --git a/src/btree/stats.hpp b/src/btree/stats.hpp deleted file mode 100644 index 3f148b6bd61..00000000000 --- a/src/btree/stats.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef BTREE_STATS_HPP_ -#define BTREE_STATS_HPP_ - -#include "perfmon/perfmon.hpp" - -class btree_stats_t { -public: - explicit btree_stats_t(perfmon_collection_t *parent, - const std::string &identifier) - : btree_collection(), - pm_keys_read(secs_to_ticks(1)), - pm_keys_set(secs_to_ticks(1)), - pm_keys_membership(&btree_collection, - &pm_keys_read, "keys_read", - &pm_total_keys_read, "total_keys_read", - &pm_keys_set, "keys_set", - &pm_total_keys_set, "total_keys_set") { - if (parent != nullptr) { - rename(parent, identifier); - } - } - - void hide() { - btree_collection_membership.reset(); - } - - void rename(perfmon_collection_t *parent, - const std::string &identifier) { - btree_collection_membership.reset(); - btree_collection_membership.init(new perfmon_membership_t( - parent, - &btree_collection, - "btree-" + identifier)); - } - - perfmon_collection_t btree_collection; - scoped_ptr_t btree_collection_membership; - perfmon_rate_monitor_t - pm_keys_read, - pm_keys_set; - perfmon_counter_t - pm_total_keys_read, - pm_total_keys_set; - perfmon_multi_membership_t pm_keys_membership; -}; - - -#endif // BTREE_STATS_HPP_ diff --git a/src/btree/superblock.hpp b/src/btree/superblock.hpp deleted file mode 100644 index 5c8a942099b..00000000000 --- a/src/btree/superblock.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef BTREE_SUPERBLOCK_HPP_ -#define BTREE_SUPERBLOCK_HPP_ - -#include "btree/operations.hpp" - -class refcount_superblock_t : public superblock_t { -public: - refcount_superblock_t(superblock_t *sb, int rc) : - sub_superblock(sb), refcount(rc) { } - - void release() { - refcount--; - rassert(refcount >= 0); - if (refcount == 0) { - sub_superblock->release(); - sub_superblock = nullptr; - } - } - - block_id_t get_root_block_id() { - return sub_superblock->get_root_block_id(); - } - - void set_root_block_id(block_id_t new_root_block) { - sub_superblock->set_root_block_id(new_root_block); - } - - block_id_t get_stat_block_id() { - return sub_superblock->get_stat_block_id(); - } - - buf_parent_t expose_buf() { - return sub_superblock->expose_buf(); - } - -private: - superblock_t *sub_superblock; - int refcount; - - DISABLE_COPYING(refcount_superblock_t); -}; - - -#endif // BTREE_SUPERBLOCK_HPP_ diff --git a/src/btree/traversal_progress.cc b/src/btree/traversal_progress.cc deleted file mode 100644 index 318804d71bc..00000000000 --- a/src/btree/traversal_progress.cc +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "btree/traversal_progress.hpp" - -#include - -#include "concurrency/pmap.hpp" -#include "containers/scoped.hpp" -#include "utils.hpp" - -traversal_progress_combiner_t::~traversal_progress_combiner_t() { - guarantee(!is_destructing); - is_destructing = true; - pmap(constituents.size(), - std::bind(&traversal_progress_combiner_t::destroy_constituent, this, ph::_1)); -} - -void traversal_progress_combiner_t::destroy_constituent(int i) { - on_thread_t th(constituents[i]->home_thread()); - delete constituents[i]; -} - -void traversal_progress_combiner_t::add_constituent(scoped_ptr_t *c) { - assert_thread(); - guarantee(!is_destructing); - constituents.push_back(c->release()); -} - -void traversal_progress_combiner_t::get_constituent_fraction(int i, std::vector *outputs) const { - guarantee(size_t(i) < constituents.size()); - rassert(size_t(i) < outputs->size()); - guarantee(!is_destructing); - - on_thread_t th(constituents[i]->home_thread()); - (*outputs)[i] = constituents[i]->guess_completion(); -} - -progress_completion_fraction_t traversal_progress_combiner_t::guess_completion() const { - assert_thread(); - guarantee(!is_destructing); - - int released = 0, total = 0; - - std::vector fractions(constituents.size(), progress_completion_fraction_t()); - pmap(fractions.size(), std::bind(&traversal_progress_combiner_t::get_constituent_fraction, this, ph::_1, &fractions)); - - for (std::vector::const_iterator it = fractions.begin(); - it != fractions.end(); - ++it) { - if (it->invalid()) { - return progress_completion_fraction_t(); - } - - released += it->estimate_of_released_nodes; - total += it->estimate_of_total_nodes; - } - - return progress_completion_fraction_t(released, total); -} diff --git a/src/btree/traversal_progress.hpp b/src/btree/traversal_progress.hpp deleted file mode 100644 index 88246c6589a..00000000000 --- a/src/btree/traversal_progress.hpp +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef BTREE_TRAVERSAL_PROGRESS_HPP_ -#define BTREE_TRAVERSAL_PROGRESS_HPP_ - -#include - -#include "errors.hpp" -#include "threading.hpp" - -template class scoped_ptr_t; - -struct progress_completion_fraction_t { - progress_completion_fraction_t() : estimate_of_released_nodes(-1), estimate_of_total_nodes(-1) { } - - progress_completion_fraction_t(int _released, int _total) : estimate_of_released_nodes(_released), estimate_of_total_nodes(_total) { - rassert(0 <= estimate_of_released_nodes && estimate_of_released_nodes <= estimate_of_total_nodes); - } - - int64_t estimate_of_released_nodes; - int64_t estimate_of_total_nodes; - - bool invalid() const { return estimate_of_total_nodes == -1; } - - double as_double() const { - if (invalid()) { - return 0; - } - - return static_cast(estimate_of_released_nodes) / estimate_of_total_nodes; - } -}; - -class traversal_progress_t : public home_thread_mixin_t { -public: - traversal_progress_t() { } - explicit traversal_progress_t(threadnum_t specified_home_thread) - : home_thread_mixin_t(specified_home_thread) { } - - virtual progress_completion_fraction_t guess_completion() const = 0; - - // This actually gets used, by traversal_progress_combiner_t. - virtual ~traversal_progress_t() { } -private: - DISABLE_COPYING(traversal_progress_t); -}; - -class traversal_progress_combiner_t : public traversal_progress_t { -public: - explicit traversal_progress_combiner_t(threadnum_t specified_home_thread) - : traversal_progress_t(specified_home_thread), is_destructing(false) { } - traversal_progress_combiner_t() : is_destructing(false) { } - ~traversal_progress_combiner_t(); - - // The constituent is welcome to have a different home thread. - void add_constituent(scoped_ptr_t *constituent); - progress_completion_fraction_t guess_completion() const; - -private: - // Used in a pmap by the destructor. - void destroy_constituent(int i); - - // Used by guess_completion in a pmap call to go to the - // constituent's home thread. - void get_constituent_fraction(int i, std::vector *outputs) const; - - // constituents _owns_ these pointers. - std::vector constituents; - - bool is_destructing; - - DISABLE_COPYING(traversal_progress_combiner_t); -}; -#endif // BTREE_TRAVERSAL_PROGRESS_HPP_ diff --git a/src/btree/types.hpp b/src/btree/types.hpp deleted file mode 100644 index 7bed95e89c4..00000000000 --- a/src/btree/types.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef BTREE_TYPES_HPP_ -#define BTREE_TYPES_HPP_ - -#include "errors.hpp" - -class buf_parent_t; - -enum class continue_bool_t { CONTINUE = 0, ABORT = 1 }; - -class value_deleter_t { -public: - value_deleter_t() { } - virtual void delete_value(buf_parent_t leaf_node, const void *value) const = 0; - -protected: - virtual ~value_deleter_t() { } - - DISABLE_COPYING(value_deleter_t); -}; - -enum class release_superblock_t {RELEASE, KEEP}; - -enum class is_stamp_read_t { NO, YES }; - -#endif /* BTREE_TYPES_HPP_ */ diff --git a/src/buffer_cache/alt.cc b/src/buffer_cache/alt.cc deleted file mode 100644 index 7f39fe02d43..00000000000 --- a/src/buffer_cache/alt.cc +++ /dev/null @@ -1,846 +0,0 @@ -#include "buffer_cache/alt.hpp" - -#include - -#include "arch/types.hpp" -#include "arch/runtime/coroutines.hpp" -#include "buffer_cache/stats.hpp" -#include "concurrency/auto_drainer.hpp" -#include "utils.hpp" - -#define ALT_DEBUG 0 - -using alt::block_version_t; -using alt::current_page_acq_t; -using alt::page_acq_t; -using alt::page_cache_t; -using alt::page_t; -using alt::page_txn_t; -using alt::throttler_acq_t; - -const int64_t MINIMUM_SOFT_UNWRITTEN_CHANGES_LIMIT = 1; -const int64_t SOFT_UNWRITTEN_CHANGES_LIMIT = 8000; -const double SOFT_UNWRITTEN_CHANGES_MEMORY_FRACTION = 0.5; - -// In addition to the data blocks themselves, transactions that are not completely -// flushed yet consume memory for the index writes and general metadata. If -// there are a lot of soft durability transactions, these can accumulate and consume -// an increasing amount of RAM. Hence we limit the number of unwritten index -// updates in addition to the number of unwritten blocks. We scale that limit -// proportionally to the unwritten block changes limit -const int64_t INDEX_CHANGES_LIMIT_FACTOR = 5; - -// There are very few ASSERT_NO_CORO_WAITING calls (instead we have -// ASSERT_FINITE_CORO_WAITING) because most of the time we're at the mercy of the -// page cache, which often may need to load or evict blocks, which may involve a -// spawn_now call. - - -// The intrusive list of alt_snapshot_node_t contains all the snapshot nodes for a -// given block id, in order by version. (See cache_t::snapshot_nodes_by_block_id_.) -class alt_snapshot_node_t : public intrusive_list_node_t { -public: - explicit alt_snapshot_node_t(scoped_ptr_t &&acq); - ~alt_snapshot_node_t(); - -private: - friend class buf_lock_t; - friend class cache_t; - - // This is never null (and is always a current_page_acq_t that has had - // declare_snapshotted() called). - scoped_ptr_t current_page_acq_; - - // A NULL pointer associated with a block id indicates that the block is deleted. - std::map children_; - - // The number of buf_lock_t's referring to this node, plus the number of - // alt_snapshot_node_t's referring to this node (via its children_ vector). - int64_t ref_count_; - - - DISABLE_COPYING(alt_snapshot_node_t); -}; - -alt_txn_throttler_t::alt_txn_throttler_t(int64_t minimum_unwritten_changes_limit) - : minimum_unwritten_changes_limit_(minimum_unwritten_changes_limit), - unwritten_block_changes_semaphore_(SOFT_UNWRITTEN_CHANGES_LIMIT), - unwritten_index_changes_semaphore_( - SOFT_UNWRITTEN_CHANGES_LIMIT * INDEX_CHANGES_LIMIT_FACTOR) { } - -alt_txn_throttler_t::~alt_txn_throttler_t() { } - -throttler_acq_t alt_txn_throttler_t::begin_txn_or_throttle(int64_t expected_change_count) { - throttler_acq_t acq; - acq.index_changes_semaphore_acq_.init( - &unwritten_index_changes_semaphore_, - expected_change_count); - acq.index_changes_semaphore_acq_.acquisition_signal()->wait(); - acq.block_changes_semaphore_acq_.init( - &unwritten_block_changes_semaphore_, - expected_change_count); - acq.block_changes_semaphore_acq_.acquisition_signal()->wait(); - return acq; -} - -void alt_txn_throttler_t::end_txn(UNUSED throttler_acq_t acq) { - // Just let the acq destructor do its thing. -} - -void alt_txn_throttler_t::inform_memory_limit_change(uint64_t memory_limit, - const block_size_t max_block_size) { - int64_t throttler_limit = std::min(SOFT_UNWRITTEN_CHANGES_LIMIT, - (memory_limit / max_block_size.ser_value()) * SOFT_UNWRITTEN_CHANGES_MEMORY_FRACTION); - - // Always provide at least one capacity in the semaphore - throttler_limit = std::max(throttler_limit, minimum_unwritten_changes_limit_); - - unwritten_index_changes_semaphore_.set_capacity( - throttler_limit * INDEX_CHANGES_LIMIT_FACTOR); - unwritten_block_changes_semaphore_.set_capacity(throttler_limit); -} - -cache_t::cache_t(serializer_t *serializer, - cache_balancer_t *balancer, - perfmon_collection_t *perfmon_collection) - : throttler_(MINIMUM_SOFT_UNWRITTEN_CHANGES_LIMIT), - page_cache_(serializer, balancer, &throttler_), - stats_(make_scoped(&page_cache_, perfmon_collection)) { } - -cache_t::~cache_t() { - guarantee(snapshot_nodes_by_block_id_.empty()); -} - -cache_account_t cache_t::create_cache_account(int priority) { - return page_cache_.create_cache_account(priority); -} - -alt_snapshot_node_t * -cache_t::matching_snapshot_node_or_null(block_id_t block_id, - block_version_t block_version) { - ASSERT_NO_CORO_WAITING; - auto list_it = snapshot_nodes_by_block_id_.find(block_id); - if (list_it == snapshot_nodes_by_block_id_.end()) { - return nullptr; - } - intrusive_list_t *list = &list_it->second; - for (alt_snapshot_node_t *p = list->tail(); p != nullptr; p = list->prev(p)) { - if (p->current_page_acq_->block_version() == block_version) { - return p; - } - } - return nullptr; -} - -void cache_t::add_snapshot_node(block_id_t block_id, - alt_snapshot_node_t *node) { - ASSERT_NO_CORO_WAITING; - snapshot_nodes_by_block_id_[block_id].push_back(node); -} - -void cache_t::remove_snapshot_node(block_id_t block_id, alt_snapshot_node_t *node) { - ASSERT_FINITE_CORO_WAITING; - // In some hypothetical cache data structure (a disk backed queue) we could have - // a long linked list of snapshot nodes. So we avoid _recursively_ removing - // snapshot nodes. - - // Nodes to be deleted. - std::stack > stack; - stack.push(std::make_pair(block_id, node)); - - while (!stack.empty()) { - auto pair = stack.top(); - stack.pop(); - // Step 1. Remove the node to be deleted from its list in - // snapshot_nodes_by_block_id_. - auto list_it = snapshot_nodes_by_block_id_.find(pair.first); - rassert(list_it != snapshot_nodes_by_block_id_.end()); - list_it->second.remove(pair.second); - if (list_it->second.empty()) { - snapshot_nodes_by_block_id_.erase(list_it); - } - - const std::map children - = std::move(pair.second->children_); - // Step 2. Destroy the node. - delete pair.second; - - // Step 3. Take its children and reduce their reference count, readying them - // for deletion if necessary. - for (auto it = children.begin(); it != children.end(); ++it) { -#if ALT_DEBUG - debugf("decring child %p from parent %p (in %p)\n", - it->second, pair.second, this); -#endif - if (it->second != nullptr) { - --it->second->ref_count_; - if (it->second->ref_count_ == 0) { -#if ALT_DEBUG - debugf("removing child %p from parent %p (in %p)\n", - it->second, pair.second, this); -#endif - stack.push(*it); - } - } - } - } -} - -txn_t::txn_t(cache_conn_t *cache_conn, - read_access_t) - : cache_(cache_conn->cache()), - cache_account_(cache_->page_cache_.default_reads_account()), - access_(access_t::read), - durability_(write_durability_t::SOFT), - is_committed_(false) { - // Right now, cache_conn is only used to control flushing of write txns. When we - // need to support other cache_conn_t related features, we'll need to do something - // fancier with read txns on cache conns. - help_construct(0, nullptr); -} - -txn_t::txn_t(cache_conn_t *cache_conn, - write_durability_t durability, - int64_t expected_change_count) - : cache_(cache_conn->cache()), - cache_account_(cache_->page_cache_.default_reads_account()), - access_(access_t::write), - durability_(durability), - is_committed_(false) { - - help_construct(expected_change_count, cache_conn); -} - -void txn_t::help_construct(int64_t expected_change_count, - cache_conn_t *cache_conn) { - cache_->assert_thread(); - guarantee(expected_change_count >= 0); - // We skip the throttler for read transactions. - // Note that this allows read transactions to skip ahead of writes. - if (access_ == access_t::write) { - // To more easily detect code that assumes that transaction creation - // does not block, we always yield in debug mode. - DEBUG_ONLY_CODE(coro_t::yield_ordered()); - } - throttler_acq_t throttler_acq( - access_ == access_t::write - ? cache_->throttler_.begin_txn_or_throttle(expected_change_count) - : throttler_acq_t()); - - ASSERT_FINITE_CORO_WAITING; - - page_txn_.init(new page_txn_t(&cache_->page_cache_, - std::move(throttler_acq), - cache_conn)); -} - -void txn_t::inform_tracker(cache_t *cache, throttler_acq_t *throttler_acq) { - cache->throttler_.end_txn(std::move(*throttler_acq)); -} - -void txn_t::pulse_and_inform_tracker(cache_t *cache, - throttler_acq_t *throttler_acq, - cond_t *pulsee) { - inform_tracker(cache, throttler_acq); - pulsee->pulse(); -} - -txn_t::~txn_t() { - guarantee(access_ == access_t::read || is_committed_, - "A transaction was aborted. To avoid data corruption, we're " - "terminating the server. Please report this bug."); - - if (access_ == access_t::read) { - cache_->page_cache_.end_read_txn(std::move(page_txn_)); - } -} - -void txn_t::commit() { - cache_->assert_thread(); - - guarantee(!is_committed_); - guarantee(access_ == access_t::write); - is_committed_ = true; - - if (durability_ == write_durability_t::SOFT) { - cache_->page_cache_.flush_and_destroy_txn(std::move(page_txn_), - std::bind(&txn_t::inform_tracker, - cache_, - ph::_1)); - } else { - cond_t cond; - cache_->page_cache_.flush_and_destroy_txn( - std::move(page_txn_), - std::bind(&txn_t::pulse_and_inform_tracker, - cache_, ph::_1, &cond)); - cond.wait(); - } -} - -void txn_t::set_account(cache_account_t *cache_account) { - cache_account_ = cache_account; -} - - -alt_snapshot_node_t::alt_snapshot_node_t(scoped_ptr_t &&acq) - : current_page_acq_(std::move(acq)), ref_count_(0) { } - -alt_snapshot_node_t::~alt_snapshot_node_t() { - // The only thing that deletes an alt_snapshot_node_t should be the - // remove_snapshot_node function. - rassert(ref_count_ == 0); - rassert(current_page_acq_.has()); - rassert(children_.empty()); -} - -buf_lock_t::buf_lock_t() - : txn_(nullptr), - current_page_acq_(), - snapshot_node_(nullptr), - access_ref_count_(0) { } - -#if ALT_DEBUG -const char *show(access_t access) { - return access == access_t::read ? "read" : "write"; -} -#endif - -alt_snapshot_node_t *buf_lock_t::help_make_child(cache_t *cache, - block_id_t child_id) { - // KSI: This allocation is sometimes just unnecessary, right? - auto acq = make_scoped(&cache->page_cache_, child_id, - read_access_t::read); - - alt_snapshot_node_t *child - = cache->matching_snapshot_node_or_null(child_id, acq->block_version()); - - if (child != nullptr) { - acq.reset(); - } else { - acq->declare_snapshotted(); - child = new alt_snapshot_node_t(std::move(acq)); - cache->add_snapshot_node(child_id, child); - } - return child; -} - -// Returns a child snapshot node (using the current version of the block) for the -// specified child block id. -alt_snapshot_node_t * -buf_lock_t::get_or_create_child_snapshot_node(cache_t *cache, - alt_snapshot_node_t *parent, - block_id_t child_id) { - ASSERT_FINITE_CORO_WAITING; - auto it = parent->children_.find(child_id); - if (it == parent->children_.end()) { - // There's no child for the snapshot node for this child id. That means a - // write transaction has not yet acquired [1]. So we create a child using - // the current version of the block. - // - // [1] assuming the cache is used properly, with the child always acquired - // via the parent, or detached, before modification - alt_snapshot_node_t *child = help_make_child(cache, child_id); - - // We don't attach the snapshot node to its parent, because this is a read - // transaction. - return child; - } else { - return it->second; - } -} - -// When a write transaction acquires a child of a parent node (for write), it needs -// to save the current value of the child buf and attach it to any of the parent's -// snapshot nodes (but only those that aren't newer than our acquisition of the -// parent [1]). -// -// [1] We might need to be careful about this because if you have two write -// acquisitions, W_X and W_Y of block B1 (where W_X acquires B1 before W_Y does), -// which then both acquire the child B2, you only want the child of acquisition W1 to -// attach to children of snapshot nodes that happened _before_ W_X and not after. -// This can happen -- the snapshot nodes that happen _after_ W_X wouldn't have -// acquired the block yet, but they'd exist, and they'd be in line, waiting for their -// turn. - -void buf_lock_t::create_child_snapshot_attachments(cache_t *cache, - block_version_t parent_version, - block_id_t parent_id, - block_id_t child_id) { - ASSERT_FINITE_CORO_WAITING; - // We create at most one child snapshot node. - - alt_snapshot_node_t *child = nullptr; - auto list_it = cache->snapshot_nodes_by_block_id_.find(parent_id); - if (list_it == cache->snapshot_nodes_by_block_id_.end()) { - return; - } - for (alt_snapshot_node_t *p = list_it->second.tail(); - p != nullptr; - p = list_it->second.prev(p)) { - auto it = p->children_.find(child_id); - if (it != p->children_.end()) { - // Already has a child, continue. - continue; - } - if (p->current_page_acq_->block_version() >= parent_version) { - // Version of snapshot node is _after_ the parent's version. - continue; - } - - if (child == nullptr) { - child = help_make_child(cache, child_id); - } - - // Attach the child to its parent. - child->ref_count_++; - p->children_.insert(std::make_pair(child_id, child)); - } -} - -// Puts markers in the parent saying that no such child exists. -void buf_lock_t::create_empty_child_snapshot_attachments(cache_t *cache, - block_version_t parent_version, - block_id_t parent_id, - block_id_t child_id) { - ASSERT_NO_CORO_WAITING; - - auto list_it = cache->snapshot_nodes_by_block_id_.find(parent_id); - if (list_it == cache->snapshot_nodes_by_block_id_.end()) { - return; - } - for (alt_snapshot_node_t *p = list_it->second.tail(); - p != nullptr; - p = list_it->second.prev(p)) { - auto it = p->children_.find(child_id); - if (it != p->children_.end()) { - // Already has a child, continue. - continue; - } - if (p->current_page_acq_->block_version() >= parent_version) { - // Version of snapshot node is _after_ the parent's version. - continue; - } - - p->children_.insert(std::make_pair(child_id, - static_cast(nullptr))); - } -} - -void buf_lock_t::help_construct(buf_parent_t parent, block_id_t _block_id, - access_t _access) { - buf_lock_t::wait_for_parent(parent, _access); - ASSERT_FINITE_CORO_WAITING; - if (parent.lock_or_null_ != nullptr && parent.lock_or_null_->snapshot_node_ != nullptr) { - rassert(_access == access_t::read); - buf_lock_t *parent_lock = parent.lock_or_null_; - rassert(!parent_lock->current_page_acq_.has()); - snapshot_node_ - = get_or_create_child_snapshot_node(txn_->cache(), - parent_lock->snapshot_node_, - _block_id); - guarantee(snapshot_node_ != nullptr, - "Tried to acquire (in cache %p) a deleted block (%" PRIu64 - " as child of %" PRIu64 ") (with read access).", - txn_->cache(), - _block_id, parent_lock->block_id()); - ++snapshot_node_->ref_count_; - } else { - if (_access == access_t::write && parent.lock_or_null_ != nullptr) { - create_child_snapshot_attachments(txn_->cache(), - parent.lock_or_null_->current_page_acq()->block_version(), - parent.lock_or_null_->block_id(), - _block_id); - } - current_page_acq_.init(new current_page_acq_t(txn_->page_txn(), _block_id, - _access)); - } - -#if ALT_DEBUG - debugf("%p: buf_lock_t %p %s %" PRIu64 "\n", cache(), this, show(_access), _block_id); -#endif -} - -buf_lock_t::buf_lock_t(buf_parent_t parent, - block_id_t _block_id, - access_t _access) - : txn_(parent.txn()), - current_page_acq_(), - snapshot_node_(nullptr), - access_ref_count_(0) { - help_construct(parent, _block_id, _access); -} - -buf_lock_t::buf_lock_t(buf_lock_t *parent, - block_id_t _block_id, - access_t _access) - : txn_(parent->txn_), - current_page_acq_(), - snapshot_node_(nullptr), - access_ref_count_(0) { - help_construct(buf_parent_t(parent), _block_id, _access); -} - -bool is_subordinate(access_t parent, access_t child) { - return parent == access_t::write || child == access_t::read; -} - -void buf_lock_t::help_construct(buf_parent_t parent, block_id_t _block_id, - alt_create_t) { - buf_lock_t::wait_for_parent(parent, access_t::write); - - // Makes sure nothing funny can happen in current_page_acq_t constructor. - // Otherwise, we'd need to make choosing a block id a separate function, and call - // create_empty_child_snapshot_attachments before constructing the - // current_page_acq_t. - ASSERT_FINITE_CORO_WAITING; - - current_page_acq_.init(new current_page_acq_t(txn_->page_txn(), - _block_id, - access_t::write, - alt::page_create_t::yes)); - - if (parent.lock_or_null_ != nullptr) { - create_empty_child_snapshot_attachments(txn_->cache(), - parent.lock_or_null_->current_page_acq()->block_version(), - parent.lock_or_null_->block_id(), - current_page_acq_->block_id()); -#if ALT_DEBUG - debugf("%p: buf_lock_t %p create %" PRIu64 " (as child of %" PRIu64 ")\n", - cache(), this, buf_lock_t::block_id(), parent.lock_or_null_->block_id()); -#endif - } else { -#if ALT_DEBUG - debugf("%p: buf_lock_t %p create %" PRIu64 " (no parent)\n", - cache(), this, buf_lock_t::block_id()); -#endif - } -} - -buf_lock_t::buf_lock_t(txn_t *_txn, - block_id_t _block_id, - alt_create_t create) - : txn_(_txn), - current_page_acq_(), - snapshot_node_(nullptr), - access_ref_count_(0) { - help_construct(buf_parent_t(_txn), _block_id, create); -} - -buf_lock_t::buf_lock_t(buf_parent_t parent, - block_id_t _block_id, - alt_create_t create) - : txn_(parent.txn()), - current_page_acq_(), - snapshot_node_(nullptr), - access_ref_count_(0) { - help_construct(parent, _block_id, create); -} - -void buf_lock_t::mark_deleted() { - ASSERT_FINITE_CORO_WAITING; -#if ALT_DEBUG - debugf("%p: buf_lock_t %p delete %" PRIu64 "\n", cache(), this, block_id()); -#endif - guarantee(!empty()); - guarantee(current_page_acq()->write_acq_signal()->is_pulsed()); - current_page_acq()->mark_deleted(); -} - -void buf_lock_t::wait_for_parent(buf_parent_t parent, access_t access) { - if (parent.lock_or_null_ != nullptr) { - buf_lock_t *lock = parent.lock_or_null_; - guarantee(is_subordinate(lock->access(), access)); - if (access == access_t::write) { - lock->write_acq_signal()->wait(); - } else { - lock->read_acq_signal()->wait(); - } - } else { - guarantee(is_subordinate(parent.txn()->access(), access)); - } -} - -void buf_lock_t::help_construct( - buf_parent_t parent, - alt_create_t, - block_type_t block_type) { - cache()->assert_thread(); - - buf_lock_t::wait_for_parent(parent, access_t::write); - - // Makes sure nothing funny can happen in current_page_acq_t constructor. - // Otherwise, we'd need to make choosing a block id a separate function, and call - // create_empty_child_snapshot_attachments before constructing the - // current_page_acq_t. - ASSERT_FINITE_CORO_WAITING; - - current_page_acq_.init(new current_page_acq_t(txn_->page_txn(), - alt_create_t::create, - block_type)); - - if (parent.lock_or_null_ != nullptr) { - create_empty_child_snapshot_attachments(txn_->cache(), - parent.lock_or_null_->current_page_acq()->block_version(), - parent.lock_or_null_->block_id(), - current_page_acq_->block_id()); -#if ALT_DEBUG - debugf("%p: buf_lock_t %p create %" PRIu64 " (as child of %" PRIu64 ")\n", - cache(), this, block_id(), parent.lock_or_null_->block_id()); -#endif - } else { -#if ALT_DEBUG - debugf("%p: buf_lock_t %p create %" PRIu64 " (no parent)\n", - cache(), this, block_id()); -#endif - } -} - -buf_lock_t::buf_lock_t(buf_parent_t parent, - alt_create_t create, - block_type_t block_type) - : txn_(parent.txn()), - current_page_acq_(), - snapshot_node_(nullptr), - access_ref_count_(0) { - help_construct(parent, create, block_type); -} - -buf_lock_t::buf_lock_t(buf_lock_t *parent, - alt_create_t create, - block_type_t block_type) - : txn_(parent->txn_), - current_page_acq_(), - snapshot_node_(nullptr), - access_ref_count_(0) { - help_construct(buf_parent_t(parent), create, block_type); -} - -buf_lock_t::~buf_lock_t() { - if (txn_ != nullptr) { - cache()->assert_thread(); -#if ALT_DEBUG - debugf("%p: buf_lock_t %p destroy %" PRIu64 "\n", cache(), this, block_id()); -#endif - } - guarantee(access_ref_count_ == 0); - - if (snapshot_node_ != nullptr) { - --snapshot_node_->ref_count_; - if (snapshot_node_->ref_count_ == 0) { -#if ALT_DEBUG - debugf("remove_snapshot_node %p by %p (in %p)\n", - snapshot_node_, this, cache()); -#endif - cache()->remove_snapshot_node(block_id(), - snapshot_node_); - } - } -} - -buf_lock_t::buf_lock_t(buf_lock_t &&movee) - : txn_(movee.txn_), - current_page_acq_(std::move(movee.current_page_acq_)), - snapshot_node_(movee.snapshot_node_), - access_ref_count_(0) { - guarantee(movee.access_ref_count_ == 0); - movee.txn_ = nullptr; - movee.current_page_acq_.reset(); - movee.snapshot_node_ = nullptr; -} - -buf_lock_t &buf_lock_t::operator=(buf_lock_t &&movee) { - guarantee(access_ref_count_ == 0); - buf_lock_t tmp(std::move(movee)); - swap(tmp); - return *this; -} - -void buf_lock_t::swap(buf_lock_t &other) { - ASSERT_NO_CORO_WAITING; - guarantee(access_ref_count_ == 0); - guarantee(other.access_ref_count_ == 0); - std::swap(txn_, other.txn_); - current_page_acq_.swap(other.current_page_acq_); - std::swap(snapshot_node_, other.snapshot_node_); -} - -void buf_lock_t::reset_buf_lock() { - ASSERT_FINITE_CORO_WAITING; - buf_lock_t tmp; - swap(tmp); -} - -bool buf_lock_t::is_snapshotted() const { - cache()->assert_thread(); - return snapshot_node_ != nullptr; -} - -void buf_lock_t::snapshot_subdag() { - cache()->assert_thread(); -#if ALT_DEBUG - debugf("%p: buf_lock_t %p snapshot %" PRIu64 "\n", cache(), this, block_id()); -#endif - ASSERT_FINITE_CORO_WAITING; - guarantee(!empty()); - if (snapshot_node_ != nullptr) { - return; - } - - alt_snapshot_node_t *matching_node - = cache()->matching_snapshot_node_or_null(block_id(), - current_page_acq_->block_version()); - - if (matching_node != nullptr) { - snapshot_node_ = matching_node; - ++matching_node->ref_count_; - } else { - const block_id_t _block_id = current_page_acq_->block_id(); - alt_snapshot_node_t *node - = new alt_snapshot_node_t(std::move(current_page_acq_)); - rassert(node->ref_count_ == 0); - ++node->ref_count_; - txn_->cache()->add_snapshot_node(_block_id, node); - snapshot_node_ = node; - node->current_page_acq_->declare_snapshotted(); - } - - // Our hold on the block now uses snapshot_node_, not current_page_acq_. - current_page_acq_.reset(); -} - -current_page_acq_t *buf_lock_t::current_page_acq() const { - ASSERT_NO_CORO_WAITING; - guarantee(!empty()); - if (snapshot_node_ != nullptr) { - return snapshot_node_->current_page_acq_.get(); - } else { - return current_page_acq_.get(); - } -} - -void buf_lock_t::detach_child(block_id_t child_id) { - ASSERT_FINITE_CORO_WAITING; - guarantee(!empty()); - guarantee(access() == access_t::write); - - // _This_ txn isn't loading the page, so use the default reads account. - buf_lock_t::create_child_snapshot_attachments( - cache(), - current_page_acq()->block_version(), - block_id(), - child_id); -} - -repli_timestamp_t buf_lock_t::get_recency() const { - guarantee(!empty()); - current_page_acq_t *cpa = current_page_acq(); - guarantee(cpa != nullptr); - - // Emulate the cpa->recency() waiting behavior. We only do this waiting here so - // that we can guarantee(!empty()) after it's pulsed. (FYI: We and the - // page_cache wait for the write_acq_signal() so that a write acquirer can't see - // its recency change before/after the write_acq_signal() gets pulsed.) - if (access() == access_t::read) { - cpa->read_acq_signal()->wait(); - } else { - cpa->write_acq_signal()->wait(); - } - - ASSERT_FINITE_CORO_WAITING; - guarantee(!empty()); - repli_timestamp_t ret = cpa->recency(); - // You may not call this on a buf lock that was marked deleted, and you - // shouldn't call it on an aux block either. - guarantee(ret != repli_timestamp_t::invalid); - return ret; -} - -void buf_lock_t::set_recency(repli_timestamp_t recency) { - guarantee(!empty()); - // You should never need to set the recency of an aux block. It will be - // discarded anyway. - guarantee(!is_aux_block_id(block_id())); - rassert(snapshot_node_ == nullptr); - - // We only wait here so that we can guarantee(!empty()) after it's pulsed. - current_page_acq_->write_acq_signal()->wait(); - - ASSERT_FINITE_CORO_WAITING; - guarantee(!empty()); - { - repli_timestamp_t s = superceding_recency(current_page_acq_->recency(), recency); - guarantee(s == recency); - } - current_page_acq_->set_recency(recency); -} - -page_t *buf_lock_t::get_held_page_for_read() { - guarantee(!empty()); - current_page_acq_t *cpa = current_page_acq(); - guarantee(cpa != nullptr); - // We only wait here so that we can guarantee(!empty()) after it's pulsed. - cpa->read_acq_signal()->wait(); - - ASSERT_FINITE_CORO_WAITING; - guarantee(!empty()); - return cpa->current_page_for_read(txn()->account()); -} - -page_t *buf_lock_t::get_held_page_for_write() { - guarantee(!empty()); - rassert(snapshot_node_ == nullptr); - // We only wait here so that we can guarantee(!empty()) after it's pulsed. - current_page_acq_->write_acq_signal()->wait(); - - ASSERT_FINITE_CORO_WAITING; - guarantee(!empty()); - return current_page_acq_->current_page_for_write(txn()->account()); -} - -buf_read_t::buf_read_t(buf_lock_t *lock) - : lock_(lock) { - guarantee(!lock_->empty()); - lock_->access_ref_count_++; -} - -buf_read_t::~buf_read_t() { - guarantee(!lock_->empty()); - lock_->access_ref_count_--; -} - -const void *buf_read_t::get_data_read(uint32_t *block_size_out) { - page_t *page = lock_->get_held_page_for_read(); - if (!page_acq_.has()) { - page_acq_.init(page, &lock_->cache()->page_cache_, - lock_->txn()->account()); - } - page_acq_.buf_ready_signal()->wait(); - *block_size_out = page_acq_.get_buf_size().value(); - return page_acq_.get_buf_read(); -} - -buf_write_t::buf_write_t(buf_lock_t *lock) - : lock_(lock) { - guarantee(lock_->access() == access_t::write); - lock_->access_ref_count_++; -} - -buf_write_t::~buf_write_t() { - guarantee(!lock_->empty()); - lock_->access_ref_count_--; -} - -void *buf_write_t::get_data_write(uint32_t block_size) { - page_t *page = lock_->get_held_page_for_write(); - if (!page_acq_.has()) { - page_acq_.init(page, &lock_->cache()->page_cache_, - lock_->txn()->account()); - } - page_acq_.buf_ready_signal()->wait(); - return page_acq_.get_buf_write(block_size_t::make_from_cache(block_size)); -} - -void *buf_write_t::get_data_write() { - return get_data_write(lock_->cache()->max_block_size().value()); -} - diff --git a/src/buffer_cache/alt.hpp b/src/buffer_cache/alt.hpp deleted file mode 100644 index 195a692a267..00000000000 --- a/src/buffer_cache/alt.hpp +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef BUFFER_CACHE_ALT_HPP_ -#define BUFFER_CACHE_ALT_HPP_ - -#include -#include -#include - -#include "buffer_cache/page_cache.hpp" -#include "buffer_cache/types.hpp" -#include "containers/two_level_array.hpp" -#include "repli_timestamp.hpp" - -class serializer_t; - -class buf_lock_t; -class alt_cache_stats_t; -class alt_snapshot_node_t; -class perfmon_collection_t; -class cache_balancer_t; - -class alt_txn_throttler_t { -public: - explicit alt_txn_throttler_t(int64_t minimum_unwritten_changes_limit); - ~alt_txn_throttler_t(); - - alt::throttler_acq_t begin_txn_or_throttle(int64_t expected_change_count); - void end_txn(alt::throttler_acq_t acq); - - void inform_memory_limit_change(uint64_t memory_limit, - block_size_t max_block_size); - -private: - const int64_t minimum_unwritten_changes_limit_; - - new_semaphore_t unwritten_block_changes_semaphore_; - new_semaphore_t unwritten_index_changes_semaphore_; - - DISABLE_COPYING(alt_txn_throttler_t); -}; - -class cache_t : public home_thread_mixin_t { -public: - explicit cache_t(serializer_t *serializer, - cache_balancer_t *balancer, - perfmon_collection_t *perfmon_collection); - ~cache_t(); - - max_block_size_t max_block_size() const { return page_cache_.max_block_size(); } - - // These todos come from the mirrored cache. The real problem is that whole - // cache account / priority thing is just one ghetto hack amidst a dozen other - // throttling systems. TODO: Come up with a consistent priority scheme, - // i.e. define a "default" priority etc. TODO: As soon as we can support it, we - // might consider supporting a mem_cap paremeter. - cache_account_t create_cache_account(int priority); - -private: - friend class txn_t; - friend class buf_read_t; - friend class buf_write_t; - friend class buf_lock_t; - - alt_snapshot_node_t *matching_snapshot_node_or_null( - block_id_t block_id, - alt::block_version_t block_version); - void add_snapshot_node(block_id_t block_id, alt_snapshot_node_t *node); - void remove_snapshot_node(block_id_t block_id, alt_snapshot_node_t *node); - - // throttler_ can cause the txn_t constructor to block - alt_txn_throttler_t throttler_; - alt::page_cache_t page_cache_; - - scoped_ptr_t stats_; - - std::map > - snapshot_nodes_by_block_id_; - - DISABLE_COPYING(cache_t); -}; - -class txn_t { -public: - // Constructor for read-only transactions. - txn_t(cache_conn_t *cache_conn, read_access_t read_access); - - txn_t(cache_conn_t *cache_conn, - write_durability_t durability, - int64_t expected_change_count); - - ~txn_t(); - - // Every write transaction must be committed before it's - // destructed. - // There is no roll-back / abort! Destructing an uncommitted - // write-transaction will terminate the server. - void commit(); - - cache_t *cache() { return cache_; } - alt::page_txn_t *page_txn() { return page_txn_.get(); } - access_t access() const { return access_; } - - void set_account(cache_account_t *cache_account); - cache_account_t *account() { return cache_account_; } - -private: - // Resets the *throttler_acq parameter. - static void inform_tracker(cache_t *cache, - alt::throttler_acq_t *throttler_acq); - - // Resets the *throttler_acq parameter. - static void pulse_and_inform_tracker(cache_t *cache, - alt::throttler_acq_t *throttler_acq, - cond_t *pulsee); - - - void help_construct(int64_t expected_change_count, cache_conn_t *cache_conn); - - cache_t *const cache_; - - // Initialized to cache()->page_cache_.default_cache_account(), and modified by - // set_account(). - cache_account_t *cache_account_; - - const access_t access_; - - // Only applicable if access_ == write. - const write_durability_t durability_; - - scoped_ptr_t page_txn_; - - bool is_committed_; - - DISABLE_COPYING(txn_t); -}; - -class buf_parent_t; - -class buf_lock_t { -public: - buf_lock_t(); - - // buf_parent_t is a type that either points at a buf_lock_t (its parent) or - // merely at a txn_t (e.g. for acquiring the superblock, which has no parent). - // If acquiring the child for read, the constructor will wait for the parent to - // be acquired for read. Similarly, if acquiring the child for write, the - // constructor will wait for the parent to be acquired for write. Once the - // constructor returns, you are "in line" for the block, meaning you'll acquire - // it in the same order relative other agents as you did when acquiring the same - // parent. (Of course, readers can intermingle.) - - // These constructors will _not_ yield the coroutine _if_ the parent is already - // {access}-acquired. - - // Acquires an existing block for read or write access. - buf_lock_t(buf_parent_t parent, - block_id_t block_id, - access_t access); - - // Creates a new block with a specified block id, one that doesn't have a parent. - buf_lock_t(txn_t *txn, - block_id_t block_id, - alt_create_t create); - - // Creates a new block with a specified block id as the child of a parent (if it's - // not just a txn_t *). - buf_lock_t(buf_parent_t parent, - block_id_t block_id, - alt_create_t create); - - // Acquires an existing block given the parent. - buf_lock_t(buf_lock_t *parent, - block_id_t block_id, - access_t access); - - // Creates a block, a new child of the given parent. It gets assigned a block id - // from one of the unused block id's. - buf_lock_t(buf_parent_t parent, - alt_create_t create, - block_type_t block_type = block_type_t::normal); - - // Creates a block, a new child of the given parent. It gets assigned a block id - // from one of the unused block id's. - buf_lock_t(buf_lock_t *parent, - alt_create_t create, - block_type_t block_type = block_type_t::normal); - - ~buf_lock_t(); - - buf_lock_t(buf_lock_t &&movee); - buf_lock_t &operator=(buf_lock_t &&movee); - - void swap(buf_lock_t &other); - void reset_buf_lock(); - bool empty() const { - return txn_ == nullptr; - } - - bool is_snapshotted() const; - void snapshot_subdag(); - - void detach_child(block_id_t child_id); - - block_id_t block_id() const { - guarantee(txn_ != nullptr); - return current_page_acq()->block_id(); - } - - // It is illegal to call this on a buf lock that has been mark_deleted, or that - // is a lock on an aux block. This never returns repli_timestamp_t::invalid. - repli_timestamp_t get_recency() const; - - // Sets the buf's recency to `superceding_recency`, which must be greater than or - // equal to its current recency. Operations that add or modify entries to the leaf - // nodes of the B-tree should call this for every node in the path from the root to - // the leaf. - void set_recency(repli_timestamp_t superceding_recency); - - access_t access() const { - guarantee(!empty()); - return current_page_acq()->access(); - } - - signal_t *read_acq_signal() { - guarantee(!empty()); - return current_page_acq()->read_acq_signal(); - } - signal_t *write_acq_signal() { - guarantee(!empty()); - return current_page_acq()->write_acq_signal(); - } - - void mark_deleted(); - - txn_t *txn() const { return txn_; } - cache_t *cache() const { return txn_->cache(); } - -private: - void help_construct(buf_parent_t parent, block_id_t block_id, access_t access); - void help_construct( - buf_parent_t parent, - alt_create_t create, - block_type_t block_type); - void help_construct(buf_parent_t parent, block_id_t block_id, alt_create_t create); - - static alt_snapshot_node_t *help_make_child(cache_t *cache, block_id_t child_id); - - - static void wait_for_parent(buf_parent_t parent, access_t access); - static alt_snapshot_node_t * - get_or_create_child_snapshot_node(cache_t *cache, - alt_snapshot_node_t *parent, - block_id_t child_id); - static void create_empty_child_snapshot_attachments( - cache_t *cache, - alt::block_version_t parent_version, - block_id_t parent_id, - block_id_t child_id); - static void create_child_snapshot_attachments(cache_t *cache, - alt::block_version_t parent_version, - block_id_t parent_id, - block_id_t child_id); - alt::current_page_acq_t *current_page_acq() const; - - friend class buf_read_t; // for get_held_page_for_read, access_ref_count_. - friend class buf_write_t; // for get_held_page_for_write, access_ref_count_. - - alt::page_t *get_held_page_for_read(); - alt::page_t *get_held_page_for_write(); - - txn_t *txn_; - - scoped_ptr_t current_page_acq_; - - alt_snapshot_node_t *snapshot_node_; - - // Keeps track of how many alt_buf_{read|write}_t have been created for - // this lock, for assertion/guarantee purposes. - intptr_t access_ref_count_; - - DISABLE_COPYING(buf_lock_t); -}; - - -class buf_parent_t { -public: - buf_parent_t() : txn_(nullptr), lock_or_null_(nullptr) { } - - explicit buf_parent_t(buf_lock_t *lock) - : txn_(lock->txn()), lock_or_null_(lock) { - guarantee(lock != nullptr); - guarantee(!lock->empty()); - } - - explicit buf_parent_t(txn_t *_txn) - : txn_(_txn), lock_or_null_(nullptr) { - rassert(_txn != NULL); - } - - void detach_child(block_id_t child_id) { - if (lock_or_null_ != nullptr) { - lock_or_null_->detach_child(child_id); - } - } - - bool empty() const { - return txn_ == nullptr; - } - - txn_t *txn() const { - guarantee(!empty()); - return txn_; - } - cache_t *cache() const { - guarantee(!empty()); - return txn_->cache(); - } - -private: - friend class buf_lock_t; - txn_t *txn_; - buf_lock_t *lock_or_null_; -}; - -class buf_read_t { -public: - explicit buf_read_t(buf_lock_t *lock); - ~buf_read_t(); - - const void *get_data_read(uint32_t *block_size_out); - const void *get_data_read() { - uint32_t block_size; - const void *data = get_data_read(&block_size); - guarantee(block_size == lock_->cache()->max_block_size().value()); - return data; - } - -private: - buf_lock_t *lock_; - alt::page_acq_t page_acq_; - - DISABLE_COPYING(buf_read_t); -}; - -class buf_write_t { -public: - explicit buf_write_t(buf_lock_t *lock); - ~buf_write_t(); - - void *get_data_write(uint32_t block_size); - // Equivalent to passing the max_block_size. - void *get_data_write(); - -private: - buf_lock_t *lock_; - alt::page_acq_t page_acq_; - - DISABLE_COPYING(buf_write_t); -}; - - -#endif // BUFFER_CACHE_ALT_HPP_ diff --git a/src/buffer_cache/blob.cc b/src/buffer_cache/blob.cc deleted file mode 100644 index 0ed86bcc31d..00000000000 --- a/src/buffer_cache/blob.cc +++ /dev/null @@ -1,800 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "buffer_cache/blob.hpp" - -#include - -#include -#include - -#include "buffer_cache/alt.hpp" -#include "concurrency/pmap.hpp" -#include "containers/buffer_group.hpp" -#include "containers/scoped.hpp" -#include "math.hpp" -#include "serializer/types.hpp" -#include "utils.hpp" - -// The maximal number of blocks to traverse concurrently. -// Roughly equivalent to the maximal number of coroutines that loading a blob -// can allocate. -const int64_t BLOB_TRAVERSAL_CONCURRENCY = 8; - -template -void clear_and_delete(std::vector *vec) { - while (!vec->empty()) { - T *back = vec->back(); - vec->pop_back(); - delete back; - } -} - -blob_acq_t::~blob_acq_t() { - clear_and_delete(&reads_); - clear_and_delete(&writes_); - clear_and_delete(&bufs_); -} - -namespace blob { - -int big_size_offset(int maxreflen) { - return maxreflen <= 255 ? 1 : 2; -} - -int block_ids_offset(int maxreflen) { - return big_size_offset(maxreflen) + sizeof(int64_t); -} - -temporary_acq_tree_node_t * -make_tree_from_block_ids(buf_parent_t parent, access_t mode, int levels, - int64_t offset, int64_t size, const block_id_t *block_ids); - -// touches_end_t specifies whether the [offset, offset + size) region given to -// expose_tree_from_block_ids touches the end of the data in the blob. -enum class touches_end_t { yes, no }; -void expose_tree_from_block_ids(buf_parent_t parent, access_t mode, - int levels, int64_t offset, int64_t size, - temporary_acq_tree_node_t *tree, - touches_end_t touches_end, - buffer_group_t *buffer_group_out, - blob_acq_t *acq_group_out); - - -int small_size(const char *ref, int maxreflen) { - // small value sizes range from 0 to maxreflen - big_size_offset(maxreflen), and - // maxreflen indicates large value. - if (maxreflen <= 255) { - return *reinterpret_cast(ref); - } else { - return *reinterpret_cast(ref); - } -} - -bool size_would_be_small(int64_t proposed_size, int maxreflen) { - return proposed_size <= maxreflen - big_size_offset(maxreflen); -} - -void set_small_size_field(char *ref, int maxreflen, int64_t size) { - if (maxreflen <= 255) { - *reinterpret_cast(ref) = size; - } else { - *reinterpret_cast(ref) = size; - } -} - -void set_small_size(char *ref, int maxreflen, int64_t size) { - rassert(size_would_be_small(size, maxreflen)); - set_small_size_field(ref, maxreflen, size); -} - -bool is_small(const char *ref, int maxreflen) { - return small_size(ref, maxreflen) < maxreflen; -} - -char *small_buffer(char *ref, int maxreflen) { - return ref + big_size_offset(maxreflen); -} - -int64_t big_size(const char *ref, int maxreflen) { - return *reinterpret_cast(ref + big_size_offset(maxreflen)); -} - -void set_big_size(char *ref, int maxreflen, int64_t new_size) { - *reinterpret_cast(ref + big_size_offset(maxreflen)) = new_size; -} - -const block_id_t *block_ids(const char *ref, int maxreflen) { - return reinterpret_cast(ref + block_ids_offset(maxreflen)); -} - -block_id_t *block_ids(char *ref, int maxreflen) { - return reinterpret_cast(ref + block_ids_offset(maxreflen)); -} - -int64_t leaf_size(max_block_size_t block_size) { - return block_size.value() - sizeof(block_magic_t); -} - -char *leaf_node_data(void *buf) { - return reinterpret_cast(buf) + sizeof(block_magic_t); -} - -const char *leaf_node_data(const void *buf) { - return reinterpret_cast(buf) + sizeof(block_magic_t); -} - -const size_t LEAF_NODE_DATA_OFFSET = sizeof(block_magic_t); - -int64_t internal_node_bytesize(max_block_size_t block_size) { - return block_size.value() - sizeof(block_magic_t); -} - -int64_t internal_node_count(max_block_size_t block_size) { - return internal_node_bytesize(block_size) / sizeof(block_id_t); -} - -const block_id_t *internal_node_block_ids(const void *buf) { - return reinterpret_cast(reinterpret_cast(buf) + sizeof(block_magic_t)); -} - -block_id_t *internal_node_block_ids(void *buf) { - return reinterpret_cast(reinterpret_cast(buf) + sizeof(block_magic_t)); -} - -ref_info_t big_ref_info(max_block_size_t block_size, int64_t size, - int maxreflen, int64_t *blockid_count_out) { - rassert(size > int64_t(maxreflen - big_size_offset(maxreflen))); - int64_t max_blockid_count = (maxreflen - block_ids_offset(maxreflen)) / sizeof(block_id_t); - - int64_t block_count = ceil_divide(size, leaf_size(block_size)); - - int levels = 1; - while (block_count > max_blockid_count) { - block_count = ceil_divide(block_count, internal_node_count(block_size)); - ++levels; - } - - ref_info_t info; - info.refsize = block_ids_offset(maxreflen) + sizeof(block_id_t) * block_count; - info.levels = levels; - *blockid_count_out = block_count; - return info; -} - -ref_info_t big_ref_info(max_block_size_t block_size, int64_t size, - int maxreflen) { - int64_t blockid_count; - return big_ref_info(block_size, size, maxreflen, &blockid_count); -} - -ref_info_t ref_info(max_block_size_t block_size, const char *ref, int maxreflen) { - int smallsize = small_size(ref, maxreflen); - if (smallsize <= maxreflen - big_size_offset(maxreflen)) { - ref_info_t info; - info.refsize = big_size_offset(maxreflen) + smallsize; - info.levels = 0; - return info; - } else { - return big_ref_info(block_size, big_size(ref, maxreflen), maxreflen); - } -} - -int ref_size(max_block_size_t block_size, const char *ref, int maxreflen) { - return ref_info(block_size, ref, maxreflen).refsize; -} - -bool ref_fits(max_block_size_t block_size, int data_length, const char *ref, int maxreflen) { - if (data_length <= 0) { - return false; - } - int smallsize = small_size(ref, maxreflen); - if (smallsize <= maxreflen - big_size_offset(maxreflen)) { - return big_size_offset(maxreflen) + smallsize <= data_length; - } else { - if (data_length < block_ids_offset(maxreflen)) { - return false; - } - - return data_length >= ref_size(block_size, ref, maxreflen); - } -} - -int maxreflen_from_blockid_count(int count) { - // The block ids offset is either 17 or 18 depending on whether - // the return value would be <= 255. - int sz = block_ids_offset(1) + count * sizeof(block_id_t); - int diff = block_ids_offset(sz) - block_ids_offset(1); - return diff + sz; -} - -int64_t value_size(const char *ref, int maxreflen) { - int smallsize = blob::small_size(ref, maxreflen); - if (smallsize <= maxreflen - big_size_offset(maxreflen)) { - return smallsize; - } else { - rassert(smallsize == maxreflen); - return blob::big_size(ref, maxreflen); - } -} - - -int btree_maxreflen = 251; -block_magic_t internal_node_magic = { { 'l', 'a', 'r', 'i' } }; -block_magic_t leaf_node_magic = { { 'l', 'a', 'r', 'l' } }; - - -int64_t stepsize(max_block_size_t block_size, int levels) { - rassert(levels > 0); - int64_t step = leaf_size(block_size); - for (int i = 0; i < levels - 1; ++i) { - step *= internal_node_count(block_size); - } - return step; -} - -int64_t max_end_offset(max_block_size_t block_size, int levels, int maxreflen) { - if (levels == 0) { - return maxreflen - big_size_offset(maxreflen); - } else { - int ref_block_ids = (maxreflen - block_ids_offset(maxreflen)) / sizeof(block_id_t); - return stepsize(block_size, levels) * ref_block_ids; - } -} - -int64_t clamp(int64_t x, int64_t lo, int64_t hi) { - return x < lo ? lo : x > hi ? hi : x; -} - - -void shrink(max_block_size_t block_size, int levels, int64_t offset, int64_t size, int index, int64_t *suboffset_out, int64_t *subsize_out) { - int64_t step = stepsize(block_size, levels); - - int64_t clamp_low = index * step; - int64_t clamp_high = clamp_low + step; - - int64_t suboffset = clamp(offset, clamp_low, clamp_high); - int64_t subsize = clamp(offset + size, clamp_low, clamp_high) - suboffset; - - *suboffset_out = suboffset - clamp_low; - *subsize_out = subsize; -} - -// Exists so callers passing a 0 offset don't have to analyze that the suboffset will -// be 0. -void shrink(max_block_size_t block_size, int levels, - int64_t size, int index, int64_t *subsize_out) { - int64_t suboffset; - shrink(block_size, levels, 0, size, index, &suboffset, subsize_out); - guarantee(suboffset == 0); -} - -void compute_acquisition_offsets(max_block_size_t block_size, int levels, int64_t offset, int64_t size, int *lo_out, int *hi_out) { - int64_t step = stepsize(block_size, levels); - - *lo_out = offset / step; - *hi_out = ceil_divide(offset + size, step); -} - -} // namespace blob - -blob_t::blob_t(max_block_size_t block_size, char *ref, int maxreflen) - : ref_(ref), maxreflen_(maxreflen) { - guarantee(maxreflen <= blob::leaf_size(block_size)); - guarantee(maxreflen - blob::block_ids_offset(maxreflen) <= blob::internal_node_bytesize(block_size)); - guarantee(maxreflen >= blob::block_ids_offset(maxreflen) + static_cast(sizeof(block_id_t))); -} - - - -int blob_t::refsize(max_block_size_t block_size) const { - return blob::ref_size(block_size, ref_, maxreflen_); -} - -int64_t blob_t::valuesize() const { - return blob::value_size(ref_, maxreflen_); -} - -void blob_t::detach_subtrees(buf_parent_t root) { - if (blob::is_small(ref_, maxreflen_)) { - return; - } - - int64_t blockid_count; - blob::big_ref_info(root.cache()->max_block_size(), - blob::big_size(ref_, maxreflen_), - maxreflen_, - &blockid_count); - - const block_id_t *ids = blob::block_ids(ref_, maxreflen_); - - for (int64_t i = 0; i < blockid_count; ++i) { - root.detach_child(ids[i]); - } -} - -union temporary_acq_tree_node_t { - buf_lock_t *buf; - temporary_acq_tree_node_t *child; -}; - -void blob_t::expose_region(buf_parent_t parent, access_t mode, - int64_t offset, int64_t size, - buffer_group_t *buffer_group_out, - blob_acq_t *acq_group_out) { - if (size == 0) { - return; - } - - if (blob::is_small(ref_, maxreflen_)) { - char *b = blob::small_buffer(ref_, maxreflen_); -#ifndef NDEBUG - int n = blob::small_size(ref_, maxreflen_); - rassert(0 <= offset && offset <= n); - rassert(0 <= size && size <= n && offset + size <= n); -#endif - buffer_group_out->add_buffer(size, b + offset); - } else { - // It's large. - - const int levels = blob::ref_info(parent.cache()->max_block_size(), - ref_, maxreflen_).levels; - - // Acquiring is done recursively in parallel, - temporary_acq_tree_node_t *tree - = blob::make_tree_from_block_ids(parent, mode, levels, - offset, size, - blob::block_ids(ref_, maxreflen_)); - - // Exposing and writing to the buffer group is done serially. - blob::expose_tree_from_block_ids(parent, mode, levels, - offset, size, - tree, - size == valuesize() - ? blob::touches_end_t::yes - : blob::touches_end_t::no, - buffer_group_out, - acq_group_out); - } -} - -void blob_t::expose_all(buf_parent_t parent, access_t mode, - buffer_group_t *buffer_group_out, - blob_acq_t *acq_group_out) { - expose_region(parent, mode, 0, valuesize(), buffer_group_out, acq_group_out); -} - -namespace blob { - -struct region_tree_filler_t { - explicit region_tree_filler_t(buf_parent_t _parent) - : parent(_parent) { } - - buf_parent_t parent; - access_t mode; - int levels; - int64_t offset, size; - const block_id_t *block_ids; - int lo, hi; - temporary_acq_tree_node_t *nodes; - - void operator()(int i) const { - if (levels > 1) { - buf_lock_t lock(parent, block_ids[lo + i], mode); - buf_read_t buf_read(&lock); - const block_id_t *sub_ids - = blob::internal_node_block_ids(buf_read.get_data_read()); - - int64_t suboffset, subsize; - shrink(parent.cache()->max_block_size(), levels, offset, size, lo + i, &suboffset, &subsize); - nodes[i].child = blob::make_tree_from_block_ids(buf_parent_t(&lock), - mode, levels - 1, - suboffset, subsize, - sub_ids); - } else { - nodes[i].buf = new buf_lock_t(parent, block_ids[lo + i], mode); - } - } -}; - -int64_t choose_concurrency(int levels) { - // Parallelize on the lowest level. That way we get a higher chance - // of sequential disk reads, assuming that the blob is laid out on disk - // from left to right (which might or might not be true, depending the details - // of how it was written and how the cache and serializer operate). - if (levels > 1) { - return 1; - } else { - rassert(levels == 1); - return BLOB_TRAVERSAL_CONCURRENCY; - } -} - -temporary_acq_tree_node_t * -make_tree_from_block_ids(buf_parent_t parent, access_t mode, int levels, - int64_t offset, int64_t size, const block_id_t *block_ids) { - rassert(size > 0); - - region_tree_filler_t filler(parent); - filler.mode = mode; - filler.levels = levels; - filler.offset = offset; - filler.size = size; - filler.block_ids = block_ids; - compute_acquisition_offsets(parent.cache()->max_block_size(), levels, offset, - size, &filler.lo, &filler.hi); - - filler.nodes = new temporary_acq_tree_node_t[filler.hi - filler.lo]; - - throttled_pmap(filler.hi - filler.lo, filler, choose_concurrency(levels)); - - return filler.nodes; -} - -void expose_tree_from_block_ids(buf_parent_t parent, access_t mode, - int levels, int64_t offset, int64_t size, - temporary_acq_tree_node_t *tree, - touches_end_t touches_end, - buffer_group_t *buffer_group_out, - blob_acq_t *acq_group_out) { - rassert(size > 0); - - int lo, hi; - blob::compute_acquisition_offsets(parent.cache()->max_block_size(), levels, - offset, size, &lo, &hi); - - for (int i = 0; i < hi - lo; ++i) { - int64_t suboffset, subsize; - blob::shrink(parent.cache()->max_block_size(), levels, offset, size, - lo + i, &suboffset, &subsize); - if (levels > 1) { - expose_tree_from_block_ids(parent, mode, levels - 1, suboffset, - subsize, tree[i].child, touches_end, - buffer_group_out, - acq_group_out); - } else { - rassert(0 < subsize && subsize <= blob::leaf_size(parent.cache()->max_block_size())); - rassert(0 <= suboffset && suboffset + subsize <= blob::leaf_size(parent.cache()->max_block_size())); - - buf_lock_t *buf = tree[i].buf; - void *leaf_buf; - if (mode == access_t::read) { - buf_read_t *buf_read = new buf_read_t(buf); - // We can't assert a specific block size here (without undesirably - // intricate logic), because immediately after creation, the blob has - // size max_value_size, but after we've written to the block, its - // size could be shrunken. - uint32_t block_size; - leaf_buf = const_cast(buf_read->get_data_read(&block_size)); - acq_group_out->add_buf(buf, buf_read); - } else { - buf_write_t *buf_write = new buf_write_t(buf); - if (touches_end == touches_end_t::yes) { - // Using suboffset + subsize is valid because we know that, when - // appending, there are no bytes in this block past suboffset + - // subsize that are a valid part of the blob. - leaf_buf = buf_write->get_data_write(suboffset + subsize - + blob::LEAF_NODE_DATA_OFFSET); - } else { - leaf_buf = buf_write->get_data_write(); - } - - acq_group_out->add_buf(buf, buf_write); - } - - char *data = blob::leaf_node_data(leaf_buf); - buffer_group_out->add_buffer(subsize, data + suboffset); - } - } - - delete[] tree; -} - -} // namespace blob - -void blob_t::append_region(buf_parent_t parent, int64_t size) { - const max_block_size_t block_size = parent.cache()->max_block_size(); - int levels = blob::ref_info(block_size, ref_, maxreflen_).levels; - - // Avoid the empty blob effect. - if (levels == 0 && blob::small_size(ref_, maxreflen_) == 0 && size > 0) { - blob::set_small_size(ref_, maxreflen_, 1); - size -= 1; - } - if (size == 0) { - return; - } - - while (!allocate_to_dimensions(parent, levels, - valuesize() + size)) { - levels = add_level(parent, levels); - } - - if (levels == 0) { - rassert(blob::is_small(ref_, maxreflen_)); - int64_t new_size = size + blob::small_size(ref_, maxreflen_); - rassert(blob::size_would_be_small(new_size, maxreflen_)); - blob::set_small_size(ref_, maxreflen_, new_size); - } else { - blob::set_big_size(ref_, maxreflen_, blob::big_size(ref_, maxreflen_) + size); - } - - rassert(blob::ref_info(block_size, ref_, maxreflen_).levels == levels); -} - -void blob_t::unappend_region(buf_parent_t parent, int64_t size) { - const max_block_size_t block_size = parent.cache()->max_block_size(); - int levels = blob::ref_info(block_size, ref_, maxreflen_).levels; - - bool emptying = false; - if (valuesize() == size && size > 0) { - emptying = true; - size -= 1; - } - - if (size != 0) { - deallocate_to_dimensions(parent, levels, - valuesize() - size); - for (;;) { - if (!remove_level(parent, &levels)) { - break; - } - } - } - - if (emptying) { - rassert(blob::is_small(ref_, maxreflen_)); - rassert(blob::small_size(ref_, maxreflen_) == 1); - blob::set_small_size(ref_, maxreflen_, 0); - } -} - -void blob_t::clear(buf_parent_t parent) { - unappend_region(parent, valuesize()); -} - -void blob_t::write_from_string(const std::string &val, - buf_parent_t parent, int64_t offset) { - buffer_group_t dest; - blob_acq_t acq; - expose_region(parent, access_t::write, offset, val.size(), &dest, &acq); - - buffer_group_t src; - src.add_buffer(val.size(), val.data()); - buffer_group_copy_data(&dest, const_view(&src)); -} - -namespace blob { - -struct traverse_helper_t { - virtual buf_lock_t preprocess(buf_parent_t parent, int levels, - block_id_t *block_id) = 0; - virtual void postprocess(buf_lock_t *lock) = 0; - virtual ~traverse_helper_t() { } -}; - -void traverse_recursively(buf_parent_t parent, int levels, - block_id_t *block_ids, - int64_t smaller_size, int64_t bigger_size, - traverse_helper_t *helper); - -void traverse_index(buf_parent_t parent, int levels, block_id_t *block_ids, - int index, int64_t smaller_size, int64_t bigger_size, - traverse_helper_t *helper) { - const max_block_size_t block_size = parent.cache()->max_block_size(); - int64_t sub_smaller_size, sub_bigger_size; - blob::shrink(block_size, levels, smaller_size, index, &sub_smaller_size); - blob::shrink(block_size, levels, bigger_size, index, &sub_bigger_size); - - if (sub_smaller_size > 0) { - if (levels > 1) { - buf_lock_t lock(parent, block_ids[index], access_t::write); - buf_write_t write(&lock); - void *b = write.get_data_write(); - - block_id_t *subids = blob::internal_node_block_ids(b); - traverse_recursively(buf_parent_t(&lock), levels - 1, subids, - sub_smaller_size, sub_bigger_size, - helper); - } - - } else { - buf_lock_t lock = helper->preprocess(parent, levels, &block_ids[index]); - - if (levels > 1) { - buf_write_t write(&lock); - void *b = write.get_data_write(); - block_id_t *subids = blob::internal_node_block_ids(b); - traverse_recursively(buf_parent_t(&lock), levels - 1, subids, - sub_smaller_size, sub_bigger_size, - helper); - } - - helper->postprocess(&lock); - } -} - -void traverse_recursively(buf_parent_t parent, int levels, block_id_t *block_ids, - int64_t smaller_size, int64_t bigger_size, - traverse_helper_t *helper) { - const max_block_size_t block_size = parent.cache()->max_block_size(); - - int old_lo, old_hi, new_lo, new_hi; - compute_acquisition_offsets(block_size, levels, 0, smaller_size, - &old_lo, &old_hi); - compute_acquisition_offsets(block_size, levels, 0, bigger_size, - &new_lo, &new_hi); - - int64_t leafsize = leaf_size(block_size); - - if (ceil_divide(bigger_size, leafsize) > ceil_divide(smaller_size, leafsize)) { - throttled_pmap(std::max(0, old_hi - 1), new_hi, - std::bind(&traverse_index, parent, levels, block_ids, ph::_1, - smaller_size, bigger_size, helper), - choose_concurrency(levels)); - } -} - - -} // namespace blob - -bool blob_t::traverse_to_dimensions(buf_parent_t parent, int levels, - int64_t smaller_size, int64_t bigger_size, - blob::traverse_helper_t *helper) { - rassert(bigger_size >= smaller_size); - const max_block_size_t block_size = parent.cache()->max_block_size(); - if (bigger_size <= blob::max_end_offset(block_size, levels, maxreflen_)) { - if (levels != 0) { - blob::traverse_recursively(parent, levels, - blob::block_ids(ref_, maxreflen_), - smaller_size, bigger_size, - helper); - } - return true; - } else { - return false; - } -} - -struct allocate_helper_t : public blob::traverse_helper_t { - buf_lock_t preprocess(buf_parent_t parent, int levels, - block_id_t *block_id) { - buf_lock_t temp_lock(parent, alt_create_t::create, block_type_t::aux); - *block_id = temp_lock.block_id(); - { - buf_write_t lock_write(&temp_lock); - void *b = lock_write.get_data_write(); - if (levels == 1) { - *static_cast(b) = blob::leaf_node_magic; - } else { - *static_cast(b) = blob::internal_node_magic; - } - } - return temp_lock; - } - void postprocess(UNUSED buf_lock_t *lock) { } -}; - -bool blob_t::allocate_to_dimensions(buf_parent_t parent, int levels, - int64_t new_size) { - allocate_helper_t helper; - return traverse_to_dimensions(parent, levels, - valuesize(), new_size, &helper); -} - -struct deallocate_helper_t : public blob::traverse_helper_t { - buf_lock_t preprocess(buf_parent_t parent, UNUSED int levels, - block_id_t *block_id) { - return buf_lock_t(parent, *block_id, access_t::write); - } - - void postprocess(buf_lock_t *lock) { - // mark_deleted() requires that we have already got the write lock. - // Make sure that's the case before we call it... - lock->write_acq_signal()->wait_lazily_unordered(); - lock->mark_deleted(); - } -}; - -void blob_t::deallocate_to_dimensions(buf_parent_t parent, int levels, - int64_t new_size) { - deallocate_helper_t helper; - if (levels == 0) { - rassert(0 <= new_size); - rassert(new_size <= blob::small_size(ref_, maxreflen_)); - blob::set_small_size(ref_, maxreflen_, new_size); - } else { - DEBUG_VAR bool res = traverse_to_dimensions(parent, levels, new_size, valuesize(), &helper); - blob::set_big_size(ref_, maxreflen_, new_size); - rassert(res); - } -} - -// Always returns levels + 1. -int blob_t::add_level(buf_parent_t parent, int levels) { - buf_lock_t lock(parent, alt_create_t::create, block_type_t::aux); - buf_write_t lock_write(&lock); - void *b = lock_write.get_data_write(); - if (levels == 0) { - *reinterpret_cast(b) = blob::leaf_node_magic; - - int sz = blob::small_size(ref_, maxreflen_); - rassert(sz < maxreflen_); - - memcpy(blob::leaf_node_data(b), blob::small_buffer(ref_, maxreflen_), sz); - - blob::set_small_size_field(ref_, maxreflen_, maxreflen_); - blob::set_big_size(ref_, maxreflen_, sz); - blob::block_ids(ref_, maxreflen_)[0] = lock.block_id(); - } else { - *reinterpret_cast(b) = blob::internal_node_magic; - - // We don't know how many block ids there could be, so we'll - // just copy as many as there can be. - int sz = maxreflen_ - blob::block_ids_offset(maxreflen_); - - memcpy(blob::internal_node_block_ids(b), blob::block_ids(ref_, maxreflen_), sz); - blob::block_ids(ref_, maxreflen_)[0] = lock.block_id(); - } - - return levels + 1; -} - - -bool blob_t::remove_level(buf_parent_t parent, int *levels_ref) { - const max_block_size_t block_size = parent.cache()->max_block_size(); - int levels = *levels_ref; - if (levels == 0) { - return false; - } - - int64_t bigsize = blob::big_size(ref_, maxreflen_); - rassert(0 <= bigsize); - if (bigsize > blob::max_end_offset(parent.cache()->max_block_size(), - levels - 1, maxreflen_)) { - return false; - } - - // If the size is zero, the entire tree will have already been deallocated. (But - // we don't do this, at this precise moment..) - rassert(bigsize != 0); - if (bigsize != 0) { - - buf_lock_t lock(parent, blob::block_ids(ref_, maxreflen_)[0], - access_t::write); - if (levels == 1) { - buf_read_t lock_read(&lock); - uint32_t unused_block_size; - const char *b = blob::leaf_node_data(lock_read.get_data_read(&unused_block_size)); - char *small = blob::small_buffer(ref_, maxreflen_); - memcpy(small, b, bigsize); - blob::set_small_size(ref_, maxreflen_, bigsize); - } else { - buf_read_t lock_read(&lock); - const block_id_t *b = blob::internal_node_block_ids(lock_read.get_data_read()); - - // Detach children: they're getting reattached to `parent`. - int lo; - int hi; - blob::compute_acquisition_offsets(block_size, levels - 1, - 0, bigsize, - &lo, &hi); - - // We must overwrite at least the first block ID, or we might end up - // processing it a second time when `remove_level` gets called again. - guarantee(lo == 0 && hi > 0); - - block_id_t *block_ids = blob::block_ids(ref_, maxreflen_); - for (int i = lo; i < hi; ++i) { - lock.detach_child(b[i]); - block_ids[i] = b[i]; - } - } - - lock.write_acq_signal()->wait_lazily_unordered(); - lock.mark_deleted(); - } - - *levels_ref = levels - 1; - return true; -} - - diff --git a/src/buffer_cache/blob.hpp b/src/buffer_cache/blob.hpp deleted file mode 100644 index 7faf0bfb64b..00000000000 --- a/src/buffer_cache/blob.hpp +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef BUFFER_CACHE_BLOB_HPP_ -#define BUFFER_CACHE_BLOB_HPP_ - -#include -#include - -#include -#include -#include - -#include "buffer_cache/types.hpp" -#include "concurrency/access.hpp" -#include "containers/buffer_group.hpp" -#include "errors.hpp" -#include "serializer/types.hpp" - -class buffer_group_t; - -/* An explanation of blobs. - - If we want to store values larger than 250 bytes (or some arbitrary inline value - size limit), we must split them into large numbers of blocks. Some kind of tree - structure is used. The blob_t type handles both kinds of values. Here's how it's - used. - -const int mrl = 251; -std::string x = ...; - -// value_in_leafnode does not point to a buffer that has 251 bytes you can use. -char *ref = get_blob_ref_from_something(); - -// Create a buffer of the maxreflen -char tmp[mrl]; -memcpy(tmp, ref, blob::ref_size(bs, ref, mrl)); -{ - blob_t b(tmp, mrl); - int64_t old_size = b.valuesize(); - tmp.append_region(leafnode, x.size()); - - { - // group holds pointers to buffers. acq maintains the buf_lock_t - // ownership itself. You cannot use group outside the - // lifetime of acq. - blob_acq_t acq; - buffer_group_t group; - - tmp.expose_region(leafnode, access_t::write, old_size, x.size(), - &group, &acq); - // There are better ways to shovel data into a buffer group if it's not - // already in a string -- please avoid excessive copying! - copy_string_to_buffer_group(&group, x); - } -} - -// The ref size changed because we modified the blob. -write_blob_ref_to_something(tmp, blob::ref_size(bs, ref, mrl)); - - */ - -class buf_lock_t; -class buf_parent_t; -class buf_read_t; -class buf_write_t; - -// Represents an acquisition of buffers owned by the blob. -class blob_acq_t { -public: - blob_acq_t() { } - ~blob_acq_t(); - - void add_buf(buf_lock_t *buf, buf_write_t *write) { - bufs_.push_back(buf); - writes_.push_back(write); - } - - void add_buf(buf_lock_t *buf, buf_read_t *read) { - bufs_.push_back(buf); - reads_.push_back(read); - } - -private: - std::vector bufs_; - // One of writes_ or reads_ will be empty. - std::vector writes_; - std::vector reads_; - - // disable copying - blob_acq_t(const blob_acq_t&); - void operator=(const blob_acq_t&); -}; - - -union temporary_acq_tree_node_t; - -namespace blob { - -struct traverse_helper_t; - -// Returns the number of bytes actually used by the blob reference. -// Returns a value in the range [1, maxreflen]. -int ref_size(max_block_size_t block_size, const char *ref, int maxreflen); - -// Returns true if the size of the blob reference is less than or -// equal to data_length, only reading memory in the range [ref, ref + -// data_length). -bool ref_fits(max_block_size_t block_size, int data_length, const char *ref, int maxreflen); - -// Returns what the maxreflen would be, given the desired number of -// block ids in the blob ref. -int maxreflen_from_blockid_count(int count); - -// The step size of a blob. -int64_t stepsize(max_block_size_t block_size, int levels); - -// The internal node block ids of an internal node. -const block_id_t *internal_node_block_ids(const void *buf); - -// Returns offset and size, clamped to and relative to the index'th subtree. -void shrink(max_block_size_t block_size, int levels, int64_t offset, int64_t size, int index, int64_t *suboffset_out, int64_t *subsize_out); - -// The maxreflen value (allegedly) appropriate for use with rdb_protocol btrees. -// It's 251. This should be renamed. -extern int btree_maxreflen; - -// The size of a blob, equivalent to blob_t(ref, maxreflen).valuesize(). -int64_t value_size(const char *ref, int maxreflen); - -struct ref_info_t { - // The ref_size of a ref. - int refsize; - // the number of levels in the underlying tree of buffers. - int levels; -}; -ref_info_t ref_info(max_block_size_t block_size, const char *ref, int maxreflen); - -// Returns the internal block ids of a non-inlined blob ref. -const block_id_t *block_ids(const char *ref, int maxreflen); - -// Returns the char bytes of a leaf node. -const char *leaf_node_data(const void *buf); - -} // namespace blob - -class blob_t { -public: - // This is a really confusing api. In order for this to create a - // new blob you need to manually bzero ref. Otherwise, it will - // interpret the garbage data as a valid ref. - - // If you're going to modify the blob, ref must be a pointer to an - // array of length maxreflen. If you're going to use the blob - // in-place, it does not need to be so. (For example, blob refs - // in leaf nodes usually take much less space -- the average size - // for a large blob would be half the space (or less, thanks to - // Benford's law), and the size for a small blob is 1 plus the - // size of the blob, or maybe 2 plus the size of the blob. - blob_t(max_block_size_t block_size, char *ref, int maxreflen); - - // Returns ref_size(block_size, ref, maxreflen), the number of - // bytes actually used in the blob ref. A value in the internal - // [1, maxreflen_]. - int refsize(max_block_size_t block_size) const; - - // Returns the actual size of the value, some number >= 0 and less - // than one gazillion. - int64_t valuesize() const; - - // Detaches the blob's subtrees from the root node (see - // buf_lock_t::detach_child). - void detach_subtrees(buf_parent_t root); - - // Acquires internal buffers and copies pointers to internal - // buffers to the buffer_group_t, initializing acq_group_out so - // that it holds the acquisition of such buffers. acq_group_out - // must not be destroyed until the buffers are finished being - // used. If you want to expose the region in a _readonly_ - // fashion, use a const_cast! I am so sorry. - void expose_region(buf_parent_t root, - access_t mode, int64_t offset, - int64_t size, buffer_group_t *buffer_group_out, - blob_acq_t *acq_group_out); - void expose_all(buf_parent_t root, access_t mode, - buffer_group_t *buffer_group_out, - blob_acq_t *acq_group_out); - - // Appends size bytes of garbage data to the blob. - void append_region(buf_parent_t root, int64_t size); - - // Removes size bytes of data from the end of the blob. size must - // be <= valuesize(). - void unappend_region(buf_parent_t root, int64_t size); - - // Empties the blob, making its valuesize() be zero. Equivalent to - // unappend_region(txn, valuesize()). In particular, you can be sure that the - // blob holds no internal blocks, once it has been cleared. - void clear(buf_parent_t root); - - // Writes over the portion of the blob, starting at offset, with - // the contents of the string val. Caller is responsible for making - // sure this portion of the blob exists - void write_from_string(const std::string &val, buf_parent_t root, - int64_t offset); - -private: - bool traverse_to_dimensions(buf_parent_t parent, int levels, - int64_t smaller_size, int64_t bigger_size, - blob::traverse_helper_t *helper); - bool allocate_to_dimensions(buf_parent_t parent, int levels, - int64_t new_size); - bool shift_at_least(buf_parent_t parent, int levels, int64_t min_shift); - void consider_big_shift(buf_parent_t parent, int levels, - int64_t *min_shift); - void consider_small_shift(buf_parent_t parent, int levels, - int64_t *min_shift); - void deallocate_to_dimensions(buf_parent_t parent, int levels, - int64_t new_size); - int add_level(buf_parent_t parent, int levels); - bool remove_level(buf_parent_t parent, int *levels_ref); - - char *ref_; - int maxreflen_; - - DISABLE_COPYING(blob_t); -}; - - -#endif // BUFFER_CACHE_BLOB_HPP_ diff --git a/src/buffer_cache/block_version.hpp b/src/buffer_cache/block_version.hpp deleted file mode 100644 index c962e3a845d..00000000000 --- a/src/buffer_cache/block_version.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef BUFFER_CACHE_BLOCK_VERSION_HPP_ -#define BUFFER_CACHE_BLOCK_VERSION_HPP_ - -#include - -namespace alt { - -class block_version_t { -public: - block_version_t() : value_(0) { } - - block_version_t subsequent() const { - block_version_t ret; - ret.value_ = value_ + 1; - return ret; - } - - bool operator<(block_version_t other) const { - return value_ < other.value_; - } - - bool operator<=(block_version_t other) const { - return value_ <= other.value_; - } - - bool operator>=(block_version_t other) const { - return value_ >= other.value_; - } - - bool operator==(block_version_t other) const { - return value_ == other.value_; - } - - bool operator!=(block_version_t other) const { - return !operator==(other); - } - - uint64_t debug_value() const { return value_; } - -private: - uint64_t value_; -}; - -} // namespace alt - -#endif // BUFFER_CACHE_BLOCK_VERSION_HPP_ diff --git a/src/buffer_cache/cache_account.cc b/src/buffer_cache/cache_account.cc deleted file mode 100644 index 941b1dff767..00000000000 --- a/src/buffer_cache/cache_account.cc +++ /dev/null @@ -1,49 +0,0 @@ -#include "buffer_cache/cache_account.hpp" - -#include "arch/types.hpp" - -cache_account_t::cache_account_t() - : thread_(-1), io_account_(nullptr) { } - -cache_account_t::cache_account_t(cache_account_t &&movee) - : thread_(movee.thread_), io_account_(movee.io_account_) { - movee.thread_ = threadnum_t(-1); - movee.io_account_ = nullptr; -} - -cache_account_t &cache_account_t::operator=(cache_account_t &&movee) { - cache_account_t tmp(std::move(movee)); - std::swap(thread_, tmp.thread_); - std::swap(io_account_, tmp.io_account_); - return *this; -} - -void cache_account_t::init(threadnum_t thread, file_account_t *io_account) { - rassert(io_account_ == nullptr); - rassert(io_account != nullptr); - io_account_ = io_account; - thread_ = thread; -} - - -cache_account_t::cache_account_t(threadnum_t thread, file_account_t *io_account) - : thread_(thread), io_account_(io_account) { - rassert(io_account != nullptr); -} - -void cache_account_t::reset() { - if (io_account_ != nullptr) { - threadnum_t local_thread = thread_; - file_account_t *local_account = io_account_; - thread_ = threadnum_t(-1); - io_account_ = nullptr; - { - on_thread_t th(local_thread); - delete local_account; - } - } -} - -cache_account_t::~cache_account_t() { - reset(); -} diff --git a/src/buffer_cache/cache_account.hpp b/src/buffer_cache/cache_account.hpp deleted file mode 100644 index f126f149042..00000000000 --- a/src/buffer_cache/cache_account.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef BUFFER_CACHE_CACHE_ACCOUNT_HPP_ -#define BUFFER_CACHE_CACHE_ACCOUNT_HPP_ - -#include "threading.hpp" - -class file_account_t; - -namespace alt { -class page_cache_t; -} - -class cache_account_t { -public: - cache_account_t(); - ~cache_account_t(); - cache_account_t(cache_account_t &&movee); - cache_account_t &operator=(cache_account_t &&movee); - - file_account_t *get() const { - return io_account_; - } -private: - friend class alt::page_cache_t; - // Takes ownership of the file_account_t pointee. - void init(threadnum_t thread, file_account_t *io_account); - cache_account_t(threadnum_t thread, file_account_t *io_account); - void reset(); - - // I hate having this thread_ variable. The file_account_t does need to be - // destroyed on the right thread, though. - threadnum_t thread_; - file_account_t *io_account_; - DISABLE_COPYING(cache_account_t); -}; - -#endif // BUFFER_CACHE_CACHE_ACCOUNT_HPP_ diff --git a/src/buffer_cache/cache_balancer.cc b/src/buffer_cache/cache_balancer.cc deleted file mode 100644 index 90c5c4a288a..00000000000 --- a/src/buffer_cache/cache_balancer.cc +++ /dev/null @@ -1,259 +0,0 @@ -#include "buffer_cache/cache_balancer.hpp" - -#include -#include - -#include "buffer_cache/evicter.hpp" -#include "arch/runtime/runtime.hpp" -#include "concurrency/pmap.hpp" - -const uint64_t alt_cache_balancer_t::rebalance_check_interval_ms = 20; -const uint64_t alt_cache_balancer_t::rebalance_access_count_threshold = 100; -const uint64_t alt_cache_balancer_t::rebalance_timeout_ms = 500; - -const double alt_cache_balancer_t::read_ahead_proportion = 0.9; - -alt_cache_balancer_t::cache_data_t::cache_data_t(alt::evicter_t *_evicter) : - evicter(_evicter), - new_size(0), - old_size(evicter->memory_limit()), - bytes_loaded(evicter->get_bytes_loaded()), - access_count(evicter->access_count()) { } - -alt_cache_balancer_t::alt_cache_balancer_t( - clone_ptr_t > _total_cache_size_watchable) : - total_cache_size_watchable(_total_cache_size_watchable), - rebalance_timer(make_scoped(rebalance_check_interval_ms, this)), - rebalance_timer_state(rebalance_timer_state_t::normal), - last_rebalance_time(0), - read_ahead_ok(true), - bytes_toward_read_ahead_limit(0), - per_thread_data(get_num_threads()), - rebalance_pumper([this](signal_t *interruptor) { rebalance_blocking(interruptor); }), - cache_size_change_subscription( - [this]() { - last_rebalance_time = 0; - wake_up_activity_happened(); - rebalance_pumper.notify(); - }) -{ - watchable_t::freeze_t freeze(total_cache_size_watchable); - cache_size_change_subscription.reset(total_cache_size_watchable, &freeze); -} - -alt_cache_balancer_t::~alt_cache_balancer_t() { - assert_thread(); -} - -bool *alt_cache_balancer_t::notify_activity_boolean(threadnum_t thread) { - guarantee(thread == get_thread_id()); - return &per_thread_data[thread.threadnum].wake_up_balancer; -} - -void alt_cache_balancer_t::wake_up_activity_happened() { - assert_thread(); - - switch (rebalance_timer_state) { - case rebalance_timer_state_t::normal: - // Do nothing -- we're already awake. - break; - case rebalance_timer_state_t::examining_other_threads: - rebalance_timer_state = rebalance_timer_state_t::normal; - break; - case rebalance_timer_state_t::deactivated: - rebalance_timer_state = rebalance_timer_state_t::normal; - rebalance_timer = make_scoped(rebalance_check_interval_ms, this); - break; - default: - unreachable(); - } -} - -void alt_cache_balancer_t::add_evicter(alt::evicter_t *evicter) { - evicter->assert_thread(); - auto res = per_thread_data[get_thread_id().threadnum].evicters.insert(evicter); - guarantee(res.second); -} - -void alt_cache_balancer_t::remove_evicter(alt::evicter_t *evicter) { - evicter->assert_thread(); - size_t res = per_thread_data[get_thread_id().threadnum].evicters.erase(evicter); - guarantee(res == 1); -} - -void alt_cache_balancer_t::on_ring() { - assert_thread(); - - // Can't block in this callback, spawn a new coroutine - // Using this `pump_coro_t`, we only have one rebalance going at once - rebalance_pumper.notify(); -} - -void alt_cache_balancer_t::rebalance_blocking(UNUSED signal_t *interruptor) { - assert_thread(); - - if (rebalance_timer_state != rebalance_timer_state_t::normal) { - return; - } - - uint64_t total_cache_size = total_cache_size_watchable->get(); - // We do some signed arithmetic with cache sizes, so the total cache size - // must fit into a signed value based on the native pointer type or bad - // things happen. - guarantee(total_cache_size_watchable->get() <= - static_cast(std::numeric_limits::max())); - - const size_t num_threads = per_thread_data.size(); - scoped_array_t > cache_data(num_threads); - scoped_array_t zero_access_counts(num_threads); - - // Get cache sizes from shards on each thread - rebalance_timer_state = rebalance_timer_state_t::examining_other_threads; - pmap(per_thread_data.size(), - std::bind(&alt_cache_balancer_t::collect_stats_from_thread, - this, ph::_1, &cache_data, &zero_access_counts)); - - bool all_zero_access_counts = true; - // Sum up the number of evicters, bytes loaded, and access counts - size_t total_evicters = 0; - uint64_t total_bytes_loaded = 0; - uint64_t total_access_count = 0; - for (size_t i = 0; i < num_threads; ++i) { - total_evicters += cache_data[i].size(); - all_zero_access_counts &= zero_access_counts[i]; - for (size_t j = 0; j < cache_data[i].size(); ++j) { - total_bytes_loaded += std::max(0, cache_data[i][j].bytes_loaded); - total_access_count += cache_data[i][j].access_count; - } - } - - // Reevaluate if read-ahead should be running - if (read_ahead_ok) { - bytes_toward_read_ahead_limit += total_bytes_loaded; - read_ahead_ok = bytes_toward_read_ahead_limit < (total_cache_size * read_ahead_proportion); - } - - // Determine if we should do a rebalance, either: - // 1. At least rebalance_timeout_ms milliseconds have passed - // 2. At least access_count_threshold accesses have occurred - // since the last rebalance. - microtime_t now = current_microtime(); - - if (now < last_rebalance_time + (rebalance_timeout_ms * 1000) && - total_access_count < rebalance_access_count_threshold) { - rebalance_timer_state = rebalance_timer_state_t::normal; - return; - } - - last_rebalance_time = now; - - // Calculate new cache sizes - if (total_evicters > 0) { - uint64_t total_new_sizes = 0; - - for (size_t i = 0; i < cache_data.size(); ++i) { - for (size_t j = 0; j < cache_data[i].size(); ++j) { - cache_data_t *data = &cache_data[i][j]; - - if (total_cache_size > 0) { - double temp = data->old_size; - temp /= static_cast(total_cache_size); - temp *= static_cast(total_bytes_loaded); - - int64_t new_size = std::max(0, data->bytes_loaded); - new_size -= static_cast(temp); - new_size += data->old_size; - new_size = std::max(new_size, 0); - - data->new_size = new_size; - total_new_sizes += new_size; - } else { - data->new_size = 0; - } - } - } - - // Distribute any rounding error across shards - int64_t extra_bytes = total_cache_size - total_new_sizes; - while (extra_bytes != 0) { - int64_t delta = extra_bytes / static_cast(total_evicters); - if (delta == 0) { - delta = ((extra_bytes < 0) ? -1 : 1); - } - for (size_t i = 0; i < cache_data.size() && extra_bytes != 0; ++i) { - for (size_t j = 0; j < cache_data[i].size() && extra_bytes != 0; ++j) { - cache_data_t *data = &cache_data[i][j]; - - // Avoid underflow - if (static_cast(data->new_size) + delta >= 0) { - data->new_size += delta; - extra_bytes -= delta; - } else { - extra_bytes += data->new_size; - data->new_size = 0; - } - } - } - } - - // Send new cache sizes to each thread - pmap(num_threads, - std::bind(&alt_cache_balancer_t::apply_rebalance_to_thread, - this, ph::_1, &cache_data, read_ahead_ok)); - } - - if (all_zero_access_counts - && rebalance_timer_state == rebalance_timer_state_t::examining_other_threads) { - rebalance_timer_state = rebalance_timer_state_t::deactivated; - rebalance_timer.reset(); - } else { - rebalance_timer_state = rebalance_timer_state_t::normal; - if (!rebalance_timer.has()) { - rebalance_timer = make_scoped(rebalance_check_interval_ms, this); - } - } -} - -void alt_cache_balancer_t::collect_stats_from_thread( - int index, - scoped_array_t > *data_out, - scoped_array_t *zero_access_count_out) { - on_thread_t rethreader((threadnum_t(index))); - - ASSERT_NO_CORO_WAITING; - const std::set *evicters = &per_thread_data[index].evicters; - std::vector *per_evicter_data = &(*data_out)[index]; - - bool all_access_counts_zero = true; - - per_evicter_data->reserve(evicters->size()); - for (auto j = evicters->begin(); j != evicters->end(); ++j) { - cache_data_t data(*j); - all_access_counts_zero &= (data.access_count == 0); - per_evicter_data->push_back(data); - } - - per_thread_data[index].wake_up_balancer = all_access_counts_zero; - (*zero_access_count_out)[index] = all_access_counts_zero; -} - -void alt_cache_balancer_t::apply_rebalance_to_thread(int index, - const scoped_array_t > *new_sizes, - bool new_read_ahead_ok) { - on_thread_t rethreader((threadnum_t(index))); - - const std::set *evicters = &per_thread_data[index].evicters; - const std::vector *sizes = &(*new_sizes)[index]; - - ASSERT_NO_CORO_WAITING; - for (auto it = sizes->begin(); it != sizes->end(); ++it) { - // Make sure the evicter still exists - if (evicters->find(it->evicter) != evicters->end()) { - it->evicter->update_memory_limit(it->new_size, - it->bytes_loaded, - it->access_count, - new_read_ahead_ok); - } - } -} - diff --git a/src/buffer_cache/cache_balancer.hpp b/src/buffer_cache/cache_balancer.hpp deleted file mode 100644 index e12cbc0992e..00000000000 --- a/src/buffer_cache/cache_balancer.hpp +++ /dev/null @@ -1,202 +0,0 @@ -#ifndef BUFFER_CACHE_CACHE_BALANCER_HPP_ -#define BUFFER_CACHE_CACHE_BALANCER_HPP_ - -#include -#include -#include - -#include "errors.hpp" -#include "time.hpp" - -#include "threading.hpp" -#include "arch/timing.hpp" -#include "concurrency/pump_coro.hpp" -#include "concurrency/watchable.hpp" -#include "containers/scoped.hpp" - -namespace alt { -class evicter_t; -} - -// Base class so we can have a dummy implementation for tests -class cache_balancer_t : public home_thread_mixin_t { -public: - cache_balancer_t() { } - virtual ~cache_balancer_t() { } - - // Used to determine the initial size of a cache - virtual uint64_t base_mem_per_store() const = 0; - - // Tells caches whether to start read ahead initially - virtual bool read_ahead_ok_at_start() const = 0; - - // Returns a pointer to a boolean for the given thread number (which must be the - // current thread) which, when set to true, means you should notify the balancer - // that it should wake up. Stuff outside the balancer should only set it from - // true to false. - // - // You should only call this once -- that way, you don't need a wasteful virtual - // function call anytime you want activity. - virtual bool *notify_activity_boolean(threadnum_t) = 0; - - // Tells the balancer that activity happened, so that maybe it should restart its - // balancing processes, if necessary (since right now they run on a timer). - virtual void wake_up_activity_happened() = 0; - -protected: - friend class alt::evicter_t; - - virtual void add_evicter(alt::evicter_t *evicter) = 0; - virtual void remove_evicter(alt::evicter_t *evicter) = 0; - - DISABLE_COPYING(cache_balancer_t); -}; - -// Dummy balancer that does nothing but provide the initial size of a cache -class dummy_cache_balancer_t final : public cache_balancer_t { -public: - explicit dummy_cache_balancer_t(uint64_t _base_mem_per_store) - : base_mem_per_store_(_base_mem_per_store), - notify_activity_boolean_(false) { } - ~dummy_cache_balancer_t() { } - - uint64_t base_mem_per_store() const final { - return base_mem_per_store_; - } - - bool read_ahead_ok_at_start() const final { - return false; - } - - bool *notify_activity_boolean(threadnum_t) final { - return ¬ify_activity_boolean_; - } - - void wake_up_activity_happened() final { } - -private: - void add_evicter(alt::evicter_t *) { } - void remove_evicter(alt::evicter_t *) { } - - uint64_t base_mem_per_store_; - - bool notify_activity_boolean_; - - DISABLE_COPYING(dummy_cache_balancer_t); -}; - -class alt_cache_balancer_t final : - public cache_balancer_t, - public repeating_timer_callback_t { -public: - explicit alt_cache_balancer_t( - clone_ptr_t > _total_cache_size_watchable); - ~alt_cache_balancer_t(); - - uint64_t base_mem_per_store() const final { - return 0; - } - - bool read_ahead_ok_at_start() const final { - return true; - } - - bool *notify_activity_boolean(threadnum_t thread) final; - - void wake_up_activity_happened() final; - -private: - friend class alt::evicter_t; - - // Constants to control how often we rebalance - static const uint64_t rebalance_access_count_threshold; - static const uint64_t rebalance_timeout_ms; - static const uint64_t rebalance_check_interval_ms; - - // Controls how much read ahead is allowed out of total cache size - static const double read_ahead_proportion; - - // Constants to determine when to stop read-ahead - static const uint64_t read_ahead_ratio_numerator; - static const uint64_t read_ahead_ratio_denominator; - - // Called by the evicter on the evicter's thread - void add_evicter(alt::evicter_t *evicter); - void remove_evicter(alt::evicter_t *evicter); - - // Print a warning if we can't fit all the tables without using extra memory - void warn_if_overcommitted(size_t num_shards); - - // Callback for repeating timer - void on_ring(); - - // Callback that handles rebalancing in a coroutine. Called by `rebalance_pumper`. - void rebalance_blocking(UNUSED signal_t *interruptor); - - // Used when calculating new cache sizes - struct cache_data_t { - explicit cache_data_t(alt::evicter_t *_evicter); - - alt::evicter_t *evicter; - uint64_t new_size; - uint64_t old_size; - int64_t bytes_loaded; - uint64_t access_count; - }; - - // Helper function to collect stats from each thread so we don't need - // atomic variables slowing down normal operations - void collect_stats_from_thread(int index, - scoped_array_t > *data_out, - scoped_array_t *zero_access_counts_out); - // Helper function that rebalances all the shards on a given thread - void apply_rebalance_to_thread(int index, - const scoped_array_t > *new_sizes, - bool new_read_ahead_ok); - - clone_ptr_t > total_cache_size_watchable; - scoped_ptr_t rebalance_timer; - enum class rebalance_timer_state_t { - // Normal operating condition: there is a timer, and it'll ping soon. Can - // switch to examining_other_threads. - normal, - // We're going to other threads to examine their cache activity. We might - // ask them to send us a message that puts us back into "normal" state. Such - // a request could arrive while we're still in "examining_other_threads" - // state, or "normal", or "deactivated." - examining_other_threads, - // rebalance_timer is null -- we are no longer running timer events. - deactivated, - }; - rebalance_timer_state_t rebalance_timer_state; - - microtime_t last_rebalance_time; - bool read_ahead_ok; - uint64_t bytes_toward_read_ahead_limit; - - struct per_thread_data_t { - per_thread_data_t() : wake_up_balancer(false) { } - std::set evicters; - // true if the balancer should wake up (because there was activity on this - // thread). - bool wake_up_balancer; - - DISABLE_COPYING(per_thread_data_t); - }; - - // This contains the extant evicter pointers for each thread, only accessed - // from each thread - scoped_array_t per_thread_data; - - /* Destructor order matters: `cache_size_change_subscription` must be destroyed - before `rebalance_pumper` because it calls `rebalance_pumper.notify()`, but - `rebalance_pumper` must be destroyed before the other member variables because it - owns the `rebalance_blocking()` coroutine, which uses other member variables. */ - pump_coro_t rebalance_pumper; - - watchable_t::subscription_t cache_size_change_subscription; - - DISABLE_COPYING(alt_cache_balancer_t); -}; - -#endif // BUFFER_CACHE_CACHE_BALANCER_HPP_ diff --git a/src/buffer_cache/evicter.cc b/src/buffer_cache/evicter.cc deleted file mode 100644 index 59449ba2995..00000000000 --- a/src/buffer_cache/evicter.cc +++ /dev/null @@ -1,257 +0,0 @@ -#include "buffer_cache/evicter.hpp" - -#include "arch/runtime/coroutines.hpp" -#include "buffer_cache/alt.hpp" -#include "buffer_cache/page.hpp" -#include "buffer_cache/page_cache.hpp" -#include "buffer_cache/cache_balancer.hpp" - -namespace alt { - -evicter_t::evicter_t() - : initialized_(false), - page_cache_(nullptr), - balancer_(nullptr), - balancer_notify_activity_boolean_(nullptr), - throttler_(nullptr), - bytes_loaded_counter_(0), - access_count_counter_(0), - access_time_counter_(INITIAL_ACCESS_TIME), - evict_if_necessary_active_(false) { } - -evicter_t::~evicter_t() { - assert_thread(); - drainer_.drain(); - if (initialized_) { - balancer_->remove_evicter(this); - } - guarantee(!evict_if_necessary_active_); -} - -void evicter_t::initialize(page_cache_t *page_cache, - cache_balancer_t *balancer, - alt_txn_throttler_t *throttler) { - assert_thread(); - guarantee(balancer != nullptr); - initialized_ = true; // Can you really say this class is 'initialized_'? - page_cache_ = page_cache; - memory_limit_ = balancer->base_mem_per_store(); - page_cache_ = page_cache; - throttler_ = throttler; - balancer_ = balancer; - balancer_notify_activity_boolean_ - = balancer_->notify_activity_boolean(get_thread_id()); - balancer_->add_evicter(this); - throttler_->inform_memory_limit_change(memory_limit_, - page_cache_->max_block_size()); -} - -void evicter_t::update_memory_limit(uint64_t new_memory_limit, - int64_t bytes_loaded_accounted_for, - uint64_t access_count_accounted_for, - bool read_ahead_ok) { - assert_thread(); - guarantee(initialized_); - - if (!read_ahead_ok) { - page_cache_->have_read_ahead_cb_destroyed(); - } - - bytes_loaded_counter_ -= bytes_loaded_accounted_for; - access_count_counter_ -= access_count_accounted_for; - memory_limit_ = new_memory_limit; - evict_if_necessary(); - - throttler_->inform_memory_limit_change(memory_limit_, - page_cache_->max_block_size()); -} - -int64_t evicter_t::get_bytes_loaded() const { - assert_thread(); - guarantee(initialized_); - return bytes_loaded_counter_; -} - -uint64_t evicter_t::memory_limit() const { - assert_thread(); - guarantee(initialized_); - return memory_limit_; -} - -uint64_t evicter_t::access_count() const { - assert_thread(); - guarantee(initialized_); - return access_count_counter_; -} - -void wake_up_balancer(cache_balancer_t *balancer, - UNUSED auto_drainer_t::lock_t drainer_lock) { - on_thread_t th(balancer->home_thread()); - balancer->wake_up_activity_happened(); -} - -void evicter_t::notify_bytes_loading(int64_t in_memory_buf_change) { - assert_thread(); - guarantee(initialized_); - bytes_loaded_counter_ += in_memory_buf_change; - access_count_counter_ += 1; - if (*balancer_notify_activity_boolean_) { - *balancer_notify_activity_boolean_ = false; - - coro_t::spawn_sometime(std::bind(&wake_up_balancer, - balancer_, - drainer_.lock())); - } -} - -void evicter_t::add_deferred_loaded(page_t *page) { - assert_thread(); - guarantee(initialized_); - evicted_.add(page, page->hypothetical_memory_usage(page_cache_)); -} - -void evicter_t::catch_up_deferred_load(page_t *page) { - assert_thread(); - guarantee(initialized_); - rassert(unevictable_.has_page(page)); - notify_bytes_loading(page->hypothetical_memory_usage(page_cache_)); -} - -void evicter_t::add_not_yet_loaded(page_t *page) { - assert_thread(); - guarantee(initialized_); - unevictable_.add(page, page->hypothetical_memory_usage(page_cache_)); - evict_if_necessary(); - notify_bytes_loading(page->hypothetical_memory_usage(page_cache_)); -} - -void evicter_t::reloading_page(page_t *page) { - assert_thread(); - guarantee(initialized_); - notify_bytes_loading(page->hypothetical_memory_usage(page_cache_)); -} - -bool evicter_t::page_is_in_unevictable_bag(page_t *page) const { - assert_thread(); - guarantee(initialized_); - return unevictable_.has_page(page); -} - -bool evicter_t::page_is_in_evicted_bag(page_t *page) const { - assert_thread(); - guarantee(initialized_); - return evicted_.has_page(page); -} - -void evicter_t::add_to_evictable_unbacked(page_t *page) { - assert_thread(); - guarantee(initialized_); - evictable_unbacked_.add(page, page->hypothetical_memory_usage(page_cache_)); - evict_if_necessary(); - notify_bytes_loading(page->hypothetical_memory_usage(page_cache_)); -} - -void evicter_t::add_to_evictable_disk_backed(page_t *page) { - assert_thread(); - guarantee(initialized_); - evictable_disk_backed_.add(page, page->hypothetical_memory_usage(page_cache_)); - evict_if_necessary(); - notify_bytes_loading(page->hypothetical_memory_usage(page_cache_)); -} - -void evicter_t::move_unevictable_to_evictable(page_t *page) { - assert_thread(); - guarantee(initialized_); - rassert(unevictable_.has_page(page)); - unevictable_.remove(page, page->hypothetical_memory_usage(page_cache_)); - eviction_bag_t *new_bag = correct_eviction_category(page); - rassert(new_bag == &evictable_disk_backed_ - || new_bag == &evictable_unbacked_); - new_bag->add(page, page->hypothetical_memory_usage(page_cache_)); - evict_if_necessary(); -} - -void evicter_t::change_to_correct_eviction_bag(eviction_bag_t *current_bag, - page_t *page) { - assert_thread(); - guarantee(initialized_); - rassert(current_bag->has_page(page)); - current_bag->remove(page, page->hypothetical_memory_usage(page_cache_)); - eviction_bag_t *new_bag = correct_eviction_category(page); - new_bag->add(page, page->hypothetical_memory_usage(page_cache_)); - evict_if_necessary(); -} - -eviction_bag_t *evicter_t::correct_eviction_category(page_t *page) { - assert_thread(); - guarantee(initialized_); - if (page->is_loading() || page->has_waiters()) { - return &unevictable_; - } else if (!page->is_loaded()) { - return &evicted_; - } else if (page->is_disk_backed()) { - return &evictable_disk_backed_; - } else { - return &evictable_unbacked_; - } -} - -void evicter_t::remove_page(page_t *page) { - assert_thread(); - guarantee(initialized_); - eviction_bag_t *bag = correct_eviction_category(page); - bag->remove(page, page->hypothetical_memory_usage(page_cache_)); - evict_if_necessary(); -} - -uint64_t evicter_t::in_memory_size() const { - assert_thread(); - guarantee(initialized_); - return unevictable_.size() - + evictable_disk_backed_.size() - + evictable_unbacked_.size(); -} - -void evicter_t::evict_if_necessary() THROWS_NOTHING { - assert_thread(); - guarantee(initialized_); - if (evict_if_necessary_active_) { - // Reentrant call to evict_if_necessary(). - // There is no need to start another eviction loop, plus we want to avoid - // recursive reentrant calls for reasons of correctness and to avoid stack - // overflows. - return; - } - // KSI: Implement eviction of unbacked evictables too. When flushing, you - // could use the page_t::eviction_index_ field to identify pages that are - // currently in the process of being evicted, to avoid reflushing a page - // currently being written for the purpose of eviction. - - evict_if_necessary_active_ = true; - page_t *page; - while (in_memory_size() > memory_limit_ - && evictable_disk_backed_.remove_oldish(&page, access_time_counter_, - page_cache_)) { - evicted_.add(page, page->hypothetical_memory_usage(page_cache_)); - page->evict_self(page_cache_); - page_cache_->consider_evicting_current_page(page->block_id()); - } - evict_if_necessary_active_ = false; -} - -usage_adjuster_t::usage_adjuster_t(page_cache_t *page_cache, page_t *page) - : page_cache_(page_cache), - page_(page), - eviction_bag_(page_cache->evicter().correct_eviction_category(page)), - original_usage_(page->hypothetical_memory_usage(page_cache)) { } - -usage_adjuster_t::~usage_adjuster_t() { - int64_t old64 = original_usage_; - int64_t new64 = page_->hypothetical_memory_usage(page_cache_); - int64_t adjustment = new64 - old64; - eviction_bag_->change_size(adjustment); - page_cache_->evicter().evict_if_necessary(); - page_cache_->evicter().notify_bytes_loading(adjustment); -} - -} // namespace alt diff --git a/src/buffer_cache/evicter.hpp b/src/buffer_cache/evicter.hpp deleted file mode 100644 index 9b52850a910..00000000000 --- a/src/buffer_cache/evicter.hpp +++ /dev/null @@ -1,126 +0,0 @@ -#ifndef BUFFER_CACHE_EVICTER_HPP_ -#define BUFFER_CACHE_EVICTER_HPP_ - -#include - -#include - -#include "buffer_cache/eviction_bag.hpp" -#include "concurrency/auto_drainer.hpp" -#include "concurrency/cache_line_padded.hpp" -#include "concurrency/pubsub.hpp" -#include "threading.hpp" - -class cache_balancer_t; -class alt_txn_throttler_t; - -namespace alt { - -class page_cache_t; - -class evicter_t : public home_thread_mixin_debug_only_t { -public: - void add_not_yet_loaded(page_t *page); - void add_deferred_loaded(page_t *page); - void catch_up_deferred_load(page_t *page); - void add_to_evictable_unbacked(page_t *page); - void add_to_evictable_disk_backed(page_t *page); - bool page_is_in_unevictable_bag(page_t *page) const; - bool page_is_in_evicted_bag(page_t *page) const; - void move_unevictable_to_evictable(page_t *page); - void change_to_correct_eviction_bag(eviction_bag_t *current_bag, page_t *page); - eviction_bag_t *correct_eviction_category(page_t *page); - eviction_bag_t *unevictable_category() { return &unevictable_; } - eviction_bag_t *evicted_category() { return &evicted_; } - void remove_page(page_t *page); - void reloading_page(page_t *page); - - // Evicter will be unusable until initialize is called - evicter_t(); - ~evicter_t(); - - void initialize(page_cache_t *page_cache, - cache_balancer_t *balancer, - alt_txn_throttler_t *throttler); - void update_memory_limit(uint64_t new_memory_limit, - int64_t bytes_loaded_accounted_for, - uint64_t access_count_accounted_for, - bool read_ahead_ok); - - uint64_t next_access_time() { - guarantee(initialized_); - return ++access_time_counter_; - } - - uint64_t memory_limit() const; - uint64_t access_count() const; - int64_t get_bytes_loaded() const; - - uint64_t in_memory_size() const; - - // This is decremented past UINT64_MAX to force code to be aware of access time - // rollovers. - static const uint64_t INITIAL_ACCESS_TIME = UINT64_MAX - 100; - -private: - friend class usage_adjuster_t; - - // Tells the cache balancer about a page being loaded - void notify_bytes_loading(int64_t ser_buf_change); - - // Evicts any evictable pages until under the memory limit - void evict_if_necessary() THROWS_NOTHING; - - bool initialized_; - page_cache_t *page_cache_; - cache_balancer_t *balancer_; - bool *balancer_notify_activity_boolean_; - - alt_txn_throttler_t *throttler_; - - uint64_t memory_limit_; - - // These are updated every time a page is loaded, created, or destroyed, and - // cleared when cache memory limits are re-evaluated. This value can go - // negative, if you keep deleting blocks or suddenly drop a snapshot. - int64_t bytes_loaded_counter_; - uint64_t access_count_counter_; - - // This gets incremented every time a page is accessed. - uint64_t access_time_counter_; - - // This is set to true while `evict_if_necessary()` is active. - // It avoids reentrant calls to that function. - bool evict_if_necessary_active_; - - // These track every page's eviction status. - eviction_bag_t unevictable_; - eviction_bag_t evictable_disk_backed_; - eviction_bag_t evictable_unbacked_; - eviction_bag_t evicted_; - - auto_drainer_t drainer_; - - DISABLE_COPYING(evicter_t); -}; - -// This adjusts the memory usage in the destructor, with the _same_ eviction bag the -// page had in the constructor -- its lifespan ends before the page would have its -// eviction bag changed. -class usage_adjuster_t { -public: - usage_adjuster_t(page_cache_t *page_cache, page_t *page); - ~usage_adjuster_t(); - -private: - page_cache_t *const page_cache_; - page_t *const page_; - eviction_bag_t *const eviction_bag_; - const uint32_t original_usage_; - - DISABLE_COPYING(usage_adjuster_t); -}; - -} // namespace alt - -#endif // BUFFER_CACHE_EVICTER_HPP_ diff --git a/src/buffer_cache/eviction_bag.cc b/src/buffer_cache/eviction_bag.cc deleted file mode 100644 index c10a19a2050..00000000000 --- a/src/buffer_cache/eviction_bag.cc +++ /dev/null @@ -1,65 +0,0 @@ -#include "buffer_cache/eviction_bag.hpp" - -#include - -#include "buffer_cache/page.hpp" -#include "random.hpp" -#include "utils.hpp" - -namespace alt { - -eviction_bag_t::eviction_bag_t() - : bag_(), size_(0) { } - -eviction_bag_t::~eviction_bag_t() { - guarantee(bag_.size() == 0); - guarantee(size_ == 0, "size was %" PRIu64, size_); -} - -void eviction_bag_t::change_size(int64_t adjustment) { - rassert(adjustment >= 0 || size_ >= static_cast(-adjustment)); - size_ += adjustment; -} - -void eviction_bag_t::add(page_t *page, uint32_t ser_buf_size) { - bag_.add(page); - size_ += ser_buf_size; -} - -void eviction_bag_t::remove(page_t *page, uint32_t ser_buf_size) { - bag_.remove(page); - uint64_t value = ser_buf_size; - rassert(value <= size_, "value = %" PRIu64 ", size_ = %" PRIu64, - value, size_); - size_ -= value; -} - -bool eviction_bag_t::has_page(page_t *page) const { - return bag_.has_element(page); -} - -bool eviction_bag_t::remove_oldish(page_t **page_out, uint64_t access_time_offset, - page_cache_t *page_cache) { - if (bag_.size() == 0) { - return false; - } else { - const size_t num_randoms = 5; - page_t *oldest = bag_.access_random(randsize(bag_.size())); - for (size_t i = 1; i < num_randoms; ++i) { - page_t *page = bag_.access_random(randsize(bag_.size())); - // We compare relative to the access time offset, so that in the unlikely - // event of a 64-bit overflow, performance degradation is "smooth". - if (access_time_offset - page->access_time() > - access_time_offset - oldest->access_time()) { - oldest = page; - } - } - - remove(oldest, oldest->hypothetical_memory_usage(page_cache)); - *page_out = oldest; - return true; - } -} - - -} // namespace alt diff --git a/src/buffer_cache/eviction_bag.hpp b/src/buffer_cache/eviction_bag.hpp deleted file mode 100644 index 355cebf15e6..00000000000 --- a/src/buffer_cache/eviction_bag.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef BUFFER_CACHE_EVICTION_BAG_HPP_ -#define BUFFER_CACHE_EVICTION_BAG_HPP_ - -#include - -#include "containers/backindex_bag.hpp" - -namespace alt { - -class page_t; -class page_cache_t; - -class eviction_bag_t { -public: - eviction_bag_t(); - ~eviction_bag_t(); - - // Adjusts the size, given how much the size has changed of one of the pages in - // the bag. - void change_size(int64_t adjustment); - - // Adds the page with its known size. - void add(page_t *page, uint32_t ser_buf_size); - - // Removes the page with its size. - void remove(page_t *page, uint32_t ser_buf_size); - - // Returns true if this bag contains the given page. - bool has_page(page_t *page) const; - - uint64_t size() const { return size_; } - - bool remove_oldish(page_t **page_out, uint64_t access_time_offset, - page_cache_t *page_cache); - -private: - backindex_bag_t bag_; - // The size in memory. - uint64_t size_; - - DISABLE_COPYING(eviction_bag_t); -}; - - -} // namespace alt - -#endif // BUFFER_CACHE_EVICTION_BAG_HPP_ diff --git a/src/buffer_cache/free_list.cc b/src/buffer_cache/free_list.cc deleted file mode 100644 index ec0776c560f..00000000000 --- a/src/buffer_cache/free_list.cc +++ /dev/null @@ -1,88 +0,0 @@ -#include "buffer_cache/free_list.hpp" - -#include "serializer/serializer.hpp" - -namespace alt { - -free_list_t::free_list_t(serializer_t *serializer) { - on_thread_t th(serializer->home_thread()); - - next_new_block_id_ = serializer->end_block_id(); - next_new_aux_block_id_ = serializer->end_aux_block_id(); - for (block_id_t i = 0; i < next_new_block_id_; ++i) { - if (serializer->get_delete_bit(i)) { - free_ids_.push_back(i); - } - } - for (block_id_t i = FIRST_AUX_BLOCK_ID; i < next_new_aux_block_id_; ++i) { - if (serializer->get_delete_bit(i)) { - free_aux_ids_.push_back(i); - } - } -} - -free_list_t::~free_list_t() { } - -block_id_t free_list_t::acquire_block_id() { - if (free_ids_.empty()) { - block_id_t ret = next_new_block_id_; - ++next_new_block_id_; - return ret; - } else { - block_id_t ret = free_ids_.back(); - free_ids_.pop_back(); - return ret; - } -} - -block_id_t free_list_t::acquire_aux_block_id() { - if (free_aux_ids_.empty()) { - block_id_t ret = next_new_aux_block_id_; - ++next_new_aux_block_id_; - return ret; - } else { - block_id_t ret = free_aux_ids_.back(); - free_aux_ids_.pop_back(); - return ret; - } -} - -void free_list_t::acquire_chosen_block_id(block_id_t block_id) { - block_id_t *next_new_id = is_aux_block_id(block_id) - ? &next_new_aux_block_id_ - : &next_new_block_id_; - segmented_vector_t *free_ids = is_aux_block_id(block_id) - ? &free_aux_ids_ - : &free_ids_; - if (block_id >= *next_new_id) { - const block_id_t old = *next_new_id; - *next_new_id = block_id + 1; - for (block_id_t i = old; i < block_id; ++i) { - free_ids->push_back(i); - } - } else { - for (size_t i = 0, e = free_ids->size(); i < e; ++i) { - if ((*free_ids)[i] == block_id) { - (*free_ids)[i] = free_ids->back(); - free_ids->pop_back(); - return; - } - } - - crash("acquire_chosen_block_id tried to use %" PR_BLOCK_ID - ", but it was taken.", block_id); - } -} - -void free_list_t::release_block_id(block_id_t block_id) { - if (is_aux_block_id(block_id)) { - free_aux_ids_.push_back(block_id); - } else { - free_ids_.push_back(block_id); - } -} - - - - -} // namespace alt diff --git a/src/buffer_cache/free_list.hpp b/src/buffer_cache/free_list.hpp deleted file mode 100644 index c8829ac1d7b..00000000000 --- a/src/buffer_cache/free_list.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef BUFFER_CACHE_FREE_LIST_HPP_ -#define BUFFER_CACHE_FREE_LIST_HPP_ - -#include "containers/segmented_vector.hpp" -#include "serializer/types.hpp" - -namespace alt { - -class free_list_t { -public: - explicit free_list_t(serializer_t *serializer); - ~free_list_t(); - - // Returns a block id. The current_page_t for this block id, if present, has no - // acquirers. - MUST_USE block_id_t acquire_block_id(); - MUST_USE block_id_t acquire_aux_block_id(); - void release_block_id(block_id_t block_id); - - // Like acquire_block_id, only instead of being told what block id you acquired, - // you tell it which block id you're acquiring. It must be available! - void acquire_chosen_block_id(block_id_t block_id); - -private: - block_id_t next_new_block_id_; - segmented_vector_t free_ids_; - block_id_t next_new_aux_block_id_; - segmented_vector_t free_aux_ids_; - DISABLE_COPYING(free_list_t); -}; - - -} // namespace alt - -#endif // BUFFER_CACHE_FREE_LIST_HPP_ diff --git a/src/buffer_cache/page.cc b/src/buffer_cache/page.cc deleted file mode 100644 index a7a8ae6ae2d..00000000000 --- a/src/buffer_cache/page.cc +++ /dev/null @@ -1,739 +0,0 @@ -#include "buffer_cache/page.hpp" - -#include "arch/runtime/coroutines.hpp" -#include "buffer_cache/page_cache.hpp" -#include "serializer/serializer.hpp" - -namespace alt { - -class page_loader_t { -public: - page_loader_t() - : abandon_page_(false) { } - virtual ~page_loader_t() { } - - virtual void added_waiter(page_cache_t *, cache_account_t *) = 0; - - virtual bool is_really_loading() const = 0; - - MUST_USE bool abandon_page() const { return abandon_page_; } - - void mark_abandon_page() { - rassert(!abandon_page_); - abandon_page_ = true; - } - -private: - bool abandon_page_; - DISABLE_COPYING(page_loader_t); -}; - -class instant_page_loader_t final : public page_loader_t { -public: - instant_page_loader_t() { } - ~instant_page_loader_t() { } - - void added_waiter(page_cache_t *, cache_account_t *) final { - // Do nothing. - } - - bool is_really_loading() const final { - return true; - } - -private: - DISABLE_COPYING(instant_page_loader_t); -}; - -// We pick a weird that forces the logic and performance to not spaz out if the -// access time counter overflows. Performance degradation is "smooth" if -// access_time_counter_ loops around past INITIAL_ACCESS_TIME -- which shouldn't be a -// problem for now, as long as we increment it one value at a time. -static const uint64_t READ_AHEAD_ACCESS_TIME = evicter_t::INITIAL_ACCESS_TIME - 1; - - -page_t::page_t(block_id_t _block_id, page_cache_t *page_cache) - : block_id_(_block_id), - loader_(nullptr), - access_time_(page_cache->evicter().next_access_time()), - snapshot_refcount_(0) { - page_cache->evicter().add_deferred_loaded(this); - - coro_t::spawn_now_dangerously(std::bind(&page_t::deferred_load_with_block_id, - this, - _block_id, - page_cache)); -} - -page_t::page_t(block_id_t _block_id, page_cache_t *page_cache, - cache_account_t *account) - : block_id_(_block_id), - loader_(nullptr), - access_time_(page_cache->evicter().next_access_time()), - snapshot_refcount_(0) { - page_cache->evicter().add_not_yet_loaded(this); - - coro_t::spawn_now_dangerously(std::bind(&page_t::load_with_block_id, - this, - _block_id, - page_cache, - account)); -} - -page_t::page_t(block_id_t _block_id, buf_ptr_t buf, - page_cache_t *page_cache) - : block_id_(_block_id), - loader_(nullptr), - buf_(std::move(buf)), - access_time_(page_cache->evicter().next_access_time()), - snapshot_refcount_(0) { - rassert(buf_.has()); - page_cache->evicter().add_to_evictable_unbacked(this); -} - -page_t::page_t(block_id_t _block_id, - buf_ptr_t buf, - const counted_t &_block_token, - page_cache_t *page_cache) - : block_id_(_block_id), - loader_(nullptr), - buf_(std::move(buf)), - block_token_(_block_token), - access_time_(READ_AHEAD_ACCESS_TIME), - snapshot_refcount_(0) { - rassert(buf_.has()); - page_cache->evicter().add_to_evictable_disk_backed(this); -} - -page_t::page_t(page_t *copyee, page_cache_t *page_cache, cache_account_t *account) - : block_id_(copyee->block_id_), - loader_(nullptr), - access_time_(page_cache->evicter().next_access_time()), - snapshot_refcount_(0) { - page_cache->evicter().add_not_yet_loaded(this); - coro_t::spawn_now_dangerously(std::bind(&page_t::load_from_copyee, - this, - copyee, - page_cache, - account)); -} - -page_t::~page_t() { - rassert(waiters_.empty()); - rassert(snapshot_refcount_ == 0); - if (loader_ != nullptr) { - loader_->mark_abandon_page(); - } -} - -bool page_t::loader_is_loading(page_loader_t *loader) { - rassert(loader != nullptr); - return loader->is_really_loading(); -} - -void page_t::load_from_copyee(page_t *page, page_t *copyee, - page_cache_t *page_cache, - cache_account_t *account) { - // This is called using spawn_now_dangerously. We need to atomically set - // loader_ and do some other things. - instant_page_loader_t loader; - rassert(page->loader_ == nullptr); - page->loader_ = &loader; - - auto_drainer_t::lock_t lock = page_cache->drainer_lock(); - page_ptr_t copyee_ptr(copyee); - - // Okay, it's safe to block. - { - page_acq_t acq; - acq.init(copyee, page_cache, account); - acq.buf_ready_signal()->wait(); - - ASSERT_FINITE_CORO_WAITING; - if (!loader.abandon_page()) { - // KSI: If somehow there are no snapshotters of copyee now (besides - // ourself), maybe we could avoid copying this memory. We need to - // carefully track snapshotters anyway, once we're comfortable with that, - // we could do it. - - { - usage_adjuster_t adjuster(page_cache, page); - page->buf_ = buf_ptr_t::alloc_copy(copyee->buf_); - page->loader_ = nullptr; - } - - page->pulse_waiters_or_make_evictable(page_cache); - } - } - - copyee_ptr.reset_page_ptr(page_cache); - // Don't bother calling consider_evicting_current_page -- we just acquired the - // page for the sake of a live current_page_t, so it wouldn't do anything if it - // did work correctly. (It's also possible that this function didn't block, that - // we're still inside the page_t constructor, and its page_ptr_t has not been - // initialized. It would turn out that we're safe, because there must be some - // page_acq_t calling get_buf_write, not to mention a current_page_acq_t. We - // choose to be performance-fragile rather than correctness-fragile. -} - -void page_t::finish_load_with_block_id(page_t *page, page_cache_t *page_cache, - counted_t block_token, - buf_ptr_t buf) { - rassert(!page->block_token_.has()); - rassert(!page->buf_.has()); - rassert(block_token.has()); - { - usage_adjuster_t adjuster(page_cache, page); - page->buf_ = std::move(buf); - page->block_token_ = std::move(block_token); - page->loader_ = nullptr; - } - - page->pulse_waiters_or_make_evictable(page_cache); -} - -// This lives on the serializer thread. -class deferred_block_token_t { -public: - counted_t token; -}; - -class deferred_page_loader_t final : public page_loader_t { -public: - explicit deferred_page_loader_t(page_t *_page) - : page_(_page), block_token_ptr_(new deferred_block_token_t) { } - - deferred_block_token_t *block_token_ptr() { - return block_token_ptr_.get(); - } - - scoped_ptr_t extract_block_token_ptr() { - return std::move(block_token_ptr_); - } - - void added_waiter(page_cache_t *page_cache, cache_account_t *account) final { - coro_t::spawn_now_dangerously(std::bind(&page_t::catch_up_with_deferred_load, - this, - page_cache, - account)); - } - - bool is_really_loading() const final { - return false; - } - - page_t *page() { return page_; } - - -private: - page_t *page_; - - // We put the block token on the heap, so that it can survive past the - // deferred_load_with_block_id coroutine (so that catch_up_with_deferred_load can - // take ownership of it). - scoped_ptr_t block_token_ptr_; - - DISABLE_COPYING(deferred_page_loader_t); -}; - -void page_t::catch_up_with_deferred_load( - deferred_page_loader_t *deferred_loader, - page_cache_t *page_cache, - cache_account_t *account) { - page_t *page = deferred_loader->page(); - - // This is called using spawn_now_dangerously. The deferred_load_with_block_id - // operation associated with `loader` is on the serializer thread, or being sent - // to the serializer thread, or returning back from the serializer thread, right - // now. - rassert(page->loader_ == deferred_loader); - instant_page_loader_t our_loader; - page->loader_ = &our_loader; - - // Before blocking, take ownership of the block_token_ptr. - scoped_ptr_t block_token_ptr - = deferred_loader->extract_block_token_ptr(); - - // Before blocking, tell the deferred loader to abandon the page. It's before - // the deferred loader has gotten back to this thread, so we can do that. - deferred_loader->mark_abandon_page(); - - // Before blocking, tell the evicter to put us in the right category. - page_cache->evicter().catch_up_deferred_load(page); - - buf_ptr_t buf; - { - serializer_t *const serializer = page_cache->serializer(); - - // We use the fact that on_thread_t preserves order with the on_thread_t in - // deferred_load_with_block_id. This means that it's already run its section - // on the serializer thread, and has initialized block_token_ptr->token. - on_thread_t th(serializer->home_thread()); - // Now finish what the rest of load_with_block_id would do. - rassert(block_token_ptr->token.has()); - buf = serializer->block_read(block_token_ptr->token, - account->get()); - } - - ASSERT_FINITE_CORO_WAITING; - if (our_loader.abandon_page()) { - return; - } - - page_t::finish_load_with_block_id(page, page_cache, - std::move(block_token_ptr->token), - std::move(buf)); -} - - -void page_t::deferred_load_with_block_id(page_t *page, block_id_t block_id, - page_cache_t *page_cache) { - // This is called using spawn_now_dangerously. We need to set - // loader_ before blocking the coroutine. - deferred_page_loader_t loader(page); - rassert(page->loader_ == nullptr); - page->loader_ = &loader; - - // Before blocking, before any catch_up_with_deferred_load can steal it, get the - // deferred_block_token_t pointer. - deferred_block_token_t *on_heap_token = loader.block_token_ptr(); - - auto_drainer_t::lock_t lock = page_cache->drainer_lock(); - - { - serializer_t *const serializer = page_cache->serializer(); - on_thread_t th(serializer->home_thread()); - // The index_read should not block. It must not block, because we're - // depending on order preservation across the on_thread_t. - ASSERT_FINITE_CORO_WAITING; - on_heap_token->token = serializer->index_read(block_id); - rassert(on_heap_token->token.has()); - } - - ASSERT_FINITE_CORO_WAITING; - if (loader.abandon_page()) { - return; - } - - rassert(!page->block_token_.has()); - rassert(!page->buf_.has()); - rassert(loader.block_token_ptr() == on_heap_token); - rassert(on_heap_token->token.has()); - { - usage_adjuster_t adjuster(page_cache, page); - page->block_token_ = std::move(on_heap_token->token); - page->loader_ = nullptr; - } - - rassert(page->waiters_.empty()); - -#ifndef NDEBUG - evicter_t *const evicter = &page_cache->evicter(); - rassert(evicter->page_is_in_evicted_bag(page)); - rassert(evicter->evicted_category() - == evicter->correct_eviction_category(page)); -#endif -} - -void page_t::load_with_block_id(page_t *page, block_id_t block_id, - page_cache_t *page_cache, - cache_account_t *account) { - // This is called using spawn_now_dangerously. We need to set - // loader_ before blocking the coroutine. - instant_page_loader_t loader; - rassert(page->loader_ == nullptr); - page->loader_ = &loader; - - auto_drainer_t::lock_t lock = page_cache->drainer_lock(); - - buf_ptr_t buf; - counted_t block_token; - - { - serializer_t *const serializer = page_cache->serializer(); - on_thread_t th(serializer->home_thread()); - block_token = serializer->index_read(block_id); - rassert(block_token.has()); - buf = serializer->block_read(block_token, - account->get()); - } - - ASSERT_FINITE_CORO_WAITING; - if (loader.abandon_page()) { - return; - } - - page_t::finish_load_with_block_id(page, page_cache, - std::move(block_token), - std::move(buf)); -} - -void page_t::add_snapshotter() { - // This may not block, because it's called at the beginning of - // page_t::load_from_copyee. - ASSERT_NO_CORO_WAITING; - ++snapshot_refcount_; -} - -void page_t::remove_snapshotter(page_cache_t *page_cache) { - rassert(snapshot_refcount_ > 0); - --snapshot_refcount_; - if (snapshot_refcount_ == 0) { - // Every page_acq_t is bounded by the lifetime of some page_ptr_t: either the - // one in current_page_acq_t or its current_page_t or the one in - // load_from_copyee. - rassert(waiters_.empty()); - - page_cache->evicter().remove_page(this); - delete this; - } -} - -size_t page_t::num_snapshot_references() { - return snapshot_refcount_; -} - -page_t *page_t::make_copy(page_cache_t *page_cache, cache_account_t *account) { - page_t *ret = new page_t(this, page_cache, account); - return ret; -} - -void page_t::pulse_waiters_or_make_evictable(page_cache_t *page_cache) { - rassert(page_cache->evicter().page_is_in_unevictable_bag(this)); - if (waiters_.empty()) { - page_cache->evicter().move_unevictable_to_evictable(this); - } else { - for (page_acq_t *p = waiters_.head(); p != nullptr; p = waiters_.next(p)) { - // The waiter's not already going to have been pulsed. - p->buf_ready_signal_.pulse(); - } - } -} - -void page_t::add_waiter(page_acq_t *acq, cache_account_t *account) { - eviction_bag_t *old_bag - = acq->page_cache()->evicter().correct_eviction_category(this); - waiters_.push_front(acq); - acq->page_cache()->evicter().change_to_correct_eviction_bag(old_bag, this); - if (buf_.has()) { - acq->buf_ready_signal_.pulse(); - } else if (loader_ != nullptr) { - loader_->added_waiter(acq->page_cache(), account); - } else if (block_token_.has()) { - coro_t::spawn_now_dangerously(std::bind(&page_t::load_using_block_token, - this, - acq->page_cache(), - account)); - } else { - crash("An unloaded block is not in a loadable state."); - } -} - -// Unevicts page. -void page_t::load_using_block_token(page_t *page, page_cache_t *page_cache, - cache_account_t *account) { - // This is called using spawn_now_dangerously. We need to set - // loader_ before blocking the coroutine. - instant_page_loader_t loader; - rassert(page->loader_ == nullptr); - page->loader_ = &loader; - - page_cache->evicter().reloading_page(page); - - auto_drainer_t::lock_t lock = page_cache->drainer_lock(); - - counted_t block_token = page->block_token_; - rassert(block_token.has()); - - buf_ptr_t buf; - { - serializer_t *const serializer = page_cache->serializer(); - - on_thread_t th(serializer->home_thread()); - buf = serializer->block_read(block_token, - account->get()); - } - - ASSERT_FINITE_CORO_WAITING; - if (loader.abandon_page()) { - return; - } - - rassert(page->block_token_.get() == block_token.get()); - rassert(!page->buf_.has()); - block_token.reset(); - { - usage_adjuster_t adjuster(page_cache, page); - page->buf_ = std::move(buf); - page->loader_ = nullptr; - } - - page->pulse_waiters_or_make_evictable(page_cache); -} - -void page_t::set_page_buf_size(block_size_t block_size, page_cache_t *page_cache) { - rassert(buf_.has(), - "Called outside page_acq_t or without waiting for the buf_ready_signal_?"); - rassert(!block_token_.has(), - "Modified a page_t without resetting the block token."); - { - usage_adjuster_t adjuster(page_cache, this); - buf_.resize_fill_zero(block_size); - } -} - -block_size_t page_t::get_page_buf_size() { - rassert(buf_.has()); - return buf_.block_size(); -} - -uint32_t page_t::hypothetical_memory_usage(page_cache_t *page_cache) const { - // Note that the `base_size` we're assigning is overly conservative. - // Not every `page_t` corresponds to one `current_page_t`. and not every - // `page_t` has a block token. However in typical workloads, most of them will. - // - // It is tempting to account for the `standard_block_token_t` only if - // `block_token_.has()` is `true`. However there are some places in the code - // that assume that `hypothetical_memory_usage` doesn't change after setting - // the block token. - size_t base_size = sizeof(current_page_t) + sizeof(page_t) + - sizeof(standard_block_token_t); - if (buf_.has()) { - return base_size + buf_.aligned_block_size(); - } else if (block_token_.has()) { - return base_size + - buf_ptr_t::compute_aligned_block_size(block_token_->block_size()); - } else { - // If the block isn't loaded and we don't know, we respond conservatively, - // to stay on the proper side of the memory limit. - return base_size + page_cache->max_block_size().ser_value(); - } -} - -void *page_t::get_page_buf(page_cache_t *page_cache) { - rassert(buf_.has()); - access_time_ = page_cache->evicter().next_access_time(); - return buf_.cache_data(); -} - -void page_t::reset_block_token(DEBUG_VAR page_cache_t *page_cache) { - // The page is supposed to have its buffer acquired in reset_block_token -- it's - // the thing modifying the page. We thus assume that the page is unevictable and - // resetting block_token_ doesn't change that. - rassert(!waiters_.empty()); - rassert(buf_.has()); - if (block_token_.has()) { - rassert(buf_.block_size().value() == block_token_->block_size().value()); -#ifndef NDEBUG - const uint32_t usage_before = hypothetical_memory_usage(page_cache); -#endif - block_token_.reset(); - // Hypothetical memory usage shouldn't have changed -- because the buf is - // already loaded in memory. - rassert(usage_before == hypothetical_memory_usage(page_cache)); - } -} - - -void page_t::remove_waiter(page_acq_t *acq) { - eviction_bag_t *old_bag - = acq->page_cache()->evicter().correct_eviction_category(this); - waiters_.remove(acq); - acq->page_cache()->evicter().change_to_correct_eviction_bag(old_bag, this); - - // page_acq_t always has a lesser lifetime than some page_ptr_t. - rassert(snapshot_refcount_ > 0); -} - -void page_t::evict_self(DEBUG_VAR page_cache_t *page_cache) { - // A page_t can only self-evict if it has a block token (for now). - rassert(waiters_.empty()); - rassert(block_token_.has()); - rassert(buf_.has()); - rassert(block_token_->block_size().value() == buf_.block_size().value()); -#ifndef NDEBUG - const uint32_t usage_before = hypothetical_memory_usage(page_cache); -#endif - buf_.reset(); - // Hypothetical memory usage shouldn't have changed -- the block token has the - // same block size. - rassert(usage_before == hypothetical_memory_usage(page_cache)); -} - -ser_buffer_t *page_t::get_loaded_ser_buffer() { - rassert(buf_.has()); - return buf_.ser_buffer(); -} - -// Used for after we've flushed the page. -void page_t::init_block_token(counted_t token, - DEBUG_VAR page_cache_t *page_cache) { - rassert(buf_.has()); - rassert(buf_.block_size().value() == token->block_size().value()); - rassert(!block_token_.has()); -#ifndef NDEBUG - const uint32_t usage_before = hypothetical_memory_usage(page_cache); -#endif - block_token_ = std::move(token); - // Hypothetical memory usage shouldn't have changed -- the block token has the - // same block size. - rassert(usage_before == hypothetical_memory_usage(page_cache)); -} - - - -page_acq_t::page_acq_t() : page_(nullptr), page_cache_(nullptr) { -} - -void page_acq_t::init(page_t *page, page_cache_t *_page_cache, - cache_account_t *account) { - rassert(page_ == nullptr); - rassert(page_cache_ == nullptr); - rassert(!buf_ready_signal_.is_pulsed()); - page_ = page; - page_cache_ = _page_cache; - page_->add_waiter(this, account); -} - -page_acq_t::~page_acq_t() { - if (page_ != nullptr) { - rassert(page_cache_ != nullptr); - page_->remove_waiter(this); - - // There's no need to call consider_evicting_current_page, because page_acq_t - // always has a lesser lifetime than some current_page_acq_t or some other - // page_ptr_t. It would be risky to call it -- page_acq_t is used in - // load_from_copyee. - } -} - -bool page_acq_t::has() const { - return page_ != nullptr; -} - -signal_t *page_acq_t::buf_ready_signal() { - return &buf_ready_signal_; -} - -block_size_t page_acq_t::get_buf_size() { - buf_ready_signal_.wait(); - return page_->get_page_buf_size(); -} - -void *page_acq_t::get_buf_write(block_size_t block_size) { - buf_ready_signal_.wait(); - page_->reset_block_token(page_cache_); - page_->set_page_buf_size(block_size, page_cache_); - return page_->get_page_buf(page_cache_); -} - -const void *page_acq_t::get_buf_read() { - buf_ready_signal_.wait(); - return page_->get_page_buf(page_cache_); -} - -page_ptr_t::page_ptr_t() : page_(nullptr) { -} - -page_ptr_t::~page_ptr_t() { - rassert(page_ == nullptr); -} - -page_ptr_t::page_ptr_t(page_ptr_t &&movee) - : page_(movee.page_) { - movee.page_ = nullptr; -} - -page_ptr_t &page_ptr_t::operator=(page_ptr_t &&movee) { - // We can't do true assignment, destructing an old page-having value, because - // reset() has to manually be called. (This assertion is redundant with the one - // that'll enforce this fact in tmp's destructor.) - rassert(page_ == nullptr); - - page_ptr_t tmp(std::move(movee)); - swap_with(&tmp); - return *this; -} - -void page_ptr_t::init(page_t *page) { - rassert(page_ == nullptr); - page_ = page; - if (page_ != nullptr) { - page_->add_snapshotter(); - } -} - -void page_ptr_t::reset_page_ptr(page_cache_t *page_cache) { - if (page_ != nullptr) { - page_t *ptr = page_; - page_ = nullptr; - ptr->remove_snapshotter(page_cache); - } -} - -page_t *page_ptr_t::get_page_for_read() const { - rassert(page_ != nullptr); - return page_; -} - -page_t *page_ptr_t::get_page_for_write(page_cache_t *page_cache, - cache_account_t *account) { - rassert(page_ != nullptr); - if (page_->num_snapshot_references() > 1) { - page_ptr_t tmp(page_->make_copy(page_cache, account)); - swap_with(&tmp); - tmp.reset_page_ptr(page_cache); - // We don't call consider_evicting_current_page here -- it wouldn't do - // anything anyway because `this` is the page_ptr_t for the - // current_page_acq_t, and we know it's not empty. Also we must have a - // page_acq_t right now. We err on the side of being performance-fragile - // instead of correctness-fragile. - } - return page_; -} - -void page_ptr_t::swap_with(page_ptr_t *other) { - std::swap(page_, other->page_); -} - -timestamped_page_ptr_t::timestamped_page_ptr_t() - : timestamp_(repli_timestamp_t::invalid) { } - -timestamped_page_ptr_t::timestamped_page_ptr_t(timestamped_page_ptr_t &&movee) - : timestamp_(movee.timestamp_), page_ptr_(std::move(movee.page_ptr_)) { - movee.timestamp_ = repli_timestamp_t::invalid; -} - -timestamped_page_ptr_t ×tamped_page_ptr_t::operator=(timestamped_page_ptr_t &&movee) { - timestamped_page_ptr_t tmp(std::move(movee)); - std::swap(timestamp_, tmp.timestamp_); - std::swap(page_ptr_, tmp.page_ptr_); - return *this; -} - -timestamped_page_ptr_t::~timestamped_page_ptr_t() { } - -bool timestamped_page_ptr_t::has() const { - return page_ptr_.has(); -} - -void timestamped_page_ptr_t::init(repli_timestamp_t _timestamp, - page_t *page) { - rassert(timestamp_ == repli_timestamp_t::invalid); - rassert(page == nullptr || - _timestamp != repli_timestamp_t::invalid || - is_aux_block_id(page->block_id())); - timestamp_ = _timestamp; - page_ptr_.init(page); -} - -page_t *timestamped_page_ptr_t::get_page_for_read() const { - return page_ptr_.get_page_for_read(); -} - -void timestamped_page_ptr_t::reset_page_ptr(page_cache_t *page_cache) { - ASSERT_NO_CORO_WAITING; - page_ptr_.reset_page_ptr(page_cache); -} - -} // namespace alt diff --git a/src/buffer_cache/page.hpp b/src/buffer_cache/page.hpp deleted file mode 100644 index a51108604f8..00000000000 --- a/src/buffer_cache/page.hpp +++ /dev/null @@ -1,256 +0,0 @@ -#ifndef BUFFER_CACHE_PAGE_HPP_ -#define BUFFER_CACHE_PAGE_HPP_ - -#include "concurrency/cond_var.hpp" -#include "containers/backindex_bag.hpp" -#include "containers/half_intrusive_list.hpp" -#include "repli_timestamp.hpp" -#include "serializer/buf_ptr.hpp" -#include "serializer/types.hpp" - -class cache_account_t; - -namespace alt { - -class page_cache_t; -class page_acq_t; - -class page_loader_t; -class deferred_page_loader_t; -class deferred_block_token_t; - -// A page_t represents a page (a byte buffer of a specific size), having a definite -// value known at the construction of the page_t (and possibly later modified -// in-place, but still a definite known value). -class page_t { -public: - // Defers loading the block for the given block id (but does go and get its block - // token ASAP, so that we can't lose access to the current version of the block). - page_t(block_id_t block_id, page_cache_t *page_cache); - // Loads the block for the given block id. - page_t(block_id_t block_id, page_cache_t *page_cache, cache_account_t *account); - - page_t(block_id_t block_id, buf_ptr_t buf, page_cache_t *page_cache); - page_t(block_id_t block_id, buf_ptr_t buf, - const counted_t &token, - page_cache_t *page_cache); - page_t(page_t *copyee, page_cache_t *page_cache, cache_account_t *account); - ~page_t(); - - page_t *make_copy(page_cache_t *page_cache, cache_account_t *account); - - void add_waiter(page_acq_t *acq, cache_account_t *account); - void remove_waiter(page_acq_t *acq); - - // These may not be called until the page_acq_t's buf_ready_signal is pulsed. - void *get_page_buf(page_cache_t *page_cache); - void reset_block_token(page_cache_t *page_cache); - void set_page_buf_size(block_size_t block_size, page_cache_t *page_cache); - - block_size_t get_page_buf_size(); - - // How much memory the block would use, if it were in memory. (If the block is - // already in memory, this is how much memory the block is currently - // using, of course.) - - uint32_t hypothetical_memory_usage(page_cache_t *page_cache) const; - uint64_t access_time() const { return access_time_; } - - bool is_loading() const { - return loader_ != nullptr && page_t::loader_is_loading(loader_); - } - bool is_deferred_loading() const { - return loader_ != nullptr && !page_t::loader_is_loading(loader_); - } - bool has_waiters() const { return !waiters_.empty(); } - bool is_loaded() const { return buf_.has(); } - bool is_disk_backed() const { return block_token_.has(); } - - void evict_self(page_cache_t *page_cache); - - block_id_t block_id() const { return block_id_; } - - bool page_ptr_count() const { return snapshot_refcount_; } - - const counted_t &block_token() const { - return block_token_; - } - - ser_buffer_t *get_loaded_ser_buffer(); - void init_block_token(counted_t token, - page_cache_t *page_cache); - -private: - friend class page_ptr_t; - friend class deferred_page_loader_t; - static bool loader_is_loading(page_loader_t *loader); - void add_snapshotter(); - void remove_snapshotter(page_cache_t *page_cache); - size_t num_snapshot_references(); - - - void pulse_waiters_or_make_evictable(page_cache_t *page_cache); - - - static void finish_load_with_block_id(page_t *page, page_cache_t *page_cache, - counted_t block_token, - buf_ptr_t buf); - - static void catch_up_with_deferred_load( - deferred_page_loader_t *deferred_loader, - page_cache_t *page_cache, - cache_account_t *account); - - static void deferred_load_with_block_id(page_t *page, block_id_t block_id, - page_cache_t *page_cache); - - - static void load_with_block_id(page_t *page, - block_id_t block_id, - page_cache_t *page_cache, - cache_account_t *account); - - static void load_from_copyee(page_t *page, page_t *copyee, - page_cache_t *page_cache, - cache_account_t *account); - - static void load_using_block_token(page_t *page, page_cache_t *page_cache, - cache_account_t *account); - - friend backindex_bag_index_t *access_backindex(page_t *page); - - // The block id. Used to (potentially) delete the page_t and current_page_t when - // it gets evicted. - const block_id_t block_id_; - - // One of loader_, buf_, or block_token_ is non-null. (Either the page is in - // memory, or there is always a way to get the page into memory.) - page_loader_t *loader_; - - buf_ptr_t buf_; - counted_t block_token_; - - uint64_t access_time_; - - // How many page_ptr_t's point at this page, expecting nothing to modify it, - // other than themselves. - size_t snapshot_refcount_; - - // A list of waiters that expect the value to be loaded, and (as long as there - // are waiters) expect the value to never be evicted. - half_intrusive_list_t waiters_; - - // This page_t's index into its eviction bag (managed by the page_cache_t -- one - // of unevictable_pages_, etc). Which bag we should be in: - // - // if loader_ is non-null: unevictable_pages_ - // else if waiters_ is non-empty: unevictable_pages_ - // else if buf_ is null: evicted_pages_ (and block_token_ is non-null) - // else if block_token_ is non-null: evictable_disk_backed_pages_ - // else: evictable_unbacked_pages_ (buf_ is non-null, block_token_ is null) - // - // So, when loader_, waiters_, buf_, or block_token_ is touched, we might - // need to change this page's eviction bag. - // - // The logic above is implemented in page_cache_t::correct_eviction_category. - backindex_bag_index_t eviction_index_; - - DISABLE_COPYING(page_t); -}; - -inline backindex_bag_index_t *access_backindex(page_t *page) { - return &page->eviction_index_; -} - -// A page_ptr_t holds a pointer to a page_t. -class page_ptr_t { -public: - explicit page_ptr_t(page_t *page) - : page_(nullptr) { init(page); } - page_ptr_t(); - - // The page_ptr_t MUST be reset before the destructor is called. - ~page_ptr_t(); - - // You MUST manually call reset_page_ptr() to reset the page_ptr_t. Then, please - // call consider_evicting_current_page if applicable. - void reset_page_ptr(page_cache_t *page_cache); - - page_ptr_t(page_ptr_t &&movee); - page_ptr_t &operator=(page_ptr_t &&movee); - - void init(page_t *page); - - page_t *get_page_for_read() const; - page_t *get_page_for_write(page_cache_t *page_cache, - cache_account_t *account); - - bool has() const { - return page_ != nullptr; - } - -private: - void swap_with(page_ptr_t *other); - - page_t *page_; - DISABLE_COPYING(page_ptr_t); -}; - -class timestamped_page_ptr_t { -public: - timestamped_page_ptr_t(); - ~timestamped_page_ptr_t(); - - timestamped_page_ptr_t(timestamped_page_ptr_t &&movee); - timestamped_page_ptr_t &operator=(timestamped_page_ptr_t &&movee); - - bool has() const; - - void init(repli_timestamp_t timestamp, page_t *page); - - page_t *get_page_for_read() const; - - repli_timestamp_t timestamp() const { return timestamp_; } - - void reset_page_ptr(page_cache_t *page_cache); - -private: - repli_timestamp_t timestamp_; - page_ptr_t page_ptr_; - DISABLE_COPYING(timestamped_page_ptr_t); -}; - -// This type's purpose is to wait for the page to be loaded, and to prevent it from -// being unloaded. -class page_acq_t : public half_intrusive_list_node_t { -public: - page_acq_t(); - ~page_acq_t(); - - void init(page_t *page, page_cache_t *page_cache, cache_account_t *account); - - page_cache_t *page_cache() const { - rassert(page_cache_ != NULL); - return page_cache_; - } - - signal_t *buf_ready_signal(); - bool has() const; - - // These block, uninterruptibly waiting for buf_ready_signal() to be pulsed. - block_size_t get_buf_size(); - void *get_buf_write(block_size_t block_size); - const void *get_buf_read(); - -private: - friend class page_t; - - page_t *page_; - page_cache_t *page_cache_; - cond_t buf_ready_signal_; - DISABLE_COPYING(page_acq_t); -}; - -} // namespace alt - -#endif // BUFFER_CACHE_PAGE_HPP_ diff --git a/src/buffer_cache/page_cache.cc b/src/buffer_cache/page_cache.cc deleted file mode 100644 index 778767fccde..00000000000 --- a/src/buffer_cache/page_cache.cc +++ /dev/null @@ -1,1575 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "buffer_cache/page_cache.hpp" - -#include -#include -#include -#include - -#include "arch/runtime/coroutines.hpp" -#include "arch/runtime/runtime.hpp" -#include "arch/runtime/runtime_utils.hpp" -#include "concurrency/auto_drainer.hpp" -#include "concurrency/new_mutex.hpp" -#include "buffer_cache/cache_balancer.hpp" -#include "do_on_thread.hpp" -#include "serializer/serializer.hpp" -#include "stl_utils.hpp" - -cache_conn_t::~cache_conn_t() { - // The user could only be expected to make sure that txn_t objects don't have - // their lifetime exceed the cache_conn_t's. Soft durability makes it possible - // that the inner page_txn_t's lifetime would exceed the cache_conn_t's. So we - // need to tell the page_txn_t that we don't exist -- we do so by nullating its - // cache_conn_ pointer (which it's capable of handling). - if (newest_txn_ != nullptr) { - newest_txn_->cache_conn_ = nullptr; - newest_txn_ = nullptr; - } -} - - -namespace alt { - -class current_page_help_t { -public: - current_page_help_t(block_id_t _block_id, page_cache_t *_page_cache) - : block_id(_block_id), page_cache(_page_cache) { } - block_id_t block_id; - page_cache_t *page_cache; -}; - -void throttler_acq_t::update_dirty_page_count(int64_t new_count) { - rassert( - block_changes_semaphore_acq_.count() == index_changes_semaphore_acq_.count()); - if (new_count > block_changes_semaphore_acq_.count()) { - block_changes_semaphore_acq_.change_count(new_count); - index_changes_semaphore_acq_.change_count(new_count); - } -} - -void throttler_acq_t::mark_dirty_pages_written() { - block_changes_semaphore_acq_.change_count(0); -} - -page_read_ahead_cb_t::page_read_ahead_cb_t(serializer_t *serializer, - page_cache_t *page_cache) - : serializer_(serializer), page_cache_(page_cache) { - serializer_->register_read_ahead_cb(this); -} - -page_read_ahead_cb_t::~page_read_ahead_cb_t() { } - -void page_read_ahead_cb_t::offer_read_ahead_buf( - block_id_t block_id, - buf_ptr_t *buf, - const counted_t &token) { - assert_thread(); - buf_ptr_t local_buf = std::move(*buf); - - block_size_t block_size = block_size_t::undefined(); - scoped_device_block_aligned_ptr_t ptr; - local_buf.release(&block_size, &ptr); - - // We're going to reconstruct the buf_ptr_t on the other side of this do_on_thread - // call, so we'd better make sure the block size is right. - guarantee(block_size.value() == token->block_size().value()); - - // Notably, this code relies on do_on_thread to preserve callback order (which it - // does do). - do_on_thread(page_cache_->home_thread(), - std::bind(&page_cache_t::add_read_ahead_buf, - page_cache_, - block_id, - copyable_unique_t >(std::move(ptr)), - token)); -} - -void page_read_ahead_cb_t::destroy_self() { - serializer_->unregister_read_ahead_cb(this); - serializer_ = nullptr; - - page_cache_t *page_cache = page_cache_; - page_cache_ = nullptr; - - do_on_thread(page_cache->home_thread(), - std::bind(&page_cache_t::read_ahead_cb_is_destroyed, page_cache)); - - // Self-deletion. Old-school. - delete this; -} - -void page_cache_t::consider_evicting_current_page(block_id_t block_id) { - ASSERT_NO_CORO_WAITING; - // We can't do anything until read-ahead is done, because it uses the existence - // of a current_page_t entry to figure out whether the read-ahead page could be - // out of date. - if (read_ahead_cb_ != nullptr) { - return; - } - - auto page_it = current_pages_.find(block_id); - if (page_it == current_pages_.end()) { - return; - } - - current_page_t *page_ptr = page_it->second; - if (page_ptr->should_be_evicted()) { - current_pages_.erase(block_id); - page_ptr->reset(this); - delete page_ptr; - } -} - -void page_cache_t::add_read_ahead_buf(block_id_t block_id, - scoped_device_block_aligned_ptr_t ptr, - const counted_t &token) { - assert_thread(); - - // We MUST stop if read_ahead_cb_ is NULL because that means current_page_t's - // could start being destroyed. - if (read_ahead_cb_ == nullptr) { - return; - } - - // We MUST stop if current_pages_[block_id] already exists, because that means - // the read-ahead page might be out of date. - if (current_pages_.count(block_id) > 0) { - return; - } - - // We know the read-ahead page is not out of date if current_pages_[block_id] - // doesn't exist and if read_ahead_cb_ still exists -- that means a current_page_t - // for the block id was never created, and thus the page could not have been - // modified (not to mention that we've already got the page in memory, so there is - // no useful work to be done). - - buf_ptr_t buf(token->block_size(), std::move(ptr)); - current_pages_[block_id] = new current_page_t(block_id, std::move(buf), token, this); -} - -void page_cache_t::have_read_ahead_cb_destroyed() { - assert_thread(); - - if (read_ahead_cb_ != nullptr) { - // By setting read_ahead_cb_ to nullptr, we make sure we only tell the read - // ahead cb to destroy itself exactly once. - page_read_ahead_cb_t *cb = read_ahead_cb_; - read_ahead_cb_ = nullptr; - - do_on_thread(cb->home_thread(), - std::bind(&page_read_ahead_cb_t::destroy_self, cb)); - - coro_t::spawn_sometime(std::bind(&page_cache_t::consider_evicting_all_current_pages, this, drainer_->lock())); - } -} - -void page_cache_t::consider_evicting_all_current_pages(page_cache_t *page_cache, - auto_drainer_t::lock_t lock) { - // Atomically grab a list of block IDs that currently exist in current_pages. - std::vector current_block_ids; - current_block_ids.reserve(page_cache->current_pages_.size()); - for (const auto ¤t_page : page_cache->current_pages_) { - current_block_ids.push_back(current_page.first); - } - - // In a separate step, evict current pages that should be evicted. - // We do this separately so that we can yield between evictions. - size_t i = 0; - for (block_id_t id : current_block_ids) { - page_cache->consider_evicting_current_page(id); - if (i % 16 == 15) { - coro_t::yield(); - if (lock.get_drain_signal()->is_pulsed()) { - return; - } - } - ++i; - } -} - - -void page_cache_t::read_ahead_cb_is_destroyed() { - assert_thread(); - read_ahead_cb_existence_.reset(); -} - -class page_cache_index_write_sink_t { -public: - // When sink is acquired, we get in line for mutex_ right away and release the - // sink. The serializer_t interface uses new_mutex_t. - fifo_enforcer_sink_t sink; - new_mutex_t mutex; -}; - -page_cache_t::page_cache_t(serializer_t *_serializer, - cache_balancer_t *balancer, - alt_txn_throttler_t *throttler) - : max_block_size_(_serializer->max_block_size()), - serializer_(_serializer), - free_list_(_serializer), - evicter_(), - read_ahead_cb_(nullptr), - drainer_(make_scoped()) { - - const bool start_read_ahead = balancer->read_ahead_ok_at_start(); - if (start_read_ahead) { - read_ahead_cb_existence_ = drainer_->lock(); - } - - page_read_ahead_cb_t *local_read_ahead_cb = nullptr; - { - on_thread_t thread_switcher(_serializer->home_thread()); - if (start_read_ahead) { - local_read_ahead_cb = new page_read_ahead_cb_t(_serializer, this); - } - default_reads_account_.init(_serializer->home_thread(), - _serializer->make_io_account(CACHE_READS_IO_PRIORITY)); - index_write_sink_.init(new page_cache_index_write_sink_t); - recencies_ = _serializer->get_all_recencies(); - } - - ASSERT_NO_CORO_WAITING; - // We don't want to accept read-ahead buffers (or any operations) until the - // evicter is ready. So we set read_ahead_cb_ here so that we accept read-ahead - // buffers at exactly the same time that we initialize the evicter. We - // initialize the read_ahead_cb_ after the evicter_ because that way reentrant - // usage by the balancer (before page_cache_t construction completes) would be - // more likely to trip an assertion. - evicter_.initialize(this, balancer, throttler); - read_ahead_cb_ = local_read_ahead_cb; -} - -page_cache_t::~page_cache_t() { - assert_thread(); - - have_read_ahead_cb_destroyed(); - - drainer_.reset(); - size_t i = 0; - for (auto &&page : current_pages_) { - if (i % 256 == 255) { - coro_t::yield(); - } - ++i; - page.second->reset(this); - delete page.second; - } - - { - /* IO accounts and a few other fields must be destroyed on the serializer - thread. */ - on_thread_t thread_switcher(serializer_->home_thread()); - // Resetting default_reads_account_ is opportunistically done here, instead - // of making its destructor switch back to the serializer thread a second - // time. - default_reads_account_.reset(); - index_write_sink_.reset(); - } -} - -// We go a bit old-school, with a self-destroying callback. -class flush_and_destroy_txn_waiter_t : public signal_t::subscription_t { -public: - flush_and_destroy_txn_waiter_t(auto_drainer_t::lock_t &&lock, - page_txn_t *txn, - std::function on_flush_complete) - : lock_(std::move(lock)), - txn_(txn), - on_flush_complete_(std::move(on_flush_complete)) { } - -private: - void run() { - // Tell everybody without delay that the flush is complete. - on_flush_complete_(&txn_->throttler_acq_); - - // We have to do the rest _later_ because of signal_t::subscription_t not - // allowing reentrant signal_t::subscription_t::reset() calls, and the like, - // even though it would be valid. - // We are using `call_later_on_this_thread` instead of spawning a coroutine - // to reduce memory overhead. - class kill_later_t : public linux_thread_message_t { - public: - explicit kill_later_t(flush_and_destroy_txn_waiter_t *self) : - self_(self) { } - void on_thread_switch() { - self_->kill_ourselves(); - delete this; - } - private: - flush_and_destroy_txn_waiter_t *self_; - }; - call_later_on_this_thread(new kill_later_t(this)); - } - - void kill_ourselves() { - // We can't destroy txn_->flush_complete_cond_ until we've reset our - // subscription, because computers. - reset(); - delete txn_; - delete this; - } - - auto_drainer_t::lock_t lock_; - page_txn_t *txn_; - std::function on_flush_complete_; - - DISABLE_COPYING(flush_and_destroy_txn_waiter_t); -}; - -void page_cache_t::flush_and_destroy_txn( - scoped_ptr_t txn, - std::function on_flush_complete) { - guarantee(txn->live_acqs_ == 0, - "A current_page_acq_t lifespan exceeds its page_txn_t's."); - guarantee(!txn->began_waiting_for_flush_); - - txn->announce_waiting_for_flush(); - - page_txn_t *page_txn = txn.release(); - flush_and_destroy_txn_waiter_t *sub - = new flush_and_destroy_txn_waiter_t(drainer_->lock(), page_txn, - std::move(on_flush_complete)); - - sub->reset(&page_txn->flush_complete_cond_); -} - -void page_cache_t::end_read_txn(scoped_ptr_t txn) { - guarantee(txn->touched_pages_.empty()); - guarantee(txn->live_acqs_ == 0, - "A current_page_acq_t lifespan exceeds its page_txn_t's."); - guarantee(!txn->began_waiting_for_flush_); - - txn->flush_complete_cond_.pulse(); -} - - -current_page_t *page_cache_t::page_for_block_id(block_id_t block_id) { - assert_thread(); - - auto page_it = current_pages_.find(block_id); - if (page_it == current_pages_.end()) { - rassert(is_aux_block_id(block_id) || - recency_for_block_id(block_id) != repli_timestamp_t::invalid, - "Expected block %" PR_BLOCK_ID " not to be deleted " - "(should you have used alt_create_t::create?).", - block_id); - page_it = current_pages_.insert( - page_it, std::make_pair(block_id, new current_page_t(block_id))); - } else { - rassert(!page_it->second->is_deleted()); - } - - return page_it->second; -} - -current_page_t *page_cache_t::page_for_new_block_id( - block_type_t block_type, - block_id_t *block_id_out) { - assert_thread(); - block_id_t block_id; - switch (block_type) { - case block_type_t::aux: - block_id = free_list_.acquire_aux_block_id(); - break; - case block_type_t::normal: - block_id = free_list_.acquire_block_id(); - break; - default: - unreachable(); - } - current_page_t *ret = internal_page_for_new_chosen(block_id); - *block_id_out = block_id; - return ret; -} - -current_page_t *page_cache_t::page_for_new_chosen_block_id(block_id_t block_id) { - assert_thread(); - // Tell the free list this block id is taken. - free_list_.acquire_chosen_block_id(block_id); - return internal_page_for_new_chosen(block_id); -} - -current_page_t *page_cache_t::internal_page_for_new_chosen(block_id_t block_id) { - assert_thread(); - rassert(is_aux_block_id(block_id) || - recency_for_block_id(block_id) == repli_timestamp_t::invalid, - "expected chosen block %" PR_BLOCK_ID "to be deleted", block_id); - if (!is_aux_block_id(block_id)) { - set_recency_for_block_id(block_id, repli_timestamp_t::distant_past); - } - - buf_ptr_t buf = buf_ptr_t::alloc_uninitialized(max_block_size_); - -#if !defined(NDEBUG) || defined(VALGRIND) - // KSI: This should actually _not_ exist -- we are ignoring legitimate errors - // where we write uninitialized data to disk. - memset(buf.cache_data(), 0xCD, max_block_size_.value()); -#endif - - auto inserted_page = current_pages_.insert(std::make_pair( - block_id, new current_page_t(block_id, std::move(buf), this))); - guarantee(inserted_page.second); - - return inserted_page.first->second; -} - -cache_account_t page_cache_t::create_cache_account(int priority) { - // We assume that a priority of 100 means that the transaction should have the - // same priority as all the non-accounted transactions together. Not sure if this - // makes sense. - - // Be aware of rounding errors... (what can be do against those? probably just - // setting the default io_priority_reads high enough) - int io_priority = std::max(1, CACHE_READS_IO_PRIORITY * priority / 100); - - // TODO: This is a heuristic. While it might not be evil, it's not really optimal - // either. - int outstanding_requests_limit = std::max(1, 16 * priority / 100); - - file_account_t *io_account; - { - // Ideally we shouldn't have to switch to the serializer thread. But that's - // what the file account API is right now, deep in the I/O layer. - on_thread_t thread_switcher(serializer_->home_thread()); - io_account = serializer_->make_io_account(io_priority, - outstanding_requests_limit); - } - - return cache_account_t(serializer_->home_thread(), io_account); -} - - -current_page_acq_t::current_page_acq_t() - : page_cache_(nullptr), the_txn_(nullptr) { } - -current_page_acq_t::current_page_acq_t(page_txn_t *txn, - block_id_t _block_id, - access_t _access, - page_create_t create) - : page_cache_(nullptr), the_txn_(nullptr) { - init(txn, _block_id, _access, create); -} - -current_page_acq_t::current_page_acq_t(page_txn_t *txn, - alt_create_t create, - block_type_t block_type) - : page_cache_(nullptr), the_txn_(nullptr) { - init(txn, create, block_type); -} - -current_page_acq_t::current_page_acq_t(page_cache_t *_page_cache, - block_id_t _block_id, - read_access_t read) - : page_cache_(nullptr), the_txn_(nullptr) { - init(_page_cache, _block_id, read); -} - -void current_page_acq_t::init(page_txn_t *txn, - block_id_t _block_id, - access_t _access, - page_create_t create) { - if (_access == access_t::read) { - rassert(create == page_create_t::no); - init(txn->page_cache(), _block_id, read_access_t::read); - } else { - txn->page_cache()->assert_thread(); - guarantee(page_cache_ == nullptr); - page_cache_ = txn->page_cache(); - the_txn_ = (_access == access_t::write ? txn : nullptr); - access_ = _access; - declared_snapshotted_ = false; - block_id_ = _block_id; - if (create == page_create_t::yes) { - current_page_ = page_cache_->page_for_new_chosen_block_id(_block_id); - } else { - current_page_ = page_cache_->page_for_block_id(_block_id); - } - dirtied_page_ = false; - touched_page_ = false; - - the_txn_->add_acquirer(this); - current_page_->add_acquirer(this); - } -} - -void current_page_acq_t::init(page_txn_t *txn, - alt_create_t, - block_type_t block_type) { - txn->page_cache()->assert_thread(); - guarantee(page_cache_ == nullptr); - page_cache_ = txn->page_cache(); - the_txn_ = txn; - access_ = access_t::write; - declared_snapshotted_ = false; - current_page_ = page_cache_->page_for_new_block_id(block_type, &block_id_); - dirtied_page_ = false; - touched_page_ = false; - - the_txn_->add_acquirer(this); - current_page_->add_acquirer(this); -} - -void current_page_acq_t::init(page_cache_t *_page_cache, - block_id_t _block_id, - read_access_t) { - _page_cache->assert_thread(); - guarantee(page_cache_ == nullptr); - page_cache_ = _page_cache; - the_txn_ = nullptr; - access_ = access_t::read; - declared_snapshotted_ = false; - block_id_ = _block_id; - current_page_ = page_cache_->page_for_block_id(_block_id); - dirtied_page_ = false; - touched_page_ = false; - - current_page_->add_acquirer(this); -} - -current_page_acq_t::~current_page_acq_t() { - assert_thread(); - // Checking page_cache_ != nullptr makes sure this isn't a default-constructed acq. - if (page_cache_ != nullptr) { - if (the_txn_ != nullptr) { - guarantee(access_ == access_t::write); - the_txn_->remove_acquirer(this); - } - rassert(current_page_ != nullptr); - if (in_a_list()) { - // Note that the current_page_acq can be in the current_page_ acquirer - // list and still be snapshotted. However it will not have a - // snapshotted_page_. - rassert(!snapshotted_page_.has()); - current_page_->remove_acquirer(this); - } - if (declared_snapshotted_) { - snapshotted_page_.reset_page_ptr(page_cache_); - current_page_->remove_keepalive(); - } - page_cache_->consider_evicting_current_page(block_id_); - } -} - -void current_page_acq_t::declare_readonly() { - assert_thread(); - access_ = access_t::read; - if (current_page_ != nullptr) { - current_page_->pulse_pulsables(this); - } -} - -void current_page_acq_t::declare_snapshotted() { - assert_thread(); - rassert(access_ == access_t::read); - - // Allow redeclaration of snapshottedness. - if (!declared_snapshotted_) { - declared_snapshotted_ = true; - rassert(current_page_ != nullptr); - current_page_->add_keepalive(); - current_page_->pulse_pulsables(this); - } -} - -signal_t *current_page_acq_t::read_acq_signal() { - assert_thread(); - return &read_cond_; -} - -signal_t *current_page_acq_t::write_acq_signal() { - assert_thread(); - rassert(access_ == access_t::write); - return &write_cond_; -} - -page_t *current_page_acq_t::current_page_for_read(cache_account_t *account) { - assert_thread(); - rassert(snapshotted_page_.has() || current_page_ != nullptr); - read_cond_.wait(); - if (snapshotted_page_.has()) { - return snapshotted_page_.get_page_for_read(); - } - rassert(current_page_ != nullptr); - return current_page_->the_page_for_read(help(), account); -} - -repli_timestamp_t current_page_acq_t::recency() { - assert_thread(); - rassert(snapshotted_page_.has() || current_page_ != nullptr); - - // We wait for write_cond_ when getting the recency (if we're a write acquirer) - // so that we can't see the recency change before/after the write_cond_ is - // pulsed. - if (access_ == access_t::read) { - read_cond_.wait(); - } else { - write_cond_.wait(); - } - - if (snapshotted_page_.has()) { - return snapshotted_page_.timestamp(); - } - rassert(current_page_ != nullptr); - return page_cache_->recency_for_block_id(block_id_); -} - -page_t *current_page_acq_t::current_page_for_write(cache_account_t *account) { - assert_thread(); - rassert(access_ == access_t::write); - rassert(current_page_ != nullptr); - write_cond_.wait(); - rassert(current_page_ != nullptr); - dirtied_page_ = true; - return current_page_->the_page_for_write(help(), account); -} - -void current_page_acq_t::set_recency(repli_timestamp_t _recency) { - assert_thread(); - rassert(access_ == access_t::write); - rassert(current_page_ != nullptr); - write_cond_.wait(); - rassert(current_page_ != nullptr); - touched_page_ = true; - page_cache_->set_recency_for_block_id(block_id_, _recency); -} - -void current_page_acq_t::mark_deleted() { - assert_thread(); - rassert(access_ == access_t::write); - rassert(current_page_ != nullptr); - write_cond_.wait(); - rassert(current_page_ != nullptr); - dirtied_page_ = true; - current_page_->mark_deleted(help()); - // No need to call consider_evicting_current_page here -- there's a - // current_page_acq_t for it: ourselves. -} - -bool current_page_acq_t::dirtied_page() const { - assert_thread(); - return dirtied_page_; -} - -bool current_page_acq_t::touched_page() const { - assert_thread(); - return touched_page_; -} - -block_version_t current_page_acq_t::block_version() const { - assert_thread(); - return block_version_; -} - - -page_cache_t *current_page_acq_t::page_cache() const { - assert_thread(); - return page_cache_; -} - -current_page_help_t current_page_acq_t::help() const { - assert_thread(); - return current_page_help_t(block_id(), page_cache_); -} - -void current_page_acq_t::pulse_read_available() { - assert_thread(); - read_cond_.pulse_if_not_already_pulsed(); -} - -void current_page_acq_t::pulse_write_available() { - assert_thread(); - write_cond_.pulse_if_not_already_pulsed(); -} - -current_page_t::current_page_t(block_id_t block_id) - : block_id_(block_id), - is_deleted_(false), - last_write_acquirer_(nullptr), - num_keepalives_(0) { - // Increment the block version so that we can distinguish between unassigned - // current_page_acq_t::block_version_ values (which are 0) and assigned ones. - rassert(last_write_acquirer_version_.debug_value() == 0); - last_write_acquirer_version_ = last_write_acquirer_version_.subsequent(); -} - -current_page_t::current_page_t(block_id_t block_id, - buf_ptr_t buf, - page_cache_t *page_cache) - : block_id_(block_id), - page_(new page_t(block_id, std::move(buf), page_cache)), - is_deleted_(false), - last_write_acquirer_(nullptr), - num_keepalives_(0) { - // Increment the block version so that we can distinguish between unassigned - // current_page_acq_t::block_version_ values (which are 0) and assigned ones. - rassert(last_write_acquirer_version_.debug_value() == 0); - last_write_acquirer_version_ = last_write_acquirer_version_.subsequent(); -} - -current_page_t::current_page_t(block_id_t block_id, - buf_ptr_t buf, - const counted_t &token, - page_cache_t *page_cache) - : block_id_(block_id), - page_(new page_t(block_id, std::move(buf), token, page_cache)), - is_deleted_(false), - last_write_acquirer_(nullptr), - num_keepalives_(0) { - // Increment the block version so that we can distinguish between unassigned - // current_page_acq_t::block_version_ values (which are 0) and assigned ones. - rassert(last_write_acquirer_version_.debug_value() == 0); - last_write_acquirer_version_ = last_write_acquirer_version_.subsequent(); -} - -current_page_t::~current_page_t() { - // Check that reset() has been called. - rassert(last_write_acquirer_version_.debug_value() == 0); - - // An imperfect sanity check. - rassert(!page_.has()); - rassert(num_keepalives_ == 0); -} - -void current_page_t::reset(page_cache_t *page_cache) { - rassert(acquirers_.empty()); - rassert(num_keepalives_ == 0); - - // KSI: Does last_write_acquirer_ even need to be NULL? Could we not just inform - // it of our impending destruction? - rassert(last_write_acquirer_ == nullptr); - - page_.reset_page_ptr(page_cache); - // No need to call consider_evicting_current_page here -- we're already getting - // destructed. - - // For the sake of the ~current_page_t assertion. - last_write_acquirer_version_ = block_version_t(); - - if (is_deleted_ && block_id_ != NULL_BLOCK_ID) { - page_cache->free_list()->release_block_id(block_id_); - block_id_ = NULL_BLOCK_ID; - } -} - -bool current_page_t::should_be_evicted() const { - // Consider reasons why the current_page_t should not be evicted. - - // A reason: It still has acquirers. (Important.) - if (!acquirers_.empty()) { - return false; - } - - // A reason: We still have a connection to last_write_acquirer_. (Important.) - if (last_write_acquirer_ != nullptr) { - return false; - } - - // A reason: The current_page_t is kept alive for another reason. (Important.) - if (num_keepalives_ > 0) { - return false; - } - - // A reason: Its page_t isn't evicted, or has other snapshotters or waiters - // anyway. (Getting this wrong can only hurt performance. We want to evict - // current_page_t's with unloaded, otherwise unused page_t's.) - if (page_.has()) { - page_t *page = page_.get_page_for_read(); - if (page->is_loading() || page->has_waiters() || page->is_loaded() - || page->page_ptr_count() != 1) { - return false; - } - // is_loading is false and is_loaded is false -- it must be disk-backed. - rassert(page->is_disk_backed() || page->is_deferred_loading()); - } - - return true; -} - -void current_page_t::add_acquirer(current_page_acq_t *acq) { - const block_version_t prev_version = last_write_acquirer_version_; - - if (acq->access_ == access_t::write) { - block_version_t v = prev_version.subsequent(); - acq->block_version_ = v; - - rassert(acq->the_txn_ != nullptr); - page_txn_t *const acq_txn = acq->the_txn_; - - last_write_acquirer_version_ = v; - - if (last_write_acquirer_ != acq_txn) { - rassert(!acq_txn->pages_write_acquired_last_.has_element(this)); - - if (last_write_acquirer_ != nullptr) { - page_txn_t *prec = last_write_acquirer_; - - rassert(prec->pages_write_acquired_last_.has_element(this)); - prec->pages_write_acquired_last_.remove(this); - - acq_txn->connect_preceder(prec); - } - - acq_txn->pages_write_acquired_last_.add(this); - last_write_acquirer_ = acq_txn; - } - } else { - rassert(acq->the_txn_ == nullptr); - acq->block_version_ = prev_version; - } - - acquirers_.push_back(acq); - pulse_pulsables(acq); -} - -void current_page_t::remove_acquirer(current_page_acq_t *acq) { - current_page_acq_t *next = acquirers_.next(acq); - acquirers_.remove(acq); - if (next != nullptr) { - pulse_pulsables(next); - } -} - -void current_page_t::pulse_pulsables(current_page_acq_t *const acq) { - const current_page_help_t help = acq->help(); - - // First, avoid pulsing when there's nothing to pulse. - { - current_page_acq_t *prev = acquirers_.prev(acq); - if (!(prev == nullptr || (prev->access_ == access_t::read - && prev->read_cond_.is_pulsed()))) { - return; - } - } - - // Second, avoid re-pulsing already-pulsed chains. - if (acq->access_ == access_t::read && acq->read_cond_.is_pulsed() - && !acq->declared_snapshotted_) { - // acq was pulsed for read, but it could have been a write acq at that time, - // so the next node might not have been pulsed for read. Also we might as - // well stop if we're at the end of the chain (and have been pulsed). - current_page_acq_t *next = acquirers_.next(acq); - if (next == nullptr || next->read_cond_.is_pulsed()) { - return; - } - } - - const repli_timestamp_t current_recency = help.page_cache->recency_for_block_id(help.block_id); - - // It's time to pulse the pulsables. - current_page_acq_t *cur = acq; - while (cur != nullptr) { - // We know that the previous node has read access and has been pulsed as - // readable, so we pulse the current node as readable. - cur->pulse_read_available(); - - if (cur->access_ == access_t::read) { - current_page_acq_t *next = acquirers_.next(cur); - if (cur->declared_snapshotted_) { - // Snapshotters get kicked out of the queue, to make way for - // write-acquirers. - - // We treat deleted pages this way because a write-acquirer may - // downgrade itself to readonly and snapshotted for the sake of - // flushing its version of the page -- and if it deleted the page, - // this is how it learns. - - cur->snapshotted_page_.init( - current_recency, - the_page_for_read_or_deleted(help)); - acquirers_.remove(cur); - } - cur = next; - } else { - // Even the first write-acquirer gets read access (there's no need for an - // "intent" mode). But subsequent acquirers need to wait, because the - // write-acquirer might modify the value. - if (acquirers_.prev(cur) == nullptr) { - // (It gets exclusive write access if there's no preceding reader.) - guarantee(!is_deleted_); - cur->pulse_write_available(); - } - break; - } - } -} - -void current_page_t::add_keepalive() { - ++num_keepalives_; -} - -void current_page_t::remove_keepalive() { - guarantee(num_keepalives_ > 0); - --num_keepalives_; -} - -void current_page_t::mark_deleted(current_page_help_t help) { - rassert(!is_deleted_); - is_deleted_ = true; - - // Only the last acquirer (the current write-acquirer) of a block may mark it - // deleted, because subsequent acquirers should not be trying to create a block - // whose block id hasn't been released to the free list yet. - rassert(acquirers_.size() == 1); - - help.page_cache->set_recency_for_block_id(help.block_id, - repli_timestamp_t::invalid); - page_.reset_page_ptr(help.page_cache); - // It's the caller's responsibility to call consider_evicting_current_page after - // we return, if that would make sense (it wouldn't though). -} - -void current_page_t::convert_from_serializer_if_necessary(current_page_help_t help, - cache_account_t *account) { - rassert(!is_deleted_); - if (!page_.has()) { - page_.init(new page_t(help.block_id, help.page_cache, account)); - } -} - -void current_page_t::convert_from_serializer_if_necessary(current_page_help_t help) { - rassert(!is_deleted_); - if (!page_.has()) { - page_.init(new page_t(help.block_id, help.page_cache)); - } -} - -page_t *current_page_t::the_page_for_read(current_page_help_t help, - cache_account_t *account) { - guarantee(!is_deleted_); - convert_from_serializer_if_necessary(help, account); - return page_.get_page_for_read(); -} - -page_t *current_page_t::the_page_for_read_or_deleted(current_page_help_t help) { - if (is_deleted_) { - return nullptr; - } else { - convert_from_serializer_if_necessary(help); - return page_.get_page_for_read(); - } -} - -page_t *current_page_t::the_page_for_write(current_page_help_t help, - cache_account_t *account) { - guarantee(!is_deleted_); - convert_from_serializer_if_necessary(help, account); - return page_.get_page_for_write(help.page_cache, account); -} - -page_txn_t::page_txn_t(page_cache_t *_page_cache, - throttler_acq_t throttler_acq, - cache_conn_t *cache_conn) - : page_cache_(_page_cache), - cache_conn_(cache_conn), - throttler_acq_(std::move(throttler_acq)), - live_acqs_(0), - began_waiting_for_flush_(false), - spawned_flush_(false), - mark_(marked_not) { - if (cache_conn != nullptr) { - page_txn_t *old_newest_txn = cache_conn->newest_txn_; - cache_conn->newest_txn_ = this; - if (old_newest_txn != nullptr) { - rassert(old_newest_txn->cache_conn_ == cache_conn); - old_newest_txn->cache_conn_ = nullptr; - connect_preceder(old_newest_txn); - } - } -} - -void page_txn_t::connect_preceder(page_txn_t *preceder) { - page_cache_->assert_thread(); - rassert(preceder->page_cache_ == page_cache_); - // We can't add ourselves as a preceder, we have to avoid that. - rassert(preceder != this); - // The flush_complete_cond_ is pulsed at the same time that this txn is removed - // entirely from the txn graph, so we can't be adding preceders after that point. - rassert(!preceder->flush_complete_cond_.is_pulsed()); - - // See "PERFORMANCE(preceders_)". - if (std::find(preceders_.begin(), preceders_.end(), preceder) - == preceders_.end()) { - preceders_.push_back(preceder); - preceder->subseqers_.push_back(this); - } -} - -void page_txn_t::remove_preceder(page_txn_t *preceder) { - // See "PERFORMANCE(preceders_)". - auto it = std::find(preceders_.begin(), preceders_.end(), preceder); - rassert(it != preceders_.end()); - preceders_.erase(it); -} - -void page_txn_t::remove_subseqer(page_txn_t *subseqer) { - // See "PERFORMANCE(subseqers_)". - auto it = std::find(subseqers_.begin(), subseqers_.end(), subseqer); - rassert(it != subseqers_.end()); - subseqers_.erase(it); -} - -page_txn_t::~page_txn_t() { - guarantee(flush_complete_cond_.is_pulsed()); - - guarantee(preceders_.empty()); - guarantee(subseqers_.empty()); - - guarantee(snapshotted_dirtied_pages_.empty()); -} - -void page_txn_t::add_acquirer(DEBUG_VAR current_page_acq_t *acq) { - rassert(acq->access_ == access_t::write); - ++live_acqs_; -} - -void page_txn_t::remove_acquirer(current_page_acq_t *acq) { - guarantee(acq->access_ == access_t::write); - // This is called by acq's destructor. - { - rassert(live_acqs_ > 0); - --live_acqs_; - } - - // It's not snapshotted because you can't snapshot write acqs. (We - // rely on this fact solely because we need to grab the block_id_t - // and current_page_acq_t currently doesn't know it.) - rassert(acq->current_page_ != nullptr); - - const block_version_t block_version = acq->block_version(); - - if (acq->dirtied_page()) { - // We know we hold an exclusive lock. - rassert(acq->write_cond_.is_pulsed()); - - // Declare readonly (so that we may declare acq snapshotted). - acq->declare_readonly(); - acq->declare_snapshotted(); - - // Steal the snapshotted page_ptr_t. - timestamped_page_ptr_t local = std::move(acq->snapshotted_page_); - // It's okay to have two dirtied_page_t's or touched_page_t's for the - // same block id -- compute_changes handles this. - snapshotted_dirtied_pages_.push_back(dirtied_page_t(block_version, - acq->block_id(), - std::move(local))); - // If you keep writing and reacquiring the same page, though, the count - // might be off and you could excessively throttle new operations. - - // LSI: We could reacquire the same block and update the dirty page count - // with a _correct_ value indicating that we're holding redundant dirty - // pages for the same block id. - throttler_acq_.update_dirty_page_count(snapshotted_dirtied_pages_.size()); - } else if (acq->touched_page()) { - // It's okay to have two dirtied_page_t's or touched_page_t's for the - // same block id -- compute_changes handles this. - touched_pages_.push_back(touched_page_t(block_version, acq->block_id(), - acq->recency())); - } -} - -void page_txn_t::announce_waiting_for_flush() { - rassert(live_acqs_ == 0); - rassert(!began_waiting_for_flush_); - rassert(!spawned_flush_); - began_waiting_for_flush_ = true; - page_cache_->im_waiting_for_flush(this); -} - -std::map -page_cache_t::compute_changes(const std::vector &txns) { - // We combine changes, using the block_version_t value to see which change - // happened later. This even works if a single transaction acquired the same - // block twice. - - // The map of changes we make. - std::map changes; - - for (auto it = txns.begin(); it != txns.end(); ++it) { - page_txn_t *txn = *it; - for (size_t i = 0, e = txn->snapshotted_dirtied_pages_.size(); i < e; ++i) { - const dirtied_page_t &d = txn->snapshotted_dirtied_pages_[i]; - - block_change_t change(d.block_version, true, - d.ptr.has() ? d.ptr.get_page_for_read() : nullptr, - d.ptr.has() ? d.ptr.timestamp() : repli_timestamp_t::invalid); - - auto res = changes.insert(std::make_pair(d.block_id, change)); - - if (!res.second) { - // The insertion failed -- we need to use the newer version. - auto const jt = res.first; - // The versions can't be the same for different write operations. - rassert(jt->second.version != change.version, - "equal versions on block %" PRIi64 ": %" PRIu64, - d.block_id, - change.version.debug_value()); - if (jt->second.version < change.version) { - jt->second = change; - } - } - } - } - - for (auto it = txns.begin(); it != txns.end(); ++it) { - page_txn_t *txn = *it; - for (size_t i = 0, e = txn->touched_pages_.size(); i < e; ++i) { - const touched_page_t &t = txn->touched_pages_[i]; - - auto res = changes.insert(std::make_pair(t.block_id, - block_change_t(t.block_version, - false, - nullptr, - t.tstamp))); - if (!res.second) { - // The insertion failed. We need to combine the versions. - auto const jt = res.first; - // The versions can't be the same for different write operations. - rassert(jt->second.version != t.block_version); - if (jt->second.version < t.block_version) { - rassert(t.tstamp == - superceding_recency(jt->second.tstamp, t.tstamp)); - jt->second.tstamp = t.tstamp; - jt->second.version = t.block_version; - } - } - } - } - - return changes; -} - -void page_cache_t::remove_txn_set_from_graph(page_cache_t *page_cache, - const std::vector &txns) { - page_cache->assert_thread(); - - for (auto it = txns.begin(); it != txns.end(); ++it) { - // We want detaching the subsequers and preceders to happen at the same time - // that the flush_complete_cond_ is pulsed. That way connect_preceder can - // check if flush_complete_cond_ has been pulsed. - ASSERT_FINITE_CORO_WAITING; - page_txn_t *txn = *it; - { - for (auto jt = txn->subseqers_.begin(); jt != txn->subseqers_.end(); ++jt) { - (*jt)->remove_preceder(txn); - } - txn->subseqers_.clear(); - } - - // We could have preceders outside this txn set, because transactions that - // don't make any modifications don't get flushed, and they don't wait for - // their preceding transactions to get flushed and then removed from the - // graph. - for (auto jt = txn->preceders_.begin(); jt != txn->preceders_.end(); ++jt) { - (*jt)->remove_subseqer(txn); - } - txn->preceders_.clear(); - - // KSI: Maybe we could remove pages_write_acquired_last_ earlier? Like when - // we begin the index write (but that's on the wrong thread) or earlier? - while (txn->pages_write_acquired_last_.size() != 0) { - current_page_t *current_page - = txn->pages_write_acquired_last_.access_random(0); - rassert(current_page->last_write_acquirer_ == txn); - -#ifndef NDEBUG - // All existing acquirers should be read acquirers, since this txn _was_ - // the last write acquirer. - for (current_page_acq_t *acq = current_page->acquirers_.head(); - acq != nullptr; - acq = current_page->acquirers_.next(acq)) { - rassert(acq->access() == access_t::read); - } -#endif - - txn->pages_write_acquired_last_.remove(current_page); - current_page->last_write_acquirer_ = nullptr; - page_cache->consider_evicting_current_page(current_page->block_id_); - } - - if (txn->cache_conn_ != nullptr) { - rassert(txn->cache_conn_->newest_txn_ == txn); - txn->cache_conn_->newest_txn_ = nullptr; - txn->cache_conn_ = nullptr; - } - - txn->flush_complete_cond_.pulse(); - } -} - -struct block_token_tstamp_t { - block_token_tstamp_t(block_id_t _block_id, - bool _is_deleted, - counted_t _block_token, - repli_timestamp_t _tstamp, - page_t *_page) - : block_id(_block_id), is_deleted(_is_deleted), - block_token(std::move(_block_token)), tstamp(_tstamp), - page(_page) { } - block_id_t block_id; - bool is_deleted; - counted_t block_token; - repli_timestamp_t tstamp; - // The page, or nullptr, if we don't know it. - page_t *page; -}; - -struct ancillary_info_t { - ancillary_info_t(repli_timestamp_t _tstamp, - page_t *_page) - : tstamp(_tstamp), page(_page) { } - repli_timestamp_t tstamp; - page_t *page; -}; - -void page_cache_t::do_flush_changes(page_cache_t *page_cache, - std::map &&changes, - const std::vector &txns, - fifo_enforcer_write_token_t index_write_token) { - rassert(!changes.empty()); - std::vector blocks_by_tokens; - blocks_by_tokens.reserve(changes.size()); - - std::vector ancillary_infos; - std::vector write_infos; - ancillary_infos.reserve(changes.size()); - write_infos.reserve(changes.size()); - - { - ASSERT_NO_CORO_WAITING; - - for (auto it = changes.begin(); it != changes.end(); ++it) { - if (it->second.modified) { - if (it->second.page == nullptr) { - // The block is deleted. - blocks_by_tokens.push_back(block_token_tstamp_t(it->first, - true, - counted_t(), - repli_timestamp_t::invalid, - nullptr)); - } else { - page_t *page = it->second.page; - if (page->block_token().has()) { - // It's already on disk, we're not going to flush it. - blocks_by_tokens.push_back(block_token_tstamp_t(it->first, - false, - page->block_token(), - it->second.tstamp, - page)); - } else { - // We can't be in the process of loading a block we're going - // to write for which we don't have a block token. That's - // because we _actually dirtied the page_. We had to have - // acquired the buf, and the only way to get rid of the buf - // is for it to be evicted, in which case the block token - // would be non-empty. - - rassert(page->is_loaded()); - - // KSI: Is there a page_acq_t for this buf we're writing? Is it - // possible that we might be trying to do an unbacked eviction - // for this page right now? (No, we don't do that yet.) - write_infos.push_back(buf_write_info_t(page->get_loaded_ser_buffer(), - page->get_page_buf_size(), - it->first)); - ancillary_infos.push_back(ancillary_info_t(it->second.tstamp, - page)); - } - } - } else { - // We only touched the page. - blocks_by_tokens.push_back(block_token_tstamp_t(it->first, - false, - counted_t(), - it->second.tstamp, - nullptr)); - } - } - } - - cond_t blocks_released_cond; - { - on_thread_t th(page_cache->serializer_->home_thread()); - - struct : public iocallback_t, public cond_t { - void on_io_complete() { - pulse(); - } - } blocks_written_cb; - - std::vector > tokens - = page_cache->serializer_->block_writes(write_infos, - /* disk account is overridden - * by merger_serializer_t */ - DEFAULT_DISK_ACCOUNT, - &blocks_written_cb); - - rassert(tokens.size() == write_infos.size()); - rassert(write_infos.size() == ancillary_infos.size()); - for (size_t i = 0; i < write_infos.size(); ++i) { - blocks_by_tokens.push_back(block_token_tstamp_t(write_infos[i].block_id, - false, - std::move(tokens[i]), - ancillary_infos[i].tstamp, - ancillary_infos[i].page)); - } - - // KSI: Unnecessary copying between blocks_by_tokens and write_ops, inelegant - // representation of deletion/touched blocks in blocks_by_tokens. - std::vector write_ops; - write_ops.reserve(blocks_by_tokens.size()); - - for (auto it = blocks_by_tokens.begin(); it != blocks_by_tokens.end(); - ++it) { - if (it->is_deleted) { - write_ops.push_back(index_write_op_t( - it->block_id, - make_optional(counted_t()), - make_optional(repli_timestamp_t::invalid))); - } else if (it->block_token.has()) { - write_ops.push_back(index_write_op_t(it->block_id, - make_optional(it->block_token), - make_optional(it->tstamp))); - } else { - write_ops.push_back(index_write_op_t(it->block_id, - r_nullopt, - make_optional(it->tstamp))); - } - } - - blocks_written_cb.wait(); - - fifo_enforcer_sink_t::exit_write_t exiter(&page_cache->index_write_sink_->sink, - index_write_token); - exiter.wait(); - new_mutex_in_line_t mutex_acq(&page_cache->index_write_sink_->mutex); - exiter.end(); - - rassert(!write_ops.empty()); - mutex_acq.acq_signal()->wait(); - page_cache->serializer_->index_write( - &mutex_acq, - [&]() { - // Update the block tokens and free the associated snapshots once the - // serializer's in-memory index has been updated (we don't need to wait - // until the index changes have been written to disk). - coro_t::spawn_on_thread([&]() { - // Update the block tokens of the written blocks - for (auto &block : blocks_by_tokens) { - if (block.block_token.has() && block.page != nullptr) { - // We know page is still a valid pointer because of the - // page_ptr_t in snapshotted_dirtied_pages_. - - // KSI: This assertion would fail if we try to force-evict the page - // simultaneously as this write. - rassert(!block.page->block_token().has()); - eviction_bag_t *old_bag - = page_cache->evicter().correct_eviction_category( - block.page); - block.page->init_block_token( - std::move(block.block_token), - page_cache); - page_cache->evicter().change_to_correct_eviction_bag( - old_bag, - block.page); - } - } - - // Clear `changes`, since we are going to evict the pages - // that it has pointers to in the next step. - changes.clear(); - for (auto &txn : txns) { - for (size_t i = 0, e = txn->snapshotted_dirtied_pages_.size(); - i < e; - ++i) { - txn->snapshotted_dirtied_pages_[i].ptr.reset_page_ptr( - page_cache); - page_cache->consider_evicting_current_page( - txn->snapshotted_dirtied_pages_[i].block_id); - } - txn->snapshotted_dirtied_pages_.clear(); - txn->throttler_acq_.mark_dirty_pages_written(); - } - blocks_released_cond.pulse(); - }, page_cache->home_thread()); - }, write_ops); - } - - // Wait until the block release coroutine has finished to we can safely - // continue (this is important because once we return, a page transaction - // or even the whole page cache might get destructed). - blocks_released_cond.wait(); -} - -void page_cache_t::do_flush_txn_set(page_cache_t *page_cache, - std::map *changes_ptr, - const std::vector &txns) { - // This is called with spawn_now_dangerously! The reason is partly so that we - // don't put a zillion coroutines on the message loop when doing a bunch of - // reads. The other reason is that passing changes through a std::bind without - // copying it would be very annoying. - page_cache->assert_thread(); - - // We're going to flush these transactions. First we need to figure out what the - // set of changes we're actually doing is, since any transaction may have touched - // the same blocks. - - std::map changes = std::move(*changes_ptr); - rassert(!changes.empty()); - - fifo_enforcer_write_token_t index_write_token - = page_cache->index_write_source_.enter_write(); - - // Okay, yield, thank you. - coro_t::yield(); - do_flush_changes(page_cache, std::move(changes), txns, index_write_token); - - // Flush complete. - - // KSI: Can't we remove_txn_set_from_graph before flushing? It would make some - // data structures smaller. - page_cache_t::remove_txn_set_from_graph(page_cache, txns); -} - -std::vector page_cache_t::maximal_flushable_txn_set(page_txn_t *base) { - // Returns all transactions that can presently be flushed, given the newest - // transaction that has had began_waiting_for_flush_ set. (We assume all - // previous such sets of transactions had flushing begin on them.) - // - // page_txn_t's `mark` fields can be in the following states: - // - not: the page has not yet been considered for processing - // - blue: the page is going to be considered for processing - // - green: the page _has_ been considered for processing, nothing bad so far - // - red: the page _has_ been considered for processing, and it is unflushable. - // - // By the end of the function (before we construct the return value), no - // page_txn_t's are blue, and all subseqers of red pages are either red or not - // marked. All flushable page_txn_t's are green. - // - // Here are all possible transitions of the mark. The states blue(1) and blue(2) - // both have a blue mark, but the latter is known to have a red parent. - // - // not -> blue(1) - // blue(1) -> red - // blue(1) -> green - // green -> blue(2) - // blue(2) -> red - // - // From this transition table you can see that every page_txn_t is processed at - // most twice. - - - ASSERT_NO_CORO_WAITING; - // An element is marked blue iff it's in `blue`. - std::vector blue; - // All elements marked red, green, or blue are in `colored` -- we unmark them and - // construct the return vector at the end of the function. - std::vector colored; - - rassert(!base->spawned_flush_); - rassert(base->began_waiting_for_flush_); - rassert(base->mark_ == page_txn_t::marked_not); - base->mark_ = page_txn_t::marked_blue; - blue.push_back(base); - colored.push_back(base); - - while (!blue.empty()) { - page_txn_t *txn = blue.back(); - blue.pop_back(); - - rassert(!txn->spawned_flush_); - rassert(txn->began_waiting_for_flush_); - rassert(txn->mark_ == page_txn_t::marked_blue); - - bool poisoned = false; - for (auto it = txn->preceders_.begin(); it != txn->preceders_.end(); ++it) { - page_txn_t *prec = *it; - if (prec->spawned_flush_) { - rassert(prec->mark_ == page_txn_t::marked_not); - } else if (!prec->began_waiting_for_flush_ - || prec->mark_ == page_txn_t::marked_red) { - poisoned = true; - } else if (prec->mark_ == page_txn_t::marked_not) { - prec->mark_ = page_txn_t::marked_blue; - blue.push_back(prec); - colored.push_back(prec); - } else { - rassert(prec->mark_ == page_txn_t::marked_green - || prec->mark_ == page_txn_t::marked_blue); - } - } - - txn->mark_ = poisoned ? page_txn_t::marked_red : page_txn_t::marked_green; - - for (auto it = txn->subseqers_.begin(); it != txn->subseqers_.end(); ++it) { - page_txn_t *subs = *it; - rassert(!subs->spawned_flush_); - if (!subs->began_waiting_for_flush_) { - rassert(subs->mark_ == page_txn_t::marked_not); - } else if (subs->mark_ == page_txn_t::marked_not) { - if (!poisoned) { - subs->mark_ = page_txn_t::marked_blue; - blue.push_back(subs); - colored.push_back(subs); - } - } else if (subs->mark_ == page_txn_t::marked_green) { - if (poisoned) { - subs->mark_ = page_txn_t::marked_blue; - blue.push_back(subs); - } - } else { - rassert(subs->mark_ == page_txn_t::marked_red - || subs->mark_ == page_txn_t::marked_blue); - } - } - } - - auto it = colored.begin(); - auto jt = it; - - while (jt != colored.end()) { - page_txn_t::mark_state_t mark = (*jt)->mark_; - (*jt)->mark_ = page_txn_t::marked_not; - if (mark == page_txn_t::marked_green) { - *it++ = *jt++; - } else { - rassert(mark == page_txn_t::marked_red); - ++jt; - } - } - - colored.erase(it, colored.end()); - return colored; -} - -void page_cache_t::im_waiting_for_flush(page_txn_t *base) { - assert_thread(); - rassert(base->began_waiting_for_flush_); - rassert(!base->spawned_flush_); - ASSERT_FINITE_CORO_WAITING; - - std::vector flush_set - = page_cache_t::maximal_flushable_txn_set(base); - if (!flush_set.empty()) { - for (auto it = flush_set.begin(); it != flush_set.end(); ++it) { - rassert(!(*it)->spawned_flush_); - (*it)->spawned_flush_ = true; - } - - std::map changes - = page_cache_t::compute_changes(flush_set); - - if (!changes.empty()) { - coro_t::spawn_now_dangerously(std::bind(&page_cache_t::do_flush_txn_set, - this, - &changes, - flush_set)); - } else { - // Flush complete. do_flush_txn_set does this in the write case. - page_cache_t::remove_txn_set_from_graph(this, flush_set); - } - } -} - - -} // namespace alt diff --git a/src/buffer_cache/page_cache.hpp b/src/buffer_cache/page_cache.hpp deleted file mode 100644 index b80f1d906d7..00000000000 --- a/src/buffer_cache/page_cache.hpp +++ /dev/null @@ -1,679 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef BUFFER_CACHE_PAGE_CACHE_HPP_ -#define BUFFER_CACHE_PAGE_CACHE_HPP_ - -#include -#include -#include -#include -#include -#include - -#include "buffer_cache/block_version.hpp" -#include "buffer_cache/cache_account.hpp" -#include "buffer_cache/evicter.hpp" -#include "buffer_cache/free_list.hpp" -#include "buffer_cache/page.hpp" -#include "buffer_cache/types.hpp" -#include "concurrency/access.hpp" -#include "concurrency/auto_drainer.hpp" -#include "concurrency/cond_var.hpp" -#include "concurrency/fifo_enforcer.hpp" -#include "concurrency/new_semaphore.hpp" -#include "containers/backindex_bag.hpp" -#include "containers/intrusive_list.hpp" -#include "containers/segmented_vector.hpp" -#include "repli_timestamp.hpp" -#include "serializer/types.hpp" - -class alt_txn_throttler_t; -class cache_balancer_t; -class auto_drainer_t; -class cache_t; -class file_account_t; - -namespace alt { -class current_page_acq_t; -class page_cache_t; -class page_txn_t; - -enum class page_create_t { no, yes }; - -} // namespace alt - -enum class alt_create_t { create }; - -enum class block_type_t { normal, aux }; - -class cache_conn_t { -public: - explicit cache_conn_t(cache_t *_cache) - : cache_(_cache), - newest_txn_(nullptr) { } - ~cache_conn_t(); - - cache_t *cache() const { return cache_; } -private: - friend class alt::page_cache_t; - friend class alt::page_txn_t; - // Here for convenience, because otherwise you'd be passing around a cache_t with - // every cache_conn_t parameter. - cache_t *cache_; - - // The most recent unflushed txn, or NULL. This gets set back to NULL when - // newest_txn_ pulses its flush_complete_cond_. It's a bidirectional pointer - // pair with the newest txn's cache_conn_ pointer -- either both point at each - // other or neither do. - alt::page_txn_t *newest_txn_; - - DISABLE_COPYING(cache_conn_t); -}; - - -namespace alt { - - -// Has information necessary for the current_page_t to do certain things -- it's -// known by the current_page_acq_t. -class current_page_help_t; - -class current_page_t { -public: - current_page_t(block_id_t block_id, buf_ptr_t buf, page_cache_t *page_cache); - current_page_t(block_id_t block_id, buf_ptr_t buf, - const counted_t &token, - page_cache_t *page_cache); - // Constructs a page to be loaded from the serializer. - explicit current_page_t(block_id_t block_id); - - // You MUST call reset() before destructing a current_page_t! - ~current_page_t(); - - // You can only call this when it's safe to do so! (Beware of - // current_page_acq_t's, last write acquirer page_txn_t's, and read-ahead logic.) - void reset(page_cache_t *page_cache); - - bool should_be_evicted() const; - -private: - // current_page_acq_t should not access our fields directly. - friend class current_page_acq_t; - void add_acquirer(current_page_acq_t *acq); - void remove_acquirer(current_page_acq_t *acq); - void pulse_pulsables(current_page_acq_t *acq); - void add_keepalive(); - void remove_keepalive(); - - page_t *the_page_for_write(current_page_help_t help, cache_account_t *account); - page_t *the_page_for_read(current_page_help_t help, cache_account_t *account); - - // Initializes page_ if necessary, providing an account because we know we'd like - // to load it ASAP. - void convert_from_serializer_if_necessary(current_page_help_t help, - cache_account_t *account); - - // Initializes page_ if necessary, deferring loading of the actual block. - void convert_from_serializer_if_necessary(current_page_help_t help); - - void mark_deleted(current_page_help_t help); - - // page_txn_t should not access our fields directly. - friend class page_txn_t; - - // Returns NULL if the page was deleted. - page_t *the_page_for_read_or_deleted(current_page_help_t help); - - // Has access to our fields. - friend class page_cache_t; - - friend backindex_bag_index_t *access_backindex(current_page_t *current_page); - - bool is_deleted() const { return is_deleted_; } - - // KSI: We could get rid of this variable if - // page_txn_t::pages_write_acquired_last_ noted each page's block_id_t. Other - // space reductions are more important. - block_id_t block_id_; - - // page_ can be null if we haven't tried loading the page yet. We don't want to - // prematurely bother loading the page if it's going to be deleted. - // the_page_for_write, the_page_for_read, or the_page_for_read_or_deleted should - // be used to access this variable. - // KSI: Could we encapsulate that rule? - page_ptr_t page_; - // True if the block is in a deleted state. page_ will be null. - bool is_deleted_; - - // The last write acquirer for this page. - page_txn_t *last_write_acquirer_; - // Our index into the last_write_acquirer_->pages_write_acquired_last_. - backindex_bag_index_t last_write_acquirer_index_; - - // The version of the page, that the last write acquirer had. - block_version_t last_write_acquirer_version_; - - // Instead of storing the recency here, we store it page_cache_t::recencies_. - - // All list elements have current_page_ != NULL, snapshotted_page_ == NULL. - intrusive_list_t acquirers_; - - // Avoids eviction if > 0. This is used by snapshotted current_page_acq_t's - // that have a snapshotted version of this block. If the current_page_t - // would be evicted that would mess with the block version. - intptr_t num_keepalives_; - - DISABLE_COPYING(current_page_t); -}; - -inline backindex_bag_index_t *access_backindex(current_page_t *current_page) { - return ¤t_page->last_write_acquirer_index_; -} - -class current_page_acq_t : public intrusive_list_node_t, - public home_thread_mixin_debug_only_t { -public: - // KSI: Right now we support a default constructor but buf_lock_t actually - // uses a scoped pointer now, because getting this type to be swappable was too - // hard. Make this type be swappable or remove the default constructor. (Remove - // the page_cache_ != NULL check in the destructor we remove the default - // constructor.) - current_page_acq_t(); - current_page_acq_t(page_txn_t *txn, - block_id_t block_id, - access_t access, - page_create_t create = page_create_t::no); - current_page_acq_t(page_txn_t *txn, - alt_create_t create, - block_type_t block_type); - current_page_acq_t(page_cache_t *cache, - block_id_t block_id, - read_access_t read); - ~current_page_acq_t(); - - // Declares ourself snapshotted. (You must be readonly to do this.) - void declare_snapshotted(); - - signal_t *read_acq_signal(); - signal_t *write_acq_signal(); - - page_t *current_page_for_read(cache_account_t *account); - repli_timestamp_t recency(); - - page_t *current_page_for_write(cache_account_t *account); - void set_recency(repli_timestamp_t recency); - - // Returns current_page_for_read, except it guarantees that the page acq has - // already snapshotted the page and is not waiting for the page_t *. - page_t *snapshotted_page_ptr(); - - block_id_t block_id() const { return block_id_; } - access_t access() const { return access_; } - - void mark_deleted(); - - block_version_t block_version() const; - - page_cache_t *page_cache() const; - -private: - void init(page_txn_t *txn, - block_id_t block_id, - access_t access, - page_create_t create); - void init(page_txn_t *txn, - alt_create_t create, - block_type_t block_type); - void init(page_cache_t *page_cache, - block_id_t block_id, - read_access_t read); - friend class page_txn_t; - friend class current_page_t; - - // Returns true if the page has been created, edited, or deleted. - bool dirtied_page() const; - - // Returns true if the page's recency has been modified. - bool touched_page() const; - - // Declares ourself readonly. Only page_txn_t::remove_acquirer can do this! - void declare_readonly(); - - current_page_help_t help() const; - - void pulse_read_available(); - void pulse_write_available(); - - page_cache_t *page_cache_; - page_txn_t *the_txn_; - access_t access_; - bool declared_snapshotted_; - // The block id of the page we acquired. - block_id_t block_id_; - // At most one of current_page_ is null or snapshotted_page_ is null, unless the - // acquired page has been deleted, in which case both are null. - current_page_t *current_page_; - timestamped_page_ptr_t snapshotted_page_; - cond_t read_cond_; - cond_t write_cond_; - - // The block version for our acquisition of the page -- every write acquirer sees - // a greater block version than the previous acquirer. The current page's block - // version will be less than or equal to this value if we have not yet acquired - // the page. It could be greater than this value if we're snapshotted (since - // we're holding an old version of the page). These values are for internal - // cache bookkeeping only. - block_version_t block_version_; - - bool dirtied_page_, touched_page_; - - DISABLE_COPYING(current_page_acq_t); -}; - -// This object lives on the serializer thread. -class page_read_ahead_cb_t : public home_thread_mixin_t, - public serializer_read_ahead_callback_t { -public: - page_read_ahead_cb_t(serializer_t *serializer, - page_cache_t *cache); - - void offer_read_ahead_buf(block_id_t block_id, - buf_ptr_t *buf, - const counted_t &token); - - void destroy_self(); - -private: - ~page_read_ahead_cb_t(); - - serializer_t *serializer_; - page_cache_t *page_cache_; - - DISABLE_COPYING(page_read_ahead_cb_t); -}; - -class throttler_acq_t { -public: - throttler_acq_t() { } - ~throttler_acq_t() { } - throttler_acq_t(throttler_acq_t &&movee) - : block_changes_semaphore_acq_(std::move(movee.block_changes_semaphore_acq_)), - index_changes_semaphore_acq_(std::move(movee.index_changes_semaphore_acq_)) { - movee.block_changes_semaphore_acq_.reset(); - movee.index_changes_semaphore_acq_.reset(); - } - - // See below: this can update how much *_changes_semaphore_acq_ holds. - void update_dirty_page_count(int64_t new_count); - - // Sets block_changes_semaphore_acq_ to 0, but keeps index_changes_semaphore_acq_ - // as it is. - void mark_dirty_pages_written(); - -private: - friend class ::alt_txn_throttler_t; - // At first, the number of dirty pages is 0 and *_changes_semaphore_acq_.count() >= - // dirtied_count_. Once the number of dirty pages gets bigger than the original - // value of *_changes_semaphore_acq_.count(), we use - // *_changes_semaphore_acq_.change_count() to keep the numbers equal. - new_semaphore_in_line_t block_changes_semaphore_acq_; - new_semaphore_in_line_t index_changes_semaphore_acq_; - - DISABLE_COPYING(throttler_acq_t); -}; - -class page_cache_index_write_sink_t; - -class page_cache_t : public home_thread_mixin_t { -public: - page_cache_t(serializer_t *serializer, - cache_balancer_t *balancer, - alt_txn_throttler_t *throttler); - ~page_cache_t(); - - // Takes a txn to be flushed. Calls on_flush_complete() (which resets the - // throttler_acq parameter) when done. - void flush_and_destroy_txn( - scoped_ptr_t txn, - std::function on_flush_complete); - // More efficient version of `flush_and_destroy_txn` for read transactions. - void end_read_txn(scoped_ptr_t txn); - - current_page_t *page_for_block_id(block_id_t block_id); - current_page_t *page_for_new_block_id( - block_type_t block_type, - block_id_t *block_id_out); - current_page_t *page_for_new_chosen_block_id(block_id_t block_id); - - // Returns how much memory is being used by all the pages in the cache at this - // moment in time. - size_t total_page_memory() const; - size_t evictable_page_memory() const; - - max_block_size_t max_block_size() const { return max_block_size_; } - - cache_account_t create_cache_account(int priority); - - cache_account_t *default_reads_account() { - return &default_reads_account_; - } - - // Considers wiping out the current_page_t (and its page_t pointee) for a - // particular block id, to save memory, if the right conditions are met. (This - // should only be called by things "outside" of current_page_t, like - // current_page_acq_t and page_txn_t, not page_t and page_ptr_t -- that way we - // know it's not called while somebody up the stack is expecting their - // `current_page_t *` to remain valid.) - void consider_evicting_current_page(block_id_t block_id); - - void have_read_ahead_cb_destroyed(); - - evicter_t &evicter() { return evicter_; } - - auto_drainer_t::lock_t drainer_lock() { return drainer_->lock(); } - serializer_t *serializer() { return serializer_; } - -private: - friend class page_read_ahead_cb_t; - void add_read_ahead_buf(block_id_t block_id, - scoped_device_block_aligned_ptr_t ptr, - const counted_t &token); - - void read_ahead_cb_is_destroyed(); - - - current_page_t *internal_page_for_new_chosen(block_id_t block_id); - - // KSI: Maybe just have txn_t hold a single list of block_change_t objects. - struct block_change_t { - block_change_t(block_version_t _version, bool _modified, - page_t *_page, repli_timestamp_t _tstamp) - : version(_version), modified(_modified), page(_page), tstamp(_tstamp) { } - block_version_t version; - - // True if the value of the block was modified (or the block was deleted), false - // if the block was only touched. - bool modified; - // If modified == true, the new value for the block, or NULL if the block was - // deleted. (The page_t's lifetime is kept by some page_txn_t's - // snapshotted_dirtied_pages_ field.) - page_t *page; - repli_timestamp_t tstamp; - }; - - friend class page_txn_t; - static void do_flush_changes(page_cache_t *page_cache, - std::map &&changes, - const std::vector &txns, - fifo_enforcer_write_token_t index_write_token); - static void do_flush_txn_set(page_cache_t *page_cache, - std::map *changes_ptr, - const std::vector &txns); - - static void remove_txn_set_from_graph(page_cache_t *page_cache, - const std::vector &txns); - - static std::map - compute_changes(const std::vector &txns); - - static std::vector maximal_flushable_txn_set(page_txn_t *base); - - void im_waiting_for_flush(page_txn_t *txns); - - friend class current_page_acq_t; - repli_timestamp_t recency_for_block_id(block_id_t id) { - // This `if` is redundant, since `recencies_.size()` will always be smaller - // than any aux block ID. It's probably a good idea to be explicit about this - // though. - if (is_aux_block_id(id)) { - return repli_timestamp_t::invalid; - } - return recencies_.size() <= id - ? repli_timestamp_t::invalid - : recencies_[id]; - } - - void set_recency_for_block_id(block_id_t id, repli_timestamp_t recency) { - if(is_aux_block_id(id)) { - guarantee(recency == repli_timestamp_t::invalid); - return; - } - while (recencies_.size() <= id) { - recencies_.push_back(repli_timestamp_t::invalid); - } - recencies_[id] = recency; - } - - friend class current_page_t; - free_list_t *free_list() { return &free_list_; } - - static void consider_evicting_all_current_pages(page_cache_t *page_cache, - auto_drainer_t::lock_t lock); - - const max_block_size_t max_block_size_; - - // We use a separate I/O account for reads in each page cache. - // Note that block writes use a shared I/O account that sits in the - // merger_serializer_t (as long as you use one, otherwise they use the - // default account). - cache_account_t default_reads_account_; - - // This fifo enforcement pair ensures ordering of index_write operations after we - // move to the serializer thread and get a bunch of blocks written. - // index_write_sink's pointee's home thread is on the serializer. - fifo_enforcer_source_t index_write_source_; - scoped_ptr_t index_write_sink_; - - serializer_t *serializer_; - segmented_vector_t recencies_; - - std::unordered_map current_pages_; - - free_list_t free_list_; - - evicter_t evicter_; - - // KSI: I bet this read_ahead_cb_ and read_ahead_cb_existence_ type could be - // packaged in some new cross_thread_ptr type. - page_read_ahead_cb_t *read_ahead_cb_; - - // Holds a lock on *drainer_ is until shortly after the page_read_ahead_cb_t is - // destroyed and all possible read-ahead operations have completed. - auto_drainer_t::lock_t read_ahead_cb_existence_; - - scoped_ptr_t drainer_; - - DISABLE_COPYING(page_cache_t); -}; - -class dirtied_page_t { -public: - dirtied_page_t() - : block_id(NULL_BLOCK_ID) { } - dirtied_page_t(block_version_t _block_version, - block_id_t _block_id, timestamped_page_ptr_t &&_ptr) - : block_version(_block_version), - block_id(_block_id), - ptr(std::move(_ptr)) { } - dirtied_page_t(dirtied_page_t &&movee) - : block_version(movee.block_version), - block_id(movee.block_id), - ptr(std::move(movee.ptr)) { } - dirtied_page_t &operator=(dirtied_page_t &&movee) { - block_version = movee.block_version; - block_id = movee.block_id; - ptr = std::move(movee.ptr); - return *this; - } - // Our block version of the dirty page. - block_version_t block_version; - // The block id of the dirty page. - block_id_t block_id; - // The snapshotted dirty page value. (If empty, the page was deleted.) - timestamped_page_ptr_t ptr; -}; - -class touched_page_t { -public: - touched_page_t() - : block_id(NULL_BLOCK_ID), - tstamp(repli_timestamp_t::invalid) { } - touched_page_t(block_version_t _block_version, - block_id_t _block_id, - repli_timestamp_t _tstamp) - : block_version(_block_version), - block_id(_block_id), - tstamp(_tstamp) { } - - block_version_t block_version; - block_id_t block_id; - repli_timestamp_t tstamp; -}; - -// page_txn_t's exist for the purpose of writing to disk. The rules are as follows: -// -// - When a page_txn_t gets "committed" (written to disk), all blocks modified with -// a given page_txn_t must be committed to disk at the same time. (That is, they -// all go in the same index_write operation.) -// -// - For all blocks N and page_txn_t S and T, if S modifies N before T modifies N, -// then S must be committed to disk before or at the same time as T. -// -// - For all page_txn_t S and T, if S is the preceding_txn of T then S must be -// committed to disk before or at the same time as T. -// -// As a result, we form a graph of txns, which gets modified on the fly, and we -// commit them in topological order. Cycles can happen (for example, if (a) two -// transactions modify the same physical memory, or (b) they modify blocks in -// opposite orders), and transactions that depend on one another (forming a cycle) -// must get flushed simultaneously (in the same serializer->index_write operation). -// Situation '(a)' can happen as a matter of course, assuming transactions don't -// greedily save their modified copy of a page. Situation '(b)' can happen if -// transactions apply a commutative operation on a block, like with the stats block. -// Right now, situation '(a)' doesn't happen because transactions do greedily keep -// their copies of the block. -// -// LSI: Make situation '(a)' happenable. -class page_txn_t { -public: - // Our transaction has to get committed to disk _after_ or at the same time as - // preceding transactions on cache_conn, if that parameter is not NULL. (The - // parameter's NULL for read txns, for now.) - page_txn_t(page_cache_t *page_cache, - throttler_acq_t throttler_acq, - cache_conn_t *cache_conn); - - // KSI: This is only to be called by the page cache -- should txn_t really use a - // scoped_ptr_t? - ~page_txn_t(); - - page_cache_t *page_cache() const { return page_cache_; } - -private: - // To set cache_conn_ to NULL. - friend class ::cache_conn_t; - - // To access throttler_acq_. - friend class flush_and_destroy_txn_waiter_t; - - // page cache has access to all of this type's innards, including fields. - friend class page_cache_t; - - // To access `pages_write_acquired_last_` and `connect_preceder()`. - friend class current_page_t; - - // Adds and connects a preceder. - void connect_preceder(page_txn_t *preceder); - - // Removes a preceder, which is already half-way disconnected. - void remove_preceder(page_txn_t *preceder); - - // Removes a subseqer, which is already half-way disconnected. - void remove_subseqer(page_txn_t *subseqer); - - // current_page_acq should only call add_acquirer and remove_acquirer. - friend class current_page_acq_t; - void add_acquirer(current_page_acq_t *acq); - void remove_acquirer(current_page_acq_t *acq); - - void announce_waiting_for_flush(); - - page_cache_t *page_cache_; - // This can be NULL, if the txn is not part of some cache conn. - cache_conn_t *cache_conn_; - - // An acquisition object for the memory tracker. - throttler_acq_t throttler_acq_; - - // page_txn_t's form a directed graph. preceders_ and subseqers_ represent the - // inward-pointing and outward-pointing arrows. (I'll let you decide which - // direction should be inward and which should be outward.) Each page_txn_t - // pointed at by subseqers_ has an entry in its preceders_ that is back-pointing - // at this page_txn_t. (And vice versa for each page_txn_t pointed at by - // preceders_.) - - // PERFORMANCE(preceders_): PERFORMANCE(subseqers_): - // - // Performance on operations linear in the number of preceders_ and subseqers_ - // should be _okay_ in any case, because we throttle transactions based on the - // number of dirty blocks. But also, the number of preceders_ and subseqers_ - // would generally be very low, because relationships for users of the same block - // or cache connection form a chain, not a giant clique. - - // The transactions that must be committed before or at the same time as this - // transaction. - std::vector preceders_; - - // txn's that we precede -- preceders_[i]->subseqers_ always contains us once. - std::vector subseqers_; - - // Pages for which this page_txn_t is the last_write_acquirer_ of that page. We - // wouldn't mind a std::vector inside the backindex_bag_t, but it's a - // segmented_vector_t -- we give it a segment size big enough to not be obnoxious - // about memory usage. - backindex_bag_t pages_write_acquired_last_; - - // How many current_page_acq_t's for this transaction that are currently alive. - size_t live_acqs_; - - // Saved pages (by block id). - // KSI: Right now we put multiple dirtied_page_t's if we reacquire the same block - // and modify it again. - segmented_vector_t snapshotted_dirtied_pages_; - - // Touched pages (by block id). - // KSI: Right now we put multiple touched_page_t's if we reacquire the same block - // and modify it again. - segmented_vector_t touched_pages_; - - // KSI: We could probably turn began_waiting_for_flush_ and spawned_flush_ into a - // generalized state enum. - // - // KSI: Should we have the spawned_flush_ variable or should we remove the txn - // from the graph? - - // Tells whether this page_txn_t has announced itself (to the cache) to be - // waiting for a flush. - bool began_waiting_for_flush_; - bool spawned_flush_; - - enum mark_state_t { - marked_not, - marked_red, - marked_blue, - marked_green, - }; - // Always `marked_not`, except temporarily, during ASSERT_NO_CORO_WAITING graph - // algorithms. - mark_state_t mark_; - - // This gets pulsed when the flush is complete or when the txn has no reason to - // exist any more. - cond_t flush_complete_cond_; - - DISABLE_COPYING(page_txn_t); -}; - -} // namespace alt - - -#endif // BUFFER_CACHE_PAGE_CACHE_HPP_ diff --git a/src/buffer_cache/serialize_onto_blob.cc b/src/buffer_cache/serialize_onto_blob.cc deleted file mode 100644 index 1caff93d8dd..00000000000 --- a/src/buffer_cache/serialize_onto_blob.cc +++ /dev/null @@ -1,20 +0,0 @@ -#include "buffer_cache/serialize_onto_blob.hpp" - -void write_onto_blob(buf_parent_t parent, blob_t *blob, - const write_message_t &wm) { - blob->clear(parent); - blob->append_region(parent, wm.size()); - - blob_acq_t acq; - buffer_group_t group; - blob->expose_all(parent, access_t::write, &group, &acq); - - buffer_group_write_stream_t stream(&group); - int res = send_write_message(&stream, &wm); - guarantee(res == 0, - "Failed to put write_message_t into buffer group. " - "(Was the blob made too small?)."); - guarantee(stream.entire_stream_filled(), - "Blob not filled by write_message_t (Was it made too big?)"); -} - diff --git a/src/buffer_cache/serialize_onto_blob.hpp b/src/buffer_cache/serialize_onto_blob.hpp deleted file mode 100644 index eee7e183022..00000000000 --- a/src/buffer_cache/serialize_onto_blob.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef BUFFER_CACHE_SERIALIZE_ONTO_BLOB_HPP_ -#define BUFFER_CACHE_SERIALIZE_ONTO_BLOB_HPP_ - -#include "buffer_cache/alt.hpp" -#include "buffer_cache/blob.hpp" -#include "containers/archive/archive.hpp" -#include "containers/archive/buffer_group_stream.hpp" -#include "containers/archive/versioned.hpp" -#include "version.hpp" - -void write_onto_blob(buf_parent_t parent, blob_t *blob, - const write_message_t &wm); - -template -void serialize_onto_blob(buf_parent_t parent, blob_t *blob, - const T &value) { - // It _should_ never make sense to serialize an earlier version onto a blob. - // Instead, you should probably update the version tag for the thing that you are - // serializing (often stored in a block magic somewhere) and serialize with the - // latest version. See cluster_metadata_superblock_t in persist.cc for an - // example -- things that modify the blobs call bring_up_to_date (which brings - // everything serialized up to the latest version, if necessary) and then - // serialize with the 'LATEST' version. - // - // The reason this type parameter exists at all is to make sure the person - // writing the code on the calling end has their head in gear. - static_assert(W == cluster_version_t::LATEST_DISK, - "It never makes sense to statically serialize an earlier version " - "onto a blob. (Or does it?)"); - - // We still make an unnecessary copy: serializing to a write_message_t instead of - // directly onto the stream. (However, don't be so sure it would be more - // efficient to serialize onto an abstract stream type -- you've got a whole - // bunch of virtual function calls that way. But we do _deserialize_ off an - // abstract stream type already, so what's the big deal?) - write_message_t wm; - serialize(&wm, value); - write_onto_blob(parent, blob, wm); -} - -template -void deserialize_for_version_from_blob(cluster_version_t cluster_version, - buf_parent_t parent, blob_t *blob, - T *value_out) { - buffer_group_t group; - blob_acq_t acq; - blob->expose_all(parent, access_t::read, &group, &acq); - deserialize_for_version_from_group(cluster_version, const_view(&group), value_out); -} - - - -#endif // BUFFER_CACHE_SERIALIZE_ONTO_BLOB_HPP_ diff --git a/src/buffer_cache/stats.cc b/src/buffer_cache/stats.cc deleted file mode 100644 index 2b9ba6c533d..00000000000 --- a/src/buffer_cache/stats.cc +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "buffer_cache/stats.hpp" - -#include "perfmon/perfmon.hpp" -#include "rdb_protocol/datum.hpp" - -alt_cache_stats_t::alt_cache_stats_t(alt::page_cache_t *_page_cache, - perfmon_collection_t *parent) : - page_cache(_page_cache), - cache_collection(), - cache_membership(parent, &cache_collection, "cache"), - in_use_bytes(this), - in_use_bytes_membership(&cache_collection, - &in_use_bytes, "in_use_bytes"), - cache_collection_membership(&cache_collection) { } - -alt_cache_stats_t::perfmon_value_t::perfmon_value_t(alt_cache_stats_t *_parent) : - parent(_parent) { } - -void *alt_cache_stats_t::perfmon_value_t::begin_stats() { - return new uint64_t; -} - -void alt_cache_stats_t::perfmon_value_t::visit_stats(void *ptr) { - if (get_thread_id() == parent->home_thread()) { - uint64_t *value = reinterpret_cast(ptr); - *value = parent->page_cache->evicter().in_memory_size(); - } -} - -ql::datum_t alt_cache_stats_t::perfmon_value_t::end_stats(void *ptr) { - uint64_t *value = reinterpret_cast(ptr); - ql::datum_t res(static_cast(*value)); - delete value; - return res; -} diff --git a/src/buffer_cache/stats.hpp b/src/buffer_cache/stats.hpp deleted file mode 100644 index b4ed4034909..00000000000 --- a/src/buffer_cache/stats.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef BUFFER_CACHE_STATS_HPP_ -#define BUFFER_CACHE_STATS_HPP_ - -#include "perfmon/perfmon.hpp" -#include "buffer_cache/page_cache.hpp" - -class alt_cache_stats_t : public home_thread_mixin_t { -public: - explicit alt_cache_stats_t(alt::page_cache_t *_page_cache, - perfmon_collection_t *parent); - - alt::page_cache_t *page_cache; - - perfmon_collection_t cache_collection; - perfmon_membership_t cache_membership; - - class perfmon_value_t : public perfmon_t { - public: - explicit perfmon_value_t(alt_cache_stats_t *_parent); - void *begin_stats(); - void visit_stats(void *); - ql::datum_t end_stats(void *); - private: - alt_cache_stats_t *parent; - DISABLE_COPYING(perfmon_value_t); - }; - perfmon_value_t in_use_bytes; - perfmon_membership_t in_use_bytes_membership; - - - perfmon_multi_membership_t cache_collection_membership; -}; - - -#endif // BUFFER_CACHE_STATS_HPP_ diff --git a/src/buffer_cache/types.cc b/src/buffer_cache/types.cc deleted file mode 100644 index 4a7c7179086..00000000000 --- a/src/buffer_cache/types.cc +++ /dev/null @@ -1,10 +0,0 @@ -#include "buffer_cache/types.hpp" - -#include "debug.hpp" - -void debug_print(printf_buffer_t *buf, block_magic_t magic) { - buf->appendf("block_magic{"); - char *bytes = magic.bytes; - debug_print_quoted_string(buf, reinterpret_cast(bytes), sizeof(magic.bytes)); - buf->appendf("}"); -} diff --git a/src/buffer_cache/types.hpp b/src/buffer_cache/types.hpp deleted file mode 100644 index b504f79588c..00000000000 --- a/src/buffer_cache/types.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef BUFFER_CACHE_TYPES_HPP_ -#define BUFFER_CACHE_TYPES_HPP_ - -#include -#include - -#include "containers/archive/archive.hpp" -#include "serializer/types.hpp" - -// write_durability_t::INVALID is an invalid value, notably it can't be serialized. -enum class write_durability_t { INVALID, SOFT, HARD }; -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE(write_durability_t, int8_t, - write_durability_t::SOFT, - write_durability_t::HARD); - - -typedef uint32_t block_magic_comparison_t; - -struct block_magic_t { - char bytes[sizeof(block_magic_comparison_t)]; - - bool operator==(const block_magic_t &other) const { - union { - block_magic_t x; - block_magic_comparison_t n; - } u, v; - - u.x = *this; - v.x = other; - - return u.n == v.n; - } - - bool operator!=(const block_magic_t &other) const { - return !(operator==(other)); - } -}; - -void debug_print(printf_buffer_t *buf, block_magic_t magic); - -#endif /* BUFFER_CACHE_TYPES_HPP_ */ diff --git a/src/build.mk b/src/build.mk deleted file mode 100644 index 264f66f389d..00000000000 --- a/src/build.mk +++ /dev/null @@ -1,405 +0,0 @@ -# Copyright 2010-2013 RethinkDB, all rights reserved. - -##### Build parameters - -# We assemble path directives. -LDFLAGS ?= -CXXFLAGS ?= -RT_LDFLAGS = $(LDFLAGS) $(RE2_LIBS) $(TERMCAP_LIBS) $(Z_LIBS) $(CURL_LIBS) $(CRYPTO_LIBS) $(SSL_LIBS) -RT_LDFLAGS += $(V8_LIBS) $(PROTOBUF_LIBS) $(PTHREAD_LIBS) $(MALLOC_LIBS) -RT_CXXFLAGS := $(CXXFLAGS) $(RE2_INCLUDE) $(V8_INCLUDE) $(PROTOBUF_INCLUDE) $(BOOST_INCLUDE) $(Z_INCLUDE) $(CURL_INCLUDE) $(CRYPTO_INCLUDE) -ALL_INCLUDE_DEPS := $(RE2_INCLUDE_DEP) $(V8_INCLUDE_DEP) $(PROTOBUF_INCLUDE_DEP) $(BOOST_INCLUDE_DEP) $(Z_INCLUDE_DEP) $(CURL_INCLUDE_DEP) $(CRYPTO_INCLUDE_DEP) $(SSL_INCLUDE_DEP) - -ifeq ($(USE_CCACHE),1) - RT_CXX := ccache $(CXX) - ifeq ($(COMPILER),CLANG) - RT_CXXFLAGS += -Qunused-arguments - endif -else - RT_CXX := $(CXX) -endif - -STATICFORCE := $(STATIC) - -ifeq ($(COMPILER),CLANG) - - ifeq ($(OS),Darwin) - # TODO: ld: unknown option: --no-as-needed - # RT_LDFLAGS += -Wl,--no-as-needed - endif - - ifeq ($(STATICFORCE),1) - # TODO(OSX) - ifeq ($(OS),Linux) - RT_LDFLAGS += -static - STATIC_LIBGCC := 1 - endif - endif - - RT_LDFLAGS += $(M_LIBS) - -else ifeq ($(COMPILER),INTEL) - RT_LDFLAGS += -B/opt/intel/bin - - ifeq ($(STATICFORCE),1) - # TODO(OSX) - ifeq ($(OS),Linux) - RT_LDFLAGS += -static - STATIC_LIBGCC = 1 - endif - endif - - RT_LDFLAGS += -lstdc++ -else ifeq ($(COMPILER),GCC) - - ifeq ($(OS),Linux) - RT_LDFLAGS += -Wl,--no-as-needed - ifeq ($(GCC_ARCH),ppc64le) - RT_CXXFLAGS += "-DV8_NEEDS_BUFFER_ALLOCATOR" - endif - endif - - ifeq ($(STATICFORCE),1) - # TODO(OSX) - ifeq ($(OS),Linux) - RT_LDFLAGS += -static - STATIC_LIBGCC := 1 - endif - endif - - RT_LDFLAGS += -endif - -RT_LDFLAGS += $(RT_LIBS) - -ifeq ($(STATICFORCE),1) - # TODO(OSX) - ifeq ($(OS),Linux) - RT_LDFLAGS += -Wl,-Bdynamic - endif -endif - -ifeq ($(BUILD_PORTABLE),1) - ifeq ($(OS),Linux) - RT_LDFLAGS += -lgcc_s - RT_LDFLAGS += -lgcc - endif -endif - -RT_LDFLAGS += $(LIB_SEARCH_PATHS) - -RT_CXXFLAGS += -I$(SOURCE_DIR) -RT_CXXFLAGS += -pthread -RT_CXXFLAGS += "-DPRODUCT_NAME=\"$(PRODUCT_NAME)\"" -RT_CXXFLAGS += "-D__STDC_LIMIT_MACROS" -RT_CXXFLAGS += "-D__STDC_FORMAT_MACROS" -RT_CXXFLAGS += -Wall -Wextra - -RT_CXXFLAGS += -DENABLE_TLS - -# Enable RapidJSON std::string functions -RT_CXXFLAGS += "-DRAPIDJSON_HAS_STDSTRING" -# Set RapidJSON to exact double parsing mode -RT_CXXFLAGS += "-DRAPIDJSON_PARSE_DEFAULT_FLAGS=kParseFullPrecisionFlag" - -# Force 64-bit off_t size on Linux -- also, sizeof(off_t) will be -# checked by a compile-time assertion. -ifeq ($(OS),Linux) - RT_CXXFLAGS += -D_FILE_OFFSET_BITS=64 -endif - - - -ifneq (1,$(ALLOW_WARNINGS)) - RT_CXXFLAGS += -Werror -endif - -RT_CXXFLAGS += -Wnon-virtual-dtor -Wno-deprecated-declarations -std=gnu++0x - -ifeq ($(COMPILER), INTEL) - RT_CXXFLAGS += -w1 -ftls-model=local-dynamic - -else ifeq ($(COMPILER), CLANG) - RT_CXXFLAGS += -Wformat=2 -Wswitch-enum -Wswitch-default # -Wno-unneeded-internal-declaration - RT_CXXFLAGS += -Wused-but-marked-unused -Wundef -Wvla -Wshadow - RT_CXXFLAGS += -Wconditional-uninitialized -Wmissing-noreturn -else ifeq ($(COMPILER), GCC) - ifeq ($(LEGACY_GCC), 1) - RT_CXXFLAGS += -Wformat=2 -Wswitch-enum -Wswitch-default - else - RT_CXXFLAGS += -Wformat=2 -Wswitch-enum -Wswitch-default -Wno-array-bounds -Wno-maybe-uninitialized - endif -endif - -ifeq ($(COVERAGE), 1) - ifeq ($(COMPILER), GCC) - RT_CXXFLAGS += --coverage - RT_LDFLAGS += --coverage - else - $(error COVERAGE=1 not yet supported for $(COMPILER)) - endif -endif - -# TODO: >() only works on bash >= 4 -LD_OUTPUT_FILTER ?= -ifeq ($(COMPILER),INTEL) - # TODO: get rid of the cause of this warning, not just the warning itself - LD_OUTPUT_FILTER += 2> >(grep -v "warning: relocation refers to discarded section") -endif - - -ifeq ($(RT_FORCE_NATIVE),1) - RT_CXXFLAGS+=-march=native -endif -ifeq ($(RT_COPY_NATIVE),1) - RT_CXXFLAGS+=-march="$(GCC_ARCH)" -endif -ifeq ($(RT_REDUCE_NATIVE),1) - RT_CXXFLAGS+=-march="$(GCC_ARCH_REDUCED)" -endif - -ifeq ($(RQL_ERROR_BT),1) - RT_CXXFLAGS+=-DRQL_ERROR_BT -endif - -# Configure debug vs. release -ifeq ($(DEBUG),1) - RT_CXXFLAGS += -O0 - ifeq ($(KEEP_INLINE),1) - RT_CXXFLAGS+=-fkeep-inline-functions - endif - -else # ifeq ($(DEBUG),1) - # use -fno-strict-aliasing to not break things - # march=native used to break the serializer - RT_CXXFLAGS += -O3 -DNDEBUG -fno-strict-aliasing # -march=native - ifeq ($(NO_OMIT_FRAME_POINTER),1) - RT_CXXFLAGS += -fno-omit-frame-pointer - endif -endif # ifeq ($(DEBUG),1) - -ifeq ($(DISABLE_BREAKPOINTS),1) - RT_CXXFLAGS += -DDISABLE_BREAKPOINTS -endif - -ifeq (${STATIC_LIBGCC},1) - RT_LDFLAGS += -static-libgcc -static-libstdc++ -endif - -ifeq ($(SYMBOLS),1) - # -rdynamic is necessary so that backtrace_symbols() works properly - ifeq ($(OS),Linux) - RT_LDFLAGS += -rdynamic - endif - RT_CXXFLAGS += -g -endif # ($(SYMBOLS),1) - -ifeq ($(JSON_SHORTCUTS),1) - RT_CXXFLAGS += -DJSON_SHORTCUTS -endif - -ifeq ($(LEGACY_LINUX),1) - RT_CXXFLAGS += -DLEGACY_LINUX -DNO_EPOLL -Wno-format -endif - -ifeq ($(LEGACY_GCC),1) - RT_CXXFLAGS += -Wno-switch-default -Wno-switch-enum -endif - -ifeq ($(NO_EVENTFD),1) - RT_CXXFLAGS += -DNO_EVENTFD -endif - -ifeq ($(NO_EPOLL),1) - RT_CXXFLAGS += -DNO_EPOLL -endif - -ifeq ($(THREADED_COROUTINES),1) - RT_CXXFLAGS += -DTHREADED_COROUTINES -endif - -ifeq ($(VALGRIND),1) - ifneq (system,$(ALLOCATOR)) - $(error cannot build with VALGRIND=1 when using a custom allocator) - endif - RT_CXXFLAGS += -DVALGRIND -endif - -ifeq ($(V8_PRE_3_19),1) - RT_CXXFLAGS += -DV8_PRE_3_19 -endif - -ifeq ($(FULL_PERFMON),1) - RT_CXXFLAGS += -DFULL_PERFMON -endif - -ifeq ($(CORO_PROFILING),1) - RT_CXXFLAGS += -DENABLE_CORO_PROFILER -endif - -ifeq ($(HAS_TERMCAP),1) - RT_CXXFLAGS += -DHAS_TERMCAP -endif - -RT_CXXFLAGS += -I$(PROTO_DIR) - -#### Finding what to build - -SOURCES := $(shell find $(SOURCE_DIR) -name '*.cc' -not -name '\.*') - -SERVER_EXEC_SOURCES := $(filter-out $(SOURCE_DIR)/unittest/%,$(SOURCES)) - -QL2_PROTO_NAMES := rdb_protocol/ql2 -QL2_PROTO_SOURCES := $(foreach _,$(QL2_PROTO_NAMES),$(SOURCE_DIR)/$_.proto) -QL2_PROTO_HEADERS := $(foreach _,$(QL2_PROTO_NAMES),$(PROTO_DIR)/$_.pb.h) -QL2_PROTO_CODE := $(foreach _,$(QL2_PROTO_NAMES),$(PROTO_DIR)/$_.pb.cc) -QL2_PROTO_OBJS := $(foreach _,$(QL2_PROTO_NAMES),$(OBJ_DIR)/$_.pb.o) - -PROTOCFLAGS_CXX := --proto_path=$(SOURCE_DIR) - -ifeq (/,$(firstword $(subst /,/ ,$(CWD)))) - DEPS_POSTFIX := .abs -else ifeq (.,$(CWD)) - DEPS_POSTFIX := -else - DEPS_POSTFIX := .$(subst /,_,$(subst ../,,$(CWD))) -endif - -NAMES := $(patsubst $(SOURCE_DIR)/%.cc,%,$(SOURCES)) -DEPS := $(patsubst %,$(DEP_DIR)/%$(DEPS_POSTFIX).d,$(NAMES)) -OBJS := $(QL2_PROTO_OBJS) $(patsubst %,$(OBJ_DIR)/%.o,$(NAMES)) - -SERVER_EXEC_OBJS := $(QL2_PROTO_OBJS) $(patsubst $(SOURCE_DIR)/%.cc,$(OBJ_DIR)/%.o,$(SERVER_EXEC_SOURCES)) - -SERVER_NOMAIN_OBJS := $(QL2_PROTO_OBJS) $(patsubst $(SOURCE_DIR)/%.cc,$(OBJ_DIR)/%.o,$(filter-out %/main.cc,$(SOURCES))) - -SERVER_UNIT_TEST_OBJS := $(SERVER_NOMAIN_OBJS) $(OBJ_DIR)/unittest/main.o - -##### Version number handling - -RT_CXXFLAGS += -DRETHINKDB_VERSION=\"$(RETHINKDB_VERSION)\" -RT_CXXFLAGS += -DRETHINKDB_CODE_VERSION=\"$(RETHINKDB_CODE_VERSION)\" - -##### Build targets - -ALL += $(SOURCE_DIR) -.PHONY: $(SOURCE_DIR)/all -$(SOURCE_DIR)/all: $(BUILD_DIR)/$(SERVER_EXEC_NAME) $(BUILD_DIR)/$(GDB_FUNCTIONS_NAME) | $(BUILD_DIR)/. - -ifeq ($(UNIT_TESTS),1) - $(SOURCE_DIR)/all: $(BUILD_DIR)/$(SERVER_UNIT_TEST_NAME) -endif - -.PHONY: unit -unit: $(BUILD_DIR)/$(SERVER_UNIT_TEST_NAME) - $P RUN $(SERVER_UNIT_TEST_NAME) - $(BUILD_DIR)/$(SERVER_UNIT_TEST_NAME) --gtest_filter=$(UNIT_TEST_FILTER) - -.PRECIOUS: $(PROTO_DIR)/. $(QL2_PROTO_HEADERS) $(QL2_PROTO_CODE) - -$(PROTO_DIR)/%.pb.h $(PROTO_DIR)/%.pb.cc: $(SOURCE_DIR)/%.proto $(PROTOC_BIN_DEP) | $(PROTO_DIR)/. - $P PROTOC - -# # See issue #2965 - +rm -f $(PROTO_DIR)/$*.pb.h $(PROTO_DIR)/$*.pb.cc - - $(PROTOC) $(PROTOCFLAGS_CXX) --cpp_out $(PROTO_DIR) $< - -$(TOP)/src/rpc/semilattice/joins/macros.hpp: $(TOP)/scripts/generate_join_macros.py -$(TOP)/src/rpc/serialize_macros.hpp: $(TOP)/scripts/generate_serialize_macros.py -$(TOP)/src/rpc/semilattice/joins/macros.hpp $(TOP)/src/rpc/serialize_macros.hpp: - $P GEN $@ - $< > $@ - -generate-headers: $(TOP)/src/rpc/semilattice/joins/macros.hpp $(TOP)/src/rpc/serialize_macros.hpp - -.PHONY: rethinkdb -rethinkdb: $(BUILD_DIR)/$(SERVER_EXEC_NAME) - -RETHINKDB_DEPENDENCIES_LIBS := $(MALLOC_LIBS_DEP) $(V8_LIBS_DEP) $(PROTOBUF_LIBS_DEP) $(RE2_LIBS_DEP) $(Z_LIBS_DEP) $(CURL_LIBS_DEP) $(CRYPTO_LIBS_DEP) $(SSL_LIBS_DEP) - -MAYBE_CHECK_STATIC_MALLOC = -ifeq ($(STATIC_MALLOC),1) # if the allocator is statically linked - ifeq (tcmalloc,$(ALLOCATOR)) - MAYBE_CHECK_STATIC_MALLOC = objdump -T $@ | c++filt | grep -q 'tcmalloc::\|google_malloc' || - MAYBE_CHECK_STATIC_MALLOC += (echo "Failed to link in TCMalloc." >&2 && false) - else ifeq (jemalloc,$(ALLOCATOR)) - RT_LDFLAGS += -ldl - MAYBE_CHECK_STATIC_MALLOC = objdump -T $@ | grep -w -q 'mallctlnametomib' || - MAYBE_CHECK_STATIC_MALLOC += (echo "Failed to link in jemalloc." >&2 && false) - endif -endif - -ifneq (1,$(SYMBOLS)) - ifeq (1,$(SPLIT_SYMBOLS)) - $(error Conflicting build flags: SYMBOLS=0 and SPLIT_SYMBOLS=1) - endif -endif - -$(BUILD_DIR)/$(SERVER_EXEC_NAME): $(SERVER_EXEC_OBJS) | $(BUILD_DIR)/. $(RETHINKDB_DEPENDENCIES_LIBS) - $P LD $@ - $(RT_CXX) $(SERVER_EXEC_OBJS) $(RT_LDFLAGS) -o $(BUILD_DIR)/$(SERVER_EXEC_NAME) $(LD_OUTPUT_FILTER) - $(MAYBE_CHECK_STATIC_MALLOC) - -ifeq (1,$(SPLIT_SYMBOLS)) - ifeq (Darwin,$(OS)) - $P STRIP $@.dSYM - cd $(BUILD_DIR) && dsymutil --out=$(notdir $@.dSYM) $(notdir $@) - strip $@ - else - $P STRIP $@.debug - objcopy --only-keep-debug $@ $@.debug - objcopy --strip-debug $@ - cd $(BUILD_DIR) && objcopy --add-gnu-debuglink=$(notdir $@.debug) $(notdir $@) - endif -endif - -# The unittests use gtest, which uses macros that expand into switch statements which don't contain -# default cases. So we have to remove the -Wswitch-default argument for them. -$(SERVER_UNIT_TEST_OBJS): RT_CXXFLAGS := $(filter-out -Wswitch-default,$(RT_CXXFLAGS)) $(GTEST_INCLUDE) - -$(SERVER_UNIT_TEST_OBJS): | $(GTEST_INCLUDE_DEP) - -$(BUILD_DIR)/$(SERVER_UNIT_TEST_NAME): $(SERVER_UNIT_TEST_OBJS) $(GTEST_LIBS_DEP) | $(BUILD_DIR)/. $(RETHINKDB_DEPENDENCIES_LIBS) - $P LD $@ - $(RT_CXX) $(SERVER_UNIT_TEST_OBJS) $(RT_LDFLAGS) $(GTEST_LIBS) -o $@ $(LD_OUTPUT_FILTER) - -$(BUILD_DIR)/$(GDB_FUNCTIONS_NAME): | $(BUILD_DIR)/. - $P CP $@ - cp $(SCRIPTS_DIR)/$(GDB_FUNCTIONS_NAME) $@ - -$(OBJ_DIR)/%.pb.o: $(PROTO_DIR)/%.pb.cc $(MAKEFILE_DEPENDENCY) $(QL2_PROTO_HEADERS) - mkdir -p $(dir $@) - $P CC - $(RT_CXX) $(RT_CXXFLAGS) -w -c -o $@ $< - -$(OBJ_DIR)/%.o: $(SOURCE_DIR)/%.cc $(MAKEFILE_DEPENDENCY) $(ALL_INCLUDE_DEPS) | $(QL2_PROTO_OBJS) - mkdir -p $(dir $@) $(dir $(DEP_DIR)/$*) - $P CC - $(RT_CXX) $(RT_CXXFLAGS) -c -o $@ $< \ - -MP -MQ $@ -MD -MF $(DEP_DIR)/$*$(DEPS_POSTFIX).d - test $(DEP_DIR)/$*$(DEPS_POSTFIX).d -nt $< || ( \ - echo 'Warning: Missing dep file: `$(DEP_DIR)/$*$(DEPS_POSTFIX).d` should have been generated by $(RT_CXX)' ; \ - sleep 1; touch $< \ - ) - -FORCE_ALL_DEPS := $(patsubst %,force-dep/%,$(NAMES)) -force-dep/%: $(SOURCE_DIR)/%.cc $(QL2_PROTO_HEADERS) $(ALL_INCLUDE_DEPS) - $P CXX_DEPS $(DEP_DIR)/$*$(DEPS_POSTFIX).d - mkdir -p $(dir $(DEP_DIR)/$*) - $(RT_CXX) $(RT_CXXFLAGS) $(SOURCE_DIR)/$*.cc -MP -MQ $(OBJ_DIR)/$*.o -M -MF $(DEP_DIR)/$*$(DEPS_POSTFIX).d - -.PHONY: deps -deps: $(FORCE_ALL_DEPS) - --include $(DEPS) - -.PHONY: build-clean -build-clean: - $P RM $(BUILD_ROOT_DIR) - rm -rf $(BUILD_ROOT_DIR) - -# For emacs' flymake-mode -.PHONY: check-syntax -check-syntax: - $(RT_CXX) $(RT_CXXFLAGS) -c -o /dev/null $(patsubst %,$(CWD)/%,$(CHK_SOURCES)) diff --git a/src/cjson/cJSON.cc b/src/cjson/cJSON.cc deleted file mode 100644 index fc0aa1f411f..00000000000 --- a/src/cjson/cJSON.cc +++ /dev/null @@ -1,924 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -/* - Copyright (c) 2009 Dave Gamble - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -/* cJSON */ -/* JSON parser in C. */ - -#include "cjson/cJSON.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "errors.hpp" -#include "memory_utils.hpp" -#include "math.hpp" - -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wunreachable-code" -#endif - -static const char *ep; - -const char *cJSON_GetErrorPtr() {return ep;} - -static int cJSON_strcasecmp(const char *s1,const char *s2) -{ - if (!s1) return (s1==s2)?0:1; - if (!s2) return 1; - for (; tolower(*s1) == tolower(*s2); ++s1, ++s2) if (*s1 == 0) return 0; - return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); -} - -static void *(*cJSON_malloc)(size_t sz) = rmalloc; -static void (*cJSON_free)(void *ptr) = free; - -static char* cJSON_strdup(const char* str) -{ - size_t len; - char* copy; - - len = strlen(str) + 1; - if (!(copy = (char*)cJSON_malloc(len))) return 0; - memcpy(copy,str,len); - return copy; -} - -static char* cJSON_strdup(const char* str, size_t size) -{ - size_t len; - char* copy; - - len = size + 1; - if (!(copy = (char*)cJSON_malloc(len))) return 0; - memcpy(copy,str,size); - copy[size] = '\0'; - return copy; -} - -/* Internal constructor. */ -static cJSON *cJSON_New_Item() -{ - cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); - if (node) memset(node,0,sizeof(cJSON)); - return node; -} - -/* Delete a cJSON structure. */ -void cJSON_Delete(cJSON *c) -{ - cJSON *next; - while (c) - { - next=c->next; - if (!(c->type&cJSON_IsReference) && c->head) cJSON_Delete(c->head); - if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); - if (c->string) cJSON_free(c->string); - cJSON_free(c); - c=next; - } -} - -/* Parse the input text to generate a number, and populate the result into item. */ -static const char *parse_number(cJSON *item, const char *num) { - double n; - int offset; - // `%lg` differs from the JSON spec in two ways: it accepts numbers prefixed - // with `+`, and it accepts hexadecimal floats (sometimes). We don't need - // to check for numbers prefixed with `+` because `parse_value` only calls - // us if `item[0]` is `-` or `[0-9]`. We check for hexadecimal floats below - // (cJSON's convention is to just ignore trailing garbage). - if (num[0] == '0' && (num[1] == 'x' || num[1] == 'X')) { - n = 0; - offset = 1; - } else { - char *end; - n = strtod(num, &end); - if (end == num) { - return 0; - } - offset = end - num; - } - item->valuedouble = n; - item->valueint = (int)n; - item->type = cJSON_Number; - return num + offset; -} - -int ATTR_FORMAT(printf, 2, 3) cJSON_asprintf(char **out, const char *format, ...) { - va_list ap; - va_start(ap, format); - int ret; -#ifdef _WIN32 - char dummy; - int size = vsnprintf(&dummy, 1, format, ap); - if (size < 0) { - ret = size; - } else { - *out = reinterpret_cast(cJSON_malloc(size + 1)); - ret = vsnprintf(*out, size + 1, format, ap); - rassert(ret == size); - } -#else - ret = vasprintf(out, format, ap); -#endif - va_end(ap); - return ret; -} - -/* Render the number nicely from the given item into a string. */ -static char *print_number(cJSON *item) { - double d = item->valuedouble; - guarantee(risfinite(d)); - if (d == 0.0 && std::signbit(d)) { - return cJSON_strdup("-0.0"); - } else { - char *str; - int ret = cJSON_asprintf(&str, "%.20g", d); - guarantee(ret); - return str; - } -} - -static unsigned parse_hex4(const char *str) -{ - unsigned h = 0; - if (*str >= '0' && *str <= '9') { - h += (*str) - '0'; - } else if (*str >= 'A' && *str <= 'F') { - h += 10 + (*str) - 'A'; - } else if (*str >= 'a' && *str <= 'f') { - h += 10 + (*str) - 'a'; - } else { - return 0; - } - h = h<<4; - str++; - if (*str >= '0' && *str <= '9') { - h += (*str) - '0'; - } else if (*str >= 'A' && *str <= 'F') { - h += 10 + (*str) - 'A'; - } else if (*str >= 'a' && *str <= 'f') { - h += 10 + (*str) - 'a'; - } else { - return 0; - } - h = h<<4; - str++; - if (*str >= '0' && *str <= '9') { - h += (*str) - '0'; - } else if (*str >= 'A' && *str <= 'F') { - h += 10 + (*str) - 'A'; - } else if (*str >= 'a' && *str <= 'f') { - h += 10 + (*str) - 'a'; - } else { - return 0; - } - h = h<<4; - str++; - if (*str >= '0' && *str <= '9') { - h += (*str) - '0'; - } else if (*str >= 'A' && *str <= 'F') { - h += 10 + (*str) - 'A'; - } else if (*str >= 'a' && *str <= 'f') { - h += 10 + (*str) - 'a'; - } else { - return 0; - } - return h; -} - -/* Parse the input text into an unescaped cstring, and populate item. */ -static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; - -static const char *parse_string(cJSON *item,const char *str) -{ - const char *ptr = str+1; - char *ptr2; - char *out; - int len = 0; - unsigned uc, uc2; - - if (*str != '\"') { - // Not a string. - ep = str; - return 0; - } - - while (*ptr != '\"' && *ptr && ++len) { - // Skip escaped quotes. - if (*ptr++ == '\\') ptr++; - } - - // This is how long we need for the string, roughly. - out=(char*)cJSON_malloc(len+1); - if (!out) { - return 0; - } - - ptr=str+1; - ptr2=out; - while (*ptr != '\"' && *ptr) { - if (*ptr != '\\') { - *ptr2++ = *ptr++; - } else { - ptr++; - switch (*ptr) - { - case 'b': *ptr2++ = '\b'; break; - case 'f': *ptr2++ = '\f'; break; - case 'n': *ptr2++ = '\n'; break; - case 'r': *ptr2++ = '\r'; break; - case 't': *ptr2++ = '\t'; break; - case 'u': /* transcode utf16 to utf8. */ - /* get the unicode char. */ - uc = parse_hex4(ptr+1); - ptr += 4; - - // Fail on invalid Unicode characters, unlike normal cJSON. - if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) { - cJSON_free(out); - return 0; - } - - if (uc >= 0xD800 && uc <= 0xDBFF) { - /* UTF16 surrogate pairs. */ - if (ptr[1] != '\\' || ptr[2] != 'u') { - /* missing second-half of surrogate. */ - break; - } - uc2 = parse_hex4(ptr + 3); - ptr += 6; - if (uc2 < 0xDC00 || uc2 > 0xDFFF) { - break; /* invalid second-half of surrogate. */ - } - uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF)); - } - - len=4; - if (uc < 0x80) { - len = 1; - } else if (uc < 0x800) { - len = 2; - } else if (uc < 0x10000) { - len = 3; - } - ptr2 += len; - - switch (len) { - case 4: *--ptr2 = ((uc | 0x80) & 0xBF); uc >>= 6; - case 3: *--ptr2 = ((uc | 0x80) & 0xBF); uc >>= 6; - case 2: *--ptr2 = ((uc | 0x80) & 0xBF); uc >>= 6; - case 1: *--ptr2 = (uc | firstByteMark[len]); - } - ptr2 += len; - break; - default: *ptr2++ = *ptr; break; - } - ptr++; - } - } - - *ptr2 = 0; - if (*ptr == '\"') ptr++; - - item->valuestring = out; - item->type = cJSON_String; - - return ptr; -} - -/* Render the cstring provided to an escaped version that can be printed. */ -static char *print_string_ptr(const char *str) -{ - const char *ptr;char *ptr2,*out;int len=0;unsigned char token; - - if (!str) return cJSON_strdup(""); - ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} - - out=(char*)cJSON_malloc(len+3); - if (!out) return 0; - - ptr2=out;ptr=str; - *ptr2++='\"'; - while (*ptr) - { - if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; - else - { - *ptr2++='\\'; - switch (token=*ptr++) - { - case '\\': *ptr2++='\\'; break; - case '\"': *ptr2++='\"'; break; - case '\b': *ptr2++='b'; break; - case '\f': *ptr2++='f'; break; - case '\n': *ptr2++='n'; break; - case '\r': *ptr2++='r'; break; - case '\t': *ptr2++='t'; break; - default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ // NOLINT(runtime/printf) - } - } - } - *ptr2++='\"';*ptr2++=0; - return out; -} -/* Invote print_string_ptr (which is useful) on an item. */ -static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} - -/* Predeclare these prototypes. */ -static const char *parse_value(cJSON *item,const char *value); -static char *print_value(cJSON *item,int depth,int fmt); -static const char *parse_array(cJSON *item,const char *value); -static char *print_array(cJSON *item,int depth,int fmt); -static const char *parse_object(cJSON *item,const char *value); -static char *print_object(cJSON *item,int depth,int fmt); - -/* Utility to jump whitespace and cr/lf */ -static const char *skip(const char *in) {while (in && *in && strchr("\t\r\n ",*in)) in++; return in;} - -/* Parse an object - create a new root, and populate. */ -cJSON *cJSON_Parse(const char *value) -{ - cJSON *c=cJSON_New_Item(); - ep=0; - if (!c) return 0; /* memory fail */ - - const char *end = parse_value(c, skip(value)); - if (end == NULL || *skip(end) != '\0') {cJSON_Delete(c);return 0;} - return c; -} - -/* Render a cJSON item/entity/structure to text. */ -char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} -char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} - -/* Parser core - when encountering text, process appropriately. */ -static const char *parse_value(cJSON *item,const char *value) -{ - if (!value) return 0; /* Fail on null. */ - if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } - if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } - if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } - if (*value=='\"') { return parse_string(item,value); } - if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } - if (*value=='[') { return parse_array(item,value); } - if (*value=='{') { return parse_object(item,value); } - - ep=value;return 0; /* failure. */ -} - -/* Render a value to text. */ -static char *print_value(cJSON *item,int depth,int fmt) -{ - char *out=0; - if (!item) return 0; - switch ((item->type)&255) - { - case cJSON_NULL: out=cJSON_strdup("null"); break; - case cJSON_False: out=cJSON_strdup("false");break; - case cJSON_True: out=cJSON_strdup("true"); break; - case cJSON_Number: out=print_number(item);break; - case cJSON_String: out=print_string(item);break; - case cJSON_Array: out=print_array(item,depth,fmt);break; - case cJSON_Object: out=print_object(item,depth,fmt);break; - default: break; - } - return out; -} - -/* Build an array from input text. */ -static const char *parse_array(cJSON *item,const char *value) -{ - cJSON *node; - if (*value!='[') {ep=value;return 0;} /* not an array! */ - - item->type=cJSON_Array; - value=skip(value+1); - if (*value==']') return value+1; /* empty array. */ - - item->head=node=cJSON_New_Item(); - if (!item->head) return 0; /* memory fail */ - value=skip(parse_value(node,skip(value))); /* skip any spacing, get the value. */ - if (!value) return 0; - - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ - node->next=new_item;new_item->prev=node;node=new_item; - value=skip(parse_value(node,skip(value+1))); - if (!value) return 0; /* memory fail */ - } - - item->tail = node; - - if (*value==']') return value+1; /* end of array */ - ep=value;return 0; /* malformed. */ -} - -/* Render an array to text */ -static char *print_array(cJSON *item,int depth,int fmt) -{ - char **entries; - char *out=0,*ptr,*ret;int len=5; - cJSON *node=item->head; - int numentries=0,i=0,fail=0; - - /* How many entries in the array? */ - while (node) { - numentries++; - node=node->next; - } - /* Allocate an array to hold the values for each */ - entries=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!entries) return 0; - memset(entries,0,numentries*sizeof(char*)); - /* Retrieve all the results: */ - node=item->head; - while (node && !fail) - { - ret=print_value(node,depth+1,fmt); - entries[i++]=ret; - if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; - node=node->next; - } - - /* If we didn't fail, try to rmalloc the output string */ - if (!fail) out=(char*)cJSON_malloc(len); - /* If that fails, we fail. */ - if (!out) fail=1; - - /* Handle failure. */ - if (fail) - { - for (i=0;itype=cJSON_Object; - value=skip(value+1); - if (*value=='}') { - /* empty array. */ - return value+1; - } - - item->head=node=cJSON_New_Item(); - if (!item->head) { - return 0; - } - - value=skip(parse_string(node,skip(value))); - if (!value) { - return 0; - } - - node->string=node->valuestring;node->valuestring=0; - - if (*value!=':') { - /* fail! */ - ep=value; - return 0; - } - - value=skip(parse_value(node,skip(value+1))); /* skip any spacing, get the value. */ - - if (!value) { - return 0; - } - - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) { - return 0; /* memory fail */ - } - - node->next=new_item; - new_item->prev=node; - node=new_item; - value=skip(parse_string(node,skip(value+1))); - if (!value) return 0; - node->string=node->valuestring;node->valuestring=0; - if (*value!=':') {ep=value;return 0;} /* fail! */ - value=skip(parse_value(node,skip(value+1))); /* skip any spacing, get the value. */ - if (!value) return 0; - } - - item->tail = node; - - if (*value=='}') return value+1; /* end of array */ - ep=value;return 0; /* malformed. */ -} - -/* Render an object to text. */ -static char *print_object(cJSON *item,int depth,int fmt) -{ - char **entries=0,**names=0; - char *out=0,*ptr,*ret,*str;int len=7,i=0,j; - cJSON *node=item->head; - int numentries=0,fail=0; - /* Count the number of entries. */ - while (node) { - numentries++; - node=node->next; - } - /* Allocate space for the names and the objects */ - entries=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!entries) return 0; - names=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!names) {cJSON_free(entries);return 0;} - memset(entries,0,sizeof(char*)*numentries); - memset(names,0,sizeof(char*)*numentries); - - /* Collect all the results into our arrays: */ - node=item->head;depth++;if (fmt) len+=depth; - while (node) - { - names[i]=str=print_string_ptr(node->string); - entries[i++]=ret=print_value(node,depth,fmt); - if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; - node=node->next; - } - - /* Try to allocate the output string */ - if (!fail) out=(char*)cJSON_malloc(len); - if (!out) fail=1; - - /* Handle failure */ - if (fail) - { - for (i=0;ihead;int i=0;while(c)i++,c=c->next;return i;} -cJSON *cJSON_slow_GetArrayItem(cJSON *array,int item) {cJSON *c=array->head; while (c && item>0) item--,c=c->next; return c;} -cJSON *cJSON_slow_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->head; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} - -/* Utility for array list handling. */ -static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} -/* Utility for handling references. */ -static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} - -/* Add item to array/object. */ -//TODO all of these functions take time linear in the number of elements in the -//array. This needs to be fixed. -void cJSON_AddItemToArray(cJSON *array, cJSON *item) { - if (!array->tail) { - array->head=item; - array->tail = item; - } else { - suffix_object(array->tail, item); - array->tail = item; - } -} - -void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; char *tmp = cJSON_strdup(string); if (item->string) cJSON_free(item->string);item->string=tmp;cJSON_AddItemToArray(object,item);} -void cJSON_AddItemToObjectN(cJSON *object,const char *string,size_t string_size,cJSON *item) {if (!item) return; char *tmp = cJSON_strdup(string, string_size); if (item->string) cJSON_free(item->string);item->string=tmp;cJSON_AddItemToArray(object,item);} - -void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} -void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} - -cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) { - cJSON *c=array->head; - while (c && which>0) { - c=c->next,which--; - } - if (!c) { - return 0; - } - - if (c->prev) { - c->prev->next=c->next; - } - - if (c->next) { - c->next->prev=c->prev; - } - - if (c==array->head) { - array->head=c->next; - } - - if (c==array->tail) { - array->tail=c->prev; - } - - c->prev=c->next=0; - - return c; -} - -void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} - -cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->head;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} -void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} - -/* Replace array/object items with new ones. */ -void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) { - cJSON *c=array->head; - - while (c && which>0) { - c=c->next,which--; - } - - if (!c) { - return; - } - - newitem->next=c->next; - newitem->prev=c->prev; - - if (c==array->tail) { - array->tail=newitem; - } else { - newitem->next->prev=newitem; - } - - if (c==array->head) { - array->head=newitem; - } else { - newitem->prev->next=newitem; - } - - c->next=c->prev=0; - cJSON_Delete(c); -} - -void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem) { - int i=0; - cJSON *c=object->head; - - while (c && cJSON_strcasecmp(c->string,string)) { - i++; - c=c->next; - } - - if(c) { - newitem->string=cJSON_strdup(string); - cJSON_ReplaceItemInArray(object,i,newitem); - } -} - - -/* Create basic types: */ -cJSON *cJSON_CreateBlank() {return cJSON_New_Item();} -cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} -cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} -cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} -cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} -cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} -cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} -cJSON *cJSON_CreateStringN(const char *string, size_t size) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string, size);}return item;} -cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} -cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} - -/* Create Arrays: */ -template -cJSON *cJSON_CreateNumArray(T numbers,int count) { - cJSON *n=0,*p=0,*a=cJSON_CreateArray(); - for (int i=0;a && ihead=n; - } else { - suffix_object(p,n); - } - p=n; - } - a->tail = p; - - return a; -} - -cJSON *cJSON_CreateIntArray(int *numbers,int count) { - return cJSON_CreateNumArray(numbers,count); -} - -cJSON *cJSON_CreateDoubleArray(double *numbers,int count) { - return cJSON_CreateNumArray(numbers,count); -} - -cJSON *cJSON_CreateStringArray(const char **strings,int count) { - int i; - cJSON *n=0,*p=0,*a=cJSON_CreateArray(); - for (i=0;a && ihead=n; - } else { - suffix_object(p,n); - } - p=n; - } - a->tail = p; - - return a; -} - - -cJSON *cJSON_DeepCopy(cJSON *target) { - if (!target) return 0; - cJSON *hd; - switch (target->type) { - case cJSON_False: - return cJSON_CreateFalse(); - break; - case cJSON_True: - return cJSON_CreateTrue(); - break; - case cJSON_NULL: - return cJSON_CreateNull(); - break; - case cJSON_Number: - return cJSON_CreateNumber(target->valuedouble); - break; - case cJSON_String: - return cJSON_CreateString(target->valuestring); - break; - case cJSON_Array: - { - cJSON *array_res = cJSON_CreateArray(); - - hd = target->head; - while (hd) { - cJSON_AddItemToArray(array_res, cJSON_DeepCopy(hd)); - hd = hd->next; - } - return array_res; - } - break; - case cJSON_Object: - { - cJSON *obj_res = cJSON_CreateObject(); - - hd = target->head; - while (hd) { - cJSON_AddItemToObject(obj_res, hd->string, cJSON_DeepCopy(hd)); - hd = hd->next; - } - - return obj_res; - } - break; - default: - crash("Unreachable"); - break; - } -} - -bool cJSON_Equal(cJSON *x, cJSON *y) { - if (!x || !y || x->type != y->type) { - return false; - } - - switch (x->type) { - case cJSON_False: - case cJSON_True: - case cJSON_NULL: - return true; - break; - case cJSON_Number: - if (x->valuedouble == y->valuedouble) { - rassert(x->valueint == y->valueint); - return true; - } else { - return false; - } - return false; - break; - case cJSON_String: - return strcmp(x->valuestring, y->valuestring) == 0; - break; - case cJSON_Array: - { - cJSON *xhd = x->head, *yhd = y->head; - while (xhd) { - if (!yhd) { - return false; - } - - if (!cJSON_Equal(xhd, yhd)) { - return false; - } - - xhd = xhd->next; - yhd = yhd->next; - } - - if (yhd) { - return false; - } - } - return true; - break; - case cJSON_Object: - { - cJSON *xhd = x->head; - int child_count = 0; - while (xhd) { - child_count++; - //inefficient becase cjson sucks - cJSON *other_item = cJSON_slow_GetObjectItem(y, xhd->string); - - if (!other_item || !cJSON_Equal(xhd, other_item)) { - return false; - } - xhd = xhd->next; - } - - cJSON *yhd = y->head; - - while (yhd) { - child_count--; - yhd = yhd->next; - } - if (child_count != 0) { - return false; - } - - return true; - } - break; - default: - crash("Unreachable"); - break; - - } - crash("Unreachable"); -} - - diff --git a/src/cjson/cJSON.hpp b/src/cjson/cJSON.hpp deleted file mode 100644 index e1f2b1ddb3e..00000000000 --- a/src/cjson/cJSON.hpp +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -/* - Copyright (c) 2009 Dave Gamble - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -#ifndef CJSON_CJSON_HPP_ -#define CJSON_CJSON_HPP_ - -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - -/* cJSON Types: */ -#define cJSON_False 0 -#define cJSON_True 1 -#define cJSON_NULL 2 -#define cJSON_Number 3 -#define cJSON_String 4 -#define cJSON_Array 5 -#define cJSON_Object 6 - -#define cJSON_IsReference 256 - -/* The cJSON structure: */ -struct cJSON { - struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ - struct cJSON *head, *tail; /* An array or object item will have a head and tail pointer pointing to a chain of the items in the array/object. */ - - int type; /* The type of the item, as above. */ - - char *valuestring; /* The item's string, if type==cJSON_String */ - int valueint; /* The item's number, if type==cJSON_Number */ - double valuedouble; /* The item's number, if type==cJSON_Number */ - - char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ -}; - -/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ -extern cJSON *cJSON_Parse(const char *value); -/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ -extern char *cJSON_Print(cJSON *item); -/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ -extern char *cJSON_PrintUnformatted(cJSON *item); -/* Delete a cJSON entity and all subentities. */ -extern void cJSON_Delete(cJSON *c); - -/* Returns the number of items in an array (or object). */ -// WARNING: O(n) -extern int cJSON_slow_GetArraySize(const cJSON *array); -/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ -// WARNING: O(item) -extern cJSON *cJSON_slow_GetArrayItem(cJSON *array,int item); -/* Get item "string" from object. Case insensitive. */ -// WARNING: O(n) -extern cJSON *cJSON_slow_GetObjectItem(cJSON *object,const char *string); - -/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ -extern const char *cJSON_GetErrorPtr(); - -/* These calls create a cJSON item of the appropriate type. */ -extern cJSON *cJSON_CreateBlank(); -extern cJSON *cJSON_CreateNull(); -extern cJSON *cJSON_CreateTrue(); -extern cJSON *cJSON_CreateFalse(); -extern cJSON *cJSON_CreateBool(int b); -extern cJSON *cJSON_CreateNumber(double num); -extern cJSON *cJSON_CreateString(const char *string); -extern cJSON *cJSON_CreateStringN(const char *string, size_t size); -extern cJSON *cJSON_CreateArray(); -extern cJSON *cJSON_CreateObject(); - -/* These utilities create an Array of count items. */ -extern cJSON *cJSON_CreateIntArray(int *numbers,int count); -extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count); -extern cJSON *cJSON_CreateStringArray(const char **strings,int count); - -/* Append item to the specified array/object. */ -extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); -extern void cJSON_AddItemToObjectN(cJSON *object,const char *string,size_t string_size,cJSON *item); -/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ -extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); - -/* Remove/Detatch items from Arrays/Objects. */ -extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); -extern void cJSON_DeleteItemFromArray(cJSON *array,int which); -extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); -extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); - -/* Update array items. */ -extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); -extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); - -#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) -#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) -#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) -#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) -#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) - -/* Copy function. */ -extern cJSON *cJSON_DeepCopy(cJSON *target); - -extern bool cJSON_Equal(cJSON *x, cJSON *y); - -#ifdef __cplusplus -} -#endif - -#endif /* CJSON_CJSON_HPP_ */ - diff --git a/src/cjson/json.cc b/src/cjson/json.cc deleted file mode 100644 index 9727e59e8f2..00000000000 --- a/src/cjson/json.cc +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "cjson/json.hpp" - -#include -#include - -#include -#include - -#include "stl_utils.hpp" -#include "utils.hpp" - -std::string cJSON_print_lexicographic(const cJSON *json) { - std::string acc; - guarantee(json->type == cJSON_Number || json->type == cJSON_String); - if (json->type == cJSON_Number) { - acc += "N"; - - union { - double d; - int64_t u; - } packed; - rassert(sizeof(packed.d) == sizeof(packed.u)); - packed.d = json->valuedouble; - - // Mangle the value so that lexicographic ordering matches double ordering - if (packed.u & (1ULL << 63)) { - // If we have a negative double, flip all the bits. Flipping the - // highest bit causes the negative doubles to sort below the - // positive doubles (which will also have their highest bit - // flipped), and flipping all the other bits causes more negative - // doubles to sort below less negative doubles. - packed.u = ~packed.u; - } else { - // If we have a non-negative double, flip the highest bit so that it - // sorts higher than all the negative doubles (which had their - // highest bit flipped as well). - packed.u ^= (1ULL << 63); - } - - acc += strprintf("%.*" PRIx64, static_cast(sizeof(double) * 2), packed.u); - acc += strprintf("#%.20g", json->valuedouble); - } else { - guarantee(json->type == cJSON_String); - acc += "S"; - acc += json->valuestring; - } - return acc; -} - -scoped_cJSON_t::scoped_cJSON_t(cJSON *_val) - : val(_val) -{ } - -scoped_cJSON_t::~scoped_cJSON_t() { - if (val) { - cJSON_Delete(val); - } -} - -/* Render a cJSON entity to text for transfer/storage. */ -std::string scoped_cJSON_t::Print() const THROWS_NOTHING { - return cJSON_print_std_string(val); -} -/* Render a cJSON entity to text for transfer/storage without any formatting. */ -std::string scoped_cJSON_t::PrintUnformatted() const THROWS_NOTHING { - char *s = cJSON_PrintUnformatted(val); - guarantee(s); - std::string res(s); - free(s); - - return res; -} - -std::string scoped_cJSON_t::PrintLexicographic() const THROWS_NOTHING { - return cJSON_print_lexicographic(val); -} - -cJSON *scoped_cJSON_t::get() const { - return val; -} - -cJSON *scoped_cJSON_t::release() { - cJSON *tmp = val; - val = NULL; - return tmp; -} - -void scoped_cJSON_t::reset(cJSON *v) { - if (val) { - cJSON_Delete(val); - } - val = v; -} - -json_iterator_t::json_iterator_t(cJSON *target) { - node = target->head; -} - -cJSON *json_iterator_t::next() { - cJSON *res = node; - if (node) { - node = node->next; - } - return res; -} - -json_object_iterator_t::json_object_iterator_t(cJSON *target) - : json_iterator_t(target) -{ - guarantee(target); - guarantee(target->type == cJSON_Object); -} - -json_array_iterator_t::json_array_iterator_t(cJSON *target) - : json_iterator_t(target) -{ - guarantee(target); - guarantee(target->type == cJSON_Array); -} - -std::string cJSON_print_std_string(cJSON *json) THROWS_NOTHING { - guarantee(json); - char *s = cJSON_Print(json); - guarantee(s); - std::string res(s); - free(s); - - return res; -} diff --git a/src/cjson/json.hpp b/src/cjson/json.hpp deleted file mode 100644 index 2447fb1a392..00000000000 --- a/src/cjson/json.hpp +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CJSON_JSON_HPP_ -#define CJSON_JSON_HPP_ - -#include -#include -#include - -#include "cjson/cJSON.hpp" -#include "containers/archive/archive.hpp" - -std::string cJSON_print_lexicographic(const cJSON *json); -std::string cJSON_print_std_string(cJSON *json) THROWS_NOTHING; - -class scoped_cJSON_t { -private: - cJSON *val; - - DISABLE_COPYING(scoped_cJSON_t); - - void swap(scoped_cJSON_t &other) { - cJSON *tmp = val; - val = other.val; - other.val = tmp; - } - -public: - scoped_cJSON_t() : val(NULL) { } - explicit scoped_cJSON_t(cJSON *v); - - scoped_cJSON_t(scoped_cJSON_t &&movee) : val(movee.val) { - movee.val = NULL; - } - - scoped_cJSON_t &operator=(scoped_cJSON_t &&movee) { - scoped_cJSON_t tmp(std::move(movee)); - swap(tmp); - return *this; - } - - ~scoped_cJSON_t(); - cJSON *get() const; - cJSON *release(); - void reset(cJSON *v); - - int type() const { - return val->type; - } - - /* Render a cJSON entity to text for transfer/storage. */ - std::string Print() const THROWS_NOTHING; - /* Render a cJSON entity to text for transfer/storage without any formatting. */ - std::string PrintUnformatted() const THROWS_NOTHING; - /* Render a cJSON entitiy to text for lexicographic sorting. MUST - be a number or a string. */ - std::string PrintLexicographic() const THROWS_NOTHING; - - /* Append item to the specified array/object. */ - void AddItemToArray(cJSON *item) { - guarantee(item); - return cJSON_AddItemToArray(val, item); - } - void AddItemToObject(const char *string, cJSON *item) { - guarantee(string); - guarantee(item); - return cJSON_AddItemToObject(val, string, item); - } - void AddItemToObject(const char *string, size_t string_size, cJSON *item) { - guarantee(string); - guarantee(item); - return cJSON_AddItemToObjectN(val, string, string_size, item); - } - - /* Remove/Detatch items from Arrays/Objects. Returns NULL if unsuccessful. */ - cJSON* DetachItemFromArray(int which) { - guarantee(which >= 0); - return cJSON_DetachItemFromArray(val, which); - } - void DeleteItemFromArray(int which) { - guarantee(which >= 0); - cJSON_DeleteItemFromArray(val, which); - } - cJSON* DetachItemFromObject(const char *string) { - guarantee(string); - return cJSON_DetachItemFromObject(val, string); - } - void DeleteItemFromObject(const char *string) { - guarantee(string); - cJSON_DeleteItemFromObject(val, string); - } - - /* Update array items. */ - void ReplaceItemInArray(int which, cJSON *newitem) { - guarantee(which >= 0); - guarantee(newitem); - return cJSON_ReplaceItemInArray(val, which, newitem); - } - void ReplaceItemInObject(const char *string, cJSON *newitem) { - guarantee(string); - guarantee(newitem); - return cJSON_ReplaceItemInObject(val, string, newitem); - } - - /* Copy function. */ - cJSON *DeepCopy() const { - cJSON *retval = cJSON_DeepCopy(val); - guarantee(retval); - return retval; - } -}; - -class json_iterator_t { -public: - explicit json_iterator_t(cJSON *target); - - cJSON *next(); -private: - cJSON *node; -}; - -class json_object_iterator_t : public json_iterator_t { -public: - explicit json_object_iterator_t(cJSON *target); -}; - -class json_array_iterator_t : public json_iterator_t { -public: - explicit json_array_iterator_t(cJSON *target); -}; - - -#endif /* CJSON_JSON_HPP_ */ - diff --git a/src/client_protocol/client_server_error.hpp b/src/client_protocol/client_server_error.hpp deleted file mode 100644 index 5e1a95c0335..00000000000 --- a/src/client_protocol/client_server_error.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CLIENT_PROTOCOL_ERROR_HPP -#define CLIENT_PROTOCOL_ERROR_HPP - -#include - -namespace client_protocol { - -class client_server_error_t : public std::runtime_error { -public: - client_server_error_t(uint32_t error_code, std::string const &_what) - : std::runtime_error(_what), - m_error_code(error_code) { - } - - uint32_t get_error_code() const { - return m_error_code; - } - -private: - uint32_t m_error_code; -}; - -} // namespace client_protocol - -#endif // CLIENT_PROTOCOL_ERROR_HPP diff --git a/src/client_protocol/json.cc b/src/client_protocol/json.cc deleted file mode 100644 index d38f5b29167..00000000000 --- a/src/client_protocol/json.cc +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "client_protocol/json.hpp" - -#include "arch/io/network.hpp" -#include "arch/timing.hpp" -#include "client_protocol/protocols.hpp" -#include "concurrency/pmap.hpp" -#include "containers/scoped.hpp" -#include "rapidjson/document.h" -#include "rapidjson/stringbuffer.h" -#include "rapidjson/writer.h" -#include "rdb_protocol/rdb_backtrace.hpp" -#include "rdb_protocol/ql2proto.hpp" -#include "rdb_protocol/query_params.hpp" -#include "rdb_protocol/response.hpp" -#include "rdb_protocol/term_storage.hpp" -#include "utils.hpp" - -scoped_ptr_t json_protocol_t::parse_query_from_buffer( - scoped_array_t &&buffer, size_t offset, - ql::query_cache_t *query_cache, int64_t token, - ql::response_t *error_out) { - rapidjson::Document doc; - doc.ParseInsitu(buffer.data() + offset); - - scoped_ptr_t res; - if (!doc.HasParseError()) { - try { - res = make_scoped(token, query_cache, - scoped_ptr_t( - new ql::json_term_storage_t(std::move(buffer), std::move(doc)))); - } catch (const ql::bt_exc_t &ex) { - error_out->fill_error(Response::CLIENT_ERROR, - ex.error_type, - strprintf("Server could not parse query: %s", - ex.message.c_str()), - ex.bt_datum); - } - } else { - error_out->fill_error(Response::CLIENT_ERROR, - Response::RESOURCE_LIMIT, - wire_protocol_t::unparseable_query_message, - ql::backtrace_registry_t::EMPTY_BACKTRACE); - } - return res; -} - -scoped_ptr_t json_protocol_t::parse_query( - tcp_conn_t *conn, - signal_t *interruptor, - ql::query_cache_t *query_cache) { - int64_t token; - uint32_t size; - conn->read_buffered(&token, sizeof(token), interruptor); - conn->read_buffered(&size, sizeof(size), interruptor); - ql::response_t error; - -#ifdef __s390x__ - token = __builtin_bswap64(token); - size = __builtin_bswap32(size); -#endif - - if (size >= wire_protocol_t::TOO_LARGE_QUERY_SIZE) { - error.fill_error(Response::CLIENT_ERROR, - Response::RESOURCE_LIMIT, - wire_protocol_t::too_large_query_message(size), - ql::backtrace_registry_t::EMPTY_BACKTRACE); - - if (size < wire_protocol_t::HARD_LIMIT_TOO_LARGE_QUERY_SIZE) { - // Ignore all the extra data that the client is trying to send. - // within reason. This is so it doesn't look like a broken pipe error. - - signal_timer_t read_timeout_interruptor{wire_protocol_t::TOO_LONG_QUERY_TIME}; - wait_any_t pop_interruptor(interruptor, &read_timeout_interruptor); - conn->pop(size, &pop_interruptor); - } - - send_response(&error, token, conn, interruptor); - throw tcp_conn_read_closed_exc_t(); - } - - scoped_array_t data(size + 1); - // It's *usually* more efficient to do an un-buffered read here. The client is - // usually not going to group multiple queries into the same network package - // (especially not with tcp_nodelay set), and using the non-buffered `read` can - // avoid an extra copy. - conn->read(data.data(), size, interruptor); - data[size] = 0; // Null terminate the string, which the json parser requires - - scoped_ptr_t res = - parse_query_from_buffer(std::move(data), 0, query_cache, token, &error); - - if (!res.has()) { - send_response(&error, token, conn, interruptor); - } - return res; -} - -void write_response_internal(ql::response_t *response, - rapidjson::StringBuffer *buffer_out, - bool throw_errors) { - rapidjson::Writer writer(*buffer_out); - size_t start_offset = buffer_out->GetSize(); - - try { - writer.StartObject(); - writer.Key("t", 1); - writer.Int(response->type()); - if (response->type() == Response::RUNTIME_ERROR && - response->error_type()) { - writer.Key("e", 1); - writer.Int(*response->error_type()); - } - - writer.Key("r", 1); - writer.StartArray(); - const size_t PARALLELIZATION_THRESHOLD = 500; - if (response->data().size() > PARALLELIZATION_THRESHOLD) { - int64_t num_threads = std::min(16, get_num_db_threads()); - int32_t thread_offset = get_thread_id().threadnum; - std::vector buffers(num_threads); - - size_t per_thread = response->data().size() / num_threads; - pmap(num_threads, [&](int64_t m) { - int32_t target_thread = - (thread_offset + static_cast(m)) % get_num_db_threads(); - on_thread_t rethreader((threadnum_t(target_thread))); - rapidjson::StringBuffer *thread_buffer = &buffers[m]; - rapidjson::Writer - thread_writer(*thread_buffer); - - thread_writer.StartArray(); - size_t offset = per_thread * m; - size_t end = (m == num_threads - 1) ? - response->data().size() : (per_thread * (m + 1)); - - for (size_t i = offset; i < end; ++i) { - const size_t YIELD_INTERVAL = 2000; - if ((i + 1) % YIELD_INTERVAL == 0) { - coro_t::yield(); - } - response->data()[i].write_json(&thread_writer); - } - - thread_writer.EndArray(); - }); - - for (const auto &buffer : buffers) { - writer.SpliceArray(buffer); - } - } else { - for (const auto &item : response->data()) { - item.write_json(&writer); - } - } - writer.EndArray(); - if (response->backtrace()) { - writer.Key("b", 1); - response->backtrace()->write_json(&writer); - } - if (response->profile()) { - writer.Key("p", 1); - response->profile()->write_json(&writer); - } - if (response->type() == Response::SUCCESS_PARTIAL || - response->type() == Response::SUCCESS_SEQUENCE) { - writer.Key("n", 1); - writer.StartArray(); - for (const auto ¬e : response->notes()) { - writer.Int(note); - } - writer.EndArray(); - } - writer.EndObject(); - guarantee(writer.IsComplete()); - } catch (const ql::base_exc_t &ex) { - buffer_out->Pop(buffer_out->GetSize() - start_offset); - response->fill_error(Response::RUNTIME_ERROR, Response::QUERY_LOGIC, - ex.what(), ql::backtrace_registry_t::EMPTY_BACKTRACE); - write_response_internal(response, buffer_out, true); - } catch (const std::exception &ex) { - if (throw_errors) { - throw; - } - - buffer_out->Pop(buffer_out->GetSize() - start_offset); - response->fill_error(Response::RUNTIME_ERROR, Response::INTERNAL, - strprintf("Internal error in json_protocol_t::write: %s", ex.what()), - ql::backtrace_registry_t::EMPTY_BACKTRACE); - write_response_internal(response, buffer_out, true); - } -} - -// Small wrapper - in debug mode we would rather crash than send the error back -void json_protocol_t::write_response_to_buffer(ql::response_t *response, - rapidjson::StringBuffer *buffer_out) { -#ifdef NDEBUG - write_response_internal(response, buffer_out, false); -#else - write_response_internal(response, buffer_out, true); -#endif -} - -void json_protocol_t::send_response(ql::response_t *response, - int64_t token, - tcp_conn_t *conn, - signal_t *interruptor) { - uint32_t data_size; // filled in below - const size_t prefix_size = sizeof(token) + sizeof(data_size); - - // Reserve space for the token and the size - rapidjson::StringBuffer buffer; - buffer.Push(prefix_size); - - write_response_to_buffer(response, &buffer); - int64_t payload_size = buffer.GetSize() - prefix_size; - guarantee(payload_size > 0); - - static_assert(std::is_same::value, - "The largest response must fit in 32 bits."); - - if (payload_size >= wire_protocol_t::TOO_LARGE_RESPONSE_SIZE) { - response->fill_error(Response::RUNTIME_ERROR, - Response::RESOURCE_LIMIT, - wire_protocol_t::too_large_response_message(payload_size), - ql::backtrace_registry_t::EMPTY_BACKTRACE); - send_response(response, token, conn, interruptor); - return; - } - - // Fill in the token and size - char *mutable_buffer = buffer.GetMutableBuffer(); -#ifdef __s390x__ - token = __builtin_bswap64(token); -#endif - for (size_t i = 0; i < sizeof(token); ++i) { - mutable_buffer[i] = reinterpret_cast(&token)[i]; - } - - data_size = static_cast(payload_size); -#ifdef __s390x__ - data_size = __builtin_bswap32(data_size); -#endif - for (size_t i = 0; i < sizeof(data_size); ++i) { - mutable_buffer[i + sizeof(token)] = - reinterpret_cast(&data_size)[i]; - } - - conn->write(buffer.GetString(), buffer.GetSize(), interruptor); -} - diff --git a/src/client_protocol/json.hpp b/src/client_protocol/json.hpp deleted file mode 100644 index 63e356de9b1..00000000000 --- a/src/client_protocol/json.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLIENT_PROTOCOL_JSON_HPP_ -#define CLIENT_PROTOCOL_JSON_HPP_ - -#include - -#include "arch/types.hpp" -#include "containers/scoped.hpp" -#include "rapidjson/stringbuffer.h" - -class signal_t; - -namespace ql { -class response_t; -class query_cache_t; -class query_params_t; -} - -// This is a class rather than a namespace so we can templatize the connection loop on -// the protocol type. -class json_protocol_t { -public: - static scoped_ptr_t parse_query_from_buffer( - scoped_array_t &&mutable_buffer, size_t offset, - ql::query_cache_t *query_cache, int64_t token, - ql::response_t *error_out); - - static scoped_ptr_t parse_query(tcp_conn_t *conn, - signal_t *interruptor, - ql::query_cache_t *query_cache); - - // Used by the HTTP ReQL server to write the query response into the HTTP response - static void write_response_to_buffer(ql::response_t *response, - rapidjson::StringBuffer *buffer_out); - - static void send_response(ql::response_t *response, - int64_t token, - tcp_conn_t *conn, - signal_t *interruptor); -}; - -#endif // CLIENT_PROTOCOL_JSON_HPP_ diff --git a/src/client_protocol/protocols.cc b/src/client_protocol/protocols.cc deleted file mode 100644 index 0ab42c0fc6f..00000000000 --- a/src/client_protocol/protocols.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "client_protocol/protocols.hpp" - -#include - -#include "utils.hpp" - -const uint32_t wire_protocol_t::HARD_LIMIT_TOO_LARGE_QUERY_SIZE = GIGABYTE; -const uint32_t wire_protocol_t::TOO_LONG_QUERY_TIME = 5*60*1000; // ms -const uint32_t wire_protocol_t::TOO_LARGE_QUERY_SIZE = 128 * MEGABYTE; -const uint32_t wire_protocol_t::TOO_LARGE_RESPONSE_SIZE = - std::numeric_limits::max(); - -const std::string wire_protocol_t::unparseable_query_message = - "Client is buggy (failed to deserialize query)."; - -std::string wire_protocol_t::too_large_query_message(uint32_t size) { - return strprintf("Query size (%" PRIu32 ") greater than maximum (%" PRIu32 ").", - size, TOO_LARGE_QUERY_SIZE - 1); -} - -std::string wire_protocol_t::too_large_response_message(size_t size) { - return strprintf("Response size (%zu) greater than maximum (%" PRIu32 ").", - size, TOO_LARGE_RESPONSE_SIZE - 1); -} diff --git a/src/client_protocol/protocols.hpp b/src/client_protocol/protocols.hpp deleted file mode 100644 index c9be2876e25..00000000000 --- a/src/client_protocol/protocols.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLIENT_PROTOCOL_PROTOCOLS_HPP_ -#define CLIENT_PROTOCOL_PROTOCOLS_HPP_ - -#include - -#include - -// Include all available wire protocols -#include "client_protocol/json.hpp" - -// Contains common declarations used by all wire protocols, this is a class rather than -// a namespace so we don't have to extern stuff. -class wire_protocol_t { -public: - static const uint32_t HARD_LIMIT_TOO_LARGE_QUERY_SIZE; - static const uint32_t TOO_LONG_QUERY_TIME; - static const uint32_t TOO_LARGE_QUERY_SIZE; - static const uint32_t TOO_LARGE_RESPONSE_SIZE; - - static const std::string unparseable_query_message; - static std::string too_large_query_message(uint32_t size); - static std::string too_large_response_message(size_t size); -}; - -#endif // CLIENT_PROTOCOL_PROTOCOLS_HPP_ diff --git a/src/client_protocol/server.cc b/src/client_protocol/server.cc deleted file mode 100644 index f2d5d4001fc..00000000000 --- a/src/client_protocol/server.cc +++ /dev/null @@ -1,771 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. - -// We need to include `openssl/evp.h` first, since it declares a function with the -// name `final`. -// Because of missing support for the `final` annotation in older GCC versions, -// we redefine final to the empty string in `errors.hpp`. So we must make -// sure that we haven't included `errors.hpp` by the time we include `evp.h`. -#include // NOLINT(build/include_order) - -#include "client_protocol/server.hpp" // NOLINT(build/include_order) - -#include // NOLINT(build/include_order) - -#include // NOLINT(build/include_order) -#include // NOLINT(build/include_order) -#include // NOLINT(build/include_order) -#include // NOLINT(build/include_order) - -#include "arch/arch.hpp" -#include "arch/io/network.hpp" -#include "client_protocol/client_server_error.hpp" -#include "client_protocol/protocols.hpp" -#include "clustering/administration/auth/authentication_error.hpp" -#include "clustering/administration/auth/plaintext_authenticator.hpp" -#include "clustering/administration/auth/scram_authenticator.hpp" -#include "clustering/administration/auth/username.hpp" -#include "clustering/administration/metadata.hpp" -#include "concurrency/coro_pool.hpp" -#include "concurrency/cross_thread_signal.hpp" -#include "concurrency/queue/limited_fifo.hpp" -#include "crypto/error.hpp" -#include "perfmon/perfmon.hpp" -#include "rapidjson/document.h" -#include "rapidjson/stringbuffer.h" -#include "rapidjson/writer.h" -#include "rdb_protocol/rdb_backtrace.hpp" -#include "rdb_protocol/base64.hpp" -#include "rdb_protocol/datum_json.hpp" -#include "rdb_protocol/env.hpp" -#include "rdb_protocol/ql2proto.hpp" -#include "rdb_protocol/query_server.hpp" -#include "rdb_protocol/query_cache.hpp" -#include "rdb_protocol/response.hpp" -#include "rpc/semilattice/view.hpp" -#include "time.hpp" - -http_conn_cache_t::http_conn_t::http_conn_t(rdb_context_t *rdb_ctx, - ip_and_port_t client_addr_port) : - last_accessed(time(nullptr)), - // We always return empty normal batches after the timeout for HTTP - // connections; I think we have to do this to keep the conn cache - // from timing out. - query_cache( - new ql::query_cache_t( - rdb_ctx, - client_addr_port, - ql::return_empty_normal_batches_t::YES, - auth::user_context_t(auth::username_t("admin")))), - counter(&rdb_ctx->stats.client_connections) { } - -ql::query_cache_t *http_conn_cache_t::http_conn_t::get_query_cache() { - last_accessed = time(nullptr); - return query_cache.get(); -} - -signal_t *http_conn_cache_t::http_conn_t::get_interruptor() { - return &interruptor; -} - -void http_conn_cache_t::http_conn_t::pulse() { - rassert(!interruptor.is_pulsed()); - interruptor.pulse(); -} - -time_t http_conn_cache_t::http_conn_t::last_accessed_time() const { - return last_accessed; -} - -http_conn_cache_t::http_conn_cache_t(uint32_t _http_timeout_sec) : - http_timeout_timer(TIMER_RESOLUTION_MS, this), - http_timeout_sec(_http_timeout_sec) { - - // Seed the random number generator from a true random source. - // Note1: On some platforms std::random_device might not actually be - // non-deterministic, and it seems that there is no reliable way to tell. - // On major platforms it should be fine. - // Note2: std::random_device() might block for a while. Since we only create - // http_conn_cache_t once at startup, that should be fine. But it's something - // to keep in mind. - - // Seed with an amount of bits equal to the state size of key_generator. - static_assert(std::mt19937::word_size == 32, - "std::mt19937's word size doesn't match what we expected."); - std::array seed_data; - std::random_device rd; - for (size_t i = 0; i < seed_data.size(); ++i) { - seed_data[i] = rd(); - } - std::seed_seq seed_seq(seed_data.begin(), seed_data.end()); - key_generator.seed(seed_seq); -} - -http_conn_cache_t::~http_conn_cache_t() { - for (auto &pair : cache) pair.second->pulse(); -} - -std::string http_conn_cache_t::expired_error_message() const { - return strprintf("HTTP ReQL query timed out after %" PRIu32 " seconds.", - http_timeout_sec); -} - -bool http_conn_cache_t::is_expired(const http_conn_t &conn) const { - return difftime(time(nullptr), conn.last_accessed_time()) > http_timeout_sec; -} - -counted_t http_conn_cache_t::find( - const conn_key_t &key) { - assert_thread(); - auto conn_it = cache.find(key); - if (conn_it == cache.end()) return counted_t(); - return conn_it->second; -} - -http_conn_cache_t::conn_key_t http_conn_cache_t::create( - rdb_context_t *rdb_ctx, - ip_and_port_t client_addr_port) { - assert_thread(); - // Generate a 128 bit random key to avoid XSS attacks where someone - // could run queries by guessing the connection ID. - // The same origin policy of browsers will stop attackers from seeing - // the response of the connection setup, so the attacker will have no chance - // of getting a valid connection ID. - uint32_t key_buf[4]; - for(size_t i = 0; i < 4; ++i) { - key_buf[i] = key_generator(); - } - conn_key_t key = encode_base64(reinterpret_cast(key_buf), - sizeof(key_buf)); - - cache.insert( - std::make_pair(key, make_counted(rdb_ctx, client_addr_port))); - return key; -} - -void http_conn_cache_t::erase(const conn_key_t &key) { - assert_thread(); - auto it = cache.find(key); - if (it != cache.end()) { - it->second->pulse(); - cache.erase(it); - } -} - -void http_conn_cache_t::on_ring() { - assert_thread(); - for (auto it = cache.begin(); it != cache.end();) { - auto tmp = it++; - if (is_expired(*tmp->second)) { - // We go through some rigmarole to make sure we erase from the - // cache immediately and call the possibly-blocking destructor - // in a separate coroutine to satisfy the - // `ASSERT_FINITE_CORO_WAITING` in `call_ringer` in - // `arch/timing.cc`. - counted_t conn(std::move(tmp->second)); - conn->pulse(); - cache.erase(tmp); - coro_t::spawn_now_dangerously( - [&conn]() { - counted_t conn2; - conn2.swap(conn); - }); - guarantee(!conn); - } - } -} - -size_t http_conn_cache_t::sha_hasher_t::operator()(const conn_key_t &x) const { - EVP_MD_CTX *c = EVP_MD_CTX_create(); - EVP_DigestInit(c, EVP_sha256()); - EVP_DigestUpdate(c, x.data(), x.size()); - unsigned char digest[EVP_MAX_MD_SIZE]; - unsigned int digest_size = 0; - EVP_DigestFinal(c, digest, &digest_size); - EVP_MD_CTX_destroy(c); - rassert(digest_size >= sizeof(size_t)); - size_t res = 0; - memcpy(&res, digest, std::min(sizeof(size_t), static_cast(digest_size))); - return res; -} - -query_server_t::query_server_t(rdb_context_t *_rdb_ctx, - const std::set &local_addresses, - int port, - query_handler_t *_handler, - uint32_t http_timeout_sec, - tls_ctx_t *_tls_ctx) : - tls_ctx(_tls_ctx), - rdb_ctx(_rdb_ctx), - handler(_handler), - http_conn_cache(http_timeout_sec), - next_thread(0) { - rassert(rdb_ctx != nullptr); - try { - tcp_listener.init(new tcp_listener_t(local_addresses, port, - std::bind(&query_server_t::handle_conn, - this, ph::_1, auto_drainer_t::lock_t(&drainer)))); - } catch (const address_in_use_exc_t &ex) { - throw address_in_use_exc_t( - strprintf("Could not bind to RDB protocol port: %s", ex.what())); - } -} - -query_server_t::~query_server_t() { } - -int query_server_t::get_port() const { - return tcp_listener->get_port(); -} - -void write_datum(tcp_conn_t *connection, ql::datum_t datum, signal_t *interruptor) { - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - datum.write_json(&writer); - buffer.Put('\0'); - connection->write(buffer.GetString(), buffer.GetSize(), interruptor); -} - -ql::datum_t read_datum(tcp_conn_t *connection, signal_t *interruptor) { - std::array buffer; - - std::size_t offset = 0; - do { - connection->read_buffered(buffer.data() + offset, 1, interruptor); - } while (buffer[offset++] != '\0' && offset < buffer.max_size()); - if (offset == 2048) { - throw client_protocol::client_server_error_t(7, "Limited read buffer size."); - } - - rapidjson::Document document; - document.Parse(buffer.data()); - if (document.HasParseError()) { - throw client_protocol::client_server_error_t(8, "Invalid JSON object."); - } - - try { - return ql::to_datum( - document, - ql::configured_limits_t::unlimited, - reql_version_t::LATEST); - } catch (ql::base_exc_t const &) { - throw client_protocol::client_server_error_t(9, "Failed to convert JSON to datum."); - } -} - -void query_server_t::handle_conn(const scoped_ptr_t &nconn, - auto_drainer_t::lock_t keepalive) { - threadnum_t chosen_thread = threadnum_t(next_thread); - next_thread = (next_thread + 1) % get_num_db_threads(); - - cross_thread_signal_t ct_keepalive(keepalive.get_drain_signal(), chosen_thread); - on_thread_t rethreader(chosen_thread); - - scoped_ptr_t conn; - - try { - nconn->make_server_connection(tls_ctx, &conn, &ct_keepalive); - } catch (const interrupted_exc_t &) { - // TLS handshake was interrupted. - return; - } catch (const crypto::openssl_error_t &err) { - // TLS handshake failed. - logWRN("Driver connection TLS handshake failed: %s", err.what()); - return; - } - - uint8_t version = 0; - std::unique_ptr authenticator; - uint32_t error_code = 0; - std::string error_message; - try { - conn->enable_keepalive(); - - int32_t client_magic_number; - conn->read_buffered( - &client_magic_number, sizeof(client_magic_number), &ct_keepalive); -#ifdef __s390x__ - client_magic_number = __builtin_bswap32(client_magic_number); -#endif - - switch (client_magic_number) { - case VersionDummy::V0_1: - version = 1; - break; - case VersionDummy::V0_2: - version = 2; - break; - case VersionDummy::V0_3: - version = 3; - break; - case VersionDummy::V0_4: - version = 4; - break; - case VersionDummy::V1_0: - version = 10; - break; - default: - throw client_protocol::client_server_error_t( - -1, - "Received an unsupported protocol version. This port is for " - "RethinkDB queries. Does your client driver version not match the " - "server?"); - } - if (version < 3) { - // `V0_1` and `V0_2` only supported the PROTOBUF protocol - throw client_protocol::client_server_error_t( - -1, "The PROTOBUF client protocol is no longer supported"); - } else if (version < 10) { - // We'll get std::make_unique in C++14 - authenticator.reset( - new auth::plaintext_authenticator_t(rdb_ctx->get_auth_watchable())); - - uint32_t auth_key_size; - conn->read_buffered(&auth_key_size, sizeof(uint32_t), &ct_keepalive); -#ifdef __s390x__ - auth_key_size = __builtin_bswap32(auth_key_size); -#endif - if (auth_key_size > 2048) { - throw client_protocol::client_server_error_t( - -1, "Client provided an authorization key that is too long."); - } - - scoped_array_t auth_key_buffer(auth_key_size); - conn->read_buffered(auth_key_buffer.data(), auth_key_size, &ct_keepalive); - - try { - authenticator->next_message( - std::string(auth_key_buffer.data(), auth_key_size)); - } catch (auth::authentication_error_t const &) { - // Note we must not change this message for backwards compatibility - throw client_protocol::client_server_error_t( - -1, "Incorrect authorization key."); - } - - int32_t wire_protocol; - conn->read_buffered(&wire_protocol, sizeof(wire_protocol), &ct_keepalive); -#ifdef __s390x__ - wire_protocol = __builtin_bswap32(wire_protocol); -#endif - switch (wire_protocol) { - case VersionDummy::JSON: - break; - case VersionDummy::PROTOBUF: - throw client_protocol::client_server_error_t( - -1, "The PROTOBUF client protocol is no longer supported"); - break; - default: - throw client_protocol::client_server_error_t( - -1, - strprintf( - "Unrecognized protocol specified: '%d'", wire_protocol)); - } - - char const *success_msg = "SUCCESS"; - conn->write(success_msg, strlen(success_msg) + 1, &ct_keepalive); - } else { - authenticator.reset( - new auth::scram_authenticator_t(rdb_ctx->get_auth_watchable())); - - { - ql::datum_object_builder_t datum_object_builder; - datum_object_builder.overwrite("success", ql::datum_t::boolean(true)); - datum_object_builder.overwrite("max_protocol_version", ql::datum_t(0.0)); - datum_object_builder.overwrite("min_protocol_version", ql::datum_t(0.0)); - datum_object_builder.overwrite( - "server_version", ql::datum_t(RETHINKDB_VERSION)); - - write_datum( - conn.get(), - std::move(datum_object_builder).to_datum(), - &ct_keepalive); - } - - { - ql::datum_t datum = read_datum(conn.get(), &ct_keepalive); - - ql::datum_t protocol_version = - datum.get_field("protocol_version", ql::NOTHROW); - if (protocol_version.get_type() != ql::datum_t::R_NUM) { - throw client_protocol::client_server_error_t( - 1, "Expected a number for `protocol_version`."); - } - if (protocol_version.as_num() != 0.0) { - throw client_protocol::client_server_error_t( - 2, "Unsupported `protocol_version`."); - } - - ql::datum_t authentication_method = - datum.get_field("authentication_method", ql::NOTHROW); - if (authentication_method.get_type() != ql::datum_t::R_STR) { - throw client_protocol::client_server_error_t( - 3, "Expected a string for `authentication_method`."); - } - if (authentication_method.as_str() != "SCRAM-SHA-256") { - throw client_protocol::client_server_error_t( - 4, "Unsupported `authentication_method`."); - } - - ql::datum_t authentication = - datum.get_field("authentication", ql::NOTHROW); - if (authentication.get_type() != ql::datum_t::R_STR) { - throw client_protocol::client_server_error_t( - 5, "Expected a string for `authentication`."); - } - - ql::datum_object_builder_t datum_object_builder; - datum_object_builder.overwrite("success", ql::datum_t::boolean(true)); - datum_object_builder.overwrite( - "authentication", - ql::datum_t( - authenticator->next_message(authentication.as_str().to_std()))); - - write_datum( - conn.get(), - std::move(datum_object_builder).to_datum(), - &ct_keepalive); - } - - { - ql::datum_t datum = read_datum(conn.get(), &ct_keepalive); - - ql::datum_t authentication = - datum.get_field("authentication", ql::NOTHROW); - if (authentication.get_type() != ql::datum_t::R_STR) { - throw client_protocol::client_server_error_t( - 5, "Expected a string for `authentication`."); - } - - ql::datum_object_builder_t datum_object_builder; - datum_object_builder.overwrite("success", ql::datum_t::boolean(true)); - datum_object_builder.overwrite( - "authentication", - ql::datum_t( - authenticator->next_message(authentication.as_str().to_std()))); - - write_datum( - conn.get(), - std::move(datum_object_builder).to_datum(), - &ct_keepalive); - } - } - - ip_and_port_t client_addr_port(ip_address_t::any(AF_INET), port_t(0)); - UNUSED bool peer_res = conn->getpeername(&client_addr_port); - - guarantee(authenticator != nullptr); - ql::query_cache_t query_cache( - rdb_ctx, - client_addr_port, - (version < 4) - ? ql::return_empty_normal_batches_t::YES - : ql::return_empty_normal_batches_t::NO, - auth::user_context_t(authenticator->get_authenticated_username())); - - connection_loop( - conn.get(), - (version < 4) - ? 1 - : 1024, - &query_cache, - &ct_keepalive); - } catch (client_protocol::client_server_error_t const &error) { - // We can't write the response here due to coroutine switching inside an - // exception handler - error_code = error.get_error_code(); - error_message = error.what(); - } catch (interrupted_exc_t const &) { - // If we have been interrupted, we can't write a message to the client, as that - // may block (and we would just be interrupted again anyway), just close. - } catch (auth::authentication_error_t const &error) { - // Note these have error codes 10 to 20 - error_code = error.get_error_code(); - error_message = error.what(); - } catch (crypto::error_t const &error) { - error_code = 21; - error_message = error.what(); - } catch (crypto::openssl_error_t const &error) { - error_code = 22; - error_message = error.code().message(); - } catch (const tcp_conn_read_closed_exc_t &) { - } catch (const tcp_conn_write_closed_exc_t &) { - } catch (const std::exception &ex) { - logERR("Unexpected exception in client handler: %s", ex.what()); - } - - if (!error_message.empty()) { - try { - if (version < 10) { - std::string error = "ERROR: " + error_message + "\n"; - conn->write(error.c_str(), error.length() + 1, &ct_keepalive); - } else { - ql::datum_object_builder_t datum_object_builder; - datum_object_builder.overwrite("success", ql::datum_t::boolean(false)); - datum_object_builder.overwrite("error", ql::datum_t(error_message)); - datum_object_builder.overwrite( - "error_code", ql::datum_t(static_cast(error_code))); - - write_datum( - conn.get(), - std::move(datum_object_builder).to_datum(), - &ct_keepalive); - } - - conn->shutdown_write(); - } catch (client_protocol::client_server_error_t const &error) { - } catch (interrupted_exc_t const &) { - } catch (const tcp_conn_write_closed_exc_t &) { - // Writing the error message failed, there is nothing we can do at this point - } - } -} - -void query_server_t::make_error_response(bool is_draining, - const tcp_conn_t &conn, - const std::string &err_str, - ql::response_t *response_out) { - // Best guess at the error that occurred. - if (!conn.is_write_open()) { - // The other side closed it's socket - it won't get this message - response_out->fill_error(Response::RUNTIME_ERROR, - Response::OP_INDETERMINATE, - "Client closed the connection.", - ql::backtrace_registry_t::EMPTY_BACKTRACE); - } else if (is_draining) { - // The query_server_t is being destroyed so this won't actually be written - response_out->fill_error(Response::RUNTIME_ERROR, - Response::OP_INDETERMINATE, - "Server is shutting down.", - ql::backtrace_registry_t::EMPTY_BACKTRACE); - } else { - // Sort of a catch-all - there could be other reasons for this - response_out->fill_error( - Response::RUNTIME_ERROR, Response::OP_INDETERMINATE, - strprintf("Fatal error on another query: %s", err_str.c_str()), - ql::backtrace_registry_t::EMPTY_BACKTRACE); - } -} - -template -void save_exception(std::exception_ptr *err, - std::string *err_str, - cond_t *abort, - Callable &&fn) { - try { - fn(); - } catch (const std::exception &ex) { - if (!(*err)) { - *err = std::current_exception(); - err_str->assign(ex.what()); - } - abort->pulse_if_not_already_pulsed(); - } -} - -template -void query_server_t::connection_loop(tcp_conn_t *conn, - size_t max_concurrent_queries, - ql::query_cache_t *query_cache, - signal_t *drain_signal) { - std::exception_ptr err; - std::string err_str; - cond_t abort; - new_mutex_t send_mutex; - scoped_perfmon_counter_t connection_counter(&rdb_ctx->stats.client_connections); - -#ifdef __linux - linux_event_watcher_t *ew = conn->get_event_watcher(); - linux_event_watcher_t::watch_t conn_interrupted(ew, poll_event_rdhup); - wait_any_t interruptor(drain_signal, &abort, &conn_interrupted); -#else - wait_any_t interruptor(drain_signal, &abort); -#endif // __linux - - new_semaphore_t sem(max_concurrent_queries); - auto_drainer_t coro_drainer; - while (!err) { - scoped_ptr_t outer_query = - protocol_t::parse_query(conn, &interruptor, query_cache); - if (outer_query.has()) { - outer_query->throttler.init(&sem, 1); - wait_interruptible(outer_query->throttler.acquisition_signal(), - &interruptor); - coro_t::spawn_now_dangerously([&]() { - // We grab this right away while it's still valid. - scoped_ptr_t query = std::move(outer_query); - // Since we `spawn_now_dangerously` it's always safe to acquire this. - auto_drainer_t::lock_t coro_drainer_lock(&coro_drainer); - wait_any_t cb_interruptor(coro_drainer_lock.get_drain_signal(), - &interruptor); - ql::response_t response; - bool replied = false; - - save_exception(&err, &err_str, &abort, [&]() { - handler->run_query(query.get(), &response, &cb_interruptor); - if (!query->noreply) { - new_mutex_acq_t send_lock(&send_mutex, &cb_interruptor); - protocol_t::send_response(&response, query->token, - conn, &cb_interruptor); - replied = true; - } - }); - save_exception(&err, &err_str, &abort, [&]() { - if (!replied && !query->noreply) { - make_error_response(drain_signal->is_pulsed(), *conn, - err_str, &response); - new_mutex_acq_t send_lock(&send_mutex, drain_signal); - protocol_t::send_response(&response, query->token, - conn, &cb_interruptor); - } - }); - }); - guarantee(!outer_query.has()); - // Since we're using `spawn_now_dangerously` above, we need to yield - // here to stop a client sending a constant stream of expensive queries - // from stalling the thread. - coro_t::yield(); - } - } - - if (err) { - std::rethrow_exception(err); - } -} - -void query_server_t::handle(const http_req_t &req, - http_res_t *result, - signal_t *interruptor) { - auto_drainer_t::lock_t auto_drainer_lock(&drainer); - if (req.method == http_method_t::POST && - req.resource.as_string().find("open-new-connection") != std::string::npos) { - http_conn_cache_t::conn_key_t conn_id - = http_conn_cache.create(rdb_ctx, req.peer); - - result->set_body("text/plain", conn_id); - result->code = http_status_code_t::OK; - return; - } - - optional optional_conn_id = req.find_query_param("conn_id"); - if (!optional_conn_id) { - *result = http_res_t(http_status_code_t::BAD_REQUEST, "application/text", - "Required parameter \"conn_id\" missing\n"); - return; - } - - http_conn_cache_t::conn_key_t conn_id = *optional_conn_id; - - if (req.method == http_method_t::POST && - req.resource.as_string().find("close-connection") != std::string::npos) { - http_conn_cache.erase(conn_id); - result->code = http_status_code_t::OK; - return; - } - - int64_t token = 0; - if (req.body.size() < sizeof(token)) { - *result = http_res_t(http_status_code_t::BAD_REQUEST, "application/text", - "Client is buggy (request too small)."); - return; - } - - // Copy the body into a mutable buffer so we can move it into parse_json_pb. - scoped_array_t body_buf(req.body.size() + 1); - memcpy(body_buf.data(), req.body.data(), req.body.size()); - body_buf[req.body.size()] = '\0'; - - // Parse the token out from the start of the request - char *data = body_buf.data(); - token = *reinterpret_cast(data); -#ifdef __s390x__ - token = __builtin_bswap64(token); -#endif - data += sizeof(token); - - ql::response_t response; - counted_t conn = http_conn_cache.find(conn_id); - if (!conn.has()) { - response.fill_error(Response::CLIENT_ERROR, Response::INTERNAL, - "This HTTP connection is not open.", - ql::backtrace_registry_t::EMPTY_BACKTRACE); - } else { - scoped_ptr_t query = - json_protocol_t::parse_query_from_buffer(std::move(body_buf), - sizeof(token), - conn->get_query_cache(), - token, - &response); - - if (query.has()) { - // Check for noreply, which we don't support here, as it causes - // problems with interruption - if (query->noreply) { - *result = http_res_t(http_status_code_t::BAD_REQUEST, - "application/text", - "noreply queries are not supported over HTTP\n"); - return; - } - - wait_any_t true_interruptor(interruptor, conn->get_interruptor(), - drainer.get_drain_signal()); - - try { - ticks_t start = get_ticks(); - // We don't throttle HTTP queries. - handler->run_query(query.get(), &response, &true_interruptor); - ticks_t ticks = get_ticks() - start; - - if (!response.profile()) { - ql::datum_array_builder_t array_builder( - ql::configured_limits_t::unlimited); - ql::datum_object_builder_t object_builder; - object_builder.overwrite("duration(ms)", - ql::datum_t(static_cast(ticks) / MILLION)); - array_builder.add(std::move(object_builder).to_datum()); - response.set_profile(std::move(array_builder).to_datum()); - } - } catch (const interrupted_exc_t &ex) { - if (http_conn_cache.is_expired(*conn)) { - response.fill_error(Response::RUNTIME_ERROR, - Response::OP_INDETERMINATE, - http_conn_cache.expired_error_message(), - ql::backtrace_registry_t::EMPTY_BACKTRACE); - } else if (interruptor->is_pulsed()) { - response.fill_error(Response::RUNTIME_ERROR, - Response::OP_INDETERMINATE, - "This ReQL connection has been terminated.", - ql::backtrace_registry_t::EMPTY_BACKTRACE); - } else if (drainer.is_draining()) { - response.fill_error(Response::RUNTIME_ERROR, - Response::OP_INDETERMINATE, - "Server is shutting down.", - ql::backtrace_registry_t::EMPTY_BACKTRACE); - } else if (conn->get_interruptor()->is_pulsed()) { - response.fill_error(Response::RUNTIME_ERROR, - Response::OP_INDETERMINATE, - "This ReQL connection has been terminated.", - ql::backtrace_registry_t::EMPTY_BACKTRACE); - } else { - throw; - } - } - } - } - - rapidjson::StringBuffer buffer; - json_protocol_t::write_response_to_buffer(&response, &buffer); - - uint32_t size = static_cast(buffer.GetSize()); -#ifdef __s390x__ - size = __builtin_bswap32(size); - token = __builtin_bswap64(token); -#endif - char header_buffer[sizeof(token) + sizeof(size)]; - memcpy(&header_buffer[0], &token, sizeof(token)); - memcpy(&header_buffer[sizeof(token)], &size, sizeof(size)); - - std::string body_data; - body_data.reserve(sizeof(header_buffer) + buffer.GetSize()); - body_data.append(&header_buffer[0], sizeof(header_buffer)); - body_data.append(buffer.GetString(), buffer.GetSize()); - result->set_body("application/octet-stream", body_data); - result->code = http_status_code_t::OK; -} diff --git a/src/client_protocol/server.hpp b/src/client_protocol/server.hpp deleted file mode 100644 index 5f3a1cd4a2b..00000000000 --- a/src/client_protocol/server.hpp +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLIENT_PROTOCOL_SERVER_HPP_ -#define CLIENT_PROTOCOL_SERVER_HPP_ - -#include -#include -#include -#include -#include -#include -#include - -#include "arch/address.hpp" -#include "arch/io/openssl.hpp" -#include "arch/runtime/runtime.hpp" -#include "arch/timing.hpp" -#include "concurrency/auto_drainer.hpp" -#include "concurrency/cross_thread_signal.hpp" -#include "containers/archive/archive.hpp" -#include "containers/counted.hpp" -#include "http/http.hpp" -#include "perfmon/perfmon.hpp" -#include "utils.hpp" - -class auth_key_t; - -class rdb_context_t; -namespace ql { -class query_params_t; -class query_cache_t; -class response_t; -} - -class http_conn_cache_t : public repeating_timer_callback_t, - public home_thread_mixin_t { -public: - typedef std::string conn_key_t; - - class http_conn_t : public single_threaded_countable_t { - public: - http_conn_t(rdb_context_t *rdb_ctx, - ip_and_port_t client_addr_port); - - ql::query_cache_t *get_query_cache(); - signal_t *get_interruptor(); - - void pulse(); - time_t last_accessed_time() const; - - private: - cond_t interruptor; - time_t last_accessed; - scoped_ptr_t query_cache; - scoped_perfmon_counter_t counter; - DISABLE_COPYING(http_conn_t); - }; - - // WARNING: http_conn_cache_t might block since it needs to seed a random - // number generator. Don't call this except at server startup. - explicit http_conn_cache_t(uint32_t _http_timeout_sec); - ~http_conn_cache_t(); - - counted_t find(const conn_key_t &key); - conn_key_t create(rdb_context_t *rdb_ctx, ip_and_port_t client_addr_port); - void erase(const conn_key_t &key); - - void on_ring(); - bool is_expired(const http_conn_t &conn) const; - - std::string expired_error_message() const; -private: - static const int64_t TIMER_RESOLUTION_MS = 5000; - - // Random number generator used for generating cryptographic connection IDs - std::mt19937 key_generator; - - // We use a cryptographic hash with this unordered map to avoid timing - // side channel attacks that might leak information about existing connection - // keys. - struct sha_hasher_t { - size_t operator()(const conn_key_t &x) const; - }; - std::unordered_map, sha_hasher_t> cache; - repeating_timer_t http_timeout_timer; - uint32_t http_timeout_sec; -}; - -class new_semaphore_in_line_t; -class query_handler_t { -public: - virtual ~query_handler_t() { } - - virtual void run_query(ql::query_params_t *query_params, - ql::response_t *response_out, - signal_t *interruptor) = 0; -}; - -class query_server_t : public http_app_t { -public: - query_server_t( - rdb_context_t *rdb_ctx, - const std::set &local_addresses, - int port, - query_handler_t *_handler, - uint32_t http_timeout_sec, - tls_ctx_t* tls_ctx); - ~query_server_t(); - - int get_port() const; - -private: - void make_error_response(bool is_draining, - const tcp_conn_t &conn, - const std::string &err, - ql::response_t *response_out); - - // For the client driver socket - void handle_conn(const scoped_ptr_t &nconn, - auto_drainer_t::lock_t); - - // This is templatized based on the wire protocol requested by the client - template - void connection_loop(tcp_conn_t *conn, - size_t max_concurrent_queries, - ql::query_cache_t *query_cache, - signal_t *interruptor); - - // For HTTP server - void handle(const http_req_t &request, - http_res_t *result, - signal_t *interruptor); - - tls_ctx_t *tls_ctx; - rdb_context_t *const rdb_ctx; - query_handler_t *const handler; - - /* WARNING: The order here is fragile. */ - auto_drainer_t drainer; - http_conn_cache_t http_conn_cache; - scoped_ptr_t tcp_listener; - - int next_thread; -}; - -#endif /* CLIENT_PROTOCOL_SERVER_HPP_ */ diff --git a/src/clustering/administration/admin_op_exc.hpp b/src/clustering/administration/admin_op_exc.hpp deleted file mode 100644 index d830bd8e09e..00000000000 --- a/src/clustering/administration/admin_op_exc.hpp +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_ADMIN_OP_EXC_HPP_ -#define CLUSTERING_ADMINISTRATION_ADMIN_OP_EXC_HPP_ - -#include "utils.hpp" - -#include "clustering/table_manager/table_meta_client.hpp" - -struct admin_err_t { - std::string msg; - query_state_t query_state; -}; - -// These have to be macros because `rfail` and `rfail_datum` are macros. -#define REQL_RETHROW(X) do { \ - rfail((X).query_state == query_state_t::FAILED \ - ? ql::base_exc_t::OP_FAILED \ - : ql::base_exc_t::OP_INDETERMINATE, \ - "%s", (X).msg.c_str()); \ - } while (0) -#define REQL_RETHROW_DATUM(X) do { \ - rfail_datum((X).query_state == query_state_t::FAILED \ - ? ql::base_exc_t::OP_FAILED \ - : ql::base_exc_t::OP_INDETERMINATE, \ - "%s", (X).msg.c_str()); \ - } while (0) - - -/* This is a generic class for errors that occur during administrative operations. It has -a string error message which is suitable for presenting to the user. */ -class admin_op_exc_t : public std::runtime_error { -public: - explicit admin_op_exc_t(admin_err_t err) - : std::runtime_error(std::move(err.msg)), - m_query_state(err.query_state) { } - - admin_op_exc_t(std::string message, query_state_t query_state) - : std::runtime_error(std::move(message)), - m_query_state(query_state) { } - - admin_err_t to_admin_err() const { - return admin_err_t{what(), m_query_state}; - } - -private: - query_state_t m_query_state; -}; - -/* `CATCH_NAME_ERRORS` and `CATCH_OP_ERRORS` are helper macros for catching the -exceptions thrown by the `table_meta_client_t` and producing consistent error messages. -They're designed to be used as follows: - - try { - something_that_might_throw() - } CATCH_NAME_ERRORS(db, name, error_out); - -TODO: Probably these should re-throw `admin_op_exc_t` instead of setting `*error_out` and -returning `false`. */ - -#define CATCH_NAME_ERRORS(db, name, error_out) \ - catch (const no_such_table_exc_t &) { \ - *(error_out) = admin_err_t{ \ - strprintf("Table `%s.%s` does not exist.", (db).c_str(), (name).c_str()), \ - query_state_t::FAILED}; \ - return false; \ - } catch (const ambiguous_table_exc_t &) { \ - *(error_out) = admin_err_t{ \ - strprintf("Table `%s.%s` is ambiguous; there are multiple " \ - "tables with that name.", (db).c_str(), (name).c_str()), \ - query_state_t::FAILED}; \ - return false; \ - } - -/* For read operations, `no_msg` and `maybe_msg` should both be `""`. For write -operations, `no_msg` should be a string literal stating that the operation was not -performed, and `maybe_msg` should be a string literal stating that the operation may or -may not have been performed. */ -#define CATCH_OP_ERRORS(db, name, error_out, no_msg, maybe_msg) \ - catch (const failed_table_op_exc_t &) { \ - *(error_out) = admin_err_t{ \ - strprintf("The server(s) hosting table `%s.%s` are currently " \ - "unreachable. " no_msg " If you do not expect the server(s) " \ - "to recover, you can use `emergency_repair` to restore " \ - "availability of the table. " \ - "", (db).c_str(), (name).c_str()), \ - query_state_t::FAILED}; \ - return false; \ - } catch (const maybe_failed_table_op_exc_t &) { \ - *(error_out) = admin_err_t{ \ - strprintf("We lost contact with the server(s) hosting table " \ - "`%s.%s`. " maybe_msg, (db).c_str(), (name).c_str()), \ - query_state_t::INDETERMINATE}; \ - return false; \ - } - -#endif /* CLUSTERING_ADMINISTRATION_ADMIN_OP_EXC_HPP_ */ - diff --git a/src/clustering/administration/artificial_reql_cluster_interface.cc b/src/clustering/administration/artificial_reql_cluster_interface.cc deleted file mode 100644 index bdb7b162fb2..00000000000 --- a/src/clustering/administration/artificial_reql_cluster_interface.cc +++ /dev/null @@ -1,855 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/artificial_reql_cluster_interface.hpp" - -#include "clustering/administration/auth/grant.hpp" -#include "clustering/administration/main/watchable_fields.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/real_reql_cluster_interface.hpp" -#include "concurrency/cross_thread_signal.hpp" -#include "rdb_protocol/artificial_table/artificial_table.hpp" -#include "rdb_protocol/env.hpp" -#include "rpc/semilattice/view/field.hpp" - -/* static */ const name_string_t artificial_reql_cluster_interface_t::database_name = - name_string_t::guarantee_valid("rethinkdb"); - -// Note, can't use the above as order of initialization isn't guaranteed -/* static */ const uuid_u artificial_reql_cluster_interface_t::database_id = - uuid_u::from_hash(str_to_uuid("39a24924-14ec-4deb-99f1-742eda7aba5e"), "rethinkdb"); - -artificial_reql_cluster_interface_t::artificial_reql_cluster_interface_t( - std::shared_ptr> - auth_semilattice_view, - rdb_context_t *rdb_context): - m_auth_semilattice_view(auth_semilattice_view), - m_rdb_context(rdb_context), - m_next(nullptr) { -} - -bool artificial_reql_cluster_interface_t::db_create( - auth::user_context_t const &user_context, - const name_string_t &name, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out) { - if (name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` already exists.", artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->db_create( - user_context, name, interruptor, result_out, error_out); -} - -bool artificial_reql_cluster_interface_t::db_drop( - auth::user_context_t const &user_context, - const name_string_t &name, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out) { - if (name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` is special; you can't delete it.", - artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->db_drop( - user_context, name, interruptor, result_out, error_out); -} - -bool artificial_reql_cluster_interface_t::db_list( - signal_t *interruptor, - std::set *names_out, admin_err_t *error_out) { - if (m_next == nullptr || !m_next->db_list(interruptor, names_out, error_out)) { - return false; - } - guarantee(names_out->count(artificial_reql_cluster_interface_t::database_name) == 0); - names_out->insert(artificial_reql_cluster_interface_t::database_name); - return true; -} - -bool artificial_reql_cluster_interface_t::db_find( - const name_string_t &name, - signal_t *interruptor, - counted_t *db_out, - admin_err_t *error_out) { - if (name == artificial_reql_cluster_interface_t::database_name) { - *db_out = make_counted(artificial_reql_cluster_interface_t::database_id, artificial_reql_cluster_interface_t::database_name); - return true; - } - return next_or_error(error_out) && m_next->db_find(name, interruptor, db_out, error_out); -} - -bool artificial_reql_cluster_interface_t::db_config( - auth::user_context_t const &user_context, - const counted_t &db, - ql::backtrace_id_t bt, - ql::env_t *env, - scoped_ptr_t *selection_out, - admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` is special; you can't configure it.", - artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->db_config( - user_context, db, bt, env, selection_out, error_out); -} - -bool artificial_reql_cluster_interface_t::table_create( - auth::user_context_t const &user_context, - const name_string_t &name, - counted_t db, - const table_generate_config_params_t &config_params, - const std::string &primary_key, - write_durability_t durability, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` is special; you can't create new tables " - "in it.", artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->table_create( - user_context, - name, - db, - config_params, - primary_key, - durability, - interruptor, - result_out, - error_out); -} - -bool artificial_reql_cluster_interface_t::table_drop( - auth::user_context_t const &user_context, - const name_string_t &name, - counted_t db, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` is special; you can't drop tables in it.", - artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->table_drop( - user_context, name, db, interruptor, result_out, error_out); -} - -bool artificial_reql_cluster_interface_t::table_list(counted_t db, - signal_t *interruptor, - std::set *names_out, admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - for (auto it = m_table_backends.begin(); it != m_table_backends.end(); ++it) { - if (it->first.str()[0] == '_') { - /* If a table's name starts with `_`, don't show it to the user unless - they explicitly request it. */ - continue; - } - names_out->insert(it->first); - } - return true; - } - return next_or_error(error_out) && m_next->table_list( - db, interruptor, names_out, error_out); -} - -bool artificial_reql_cluster_interface_t::table_find( - const name_string_t &name, - counted_t db, - optional identifier_format, - signal_t *interruptor, - counted_t *table_out, - admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - auto it = m_table_backends.find(name); - if (it != m_table_backends.end()) { - artificial_table_backend_t *backend; - if (!identifier_format.has_value() || - *identifier_format == admin_identifier_format_t::name) { - backend = it->second.first; - } else { - backend = it->second.second; - } - table_out->reset( - new artificial_table_t(m_rdb_context, artificial_reql_cluster_interface_t::database_id, backend)); - return true; - } else { - *error_out = admin_err_t{ - strprintf("Table `%s.%s` does not exist.", - artificial_reql_cluster_interface_t::database_name.c_str(), name.c_str()), - query_state_t::FAILED}; - return false; - } - } - return next_or_error(error_out) && m_next->table_find( - name, db, identifier_format, interruptor, table_out, error_out); -} - -bool artificial_reql_cluster_interface_t::table_estimate_doc_counts( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - ql::env_t *env, - std::vector *doc_counts_out, - admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - auto it = m_table_backends.find(name); - if (it != m_table_backends.end()) { - /* We arbitrarily choose to read from the UUID version of the system table - rather than the name version. */ - std::vector datums; - if (!it->second.second->read_all_rows_filtered( - user_context, - ql::datumspec_t(ql::datum_range_t::universe()), - sorting_t::UNORDERED, - env->interruptor, - &datums, - error_out)) { - error_out->msg = "When estimating doc count: " + error_out->msg; - return false; - } - *doc_counts_out = std::vector({ static_cast(datums.size()) }); - return true; - } else { - *error_out = admin_err_t{ - strprintf("Table `%s.%s` does not exist.", - artificial_reql_cluster_interface_t::database_name.c_str(), name.c_str()), - query_state_t::FAILED}; - return false; - } - } else { - return next_or_error(error_out) && m_next->table_estimate_doc_counts( - user_context, db, name, env, doc_counts_out, error_out); - } -} - -bool artificial_reql_cluster_interface_t::table_config( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - ql::backtrace_id_t bt, - ql::env_t *env, - scoped_ptr_t *selection_out, admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` is special; you can't configure the " - "tables in it.", artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->table_config( - user_context, db, name, bt, env, selection_out, error_out); -} - -bool artificial_reql_cluster_interface_t::table_status( - counted_t db, const name_string_t &name, - ql::backtrace_id_t bt, ql::env_t *env, - scoped_ptr_t *selection_out, admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` is special; the system tables in it don't " - "have meaningful status information.", artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->table_status( - db, name, bt, env, selection_out, error_out); -} - -bool artificial_reql_cluster_interface_t::table_wait( - counted_t db, const name_string_t &name, - table_readiness_t readiness, signal_t *interruptor, - ql::datum_t *result_out, admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` is special; the system tables in it are " - "always available and don't need to be waited on.", - artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->table_wait( - db, name, readiness, interruptor, result_out, error_out); -} - -bool artificial_reql_cluster_interface_t::db_wait( - counted_t db, table_readiness_t readiness, - signal_t *interruptor, - ql::datum_t *result_out, admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` is special; the system tables in it are " - "always available and don't need to be waited on.", - artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->db_wait( - db, readiness, interruptor, result_out, error_out); -} - -bool artificial_reql_cluster_interface_t::table_reconfigure( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - const table_generate_config_params_t ¶ms, - bool dry_run, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` is special; you can't configure the " - "tables in it.", artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->table_reconfigure( - user_context, db, name, params, dry_run, interruptor, result_out, error_out); -} - -bool artificial_reql_cluster_interface_t::db_reconfigure( - auth::user_context_t const &user_context, - counted_t db, - const table_generate_config_params_t ¶ms, - bool dry_run, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` is special; you can't configure the " - "tables in it.", artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->db_reconfigure( - user_context, db, params, dry_run, interruptor, result_out, error_out); -} - -bool artificial_reql_cluster_interface_t::table_emergency_repair( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - emergency_repair_mode_t mode, - bool dry_run, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` is special; you can't configure the " - "tables in it.", artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->table_emergency_repair( - user_context, db, name, mode, dry_run, interruptor, result_out, error_out); -} - -bool artificial_reql_cluster_interface_t::table_rebalance( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` is special; you can't rebalance the " - "tables in it.", artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->table_rebalance( - user_context, db, name, interruptor, result_out, error_out); -} - -bool artificial_reql_cluster_interface_t::db_rebalance( - auth::user_context_t const &user_context, - counted_t db, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` is special; you can't rebalance the " - "tables in it.", artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->db_rebalance( - user_context, db, interruptor, result_out, error_out); -} - -bool artificial_reql_cluster_interface_t::grant_global( - auth::user_context_t const &user_context, - auth::username_t username, - ql::datum_t permissions, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out) { - return next_or_error(error_out) && m_next->grant_global( - user_context, - std::move(username), - std::move(permissions), - interruptor, - result_out, - error_out); -} - -bool artificial_reql_cluster_interface_t::grant_database( - auth::user_context_t const &user_context, - database_id_t const &database, - auth::username_t username, - ql::datum_t permissions, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out) { - if (database == artificial_reql_cluster_interface_t::database_id) { - cross_thread_signal_t cross_thread_interruptor(interruptor, home_thread()); - on_thread_t on_thread(home_thread()); - - return auth::grant( - m_auth_semilattice_view, - m_rdb_context, - user_context, - std::move(username), - std::move(permissions), - &cross_thread_interruptor, - [&](auth::user_t &user) -> auth::permissions_t & { - return user.get_database_permissions(database); - }, - result_out, - error_out); - } - return next_or_error(error_out) && m_next->grant_database( - user_context, - database, - std::move(username), - std::move(permissions), - interruptor, - result_out, - error_out); -} - -bool artificial_reql_cluster_interface_t::grant_table( - auth::user_context_t const &user_context, - database_id_t const &database, - namespace_id_t const &table, - auth::username_t username, - ql::datum_t permissions, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out) { - if (database == artificial_reql_cluster_interface_t::database_id) { - cross_thread_signal_t cross_thread_interruptor(interruptor, home_thread()); - on_thread_t on_thread(home_thread()); - - return auth::grant( - m_auth_semilattice_view, - m_rdb_context, - user_context, - std::move(username), - std::move(permissions), - &cross_thread_interruptor, - [&](auth::user_t &user) -> auth::permissions_t & { - return user.get_table_permissions(table); - }, - result_out, - error_out); - } - return next_or_error(error_out) && m_next->grant_table( - user_context, - database, - table, - std::move(username), - std::move(permissions), - interruptor, - result_out, - error_out); -} - -bool artificial_reql_cluster_interface_t::set_write_hook( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const optional &config, - signal_t *interruptor, - admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` is special; you can't set a " - "write hook on the tables in it.", - artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return m_next->set_write_hook( - user_context, db, table, config, interruptor, error_out); -} - -bool artificial_reql_cluster_interface_t::get_write_hook( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - signal_t *interruptor, - ql::datum_t *write_hook_datum_out, - admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *write_hook_datum_out = ql::datum_t::null(); - return true; - } - return m_next->get_write_hook( - user_context, db, table, interruptor, write_hook_datum_out, error_out); -} -bool artificial_reql_cluster_interface_t::sindex_create( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const std::string &name, - const sindex_config_t &config, - signal_t *interruptor, - admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Database `%s` is special; you can't create secondary " - "indexes on the tables in it.", artificial_reql_cluster_interface_t::database_name.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->sindex_create( - user_context, db, table, name, config, interruptor, error_out); -} - -bool artificial_reql_cluster_interface_t::sindex_drop( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const std::string &name, - signal_t *interruptor, - admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Index `%s` does not exist on table `%s.%s`.", - name.c_str(), db->name.c_str(), table.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->sindex_drop( - user_context, db, table, name, interruptor, error_out); -} - -bool artificial_reql_cluster_interface_t::sindex_rename( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const std::string &name, - const std::string &new_name, - bool overwrite, - signal_t *interruptor, - admin_err_t *error_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - *error_out = admin_err_t{ - strprintf("Index `%s` does not exist on table `%s.%s`.", - name.c_str(), db->name.c_str(), table.c_str()), - query_state_t::FAILED}; - return false; - } - return next_or_error(error_out) && m_next->sindex_rename( - user_context, db, table, name, new_name, overwrite, interruptor, error_out); -} - -bool artificial_reql_cluster_interface_t::sindex_list( - counted_t db, - const name_string_t &table, - signal_t *interruptor, - admin_err_t *error_out, - std::map > - *configs_and_statuses_out) { - if (db->name == artificial_reql_cluster_interface_t::database_name) { - configs_and_statuses_out->clear(); - return true; - } - return next_or_error(error_out) && m_next->sindex_list( - db, table, interruptor, error_out, configs_and_statuses_out); -} - -void artificial_reql_cluster_interface_t::set_next_reql_cluster_interface( - reql_cluster_interface_t *next) { - m_next = next; -} - -artificial_table_backend_t *artificial_reql_cluster_interface_t::get_table_backend( - name_string_t const &table_name, - admin_identifier_format_t admin_identifier_format) const { - auto table_backend = m_table_backends.find(table_name); - if (table_backend != m_table_backends.end()) { - switch (admin_identifier_format) { - case admin_identifier_format_t::name: - return table_backend->second.first; - case admin_identifier_format_t::uuid: - return table_backend->second.second; - default: - unreachable(); - } - } else { - return nullptr; - } -} - -artificial_reql_cluster_interface_t::table_backends_map_t * -artificial_reql_cluster_interface_t::get_table_backends_map_mutable() { - return &m_table_backends; -} - -artificial_reql_cluster_interface_t::table_backends_map_t const & -artificial_reql_cluster_interface_t::get_table_backends_map() const { - return m_table_backends; -} - -bool artificial_reql_cluster_interface_t::next_or_error(admin_err_t *error_out) const { - if (m_next == nullptr) { - if (error_out != nullptr) { - *error_out = admin_err_t{ - "Failed to find an interface.", query_state_t::FAILED}; - } - return false; - } - return true; -} - -artificial_reql_cluster_backends_t::artificial_reql_cluster_backends_t( - artificial_reql_cluster_interface_t *artificial_reql_cluster_interface, - real_reql_cluster_interface_t *real_reql_cluster_interface, - std::shared_ptr> - auth_semilattice_view, - std::shared_ptr> - cluster_semilattice_view, - std::shared_ptr> - heartbeat_semilattice_view, - clone_ptr_t>> directory_view, - watchable_map_t *directory_map_view, - table_meta_client_t *table_meta_client, - server_config_client_t *server_config_client, - mailbox_manager_t *mailbox_manager, - rdb_context_t *rdb_context, - lifetime_t name_resolver) { - for (int format = 0; format < 2; ++format) { - permissions_backend[format].init( - new auth::permissions_artificial_table_backend_t( - rdb_context, - name_resolver, - auth_semilattice_view, - cluster_semilattice_view, - static_cast(format))); - } - permissions_sentry = backend_sentry_t( - artificial_reql_cluster_interface->get_table_backends_map_mutable(), - name_string_t::guarantee_valid("permissions"), - std::make_pair(permissions_backend[0].get(), permissions_backend[1].get())); - - users_backend.init( - new auth::users_artificial_table_backend_t( - rdb_context, - name_resolver, - auth_semilattice_view, - cluster_semilattice_view)); - users_sentry = backend_sentry_t( - artificial_reql_cluster_interface->get_table_backends_map_mutable(), - name_string_t::guarantee_valid("users"), - std::make_pair(users_backend.get(), users_backend.get())); - - cluster_config_backend.init( - new cluster_config_artificial_table_backend_t( - rdb_context, - name_resolver, - heartbeat_semilattice_view)); - cluster_config_sentry = backend_sentry_t( - artificial_reql_cluster_interface->get_table_backends_map_mutable(), - name_string_t::guarantee_valid("cluster_config"), - std::make_pair(cluster_config_backend.get(), cluster_config_backend.get())); - - db_config_backend.init( - new db_config_artificial_table_backend_t( - rdb_context, - name_resolver, - metadata_field( - &cluster_semilattice_metadata_t::databases, - cluster_semilattice_view), - real_reql_cluster_interface)); - db_config_sentry = backend_sentry_t( - artificial_reql_cluster_interface->get_table_backends_map_mutable(), - name_string_t::guarantee_valid("db_config"), - std::make_pair(db_config_backend.get(), db_config_backend.get())); - - for (int format = 0; format < 2; ++format) { - issues_backend[format].init( - new issues_artificial_table_backend_t( - rdb_context, - name_resolver, - mailbox_manager, - cluster_semilattice_view, - directory_map_view, - server_config_client, - table_meta_client, - real_reql_cluster_interface->get_namespace_repo(), - static_cast(format))); - } - issues_sentry = backend_sentry_t( - artificial_reql_cluster_interface->get_table_backends_map_mutable(), - name_string_t::guarantee_valid("current_issues"), - std::make_pair(issues_backend[0].get(), issues_backend[1].get())); - - for (int format = 0; format < 2; ++format) { - logs_backend[format].init( - new logs_artificial_table_backend_t( - rdb_context, - name_resolver, - mailbox_manager, - directory_map_view, - server_config_client, - static_cast(format))); - } - logs_sentry = backend_sentry_t( - artificial_reql_cluster_interface->get_table_backends_map_mutable(), - name_string_t::guarantee_valid("logs"), - std::make_pair(logs_backend[0].get(), logs_backend[1].get())); - - server_config_backend.init( - new server_config_artificial_table_backend_t( - rdb_context, - name_resolver, - directory_map_view, - server_config_client)); - server_config_sentry = backend_sentry_t( - artificial_reql_cluster_interface->get_table_backends_map_mutable(), - name_string_t::guarantee_valid("server_config"), - std::make_pair(server_config_backend.get(), server_config_backend.get())); - - for (int format = 0; format < 2; ++format) { - server_status_backend[format].init( - new server_status_artificial_table_backend_t( - rdb_context, - name_resolver, - directory_map_view, - server_config_client, - static_cast(format))); - } - server_status_sentry = backend_sentry_t( - artificial_reql_cluster_interface->get_table_backends_map_mutable(), - name_string_t::guarantee_valid("server_status"), - std::make_pair(server_status_backend[0].get(), server_status_backend[1].get())); - - for (int format = 0; format < 2; ++format) { - stats_backend[format].init( - new stats_artificial_table_backend_t( - rdb_context, - name_resolver, - directory_view, - cluster_semilattice_view, - server_config_client, - table_meta_client, - mailbox_manager, - static_cast(format))); - } - stats_sentry = backend_sentry_t( - artificial_reql_cluster_interface->get_table_backends_map_mutable(), - name_string_t::guarantee_valid("stats"), - std::make_pair(stats_backend[0].get(), stats_backend[1].get())); - - for (int format = 0; format < 2; ++format) { - table_config_backend[format].init( - new table_config_artificial_table_backend_t( - rdb_context, - name_resolver, - cluster_semilattice_view, - real_reql_cluster_interface, - static_cast(format), - server_config_client, - table_meta_client)); - } - table_config_sentry = backend_sentry_t( - artificial_reql_cluster_interface->get_table_backends_map_mutable(), - name_string_t::guarantee_valid("table_config"), - std::make_pair(table_config_backend[0].get(), table_config_backend[1].get())); - - for (int format = 0; format < 2; ++format) { - table_status_backend[format].init( - new table_status_artificial_table_backend_t( - rdb_context, - name_resolver, - cluster_semilattice_view, - server_config_client, - table_meta_client, - real_reql_cluster_interface->get_namespace_repo(), - static_cast(format))); - } - table_status_sentry = backend_sentry_t( - artificial_reql_cluster_interface->get_table_backends_map_mutable(), - name_string_t::guarantee_valid("table_status"), - std::make_pair(table_status_backend[0].get(), table_status_backend[1].get())); - - for (int format = 0; format < 2; ++format) { - jobs_backend[format].init( - new jobs_artificial_table_backend_t( - rdb_context, - name_resolver, - mailbox_manager, - cluster_semilattice_view, - directory_view, - server_config_client, - table_meta_client, - static_cast(format))); - } - jobs_sentry = backend_sentry_t( - artificial_reql_cluster_interface->get_table_backends_map_mutable(), - name_string_t::guarantee_valid("jobs"), - std::make_pair(jobs_backend[0].get(), jobs_backend[1].get())); - - debug_scratch_backend.init( - new in_memory_artificial_table_backend_t( - name_string_t::guarantee_valid("_debug_scratch"), - rdb_context, - name_resolver)); - debug_scratch_sentry = backend_sentry_t( - artificial_reql_cluster_interface->get_table_backends_map_mutable(), - name_string_t::guarantee_valid("_debug_scratch"), - std::make_pair(debug_scratch_backend.get(), debug_scratch_backend.get())); - - debug_stats_backend.init( - new debug_stats_artificial_table_backend_t( - rdb_context, - name_resolver, - directory_map_view, - server_config_client, - mailbox_manager)); - debug_stats_sentry = backend_sentry_t( - artificial_reql_cluster_interface->get_table_backends_map_mutable(), - name_string_t::guarantee_valid("_debug_stats"), - std::make_pair(debug_stats_backend.get(), debug_stats_backend.get())); - - debug_table_status_backend.init( - new debug_table_status_artificial_table_backend_t( - rdb_context, - name_resolver, - cluster_semilattice_view, - table_meta_client)); - debug_table_status_sentry = backend_sentry_t( - artificial_reql_cluster_interface->get_table_backends_map_mutable(), - name_string_t::guarantee_valid("_debug_table_status"), - std::make_pair(debug_table_status_backend.get(), debug_table_status_backend.get())); -} diff --git a/src/clustering/administration/artificial_reql_cluster_interface.hpp b/src/clustering/administration/artificial_reql_cluster_interface.hpp deleted file mode 100644 index 05956c4dd75..00000000000 --- a/src/clustering/administration/artificial_reql_cluster_interface.hpp +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_ARTIFICIAL_REQL_CLUSTER_INTERFACE_HPP_ -#define CLUSTERING_ADMINISTRATION_ARTIFICIAL_REQL_CLUSTER_INTERFACE_HPP_ - -#include -#include -#include - -#include "clustering/administration/cluster_config.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/servers/server_config.hpp" -#include "clustering/administration/servers/server_status.hpp" -#include "clustering/administration/stats/debug_stats_backend.hpp" -#include "clustering/administration/stats/stats_backend.hpp" -#include "clustering/administration/tables/db_config.hpp" -#include "clustering/administration/tables/debug_table_status.hpp" -#include "clustering/administration/tables/table_config.hpp" -#include "clustering/administration/tables/table_status.hpp" -#include "clustering/administration/issues/issues_backend.hpp" -#include "clustering/administration/auth/permissions_artificial_table_backend.hpp" -#include "clustering/administration/auth/users_artificial_table_backend.hpp" -#include "clustering/administration/logs/logs_backend.hpp" -#include "clustering/administration/jobs/backend.hpp" -#include "containers/map_sentries.hpp" -#include "containers/name_string.hpp" -#include "containers/scoped.hpp" -#include "containers/uuid.hpp" -#include "rdb_protocol/artificial_table/backend.hpp" -#include "rdb_protocol/artificial_table/in_memory.hpp" -#include "rdb_protocol/context.hpp" - -class namespace_repo_t; -class name_resolver_t; -class real_reql_cluster_interface_t; -class server_config_client_t; -class table_meta_client_t; - -/* The `artificial_reql_cluster_interface_t` is responsible for handling queries to the -`rethinkdb` database. It's implemented as a proxy over the -`real_reql_cluster_interface_t`; queries go first to the `artificial_...`, and if they -aren't related to the `rethinkdb` database, they get passed on to the `real_...`. */ - -class artificial_reql_cluster_interface_t - : public reql_cluster_interface_t, - public home_thread_mixin_t { -public: - static const uuid_u database_id; - static const name_string_t database_name; - - artificial_reql_cluster_interface_t( - std::shared_ptr> - auth_semilattice_view, - rdb_context_t *rdb_context); - - bool db_create( - auth::user_context_t const &user_context, - const name_string_t &name, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool db_drop( - auth::user_context_t const &user_context, - const name_string_t &name, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool db_list( - signal_t *interruptor, - std::set *names_out, admin_err_t *error_out); - bool db_find(const name_string_t &name, - signal_t *interruptor, - counted_t *db_out, admin_err_t *error_out); - bool db_config( - auth::user_context_t const &user_context, - const counted_t &db, - ql::backtrace_id_t bt, - ql::env_t *env, - scoped_ptr_t *selection_out, - admin_err_t *error_out); - - bool table_create( - auth::user_context_t const &user_context, - const name_string_t &name, - counted_t db, - const table_generate_config_params_t &config_params, - const std::string &primary_key, - write_durability_t durability, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool table_drop( - auth::user_context_t const &user_context, - const name_string_t &name, - counted_t db, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool table_list(counted_t db, - signal_t *interruptor, - std::set *names_out, admin_err_t *error_out); - bool table_find(const name_string_t &name, counted_t db, - optional identifier_format, - signal_t *interruptor, counted_t *table_out, - admin_err_t *error_out); - bool table_estimate_doc_counts( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - ql::env_t *env, - std::vector *doc_counts_out, - admin_err_t *error_out); - bool table_config( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - ql::backtrace_id_t bt, - ql::env_t *env, - scoped_ptr_t *selection_out, - admin_err_t *error_out); - bool table_status( - counted_t db, - const name_string_t &name, - ql::backtrace_id_t bt, - ql::env_t *env, - scoped_ptr_t *selection_out, - admin_err_t *error_out); - - bool table_wait( - counted_t db, - const name_string_t &name, - table_readiness_t readiness, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool db_wait( - counted_t db, - table_readiness_t readiness, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - - bool table_reconfigure( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - const table_generate_config_params_t ¶ms, - bool dry_run, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool db_reconfigure( - auth::user_context_t const &user_context, - counted_t db, - const table_generate_config_params_t ¶ms, - bool dry_run, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - - bool table_emergency_repair( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - emergency_repair_mode_t, - bool dry_run, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - - bool table_rebalance( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool db_rebalance( - auth::user_context_t const &user_context, - counted_t db, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - - bool grant_global( - auth::user_context_t const &user_context, - auth::username_t username, - ql::datum_t permissions, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool grant_database( - auth::user_context_t const &user_context, - database_id_t const &database_id, - auth::username_t username, - ql::datum_t permissions, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool grant_table( - auth::user_context_t const &user_context, - database_id_t const &database_id, - namespace_id_t const &table_id, - auth::username_t username, - ql::datum_t permissions, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - - bool set_write_hook( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const optional &config, - signal_t *interruptor, - admin_err_t *error_out); - - bool get_write_hook( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - signal_t *interruptor, - ql::datum_t *write_hook_datum_out, - admin_err_t *error_out); - - bool sindex_create( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const std::string &name, - const sindex_config_t &config, - signal_t *interruptor, - admin_err_t *error_out); - bool sindex_drop( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const std::string &name, - signal_t *interruptor, - admin_err_t *error_out); - bool sindex_rename( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const std::string &name, - const std::string &new_name, - bool overwrite, - signal_t *interruptor, - admin_err_t *error_out); - bool sindex_list( - counted_t db, - const name_string_t &table, - signal_t *interruptor, - admin_err_t *error_out, - std::map > - *configs_and_statuses_out); - - void set_next_reql_cluster_interface(reql_cluster_interface_t *next); - - artificial_table_backend_t *get_table_backend( - name_string_t const &, - admin_identifier_format_t) const; - - using table_backends_map_t = std::map< - name_string_t, - std::pair>; - - table_backends_map_t *get_table_backends_map_mutable(); - table_backends_map_t const &get_table_backends_map() const; - -private: - bool next_or_error(admin_err_t *error_out) const; - - table_backends_map_t m_table_backends; - std::shared_ptr> - m_auth_semilattice_view; - rdb_context_t *m_rdb_context; - reql_cluster_interface_t *m_next; -}; - -class artificial_reql_cluster_backends_t { -public: - artificial_reql_cluster_backends_t( - artificial_reql_cluster_interface_t *artificial_reql_cluster_interface, - real_reql_cluster_interface_t *real_reql_cluster_interface, - std::shared_ptr> - auth_semilattice_view, - std::shared_ptr> - cluster_semilattice_view, - std::shared_ptr> - heartbeat_semilattice_view, - clone_ptr_t>> directory_view, - watchable_map_t *directory_map_view, - table_meta_client_t *table_meta_client, - server_config_client_t *server_config_client, - mailbox_manager_t *mailbox_manager, - rdb_context_t *rdb_context, - lifetime_t name_resolver); - -private: - using backend_sentry_t = map_insertion_sentry_t< - artificial_reql_cluster_interface_t::table_backends_map_t::key_type, - artificial_reql_cluster_interface_t::table_backends_map_t::mapped_type>; - - scoped_ptr_t - permissions_backend[2]; - backend_sentry_t permissions_sentry; - - scoped_ptr_t users_backend; - backend_sentry_t users_sentry; - - scoped_ptr_t cluster_config_backend; - backend_sentry_t cluster_config_sentry; - - scoped_ptr_t db_config_backend; - backend_sentry_t db_config_sentry; - - scoped_ptr_t issues_backend[2]; - backend_sentry_t issues_sentry; - - scoped_ptr_t logs_backend[2]; - backend_sentry_t logs_sentry; - - scoped_ptr_t server_config_backend; - backend_sentry_t server_config_sentry; - - scoped_ptr_t server_status_backend[2]; - backend_sentry_t server_status_sentry; - - scoped_ptr_t stats_backend[2]; - backend_sentry_t stats_sentry; - - scoped_ptr_t table_config_backend[2]; - backend_sentry_t table_config_sentry; - - scoped_ptr_t table_status_backend[2]; - backend_sentry_t table_status_sentry; - - scoped_ptr_t jobs_backend[2]; - backend_sentry_t jobs_sentry; - - scoped_ptr_t debug_scratch_backend; - backend_sentry_t debug_scratch_sentry; - - scoped_ptr_t debug_stats_backend; - backend_sentry_t debug_stats_sentry; - - scoped_ptr_t - debug_table_status_backend; - backend_sentry_t debug_table_status_sentry; -}; - -#endif /* CLUSTERING_ADMINISTRATION_ARTIFICIAL_REQL_CLUSTER_INTERFACE_HPP_ */ diff --git a/src/clustering/administration/auth/authentication_error.hpp b/src/clustering/administration/auth/authentication_error.hpp deleted file mode 100644 index 5c933caf2d3..00000000000 --- a/src/clustering/administration/auth/authentication_error.hpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_AUTH_AUTHENTICATION_ERROR_HPP -#define CLUSTERING_ADMINISTRATION_AUTH_AUTHENTICATION_ERROR_HPP - -#include -#include - -namespace auth { - // 10: "invalid-encoding" - // 11: "extensions-not-supported" ; unrecognized 'm' value - // 12: "invalid-proof" - // 13: "channel-bindings-dont-match" - // 14: "server-does-support-channel-binding" ; server does not support channel binding - // 15: "channel-binding-not-supported" - // 16: "unsupported-channel-binding-type" - // 17: "unknown-user" - // 18: "invalid-username-encoding" ; invalid username encoding (invalid UTF-8 or SASLprep failed) - // 19: "no-resources" - // 20: "other-error" - -class authentication_error_t : public std::runtime_error { -public: - authentication_error_t(uint32_t error_code, std::string const &_what) - : std::runtime_error(_what), - m_error_code(error_code) { - } - - uint32_t get_error_code() const { - return m_error_code; - } - -private: - uint32_t m_error_code; -}; - -} // namespace auth - -#endif // CLUSTERING_ADMINISTRATION_AUTH_AUTHENTICATION_ERROR_HPP diff --git a/src/clustering/administration/auth/base_artificial_table_backend.hpp b/src/clustering/administration/auth/base_artificial_table_backend.hpp deleted file mode 100644 index 39a6ce56ba3..00000000000 --- a/src/clustering/administration/auth/base_artificial_table_backend.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_AUTH_BASE_ARTIFICIAL_TABLE_BACKEND_HPP -#define CLUSTERING_ADMINISTRATION_AUTH_BASE_ARTIFICIAL_TABLE_BACKEND_HPP - -#include -#include -#include - -#include "clustering/table_manager/table_meta_client.hpp" -#include "containers/lifetime.hpp" -#include "rdb_protocol/artificial_table/caching_cfeed_backend.hpp" -#include "rpc/semilattice/view.hpp" - -class cluster_semilattice_metadata_t; -class auth_semilattice_metadata_t; - -namespace auth { - -class base_artificial_table_backend_t : - public caching_cfeed_artificial_table_backend_t -{ -public: - base_artificial_table_backend_t( - name_string_t const &table_name, - rdb_context_t *rdb_context, - lifetime_t name_resolver, - std::shared_ptr> - auth_semilattice_view, - std::shared_ptr> - cluster_semilattice_view) - : caching_cfeed_artificial_table_backend_t( - table_name, rdb_context, name_resolver), - m_auth_semilattice_view(std::move(auth_semilattice_view)), - m_auth_subscription([this](){ notify_all(); }, m_auth_semilattice_view), - m_cluster_semilattice_view(std::move(cluster_semilattice_view)) { - } - - ~base_artificial_table_backend_t() { - begin_changefeed_destruction(); - } - - std::string get_primary_key_name() { - return "id"; - } - -protected: - std::shared_ptr> - m_auth_semilattice_view; - semilattice_read_view_t::subscription_t - m_auth_subscription; - std::shared_ptr> - m_cluster_semilattice_view; -}; - -} // namespace auth - -#endif // CLUSTERING_ADMINISTRATION_AUTH_BASE_ARTIFICIAL_TABLE_BACKEND_HPP diff --git a/src/clustering/administration/auth/base_authenticator.hpp b/src/clustering/administration/auth/base_authenticator.hpp deleted file mode 100644 index 6d8226a2ace..00000000000 --- a/src/clustering/administration/auth/base_authenticator.hpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_AUTH_BASE_AUTHENTICATOR_HPP -#define CLUSTERING_ADMINISTRATION_AUTH_BASE_AUTHENTICATOR_HPP - -#include - -#include "clustering/administration/auth/authentication_error.hpp" -#include "clustering/administration/auth/username.hpp" -#include "clustering/administration/metadata.hpp" - -namespace auth { - -class base_authenticator_t { -public: - base_authenticator_t( - clone_ptr_t> auth_watchable) - : m_auth_watchable(auth_watchable) { - } - virtual ~base_authenticator_t() { - } - - virtual std::string next_message(std::string const &) - THROWS_ONLY(authentication_error_t) = 0; - virtual username_t get_authenticated_username() const - THROWS_ONLY(authentication_error_t) = 0; - -protected: - clone_ptr_t> m_auth_watchable; -}; - -} // namespace auth - -#endif // CLUSTERING_ADMINISTRATION_AUTH_BASE_AUTHENTICATOR_HPP diff --git a/src/clustering/administration/auth/grant.cc b/src/clustering/administration/auth/grant.cc deleted file mode 100644 index 92b89ae7e08..00000000000 --- a/src/clustering/administration/auth/grant.cc +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "rdb_protocol/table_common.hpp" - -#include "clustering/administration/admin_op_exc.hpp" -#include "clustering/administration/auth/user.hpp" -#include "clustering/administration/metadata.hpp" -#include "rpc/semilattice/view.hpp" - -namespace auth { - -bool grant( - std::shared_ptr> - auth_semilattice_view, - rdb_context_t *rdb_context, - auth::user_context_t const &user_context, - auth::username_t username, - ql::datum_t permissions, - signal_t *interruptor, - std::function permission_selector_function, - ql::datum_t *result_out, - admin_err_t *error_out) { - if (username.is_admin()) { - *error_out = admin_err_t{ - "The permissions of the user `" + username.to_string() + - "` can't be modified.", - query_state_t::FAILED}; - return false; - } - - counted_t db; - if (!rdb_context->cluster_interface->db_find( - name_string_t::guarantee_valid("rethinkdb"), - interruptor, - &db, - error_out)) { - return false; - } - - counted_t table; - if (!rdb_context->cluster_interface->table_find( - name_string_t::guarantee_valid("permissions"), - db, - make_optional(admin_identifier_format_t::uuid), - interruptor, - &table, - error_out)) { - return false; - } - - user_context.require_read_permission(rdb_context, db->id, table->get_id()); - user_context.require_write_permission(rdb_context, db->id, table->get_id()); - - auth_semilattice_metadata_t auth_metadata = auth_semilattice_view->get(); - auto grantee = auth_metadata.m_users.find(username); - if (grantee == auth_metadata.m_users.end() || - !static_cast(grantee->second.get_ref())) { - *error_out = admin_err_t{ - "User `" + username.to_string() + "` not found.", query_state_t::FAILED}; - return false; - } - - ql::datum_t old_permissions; - ql::datum_t new_permissions; - try { - grantee->second.apply_write([&](optional *user) { - auth::permissions_t &permissions_ref = permission_selector_function(user->get()); - old_permissions = permissions_ref.to_datum(); - permissions_ref.merge(permissions); - new_permissions = permissions_ref.to_datum(); - }); - } catch (admin_op_exc_t const &admin_op_exc) { - *error_out = admin_op_exc.to_admin_err(); - return false; - } - - auth_semilattice_view->join(auth_metadata); - - // Wait for the metadata to propegate - rdb_context->get_auth_watchable()->run_until_satisfied( - [&](auth_semilattice_metadata_t const &metadata) -> bool { - auth_semilattice_metadata_t copy = metadata; - semilattice_join(©, auth_metadata); - return copy == metadata; - }, - interruptor); - (void) rdb_context; - (void) interruptor; - - ql::datum_object_builder_t result_builder; - result_builder.overwrite("granted", ql::datum_t(1.0)); - result_builder.overwrite( - "permissions_changes", - make_replacement_pair(old_permissions, new_permissions)); - *result_out = std::move(result_builder).to_datum(); - return true; -} - -} // namespace auth - diff --git a/src/clustering/administration/auth/grant.hpp b/src/clustering/administration/auth/grant.hpp deleted file mode 100644 index ac50c6cd144..00000000000 --- a/src/clustering/administration/auth/grant.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_AUTH_GRANT_HPP -#define CLUSTERING_ADMINISTRATION_AUTH_GRANT_HPP - -#include "rdb_protocol/table_common.hpp" - -template class semilattice_readwrite_view_t; -struct admin_err_t; - -namespace auth { - -class user_t; - -bool grant( - std::shared_ptr> - auth_semilattice_view, - rdb_context_t *rdb_context, - auth::user_context_t const &user_context, - auth::username_t username, - ql::datum_t permissions, - signal_t *interruptor, - std::function permission_selector_function, - ql::datum_t *result_out, - admin_err_t *error_out); - -} // namespace auth - -#endif // CLUSTERING_ADMINISTRATION_AUTH_GRANT_HPP diff --git a/src/clustering/administration/auth/password.cc b/src/clustering/administration/auth/password.cc deleted file mode 100644 index 43a2590c755..00000000000 --- a/src/clustering/administration/auth/password.cc +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/auth/password.hpp" - -#include "errors.hpp" -#include - -#include "clustering/administration/admin_op_exc.hpp" -#include "containers/archive/stl_types.hpp" -#include "crypto/pbkcs5_pbkdf2_hmac.hpp" -#include "crypto/random.hpp" - -namespace auth { - -password_t::password_t() { -} - -password_t::password_t(std::string const &password, uint32_t iteration_count) - : m_iteration_count(iteration_count), - m_salt(crypto::random_bytes()), - m_hash(crypto::pbkcs5_pbkdf2_hmac_sha256(password, m_salt, m_iteration_count)), - m_is_empty(password.empty()) { -} - -password_t::password_t(ql::datum_t const &datum) { - if (datum.get_type() != ql::datum_t::R_OBJECT) { - throw admin_op_exc_t( - "Expected an object, got " + datum.print() + ".", query_state_t::FAILED); - } - - std::set keys; - for (size_t i = 0; i < datum.obj_size(); ++i) { - keys.insert(datum.get_pair(i).first.to_std()); - } - - ql::datum_t iterations = datum.get_field("iterations", ql::NOTHROW); - if (!iterations.has()) { - throw admin_op_exc_t("Expected a field named `iterations`.", query_state_t::FAILED); - } - try { - if (iterations.get_type() != ql::datum_t::R_NUM || iterations.as_int() < 1) { - throw admin_op_exc_t( - "Expected `iterations` to be an integer greater than zero, got " + - iterations.print() + ".", - query_state_t::FAILED); - } - } catch (ql::base_exc_t const &) { - throw admin_op_exc_t( - "Expected `iterations` to be an integer greater than zero, got " + - iterations.print() + ".", - query_state_t::FAILED); - } - keys.erase("iterations"); - m_iteration_count = iterations.as_int(); - - m_salt = crypto::random_bytes(); - - ql::datum_t password = datum.get_field("password", ql::NOTHROW); - if (!password.has()) { - throw admin_op_exc_t("Expected a field named `password`.", query_state_t::FAILED); - } - if (password.get_type() != ql::datum_t::R_STR) { - throw admin_op_exc_t( - "Expected `password` to be a string, got " + password.print() + ".", - query_state_t::FAILED); - } - keys.erase("password"); - m_hash = crypto::pbkcs5_pbkdf2_hmac_sha256( - password.as_str().to_std(), m_salt, m_iteration_count); - m_is_empty = password.as_str().to_std().empty(); - - if (!keys.empty()) { - throw admin_op_exc_t( - "Unexpected key(s) `" + boost::algorithm::join(keys, "`, `") + "`.", - query_state_t::FAILED); - } -} - -/* static */ password_t password_t::generate_password_for_unknown_user() { - password_t password; - password.m_iteration_count = password_t::default_iteration_count, - password.m_salt = crypto::random_bytes(), - password.m_hash.fill('\0'); - password.m_is_empty = false; - return password; -} - -uint32_t password_t::get_iteration_count() const { - return m_iteration_count; -} - -std::array const &password_t::get_salt() const { - return m_salt; -} - -std::array const &password_t::get_hash() const { - return m_hash; -} - -bool password_t::is_empty() const { - return m_is_empty; -} - -bool password_t::operator==(password_t const &rhs) const { - return - m_iteration_count == rhs.m_iteration_count && - m_salt == rhs.m_salt && - m_hash == rhs.m_hash && - m_is_empty == rhs.m_is_empty; -} - -RDB_IMPL_SERIALIZABLE_4( - password_t, - m_iteration_count, - m_salt, - m_hash, - m_is_empty); -INSTANTIATE_SERIALIZABLE_SINCE_v2_3(password_t); - -} // namespace auth diff --git a/src/clustering/administration/auth/password.hpp b/src/clustering/administration/auth/password.hpp deleted file mode 100644 index fe1cfdbeded..00000000000 --- a/src/clustering/administration/auth/password.hpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_AUTH_PASSWORD_HPP -#define CLUSTERING_ADMINISTRATION_AUTH_PASSWORD_HPP - -#include - -#include -#include - -#include "rdb_protocol/datum.hpp" -#include "rpc/serialize_macros.hpp" - -namespace auth { - -class password_t -{ -public: - static constexpr uint32_t default_iteration_count = 4096; - static constexpr std::size_t salt_length = 16; - - password_t(); - explicit password_t( - std::string const &password, - uint32_t iteration_count = default_iteration_count); - explicit password_t(ql::datum_t const &datum); - - static password_t generate_password_for_unknown_user(); - - uint32_t get_iteration_count() const; - std::array const &get_salt() const; - std::array const &get_hash() const; - bool is_empty() const; - - bool operator==(password_t const &rhs) const; - - RDB_DECLARE_ME_SERIALIZABLE(password_t); - -private: - uint32_t m_iteration_count; - std::array m_salt; - std::array m_hash; - bool m_is_empty; -}; - -} // namespace auth - -#endif // CLUSTERING_ADMINISTRATION_AUTH_PASSWORD_HPP diff --git a/src/clustering/administration/auth/permission_error.hpp b/src/clustering/administration/auth/permission_error.hpp deleted file mode 100644 index f12e822a04b..00000000000 --- a/src/clustering/administration/auth/permission_error.hpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_AUTH_PERMISSION_ERROR_HPP -#define CLUSTERING_ADMINISTRATION_AUTH_PERMISSION_ERROR_HPP - -#include -#include - -#include "clustering/administration/auth/username.hpp" -#include "utils.hpp" - -namespace auth { - -class permission_error_t : public std::runtime_error { -public: - explicit permission_error_t(std::string const &permission) - : std::runtime_error( - "Internal permission error, required `" + permission + "` permission.") { - } - - permission_error_t(username_t const &username, std::string const &permission) - : std::runtime_error( - "User `" + username.to_string() + "` does not have the required `" + - permission + "` permission.") { - } -}; - -} // namespace auth - -#endif // CLUSTERING_ADMINISTRATION_AUTH_PERMISSION_ERROR_HPP diff --git a/src/clustering/administration/auth/permissions.cc b/src/clustering/administration/auth/permissions.cc deleted file mode 100644 index 1bb510f2ff4..00000000000 --- a/src/clustering/administration/auth/permissions.cc +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/auth/permissions.hpp" - -#include "errors.hpp" -#include - -#include "clustering/administration/admin_op_exc.hpp" -#include "containers/archive/optional.hpp" -#include "containers/archive/versioned.hpp" - -namespace auth { - -permissions_t::permissions_t() - : m_read(tribool::Indeterminate), - m_write(tribool::Indeterminate), - m_config(tribool::Indeterminate), - m_connect(r_nullopt) { } - -permissions_t::permissions_t( - tribool read, - tribool write, - tribool config) - : m_read(read), - m_write(write), - m_config(config), - m_connect(r_nullopt) { -} - -permissions_t::permissions_t( - tribool read, - tribool write, - tribool config, - tribool connect) - : m_read(read), - m_write(write), - m_config(config), - m_connect(connect) { -} - -permissions_t::permissions_t(ql::datum_t const &datum, bool global) - : m_read(tribool::Indeterminate), - m_write(tribool::Indeterminate), - m_config(tribool::Indeterminate), - m_connect( - global - ? make_optional(tribool::Indeterminate) - : r_nullopt) { - merge(datum); -} - -tribool datum_to_tribool(ql::datum_t const &datum, std::string const &field) { - if (datum.get_type() == ql::datum_t::R_BOOL) { - return datum.as_bool() ? tribool::True : tribool::False; - } else if (datum.get_type() == ql::datum_t::R_NULL) { - return tribool::Indeterminate; - } else { - throw admin_op_exc_t( - "Expected a boolean or null for `" + field + "`, got " + datum.print() + ".", - query_state_t::FAILED); - } -} - -void permissions_t::merge(ql::datum_t const &datum) { - if (datum.get_type() != ql::datum_t::R_OBJECT) { - throw admin_op_exc_t( - "Expected an object, got " + datum.print() + ".", query_state_t::FAILED); - } - - std::set keys; - for (size_t i = 0; i < datum.obj_size(); ++i) { - keys.insert(datum.get_pair(i).first.to_std()); - } - - ql::datum_t read = datum.get_field("read", ql::NOTHROW); - if (read.has()) { - keys.erase("read"); - set_read(datum_to_tribool(read, "read")); - } - - ql::datum_t write = datum.get_field("write", ql::NOTHROW); - if (write.has()) { - keys.erase("write"); - set_write(datum_to_tribool(write, "write")); - } - - ql::datum_t config = datum.get_field("config", ql::NOTHROW); - if (config.has()) { - keys.erase("config"); - set_config(datum_to_tribool(config, "config")); - } - - ql::datum_t connect = datum.get_field("connect", ql::NOTHROW); - if (connect.has()) { - if (m_connect.has_value()) { - keys.erase("connect"); - set_connect(datum_to_tribool(connect, "connect")); - } else { - throw admin_op_exc_t( - "The `connect` permission is only valid at the global scope.", - query_state_t::FAILED); - } - } - - if (!keys.empty()) { - throw admin_op_exc_t( - "Unexpected key(s) `" + boost::algorithm::join(keys, "`, `") + "`.", - query_state_t::FAILED); - } -} - -tribool permissions_t::get_read() const { - return m_read; -} - -tribool permissions_t::get_write() const { - return m_write; -} - -tribool permissions_t::get_config() const { - return m_config; -} - -tribool permissions_t::get_connect() const { - return m_connect.value_or(tribool::Indeterminate); -} - -bool permissions_t::is_indeterminate() const { - return - m_read == tribool::Indeterminate && - m_write == tribool::Indeterminate && - m_config == tribool::Indeterminate && ( - !m_connect.has_value() || *m_connect == tribool::Indeterminate); -} - -void permissions_t::set_read(tribool read) { - m_read = read; -} - -void permissions_t::set_write(tribool write) { - m_write = write; -} - -void permissions_t::set_config(tribool config) { - m_config = config; -} - -void permissions_t::set_connect(tribool connect) { - guarantee(m_connect.has_value()); - m_connect.set(connect); -} - -ql::datum_t permissions_t::to_datum() const { - ql::datum_object_builder_t datum_object_builder; - - if (m_read != tribool::Indeterminate) { - datum_object_builder.overwrite( - "read", ql::datum_t::boolean(m_read == tribool::True)); - } - - if (m_write != tribool::Indeterminate) { - datum_object_builder.overwrite( - "write", ql::datum_t::boolean(m_write == tribool::True)); - } - - if (m_config != tribool::Indeterminate) { - datum_object_builder.overwrite( - "config", ql::datum_t::boolean(m_config == tribool::True)); - } - - if (m_connect.has_value() && m_connect.get() != tribool::Indeterminate) { - datum_object_builder.overwrite( - "connect", ql::datum_t::boolean(m_connect.get() == tribool::True)); - } - - if (datum_object_builder.empty()) { - return ql::datum_t::null(); - } else { - return std::move(datum_object_builder).to_datum(); - } -} - -std::tuple> -permissions_t::to_tuple() const { - return std::make_tuple( - static_cast(m_read), - static_cast(m_write), - static_cast(m_config), - m_connect.has_value() - ? make_optional(static_cast(m_connect.get())) - : r_nullopt); -} - -bool permissions_t::operator<(permissions_t const &rhs) const { - return to_tuple() < rhs.to_tuple(); -} - -bool permissions_t::operator==(permissions_t const &rhs) const { - return to_tuple() == rhs.to_tuple(); -} - -RDB_IMPL_SERIALIZABLE_4(permissions_t, m_read, m_write, m_config, m_connect); -INSTANTIATE_SERIALIZABLE_SINCE_v2_3(permissions_t); - -} // namespace auth diff --git a/src/clustering/administration/auth/permissions.hpp b/src/clustering/administration/auth/permissions.hpp deleted file mode 100644 index d15d116b1ac..00000000000 --- a/src/clustering/administration/auth/permissions.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_AUTH_PERMISSIONS_HPP -#define CLUSTERING_ADMINISTRATION_AUTH_PERMISSIONS_HPP - -#include "containers/optional.hpp" -#include "containers/tribool.hpp" -#include "rdb_protocol/datum.hpp" -#include "rpc/serialize_macros.hpp" - -namespace auth { - -class permissions_t -{ -public: - permissions_t(); - // The two constructors below are for the non-global and global scopes - // respectively - permissions_t( - tribool read, - tribool write, - tribool config); - permissions_t( - tribool read, - tribool write, - tribool config, - tribool connect); - permissions_t(ql::datum_t const &datum, bool global); - - void merge(ql::datum_t const &datum); - - tribool get_read() const; - tribool get_write() const; - tribool get_config() const; - tribool get_connect() const; - - bool is_indeterminate() const; - - void set_read(tribool); - void set_write(tribool); - void set_config(tribool); - void set_connect(tribool); - - ql::datum_t to_datum() const; - - bool operator<(permissions_t const &rhs) const; - bool operator==(permissions_t const &rhs) const; - - RDB_DECLARE_ME_SERIALIZABLE(permissions_t); - -private: - std::tuple> to_tuple() const; - - tribool m_read; - tribool m_write; - tribool m_config; - - // The `connect` permission is only available at the global scope - optional m_connect; -}; - -} // namespace auth - -#endif // CLUSTERING_ADMINISTRATION_AUTH_PERMISSIONS_HPP diff --git a/src/clustering/administration/auth/permissions_artificial_table_backend.cc b/src/clustering/administration/auth/permissions_artificial_table_backend.cc deleted file mode 100644 index 3a6b6c6d2c0..00000000000 --- a/src/clustering/administration/auth/permissions_artificial_table_backend.cc +++ /dev/null @@ -1,680 +0,0 @@ -// iopyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/auth/permissions_artificial_table_backend.hpp" - -#include "errors.hpp" -#include - -#include "clustering/administration/artificial_reql_cluster_interface.hpp" -#include "clustering/administration/auth/username.hpp" -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/tables/name_resolver.hpp" - -namespace auth { - -permissions_artificial_table_backend_t::permissions_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - std::shared_ptr> - auth_semilattice_view, - std::shared_ptr> - cluster_semilattice_view, - admin_identifier_format_t identifier_format) - : base_artificial_table_backend_t( - name_string_t::guarantee_valid("permissions"), - rdb_context, - name_resolver, - auth_semilattice_view, - cluster_semilattice_view), - m_name_resolver(name_resolver), - m_identifier_format(identifier_format) { -} - -bool permissions_artificial_table_backend_t::read_all_rows_as_vector( - UNUSED auth::user_context_t const &user_context, - UNUSED signal_t *interruptor, - std::vector *rows_out, - UNUSED admin_err_t *error_out) { - rows_out->clear(); - on_thread_t on_thread(home_thread()); - - cluster_semilattice_metadata_t cluster_metadata = - m_name_resolver.get_cluster_metadata(); - - // The "admin" user is faked here - { - ql::datum_t row; - if (global_to_datum( - username_t("admin"), - permissions_t(tribool::True, tribool::True, tribool::True, tribool::True), - &row)) { - rows_out->push_back(std::move(row)); - } - } - - { - ql::datum_t row; - if (database_to_datum( - username_t("admin"), - artificial_reql_cluster_interface_t::database_id, - permissions_t(tribool::True, tribool::True, tribool::True), - cluster_metadata, - &row)) { - rows_out->push_back(std::move(row)); - } - } - - auth_semilattice_metadata_t auth_metadata = m_auth_semilattice_view->get(); - for (auto const &user : auth_metadata.m_users) { - if (user.first.is_admin() || !static_cast(user.second.get_ref())) { - // The "admin" user is faked above and not displayed from the metadata. - continue; - } - - ql::datum_t username = convert_string_to_datum(user.first.to_string()); - - { - ql::datum_t row; - if (global_to_datum( - user.first, - user.second.get_ref()->get_global_permissions(), - &row)) { - rows_out->push_back(std::move(row)); - } - } - - for (auto const &database : user.second.get_ref()->get_database_permissions()) { - ql::datum_t row; - if (database_to_datum( - user.first, - database.first, - database.second, - cluster_metadata, - &row)) { - rows_out->push_back(std::move(row)); - } - } - - for (auto const &table : user.second.get_ref()->get_table_permissions()) { - // `table_to_datum` will look up the database - ql::datum_t row; - if (table_to_datum( - user.first, - r_nullopt, - table.first, - table.second, - cluster_metadata, - &row)) { - rows_out->push_back(std::move(row)); - } - } - } - - return true; -} - -bool permissions_artificial_table_backend_t::read_row( - UNUSED auth::user_context_t const &user_context, - ql::datum_t primary_key, - UNUSED signal_t *interruptor, - ql::datum_t *row_out, - UNUSED admin_err_t *error_out) { - *row_out = ql::datum_t(); - on_thread_t on_thread(home_thread()); - - cluster_semilattice_metadata_t cluster_metadata = - m_name_resolver.get_cluster_metadata(); - - username_t username; - database_id_t database_id; - namespace_id_t table_id; - uint8_t array_size = parse_primary_key( - primary_key, - cluster_metadata, - &username, - &database_id, - &table_id); - if (array_size == 0) { - return true; - } - - if (username.is_admin()) { - switch (array_size) { - case 1: - global_to_datum( - username, - permissions_t(tribool::True, tribool::True, tribool::True, tribool::True), - row_out); - break; - case 2: - if (database_id == artificial_reql_cluster_interface_t::database_id) { - database_to_datum( - username, - database_id, - permissions_t(tribool::True, tribool::True, tribool::True), - cluster_metadata, - row_out); - } - break; - } - return true; - } - - auth_semilattice_metadata_t auth_metadata = m_auth_semilattice_view->get(); - auto user = auth_metadata.m_users.find(username); - if (user == auth_metadata.m_users.end() || - !static_cast(user->second.get_ref())) { - return true; - } - - // Note these functions will only set `row_out` on success. - switch (array_size) { - case 1: - global_to_datum( - username, - user->second.get_ref()->get_global_permissions(), - row_out); - break; - case 2: - database_to_datum( - username, - database_id, - user->second.get_ref()->get_database_permissions(database_id), - cluster_metadata, - row_out); - break; - case 3: - table_to_datum( - username, - make_optional(database_id), - table_id, - user->second.get_ref()->get_table_permissions(table_id), - cluster_metadata, - row_out); - break; - } - - return true; -} - -bool permissions_artificial_table_backend_t::write_row( - UNUSED auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - UNUSED signal_t *interruptor, - admin_err_t *error_out) { - on_thread_t on_thread(home_thread()); - - if (pkey_was_autogenerated) { - *error_out = admin_err_t{ - "You must specify a primary key.", query_state_t::FAILED}; - return false; - } - - cluster_semilattice_metadata_t cluster_metadata = m_cluster_semilattice_view->get(); - - username_t username_primary; - database_id_t database_id_primary; - namespace_id_t table_id_primary; - uint8_t array_size = parse_primary_key( - primary_key, - cluster_metadata, - &username_primary, - &database_id_primary, - &table_id_primary, - error_out); - if (array_size == 0) { - return false; - } - - if (username_primary.is_admin()) { - *error_out = admin_err_t{ - "The permissions of the user `" + username_primary.to_string() + - "` can't be modified.", - query_state_t::FAILED}; - return false; - } - - auth_semilattice_metadata_t auth_metadata = m_auth_semilattice_view->get(); - auto user = auth_metadata.m_users.find(username_primary); - if (user == auth_metadata.m_users.end() || - !static_cast(user->second.get_ref())) { - *error_out = admin_err_t{ - "No user named `" + username_primary.to_string() + "`.", - query_state_t::FAILED}; - return false; - } - - if (new_value_inout->has()) { - std::set keys; - for (size_t i = 0; i < new_value_inout->obj_size(); ++i) { - keys.insert(new_value_inout->get_pair(i).first.to_std()); - } - keys.erase("id"); - - ql::datum_t username = new_value_inout->get_field("user", ql::NOTHROW); - if (username.has()) { - keys.erase("user"); - if (username_t(username.as_str().to_std()) != username_primary) { - *error_out = admin_err_t{ - "The key `user` does not match the primary key.", - query_state_t::FAILED}; - return false; - } - } - - if (array_size > 1) { - ql::datum_t database = new_value_inout->get_field("database", ql::NOTHROW); - if (database.has()) { - keys.erase("database"); - - switch (m_identifier_format) { - case admin_identifier_format_t::name: - { - name_string_t database_name; - if (!convert_name_from_datum( - database, - "database name", - &database_name, - error_out)) { - return false; - } - - - if (m_name_resolver.database_id_to_name( - database_id_primary, cluster_metadata - ).get() != database_name) { - *error_out = admin_err_t{ - "The key `database` does not match the primary key.", - query_state_t::FAILED}; - return false; - } - } - break; - case admin_identifier_format_t::uuid: - { - database_id_t database_id_secondary; - if (!convert_uuid_from_datum( - database, &database_id_secondary, error_out)) { - return false; - } - - if (database_id_primary != database_id_secondary) { - *error_out = admin_err_t{ - "The key `database` does not match the primary key.", - query_state_t::FAILED}; - return false; - } - } - break; - } - } - } - - if (array_size > 2) { - ql::datum_t table = new_value_inout->get_field("table", ql::NOTHROW); - if (table.has()) { - keys.erase("table"); - - switch (m_identifier_format) { - case admin_identifier_format_t::name: - { - name_string_t table_name; - if (!convert_name_from_datum( - table, - "table name", - &table_name, - error_out)) { - return false; - } - - optional table_basic_config = - m_name_resolver.table_id_to_basic_config( - table_id_primary, make_optional(database_id_primary)); - if (!static_cast(table_basic_config) || - table_basic_config->name != table_name) { - *error_out = admin_err_t{ - "The key `table` does not match the primary key.", - query_state_t::FAILED}; - return false; - } - } - break; - case admin_identifier_format_t::uuid: - { - namespace_id_t table_id_secondary; - if (!convert_uuid_from_datum( - table, &table_id_secondary, error_out)) { - return false; - } - - if (table_id_primary != table_id_secondary) { - *error_out = admin_err_t{ - "The key `table` does not match the primary key.", - query_state_t::FAILED}; - return false; - } - } - break; - } - } - } - - bool is_indeterminate = false; - - ql::datum_t permissions = new_value_inout->get_field("permissions", ql::NOTHROW); - if (permissions.has()) { - keys.erase("permissions"); - - try { - switch (array_size) { - case 1: - user->second.apply_write( - [&](optional *inner_user) { - auto &permissions_ref = - inner_user->get().get_global_permissions(); - - permissions_ref.merge(permissions); - is_indeterminate = permissions_ref.is_indeterminate(); - }); - break; - case 2: - user->second.apply_write( - [&](optional *inner_user) { - auto &permissions_ref = - inner_user->get().get_database_permissions( - database_id_primary); - - permissions_ref.merge(permissions); - is_indeterminate = permissions_ref.is_indeterminate(); - }); - break; - case 3: - user->second.apply_write( - [&](optional *inner_user) { - auto &permissions_ref = - inner_user->get().get_table_permissions( - table_id_primary); - - permissions_ref.merge(permissions); - is_indeterminate = permissions_ref.is_indeterminate(); - }); - break; - } - } catch (admin_op_exc_t const &admin_op_exc) { - *error_out = admin_op_exc.to_admin_err(); - return false; - } - } else { - *error_out = admin_err_t{ - "Expected a field `permissions`.", query_state_t::FAILED}; - return false; - } - - if (!keys.empty()) { - *error_out = admin_err_t{ - "Unexpected key(s) `" + boost::algorithm::join(keys, "`, `") + "`.", - query_state_t::FAILED}; - return false; - } - - // Updating the permissions to indeterminate is considered equal to a deletion - if (is_indeterminate) { - *new_value_inout = ql::datum_t(); - } - } else { - switch (array_size) { - case 1: - user->second.apply_write([](optional *inner_user) { - inner_user->get().set_global_permissions( - permissions_t( - tribool::Indeterminate, - tribool::Indeterminate, - tribool::Indeterminate, - tribool::Indeterminate)); - }); - break; - case 2: - user->second.apply_write([&](optional *inner_user) { - inner_user->get().set_database_permissions( - database_id_primary, - permissions_t( - tribool::Indeterminate, - tribool::Indeterminate, - tribool::Indeterminate)); - }); - break; - case 3: - user->second.apply_write([&](optional *inner_user) { - inner_user->get().set_table_permissions( - table_id_primary, - permissions_t( - tribool::Indeterminate, - tribool::Indeterminate, - tribool::Indeterminate)); - }); - break; - } - } - - m_auth_semilattice_view->join(auth_metadata); - - return true; -} - -uint8_t permissions_artificial_table_backend_t::parse_primary_key( - ql::datum_t const &primary_key, - cluster_semilattice_metadata_t const &cluster_metadata, - username_t *username_out, - database_id_t *database_id_out, - namespace_id_t *table_id_out, - admin_err_t *admin_err_out) { - if (primary_key.get_type() != ql::datum_t::R_ARRAY || - primary_key.arr_size() < 1 || - primary_key.arr_size() > 3) { - if (admin_err_out != nullptr) { - *admin_err_out = admin_err_t{ - "Expected an array of one to three items in the primary key, got " + - primary_key.print() + ".", - query_state_t::FAILED}; - } - return 0; - } - - ql::datum_t username = primary_key.get(0, ql::NOTHROW); - if (username.get_type() != ql::datum_t::R_STR) { - if (admin_err_out != nullptr) { - *admin_err_out = admin_err_t{ - "Expected a string as the username, got " + username.print() + ".", - query_state_t::FAILED}; - } - return 0; - } - *username_out = username_t(username.as_str().to_std()); - - if (primary_key.arr_size() > 1) { - ql::datum_t database = primary_key.get(1, ql::NOTHROW); - if (database.get_type() != ql::datum_t::R_STR || - !str_to_uuid(database.as_str().to_std(), database_id_out)) { - if (admin_err_out != nullptr) { - *admin_err_out = admin_err_t{ - "Expected a UUID as the database, got " + database.print() + ".", - query_state_t::FAILED}; - } - return 0; - } - - optional database_name = - m_name_resolver.database_id_to_name(*database_id_out, cluster_metadata); - if (!static_cast(database_name)) { - if (admin_err_out != nullptr) { - *admin_err_out = admin_err_t{ - strprintf( - "No database with UUID `%s` exists.", - uuid_to_str(*database_id_out).c_str()), - query_state_t::FAILED}; - } - return 0; - } - } - - if (primary_key.arr_size() > 2) { - ql::datum_t table = primary_key.get(2, ql::NOTHROW); - if (table.get_type() != ql::datum_t::R_STR || - !str_to_uuid(table.as_str().to_std(), table_id_out)) { - if (admin_err_out != nullptr) { - *admin_err_out = admin_err_t{ - "Expected a UUID as the table, got " + table.print() + ".", - query_state_t::FAILED}; - } - return 0; - } - - optional table_basic_config = - m_name_resolver.table_id_to_basic_config(*table_id_out); - if (!static_cast(table_basic_config)) { - if (admin_err_out != nullptr) { - *admin_err_out = admin_err_t{ - strprintf( - "No table with UUID `%s` exists.", - uuid_to_str(*table_id_out).c_str()), - query_state_t::FAILED}; - } - return 0; - } - - if (table_basic_config->database != *database_id_out) { - if (admin_err_out != nullptr) { - *admin_err_out = admin_err_t{ - strprintf( - "No table with UUID `%s` exists.", - uuid_to_str(*table_id_out).c_str()), - query_state_t::FAILED}; - } - return 0; - } - } - - return primary_key.arr_size(); -} - -bool permissions_artificial_table_backend_t::global_to_datum( - username_t const &username, - permissions_t const &permissions, - ql::datum_t *datum_out) { - ql::datum_t permissions_datum = permissions.to_datum(); - if (permissions_datum.get_type() != ql::datum_t::R_NULL) { - ql::datum_object_builder_t builder; - - ql::datum_array_builder_t id_builder(ql::configured_limits_t::unlimited); - id_builder.add(convert_string_to_datum(username.to_string())); - - builder.overwrite("id", std::move(id_builder).to_datum()); - builder.overwrite("permissions", std::move(permissions_datum)); - builder.overwrite("user", convert_string_to_datum(username.to_string())); - - *datum_out = std::move(builder).to_datum(); - return true; - } - - return false; -} - -bool permissions_artificial_table_backend_t::database_to_datum( - username_t const &username, - database_id_t const &database_id, - permissions_t const &permissions, - cluster_semilattice_metadata_t const &cluster_metadata, - ql::datum_t *datum_out) { - ql::datum_t permissions_datum = permissions.to_datum(); - if (permissions_datum.get_type() != ql::datum_t::R_NULL) { - ql::datum_object_builder_t builder; - - ql::datum_array_builder_t id_builder(ql::configured_limits_t::unlimited); - id_builder.add(convert_string_to_datum(username.to_string())); - id_builder.add(convert_uuid_to_datum(database_id)); - - ql::datum_t database_name_or_uuid; - switch (m_identifier_format) { - case admin_identifier_format_t::name: - { - optional database_name = - m_name_resolver.database_id_to_name( - database_id, cluster_metadata); - database_name_or_uuid = ql::datum_t(database_name.value_or( - name_string_t::guarantee_valid("__deleted_database__")).str()); - } - break; - case admin_identifier_format_t::uuid: - database_name_or_uuid = ql::datum_t(uuid_to_str(database_id)); - break; - } - - builder.overwrite("database", std::move(database_name_or_uuid)); - builder.overwrite("id", std::move(id_builder).to_datum()); - builder.overwrite("permissions", std::move(permissions_datum)); - builder.overwrite("user", convert_string_to_datum(username.to_string())); - - *datum_out = std::move(builder).to_datum(); - return true; - } - - return false; -} - -bool permissions_artificial_table_backend_t::table_to_datum( - username_t const &username, - optional const &database_id, - namespace_id_t const &table_id, - permissions_t const &permissions, - cluster_semilattice_metadata_t const &cluster_metadata, - ql::datum_t *datum_out) { - optional table_basic_config = - m_name_resolver.table_id_to_basic_config(table_id, database_id); - if (!static_cast(table_basic_config)) { - return false; - } - - ql::datum_t permissions_datum = permissions.to_datum(); - if (permissions_datum.get_type() != ql::datum_t::R_NULL) { - ql::datum_object_builder_t builder; - - ql::datum_array_builder_t id_builder(ql::configured_limits_t::unlimited); - id_builder.add(convert_string_to_datum(username.to_string())); - id_builder.add(convert_uuid_to_datum(table_basic_config->database)); - id_builder.add(convert_uuid_to_datum(table_id)); - - ql::datum_t database_name_or_uuid; - ql::datum_t table_name_or_uuid; - switch (m_identifier_format) { - case admin_identifier_format_t::name: - { - optional database_name = - m_name_resolver.database_id_to_name( - table_basic_config->database, cluster_metadata); - database_name_or_uuid = ql::datum_t(database_name.value_or( - name_string_t::guarantee_valid("__deleted_database__")).str()); - table_name_or_uuid = ql::datum_t(table_basic_config->name.str()); - } - break; - case admin_identifier_format_t::uuid: - database_name_or_uuid = - ql::datum_t(uuid_to_str(table_basic_config->database)); - table_name_or_uuid = ql::datum_t(uuid_to_str(table_id)); - break; - } - - builder.overwrite("database", std::move(database_name_or_uuid)); - builder.overwrite("id", std::move(id_builder).to_datum()); - builder.overwrite("permissions", std::move(permissions_datum)); - builder.overwrite("table", std::move(table_name_or_uuid)); - builder.overwrite("user", convert_string_to_datum(username.to_string())); - - *datum_out = std::move(builder).to_datum(); - return true; - } - - return false; -} - -} // namespace auth diff --git a/src/clustering/administration/auth/permissions_artificial_table_backend.hpp b/src/clustering/administration/auth/permissions_artificial_table_backend.hpp deleted file mode 100644 index c5f5b175967..00000000000 --- a/src/clustering/administration/auth/permissions_artificial_table_backend.hpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_AUTH_PERMISSIONS_ARTIFICIAL_TABLE_BACKEND_HPP -#define CLUSTERING_ADMINISTRATION_AUTH_PERMISSIONS_ARTIFICIAL_TABLE_BACKEND_HPP - -#include "clustering/administration/auth/base_artificial_table_backend.hpp" -#include "rdb_protocol/context.hpp" - -class name_resolver_t; - -namespace auth { - -class permissions_artificial_table_backend_t : - public base_artificial_table_backend_t -{ -public: - permissions_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - std::shared_ptr> - auth_semilattice_view, - std::shared_ptr> - cluster_semilattice_view, - admin_identifier_format_t identifier_format); - - bool read_all_rows_as_vector( - auth::user_context_t const &user_context, - signal_t *interruptor, - std::vector *rows_out, - admin_err_t *error_out); - - bool read_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor, - ql::datum_t *row_out, - admin_err_t *error_out); - - bool write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - UNUSED signal_t *interruptor, - admin_err_t *error_out); - -private: - uint8_t parse_primary_key( - ql::datum_t const &primary_key, - cluster_semilattice_metadata_t const &cluster_metadata, - username_t *username_out, - database_id_t *database_id_out, - namespace_id_t *table_id_out, - admin_err_t *admin_err_out = nullptr); - - bool global_to_datum( - username_t const &username, - permissions_t const &permissions, - ql::datum_t *datum_out); - - bool database_to_datum( - username_t const &username, - database_id_t const &database_id, - permissions_t const &permissions, - cluster_semilattice_metadata_t const &cluster_metadata, - ql::datum_t *datum_out); - - bool table_to_datum( - username_t const &username, - optional const &database_id, - namespace_id_t const &table_id, - permissions_t const &permissions, - cluster_semilattice_metadata_t const &cluster_metadata, - ql::datum_t *datum_out); - -private: - name_resolver_t const &m_name_resolver; - admin_identifier_format_t m_identifier_format; -}; - -} // namespace auth - -#endif // CLUSTERING_ADMINISTRATION_AUTH_PERMISSIONS_ARTIFICIAL_TABLE_BACKEND_HPP diff --git a/src/clustering/administration/auth/plaintext_authenticator.cc b/src/clustering/administration/auth/plaintext_authenticator.cc deleted file mode 100644 index 8ea49f0e8f8..00000000000 --- a/src/clustering/administration/auth/plaintext_authenticator.cc +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/auth/plaintext_authenticator.hpp" - -#include "clustering/administration/auth/authentication_error.hpp" -#include "clustering/administration/metadata.hpp" -#include "crypto/compare_equal.hpp" -#include "crypto/pbkcs5_pbkdf2_hmac.hpp" -#include "crypto/saslprep.hpp" - -namespace auth { - -plaintext_authenticator_t::plaintext_authenticator_t( - clone_ptr_t> auth_watchable, - username_t const &username) - : base_authenticator_t(auth_watchable), - m_username(username), - m_is_authenticated(false) { -} - -/* virtual */ std::string plaintext_authenticator_t::next_message( - std::string const &password) THROWS_ONLY(authentication_error_t) { - optional user; - - m_auth_watchable->apply_read( - [&](auth_semilattice_metadata_t const *auth_metadata) { - auto iter = auth_metadata->m_users.find(m_username); - if (iter != auth_metadata->m_users.end()) { - user = iter->second.get_ref(); - } - }); - - if (!static_cast(user)) { - // The user doesn't exist - throw authentication_error_t(17, "Unknown user"); - } - - std::array hash = - crypto::pbkcs5_pbkdf2_hmac_sha256( - crypto::saslprep(password), - user->get_password().get_salt(), - user->get_password().get_iteration_count()); - - if (!crypto::compare_equal(user->get_password().get_hash(), hash)) { - throw authentication_error_t(12, "Wrong password"); - } - - m_is_authenticated = true; - - return ""; -} - -/* virtual */ username_t plaintext_authenticator_t::get_authenticated_username() const - THROWS_ONLY(authentication_error_t) { - if (m_is_authenticated) { - return m_username; - } else { - throw authentication_error_t(20, "No authenticated user"); - } -} - -} // namespace auth diff --git a/src/clustering/administration/auth/plaintext_authenticator.hpp b/src/clustering/administration/auth/plaintext_authenticator.hpp deleted file mode 100644 index ce44883f247..00000000000 --- a/src/clustering/administration/auth/plaintext_authenticator.hpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_AUTH_PLAINTEXT_AUTHENTICATOR_HPP -#define CLUSTERING_ADMINISTRATION_AUTH_PLAINTEXT_AUTHENTICATOR_HPP - -#include - -#include "clustering/administration/auth/base_authenticator.hpp" -#include "clustering/administration/auth/user.hpp" - -namespace auth { - -class plaintext_authenticator_t : public base_authenticator_t { -public: - plaintext_authenticator_t( - clone_ptr_t> auth_watchable, - username_t const &username = username_t("admin")); - - /* virtual */ std::string next_message(std::string const &) - THROWS_ONLY(authentication_error_t); - /* virtual */ username_t get_authenticated_username() const - THROWS_ONLY(authentication_error_t); - -private: - username_t m_username; - bool m_is_authenticated; -}; - -} // namespace auth - -#endif // CLUSTERING_ADMINISTRATION_AUTH_PLAINTEXT_AUTHENTICATOR_HPP diff --git a/src/clustering/administration/auth/scram_authenticator.cc b/src/clustering/administration/auth/scram_authenticator.cc deleted file mode 100644 index 7719ffa5312..00000000000 --- a/src/clustering/administration/auth/scram_authenticator.cc +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/auth/scram_authenticator.hpp" - -#include "clustering/administration/auth/authentication_error.hpp" -#include "clustering/administration/auth/password.hpp" -#include "clustering/administration/auth/username.hpp" -#include "crypto/base64.hpp" -#include "crypto/error.hpp" -#include "crypto/hash.hpp" -#include "crypto/hmac.hpp" -#include "crypto/random.hpp" - -namespace auth { - -scram_authenticator_t::scram_authenticator_t( - clone_ptr_t> auth_watchable) - : base_authenticator_t(auth_watchable), - m_state(state_t::FIRST_MESSAGE) { -} - -std::string scram_authenticator_t::next_message(std::string const &message) - THROWS_ONLY(authentication_error_t) { - try { - switch (m_state) { - case state_t::FIRST_MESSAGE: { - if (message.find("n,") != 0) { - throw authentication_error_t(15, "Channel binding is not supported"); - } - size_t client_first_message_bare_offset = message.find(',', 2); - if (client_first_message_bare_offset == std::string::npos) { - throw authentication_error_t(10, "Invalid encoding"); - } - m_client_first_message_bare = - message.substr(client_first_message_bare_offset + 1); - - std::string client_nonce; - std::map attributes = - split_attributes(m_client_first_message_bare); - for (auto const &attribute : attributes) { - switch (attribute.first) { - case 'n': - m_username = saslname_decode(attribute.second); - break; - case 'r': - client_nonce = attribute.second; - break; - case 'm': - throw authentication_error_t(11, "Extensions not supported"); - default: - throw authentication_error_t(10, "Invalid encoding"); - } - } - if (attributes.count('n') == 0 || attributes.count('r') == 0) { - throw authentication_error_t(10, "Invalid encoding"); - } - - m_auth_watchable->apply_read( - [&](auth_semilattice_metadata_t const *auth_metadata) { - auto user = auth_metadata->m_users.find(m_username); - if (user == auth_metadata->m_users.end() || - !static_cast(user->second.get_ref())) { - m_is_user_known = false; - m_password = - password_t::generate_password_for_unknown_user(); - } else { - m_is_user_known = true; - m_password = user->second.get_ref()->get_password(); - } - }); - - std::array server_nonce = crypto::random_bytes<18>(); - m_nonce = client_nonce + crypto::base64_encode(server_nonce); - - m_server_first_message = - "r=" + m_nonce + - ",s=" + crypto::base64_encode(m_password.get_salt()) + - ",i=" + std::to_string(m_password.get_iteration_count()); - - m_state = state_t::FINAL_MESSAGE; - - return m_server_first_message; - } - case state_t::FINAL_MESSAGE: { - if (!m_is_user_known) { - throw authentication_error_t(17, "Unknown user"); - } - - // ClientKey := HMAC(SaltedPassword, "Client Key") - std::array client_key = - crypto::hmac_sha256(m_password.get_hash(), "Client Key"); - - // StoredKey := H(ClientKey) - std::array stored_key = - crypto::sha256(client_key); - - /* AuthMessage := client-first-message-bare + "," + - server-first-message + "," + - client-final-message-without-proof */ - std::string auth_message = - m_client_first_message_bare + "," + - m_server_first_message + "," + - message.substr(0, message.find(",p=")); - - // ClientSignature := HMAC(StoredKey, AuthMessage) - std::array client_signature = - crypto::hmac_sha256(stored_key, auth_message); - - // ClientProof := ClientKey XOR ClientSignature - std::array client_proof; - for (size_t i = 0; i < SHA256_DIGEST_LENGTH; ++i) { - client_proof[i] = (client_key[i] ^ client_signature[i]); - } - - std::map attributes = split_attributes(message); - for (auto const &attribute : attributes) { - switch (attribute.first) { - case 'c': - if (attribute.second != "biws") { - throw authentication_error_t(10, "Invalid encoding"); - } - break; - case 'r': - if (attribute.second != m_nonce) { - // There's no specific error for invalid nonce - throw authentication_error_t(20, "Other error"); - } - break; - case 'p': - if (attribute.second != - crypto::base64_encode(client_proof)) { - throw authentication_error_t(12, "Wrong password"); - } - m_state = state_t::AUTHENTICATED; - break; - case 'm': - throw authentication_error_t(11, "Extensions not supported"); - default: - throw authentication_error_t(10, "Invalid encoding"); - } - } - if (attributes.count('c') == 0 || - attributes.count('r') == 0 || - attributes.count('p') == 0) { - throw authentication_error_t(10, "Invalid encoding"); - } - - // ServerKey := HMAC(SaltedPassword, "Server Key") - std::array server_key = - crypto::hmac_sha256(m_password.get_hash(), "Server Key"); - - // ServerSignature := HMAC(ServerKey, AuthMessage) - std::array server_signature = - crypto::hmac_sha256(server_key, auth_message); - - return "v=" + crypto::base64_encode(server_signature); - } - case state_t::ERROR: - throw authentication_error_t( - 20, "A previous error occured, no more messages expected."); - case state_t::AUTHENTICATED: - throw authentication_error_t( - 20, "Already authenticated, no more messages expected."); - default: - unreachable(); - } - } catch (...) { - m_state = state_t::ERROR; - throw; - } -} - -/* virtual */ username_t scram_authenticator_t::get_authenticated_username() const - THROWS_ONLY(authentication_error_t) { - if (m_state == state_t::AUTHENTICATED) { - return m_username; - } else { - throw authentication_error_t(20, "No authenticated user"); - } -} - -/* static */ std::map scram_authenticator_t::split_attributes( - std::string const &message) { - std::map attributes; - - size_t attribute_offset = 0; - while (attribute_offset < message.size()) { - size_t equals_sign_offset = message.find('=', attribute_offset); - if (equals_sign_offset == std::string::npos) { - throw authentication_error_t(10, "Invalid encoding"); - } - if ((equals_sign_offset - attribute_offset) != 1) { - throw authentication_error_t(10, "Invalid encoding"); - } - - char key = message[attribute_offset]; - if (attributes.count(key) != 0) { - throw authentication_error_t(10, "Invalid encoding"); - } - - size_t value_offset = equals_sign_offset + 1; - size_t comma_offset = message.find(',', value_offset); - if (comma_offset == std::string::npos) { - attributes.insert(std::make_pair(key, message.substr(value_offset))); - attribute_offset = message.size(); - } else { - attributes.insert(std::make_pair( - key, message.substr(value_offset, comma_offset - value_offset))); - attribute_offset = comma_offset + 1; - } - } - - return attributes; -} - -/* static */ username_t scram_authenticator_t::saslname_decode( - std::string const &saslname) { - std::string username; - username.reserve(saslname.size()); - - for (size_t offset = 0; offset < saslname.size(); ++offset) { - switch (saslname[offset]) { - case '=': - if (offset + 2 >= saslname.size()) { - throw authentication_error_t(18, "Invalid username encoding"); - } - - if (saslname[offset + 1] == '2' && saslname[offset + 2] == 'C') { - username.push_back(','); - } else if (saslname[offset + 1] == '3' && saslname[offset + 2] == 'D') { - username.push_back('='); - } else { - throw authentication_error_t(18, "Invalid username encoding"); - } - - offset += 2; - default: - username.push_back(saslname[offset]); - break; - } - } - - try { - // Note that the `username_t` constructor applies SASLPrep, and may throw - return username_t(username); - } catch (crypto::error_t const &) { - throw authentication_error_t(18, "Invalid username encoding"); - } -} - -} // namespace auth diff --git a/src/clustering/administration/auth/scram_authenticator.hpp b/src/clustering/administration/auth/scram_authenticator.hpp deleted file mode 100644 index 9ed40c67baa..00000000000 --- a/src/clustering/administration/auth/scram_authenticator.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_AUTH_SCRAM_AUTHENTICATOR_HPP -#define CLUSTERING_ADMINISTRATION_AUTH_SCRAM_AUTHENTICATOR_HPP - -#include -#include - -#include "clustering/administration/auth/base_authenticator.hpp" -#include "clustering/administration/auth/password.hpp" -#include "clustering/administration/auth/username.hpp" -#include "clustering/administration/metadata.hpp" - -namespace auth { - -class scram_authenticator_t : public base_authenticator_t { -public: - scram_authenticator_t( - clone_ptr_t> auth_watchable); - - /* virtual */ std::string next_message(std::string const &) - THROWS_ONLY(authentication_error_t); - /* virtual */ username_t get_authenticated_username() const - THROWS_ONLY(authentication_error_t); - - static std::map split_attributes(std::string const &message); - static username_t saslname_decode(std::string const &saslname); - -private: - enum class state_t{FIRST_MESSAGE, FINAL_MESSAGE, ERROR, AUTHENTICATED} m_state; - std::string m_client_first_message_bare; - username_t m_username; - password_t m_password; - bool m_is_user_known; - std::string m_nonce; - std::string m_server_first_message; -}; - -} // namespace auth - -#endif // CLUSTERING_ADMINISTRATION_AUTH_SCRAM_AUTHENTICATOR_HPP diff --git a/src/clustering/administration/auth/user.cc b/src/clustering/administration/auth/user.cc deleted file mode 100644 index 4f0a24631c5..00000000000 --- a/src/clustering/administration/auth/user.cc +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/auth/user.hpp" - -#include -#include - -#include "errors.hpp" -#include - -#include "arch/runtime/runtime_utils.hpp" -#include "clustering/administration/artificial_reql_cluster_interface.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/tables/table_metadata.hpp" -#include "clustering/table_manager/table_meta_client.hpp" -#include "containers/archive/boost_types.hpp" -#include "containers/archive/stl_types.hpp" -#include "containers/archive/versioned.hpp" - -namespace auth { - -// Note that `m_global_permissions` has a different constructor from the other -// permissions since it has the `"connect"` permission, be sure to construct it - -user_t::user_t() - : m_password(), - m_global_permissions( - tribool::Indeterminate, - tribool::Indeterminate, - tribool::Indeterminate, - tribool::Indeterminate) { -} - -user_t::user_t(password_t password, permissions_t global_permissions) - : m_password(std::move(password)), - m_global_permissions(std::move(global_permissions)) { -} - -user_t::user_t(ql::datum_t const &datum) - : m_password(password_t("")), - m_global_permissions( - tribool::Indeterminate, - tribool::Indeterminate, - tribool::Indeterminate, - tribool::Indeterminate) { - merge(datum); -} - -void user_t::merge(ql::datum_t const &datum) { - if (!datum.has() || datum.get_type() != ql::datum_t::R_OBJECT) { - throw admin_op_exc_t( - "Expected an object, got " + datum.print() + ".", query_state_t::FAILED); - } - - ql::datum_t id = datum.get_field("id", ql::NOTHROW); - if (!id.has()) { - throw admin_op_exc_t("Expected a field named `id`.", query_state_t::FAILED); - } - - ql::datum_t password = datum.get_field("password", ql::NOTHROW); - if (!password.has()) { - throw admin_op_exc_t("Expected a field named `password`.", query_state_t::FAILED); - } - - if (datum.obj_size() != 2) { - std::set keys; - for (size_t i = 0; i < datum.obj_size(); ++i) { - keys.insert(datum.get_pair(i).first.to_std()); - } - keys.erase("id"); - keys.erase("password"); - - throw admin_op_exc_t( - "Unexpected key(s) `" + boost::algorithm::join(keys, "`, `") + "`.", - query_state_t::FAILED); - } - - if (password.get_type() == ql::datum_t::R_OBJECT) { - set_password(password_t(password)); - } else if (password.get_type() == ql::datum_t::R_STR) { - set_password(password_t(password.as_str().to_std())); - } else if (password.get_type() == ql::datum_t::R_BOOL) { - if (password.as_bool()) { - if (m_password.is_empty()) { - throw admin_op_exc_t( - "Expected an object or string to set the password, or `false` to " - "keep it unset, got " + password.print() + ".", - query_state_t::FAILED); - } - } else { - set_password(password_t("")); - } - } else { - throw admin_op_exc_t( - "Expected an object, string or boolean for `password`, got " + - password.print() + ".", - query_state_t::FAILED); - } -} - -password_t const &user_t::get_password() const { - return m_password; -} - -void user_t::set_password(password_t password) { - m_password = std::move(password); -} - -permissions_t const &user_t::get_global_permissions() const { - return m_global_permissions; -} - -permissions_t &user_t::get_global_permissions() { - return m_global_permissions; -} - -void user_t::set_global_permissions(permissions_t permissions) { - m_global_permissions = std::move(permissions); -} - -std::map const &user_t::get_database_permissions() const { - return m_database_permissions; -} - -permissions_t user_t::get_database_permissions(database_id_t const &database_id) const { - auto iter = m_database_permissions.find(database_id); - if (iter != m_database_permissions.end()) { - return iter->second; - } else { - return permissions_t( - tribool::Indeterminate, tribool::Indeterminate, tribool::Indeterminate); - } -} - -permissions_t &user_t::get_database_permissions(database_id_t const &database_id) { - return m_database_permissions.insert( - std::make_pair( - database_id, - permissions_t( - tribool::Indeterminate, - tribool::Indeterminate, - tribool::Indeterminate))).first->second; -} - -void user_t::set_database_permissions( - database_id_t const &database_id, - permissions_t permissions) { - if (permissions.is_indeterminate()) { - m_database_permissions.erase(database_id); - } else { - m_database_permissions[database_id] = std::move(permissions); - } -} - -std::map const &user_t::get_table_permissions() const { - return m_table_permissions; -} - -permissions_t user_t::get_table_permissions(namespace_id_t const &table_id) const { - auto iter = m_table_permissions.find(table_id); - if (iter != m_table_permissions.end()) { - return iter->second; - } else { - return permissions_t( - tribool::Indeterminate, tribool::Indeterminate, tribool::Indeterminate); - } -} - -permissions_t &user_t::get_table_permissions(namespace_id_t const &table_id) { - return m_table_permissions.insert( - std::make_pair( - table_id, - permissions_t( - tribool::Indeterminate, - tribool::Indeterminate, - tribool::Indeterminate))).first->second; -} - -void user_t::set_table_permissions( - namespace_id_t const &table_id, - permissions_t permissions) { - if (permissions.is_indeterminate()) { - m_table_permissions.erase(table_id); - } else { - m_table_permissions[table_id] = std::move(permissions); - } -} - -bool user_t::has_read_permission( - database_id_t const &database_id, - namespace_id_t const &table_id) const { - auto table = m_table_permissions.find(table_id); - if (table != m_table_permissions.end()) { - tribool table_permission = table->second.get_read(); - if (table_permission != tribool::Indeterminate) { - return table_permission == tribool::True; - } - } - - auto database = m_database_permissions.find(database_id); - if (database != m_database_permissions.end()) { - tribool database_permission = database->second.get_read(); - if (database_permission != tribool::Indeterminate) { - return database_permission == tribool::True; - } - } - - if (database_id != artificial_reql_cluster_interface_t::database_id) { - return m_global_permissions.get_read() == tribool::True; - } else { - // The artificial table does not inherit permissions from the global scope. - return false; - } -} - -bool user_t::has_write_permission( - database_id_t const &database_id, - namespace_id_t const &table_id) const { - auto table = m_table_permissions.find(table_id); - if (table != m_table_permissions.end()) { - tribool table_permission = table->second.get_write(); - if (table_permission != tribool::Indeterminate) { - return table_permission == tribool::True; - } - } - - auto database = m_database_permissions.find(database_id); - if (database != m_database_permissions.end()) { - tribool database_permission = database->second.get_write(); - if (database_permission != tribool::Indeterminate) { - return database_permission == tribool::True; - } - } - - if (database_id != artificial_reql_cluster_interface_t::database_id) { - return m_global_permissions.get_write() == tribool::True; - } else { - // The artificial table does not inherit permissions from the global scope. - return false; - } -} - -bool user_t::has_config_permission() const { - return m_global_permissions.get_config() == tribool::True; -} - -bool user_t::has_config_permission( - database_id_t const &database_id) const { - auto database = m_database_permissions.find(database_id); - if (database != m_database_permissions.end()) { - tribool database_permission = database->second.get_config(); - if (database_permission != tribool::Indeterminate) { - return database_permission == tribool::True; - } - } - - if (database_id != artificial_reql_cluster_interface_t::database_id) { - return has_config_permission(); - } else { - // The artificial table does not inherit permissions from the global scope. - return false; - } -} - -bool user_t::has_config_permission( - database_id_t const &database_id, - namespace_id_t const &table_id) const { - auto table = m_table_permissions.find(table_id); - if (table != m_table_permissions.end()) { - tribool table_permission = table->second.get_config(); - if (table_permission != tribool::Indeterminate) { - return table_permission == tribool::True; - } - } - - return has_config_permission(database_id); -} - -bool user_t::has_connect_permission() const { - return m_global_permissions.get_connect() == tribool::True; -} - -bool user_t::operator==(user_t const &rhs) const { - return - m_password == rhs.m_password && - m_global_permissions == rhs.m_global_permissions && - m_database_permissions == rhs.m_database_permissions && - m_table_permissions == rhs.m_table_permissions; -} - -RDB_IMPL_SERIALIZABLE_4( - user_t, - m_password, - m_global_permissions, - m_database_permissions, - m_table_permissions); -INSTANTIATE_SERIALIZABLE_SINCE_v2_3(user_t); - -} // namespace auth diff --git a/src/clustering/administration/auth/user.hpp b/src/clustering/administration/auth/user.hpp deleted file mode 100644 index 4609c9db9c4..00000000000 --- a/src/clustering/administration/auth/user.hpp +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_AUTH_USER_HPP -#define CLUSTERING_ADMINISTRATION_AUTH_USER_HPP - -#include -#include - -#include "clustering/administration/auth/password.hpp" -#include "clustering/administration/auth/permissions.hpp" -#include "clustering/administration/auth/username.hpp" -#include "containers/uuid.hpp" -#include "rdb_protocol/context.hpp" -#include "rpc/serialize_macros.hpp" - -class cluster_semilattice_metadata_t; -class table_basic_config_t; -class table_meta_client_t; - -namespace auth { - -class user_t { -public: - user_t(); - explicit user_t(password_t password, permissions_t global_permissions = permissions_t()); - explicit user_t(ql::datum_t const &datum); - - void merge(ql::datum_t const &datum); - - password_t const &get_password() const; - void set_password(password_t password); - - permissions_t const &get_global_permissions() const; - permissions_t &get_global_permissions(); - void set_global_permissions(permissions_t permissions); - - std::map const &get_database_permissions() const; - permissions_t get_database_permissions(database_id_t const &database_id) const; - permissions_t &get_database_permissions(database_id_t const &database_id); - void set_database_permissions( - database_id_t const &database_id, - permissions_t permissions); - - std::map const &get_table_permissions() const; - permissions_t get_table_permissions(namespace_id_t const &table_id) const; - permissions_t &get_table_permissions(namespace_id_t const &table_id); - void set_table_permissions( - namespace_id_t const &table_id, - permissions_t permissions); - - bool has_read_permission( - database_id_t const &database_id, - namespace_id_t const &table_id) const; - - bool has_write_permission( - database_id_t const &database_id, - namespace_id_t const &table_id) const; - - bool has_config_permission() const; - bool has_config_permission( - database_id_t const &database_id) const; - bool has_config_permission( - database_id_t const &database_id, - namespace_id_t const &table_id) const; - - bool has_connect_permission() const; - - bool operator==(user_t const &rhs) const; - - RDB_DECLARE_ME_SERIALIZABLE(user_t); - -private: - password_t m_password; - permissions_t m_global_permissions; - std::map m_database_permissions; - std::map m_table_permissions; -}; - -} // namespace auth - -#endif // CLUSTERING_ADMINISTRATION_AUTH_USER_HPP diff --git a/src/clustering/administration/auth/user_context.cc b/src/clustering/administration/auth/user_context.cc deleted file mode 100644 index 41bdc9897e4..00000000000 --- a/src/clustering/administration/auth/user_context.cc +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/auth/user_context.hpp" - -#include "clustering/administration/metadata.hpp" -#include "containers/archive/boost_types.hpp" -#include "rdb_protocol/context.hpp" - -namespace auth { - -user_context_t::user_context_t() { } - -user_context_t::user_context_t(permissions_t permissions) - : m_context(std::move(permissions)), - m_read_only(false) { -} - -user_context_t::user_context_t(username_t username, bool read_only) - : m_context(std::move(username)), - m_read_only(read_only) { -} - -bool user_context_t::is_admin_user() const { - if (auto const *username = boost::get(&m_context)) { - return username->is_admin(); - } else { - return false; - } -} - -void user_context_t::require_admin_user() const THROWS_ONLY(permission_error_t) { - if (!is_admin_user()) { - throw permission_error_t("admin"); - } -} - -template -void require_permission_internal( - boost::variant const &context, - bool read_only, - rdb_context_t *rdb_context, - F permissions_selector_function, - G username_selector_function, - std::string const &permission_name) { - if (rdb_context == nullptr) { - // This can only happen if the environment was constructed via the constructor - // that exists for certain sindex and unit-testing purposes - return; - } - - // Note I'd preferred to have used a `boost::static_visitor`, but that would require - // storing the functions in an `std::function`, causing an allocation - if (auto const *permissions = boost::get(&context)) { - if (!permissions_selector_function(*permissions)) { - throw permission_error_t(permission_name); - } - } else if (auto const *username = boost::get(&context)) { - if (read_only) { - throw auth::permission_error_t(*username, permission_name); - } - // The admin user always has the permission - if (!username->is_admin()) { - rdb_context->get_auth_watchable()->apply_read( - [&](auth_semilattice_metadata_t const *auth_metadata) { - auto user = auth_metadata->m_users.find(*username); - if (user == auth_metadata->m_users.end() || - !static_cast(user->second.get_ref()) || - !username_selector_function(user->second.get_ref().get())) { - throw auth::permission_error_t(*username, permission_name); - } - }); - } - } else { - unreachable(); - } -} - -void user_context_t::require_read_permission( - rdb_context_t *rdb_context, - database_id_t const &database_id, - namespace_id_t const &table_id) const THROWS_ONLY(permission_error_t) { - require_permission_internal( - m_context, - // Ignore the read-only flag for reads - false, - rdb_context, - [&](permissions_t const &permissions) -> bool { - return permissions.get_read() == tribool::True; - }, - [&](user_t const &user) -> bool { - return user.has_read_permission(database_id, table_id); - }, - "read"); -} - -void user_context_t::require_write_permission( - rdb_context_t *rdb_context, - database_id_t const &database_id, - namespace_id_t const &table_id) const THROWS_ONLY(permission_error_t) { - require_permission_internal( - m_context, - m_read_only, - rdb_context, - [&](permissions_t const &permissions) -> bool { - return permissions.get_read() == tribool::True - && permissions.get_write() == tribool::True; - }, - [&](user_t const &user) -> bool { - return user.has_read_permission(database_id, table_id) && - user.has_write_permission(database_id, table_id); - }, - "write"); -} - -void user_context_t::require_config_permission( - rdb_context_t *rdb_context) const THROWS_ONLY(permission_error_t) { - require_permission_internal( - m_context, - m_read_only, - rdb_context, - [&](permissions_t const &permissions) -> bool { - return permissions.get_config() == tribool::True; - }, - [&](auth::user_t const &user) -> bool { - return user.has_config_permission(); - }, - "config"); -}; - -void user_context_t::require_config_permission( - rdb_context_t *rdb_context, - database_id_t const &database_id) const THROWS_ONLY(permission_error_t) { - require_permission_internal( - m_context, - m_read_only, - rdb_context, - [&](permissions_t const &permissions) -> bool { - return permissions.get_config() == tribool::True; - }, - [&](auth::user_t const &user) -> bool { - return user.has_config_permission(database_id); - }, - "config"); -} - -void user_context_t::require_config_permission( - rdb_context_t *rdb_context, - database_id_t const &database_id, - namespace_id_t const &table_id) const THROWS_ONLY(permission_error_t) { - require_permission_internal( - m_context, - m_read_only, - rdb_context, - [&](permissions_t const &permissions) -> bool { - return permissions.get_config() == tribool::True; - }, - [&](auth::user_t const &user) -> bool { - return user.has_config_permission(database_id, table_id); - }, - "config"); -} - -void user_context_t::require_config_permission( - rdb_context_t *rdb_context, - database_id_t const &database_id, - std::set const &table_ids) const THROWS_ONLY(permission_error_t) { - require_permission_internal( - m_context, - m_read_only, - rdb_context, - [&](permissions_t const &permissions) -> bool { - return permissions.get_config() == tribool::True; - }, - [&](auth::user_t const &user) -> bool { - // First check the permissions on the database - if (!user.has_config_permission(database_id)) { - return false; - } - - // Next, for every table, check if the user has permissions on that table - for (auto const &table_id : table_ids) { - if (!user.has_config_permission(database_id, table_id)) { - return false; - } - } - - return true; - }, - "config"); -} - -void user_context_t::require_connect_permission( - rdb_context_t *rdb_context) const THROWS_ONLY(permission_error_t) { - require_permission_internal( - m_context, - // Ignore the read-only flag for connect, they have no write or config impact - false, - rdb_context, - [&](permissions_t const &permissions) -> bool { - return permissions.get_connect() == tribool::True; - }, - [&](user_t const &user) -> bool { - return user.has_connect_permission(); - }, - "connect"); -} - -std::string user_context_t::to_string() const { - if (auto const *username = boost::get(&m_context)) { - return username->to_string(); - } else { - return "internal"; - } -} - -bool user_context_t::operator<(user_context_t const &rhs) const { - return std::tie(m_context, m_read_only) < std::tie(rhs.m_context, rhs.m_read_only); -} - -bool user_context_t::operator==(user_context_t const &rhs) const { - return std::tie(m_context, m_read_only) == std::tie(rhs.m_context, rhs.m_read_only); -} - -bool user_context_t::operator!=(user_context_t const &rhs) const { - return !(*this == rhs); -} - -RDB_IMPL_SERIALIZABLE_2( - user_context_t, - m_context, - m_read_only); -INSTANTIATE_SERIALIZABLE_FOR_CLUSTER(user_context_t); - -} // namespace auth diff --git a/src/clustering/administration/auth/user_context.hpp b/src/clustering/administration/auth/user_context.hpp deleted file mode 100644 index f14d05d922b..00000000000 --- a/src/clustering/administration/auth/user_context.hpp +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_AUTH_USER_CONTEXT_HPP -#define CLUSTERING_ADMINISTRATION_AUTH_USER_CONTEXT_HPP - -#include -#include - -#include "errors.hpp" -#include - -#include "clustering/administration/auth/permission_error.hpp" -#include "clustering/administration/auth/permissions.hpp" -#include "clustering/administration/auth/username.hpp" -#include "containers/uuid.hpp" -#include "rpc/serialize_macros.hpp" - -namespace auth { - -class user_context_t -{ -public: - user_context_t(); - explicit user_context_t(permissions_t permissions); - explicit user_context_t(username_t username, bool read_only = false); - - bool is_admin_user() const; - - void require_admin_user() const THROWS_ONLY(permission_error_t); - - void require_read_permission( - rdb_context_t *rdb_context, - database_id_t const &database_id, - namespace_id_t const &table_id) const THROWS_ONLY(permission_error_t); - - void require_write_permission( - rdb_context_t *rdb_context, - database_id_t const &database_id, - namespace_id_t const &table_id) const THROWS_ONLY(permission_error_t); - - void require_config_permission( - rdb_context_t *rdb_context) const THROWS_ONLY(permission_error_t); - void require_config_permission( - rdb_context_t *rdb_context, - database_id_t const &database) const THROWS_ONLY(permission_error_t); - void require_config_permission( - rdb_context_t *rdb_context, - database_id_t const &database_id, - namespace_id_t const &table_id) const THROWS_ONLY(permission_error_t); - void require_config_permission( - rdb_context_t *rdb_context, - database_id_t const &database_id, - std::set const &table_ids) const THROWS_ONLY( - permission_error_t); - - void require_connect_permission( - rdb_context_t *rdb_context) const THROWS_ONLY(permission_error_t); - - std::string to_string() const; - - bool operator<(user_context_t const &rhs) const; - bool operator==(user_context_t const &rhs) const; - bool operator!=(user_context_t const &rhs) const; - - RDB_DECLARE_ME_SERIALIZABLE(user_context_t); - -private: - boost::variant m_context; - bool m_read_only; -}; - -} // namespace auth - -#endif // CLUSTERING_ADMINISTRATION_AUTH_USER_CONTEXT_HPP diff --git a/src/clustering/administration/auth/username.cc b/src/clustering/administration/auth/username.cc deleted file mode 100644 index 133ddd0d1b9..00000000000 --- a/src/clustering/administration/auth/username.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/auth/username.hpp" - -#include "clustering/administration/auth/authentication_error.hpp" -#include "containers/archive/stl_types.hpp" -#include "crypto/saslprep.hpp" - -namespace auth { - -username_t::username_t() { } - -username_t::username_t(std::string const &username) { - m_username = crypto::saslprep(username); - if (m_username.empty()) { - throw authentication_error_t(18, "Empty username"); - } -} - -bool username_t::is_admin() const { - return m_username == "admin"; -} - -std::string const &username_t::to_string() const { - return m_username; -} - -bool username_t::operator<(username_t const &rhs) const { - return m_username < rhs.m_username; -} - -bool username_t::operator==(username_t const &rhs) const { - return m_username == rhs.m_username; -} - -bool username_t::operator!=(username_t const &rhs) const { - return !(*this == rhs); -} - -RDB_IMPL_SERIALIZABLE_1(username_t, m_username); -INSTANTIATE_SERIALIZABLE_SINCE_v2_3(username_t); - -} // namespace auth diff --git a/src/clustering/administration/auth/username.hpp b/src/clustering/administration/auth/username.hpp deleted file mode 100644 index 15ec59e98b8..00000000000 --- a/src/clustering/administration/auth/username.hpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_AUTH_USERNAME_HPP -#define CLUSTERING_ADMINISTRATION_AUTH_USERNAME_HPP - -#include - -#include "rpc/serialize_macros.hpp" - -namespace auth { - -class username_t -{ -public: - username_t(); - explicit username_t(std::string const &username); - - bool is_admin() const; - - std::string const &to_string() const; - - bool operator<(username_t const &rhs) const; - bool operator==(username_t const &rhs) const; - bool operator!=(username_t const &rhs) const; - - RDB_DECLARE_ME_SERIALIZABLE(username_t); - -private: - std::string m_username; -}; - -} // namespace auth - -#endif // CLUSTERING_ADMINISTRATION_AUTH_USERNAME_HPP diff --git a/src/clustering/administration/auth/users_artificial_table_backend.cc b/src/clustering/administration/auth/users_artificial_table_backend.cc deleted file mode 100644 index 68f4e03ffcf..00000000000 --- a/src/clustering/administration/auth/users_artificial_table_backend.cc +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/auth/users_artificial_table_backend.hpp" - -#include "clustering/administration/auth/user.hpp" -#include "clustering/administration/metadata.hpp" - -namespace auth { - -users_artificial_table_backend_t::users_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - std::shared_ptr> - auth_semilattice_view, - std::shared_ptr> - cluster_semilattice_view) - : base_artificial_table_backend_t( - name_string_t::guarantee_valid("users"), - rdb_context, - name_resolver, - auth_semilattice_view, - cluster_semilattice_view) { -} - -ql::datum_t user_to_datum(username_t const &username, user_t const &user) { - ql::datum_object_builder_t builder; - builder.overwrite("id", ql::datum_t(datum_string_t(username.to_string()))); - builder.overwrite("password", ql::datum_t::boolean(!user.get_password().is_empty())); - return std::move(builder).to_datum(); -} - -bool users_artificial_table_backend_t::read_all_rows_as_vector( - UNUSED auth::user_context_t const &user_context, - UNUSED signal_t *interruptor, - std::vector *rows_out, - UNUSED admin_err_t *error_out) { - rows_out->clear(); - on_thread_t on_thread(home_thread()); - - auth_semilattice_metadata_t auth_metadata = m_auth_semilattice_view->get(); - for (auto const &user : auth_metadata.m_users) { - if (!static_cast(user.second.get_ref())) { - continue; - } - - rows_out->push_back(user_to_datum(user.first, user.second.get_ref().get())); - } - - return true; -} - -bool users_artificial_table_backend_t::read_row( - UNUSED auth::user_context_t const &user_context, - ql::datum_t primary_key, - UNUSED signal_t *interruptor, - ql::datum_t *row_out, - UNUSED admin_err_t *error_out) { - *row_out = ql::datum_t(); - on_thread_t on_thread(home_thread()); - - if (primary_key.get_type() == ql::datum_t::R_STR) { - username_t username(primary_key.as_str().to_std()); - - auth_semilattice_metadata_t auth_metadata = m_auth_semilattice_view->get(); - auto user = auth_metadata.m_users.find(username); - if (user != auth_metadata.m_users.end() && - static_cast(user->second.get_ref())) { - *row_out = user_to_datum(user->first, user->second.get_ref().get()); - } - } - - return true; -} - -bool users_artificial_table_backend_t::write_row( - UNUSED auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - UNUSED signal_t *interruptor, - admin_err_t *error_out) { - on_thread_t on_thread(home_thread()); - - if (primary_key.get_type() != ql::datum_t::R_STR || pkey_was_autogenerated) { - *error_out = admin_err_t{ - "Expected a username as the primary key, got " + primary_key.print() + ".", - query_state_t::FAILED}; - return false; - } - - username_t username(primary_key.as_str().to_std()); - - if (new_value_inout->has()) { - try { - auth_semilattice_metadata_t auth_metadata = m_auth_semilattice_view->get(); - auto user = auth_metadata.m_users.find(username); - if (user != auth_metadata.m_users.end() && - static_cast(user->second.get_ref())) { - user->second.apply_write([&](optional *inner_user) { - inner_user->get().merge(*new_value_inout); - }); - } else { - auth_metadata.m_users[username].set(make_optional(user_t(*new_value_inout))); - } - m_auth_semilattice_view->join(auth_metadata); - } catch(admin_op_exc_t const &admin_op_exc) { - *error_out = admin_op_exc.to_admin_err(); - return false; - } - } else { - if (username.is_admin()) { - *error_out = admin_err_t{ - "The user `" + username.to_string() + "` can't be deleted.", - query_state_t::FAILED}; - return false; - } - - auth_semilattice_metadata_t auth_metadata = m_auth_semilattice_view->get(); - auto user = auth_metadata.m_users.find(username); - if (user != auth_metadata.m_users.end() && - static_cast(user->second.get_ref())) { - user->second.set(r_nullopt); - } else { - *error_out = admin_err_t{ - "User `" + username.to_string() + "` not found.", - query_state_t::FAILED}; - return false; - } - m_auth_semilattice_view->join(auth_metadata); - } - - return true; -} - -} // namespace auth diff --git a/src/clustering/administration/auth/users_artificial_table_backend.hpp b/src/clustering/administration/auth/users_artificial_table_backend.hpp deleted file mode 100644 index 3fb41ca2116..00000000000 --- a/src/clustering/administration/auth/users_artificial_table_backend.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_AUTH_USER_ARTIFICIAL_TABLE_BACKEND_HPP -#define CLUSTERING_ADMINISTRATION_AUTH_USER_ARTIFICIAL_TABLE_BACKEND_HPP - -#include "clustering/administration/auth/base_artificial_table_backend.hpp" - -namespace auth { - -class users_artificial_table_backend_t : - public base_artificial_table_backend_t -{ -public: - users_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - std::shared_ptr> - auth_semilattice_view, - std::shared_ptr> - cluster_semilattice_view); - - bool read_all_rows_as_vector( - auth::user_context_t const &user_context, - signal_t *interruptor, - std::vector *rows_out, - admin_err_t *error_out); - - bool read_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor, - ql::datum_t *row_out, - admin_err_t *error_out); - - bool write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor, - admin_err_t *error_out); -}; - -} // namespace auth - -#endif // CLUSTERING_ADMINISTRATION_AUTH_USER_ARTIFICIAL_TABLE_BACKEND_HPP diff --git a/src/clustering/administration/cluster_config.cc b/src/clustering/administration/cluster_config.cc deleted file mode 100644 index 022446400ee..00000000000 --- a/src/clustering/administration/cluster_config.cc +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/cluster_config.hpp" - -#include "clustering/administration/admin_op_exc.hpp" -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/metadata.hpp" - -cluster_config_artificial_table_backend_t::cluster_config_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - std::shared_ptr > _heartbeat_sl_view) - : caching_cfeed_artificial_table_backend_t( - name_string_t::guarantee_valid("cluster_config"), rdb_context, name_resolver), - heartbeat_doc(_heartbeat_sl_view) { - docs["heartbeat"] = &heartbeat_doc; -} - -cluster_config_artificial_table_backend_t::~cluster_config_artificial_table_backend_t() { - begin_changefeed_destruction(); -} - -std::string cluster_config_artificial_table_backend_t::get_primary_key_name() { - return "id"; -} - -bool cluster_config_artificial_table_backend_t::read_all_rows_as_vector( - UNUSED auth::user_context_t const &user_context, - signal_t *interruptor, - std::vector *rows_out, - admin_err_t *error_out) { - rows_out->clear(); - - for (auto it = docs.begin(); it != docs.end(); ++it) { - ql::datum_t row; - if (!it->second->read(interruptor, &row, error_out)) { - return false; - } - rows_out->push_back(row); - } - return true; -} - -bool cluster_config_artificial_table_backend_t::read_row( - UNUSED auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor, - ql::datum_t *row_out, - admin_err_t *error_out) { - if (primary_key.get_type() != ql::datum_t::R_STR) { - *row_out = ql::datum_t(); - return true; - } - auto it = docs.find(primary_key.as_str().to_std()); - if (it == docs.end()) { - *row_out = ql::datum_t(); - return true; - } - return it->second->read(interruptor, row_out, error_out); -} - -bool cluster_config_artificial_table_backend_t::write_row( - UNUSED auth::user_context_t const &user_context, - ql::datum_t primary_key, - UNUSED bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor, - admin_err_t *error_out) { - if (!new_value_inout->has()) { - *error_out = admin_err_t{ - "It's illegal to delete rows from the `rethinkdb.cluster_config` table.", - query_state_t::FAILED}; - return false; - } - const char *missing_message = "It's illegal to insert new rows into the " - "`rethinkdb.cluster_config` table."; - if (primary_key.get_type() != ql::datum_t::R_STR) { - *error_out = admin_err_t{missing_message, query_state_t::FAILED}; - return false; - } - auto it = docs.find(primary_key.as_str().to_std()); - if (it == docs.end()) { - *error_out = admin_err_t{missing_message, query_state_t::FAILED}; - return false; - } - return it->second->write(interruptor, new_value_inout, error_out); -} - -void cluster_config_artificial_table_backend_t::set_notifications(bool should_notify) { - /* Note that we aren't actually modifying the `docs` map itself, just the objects - that it points at. So this could have been `const auto &pair`, but that might be - misleading. */ - for (auto &&pair : docs) { - if (should_notify) { - std::string name = pair.first; - pair.second->set_notification_callback( - [this, name]() { - notify_row(ql::datum_t(datum_string_t(name))); - }); - } else { - pair.second->set_notification_callback(nullptr); - } - } -} - -bool cluster_config_artificial_table_backend_t::heartbeat_doc_t::read( - UNUSED signal_t *interruptor, - ql::datum_t *row_out, - UNUSED admin_err_t *error_out) { - on_thread_t thread_switcher(sl_view->home_thread()); - ql::datum_object_builder_t obj_builder; - obj_builder.overwrite("id", ql::datum_t("heartbeat")); - obj_builder.overwrite("heartbeat_timeout_secs", ql::datum_t(static_cast( - sl_view->get().heartbeat_timeout.get_ref()) / 1000)); - *row_out = std::move(obj_builder).to_datum(); - return true; -} - -bool cluster_config_artificial_table_backend_t::heartbeat_doc_t::write( - UNUSED signal_t *interruptor, - ql::datum_t *row_inout, - admin_err_t *error_out) { - converter_from_datum_object_t converter; - admin_err_t dummy_error; - if (!converter.init(*row_inout, &dummy_error)) { - crash("artificial_table_t should guarantee input is an object"); - } - ql::datum_t dummy_pkey; - if (!converter.get("id", &dummy_pkey, &dummy_error)) { - crash("artificial_table_t should guarantee primary key is present and correct"); - } - - ql::datum_t heartbeat_timeout_datum; - if (!converter.get("heartbeat_timeout_secs", &heartbeat_timeout_datum, error_out)) { - return false; - } - double heartbeat_timeout; - if (heartbeat_timeout_datum.get_type() == ql::datum_t::R_NUM) { - heartbeat_timeout = heartbeat_timeout_datum.as_num(); - if (heartbeat_timeout < 2) { - *error_out = admin_err_t{ - "The heartbeat timeout must be at least two seconds", - query_state_t::FAILED}; - return false; - } - } else { - *error_out = admin_err_t{ - "Expected a number; got " + heartbeat_timeout_datum.print(), - query_state_t::FAILED}; - return false; - } - - if (!converter.check_no_extra_keys(error_out)) { - return false; - } - - { - on_thread_t thread_switcher(sl_view->home_thread()); - heartbeat_semilattice_metadata_t metadata = sl_view->get(); - metadata.heartbeat_timeout.set(heartbeat_timeout * 1000); - sl_view->join(metadata); - } - - return true; -} - -void -cluster_config_artificial_table_backend_t::heartbeat_doc_t::set_notification_callback( - const std::function &fun) { - if (static_cast(fun)) { - subs = make_scoped::subscription_t>(fun, sl_view); - } else { - subs.reset(); - } -} diff --git a/src/clustering/administration/cluster_config.hpp b/src/clustering/administration/cluster_config.hpp deleted file mode 100644 index cf600cbea05..00000000000 --- a/src/clustering/administration/cluster_config.hpp +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_CLUSTER_CONFIG_HPP_ -#define CLUSTERING_ADMINISTRATION_CLUSTER_CONFIG_HPP_ - -#include -#include -#include - -#include "rdb_protocol/artificial_table/caching_cfeed_backend.hpp" -#include "rpc/semilattice/view.hpp" - -class heartbeat_semilattice_metadata_t; - -/* The `rethinkdb.cluster_config` table is a catch-all for settings that don't fit -elsewhere but aren't complicated enough to deserve their own table. It has a fixed set of -rows, each of which has a unique format and corresponds to a different setting. - -Right now the only row is `heartbeat`, with the format -`{"id": "heartbeat", "heartbeat_timeout_secs": ...}`. */ - -class cluster_config_artificial_table_backend_t : - public caching_cfeed_artificial_table_backend_t -{ -public: - cluster_config_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - std::shared_ptr > _heartbeat_sl_view); - ~cluster_config_artificial_table_backend_t(); - - std::string get_primary_key_name(); - - bool read_all_rows_as_vector( - auth::user_context_t const &user_context, - signal_t *interruptor, - std::vector *rows_out, - admin_err_t *error_out); - - bool read_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor, - ql::datum_t *row_out, - admin_err_t *error_out); - - bool write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor, - admin_err_t *error_out); - - void set_notifications(bool); - -private: - /* The abstract class `doc_t` represents a row in the `cluster_config` table. In the - future it will have more subclasses. We might eventually want to move it and its - subclasses out of this file. */ - class doc_t { - public: - /* Computes the current value of the row, including the primary key. */ - virtual bool read( - signal_t *interruptor, - ql::datum_t *row_out, - admin_err_t *error_out) = 0; - /* Applies a change to the row. */ - virtual bool write( - signal_t *interruptor, - ql::datum_t *value_inout, - admin_err_t *error_out) = 0; - /* Registers or deregisters a function that should be called if the row ever - changes. */ - virtual void set_notification_callback(const std::function &) = 0; - protected: - virtual ~doc_t() { } // make compiler happy - }; - - class heartbeat_doc_t : public doc_t { - public: - explicit heartbeat_doc_t(std::shared_ptr > _sl_view) : sl_view(_sl_view) { } - bool read( - signal_t *interruptor, - ql::datum_t *row_out, - admin_err_t *error_out); - bool write( - signal_t *interruptor, - ql::datum_t *row_out, - admin_err_t *error_out); - void set_notification_callback(const std::function &fun); - private: - std::shared_ptr< - semilattice_readwrite_view_t > sl_view; - scoped_ptr_t< - semilattice_read_view_t::subscription_t - > subs; - }; - - heartbeat_doc_t heartbeat_doc; - - std::map docs; -}; - -#endif /* CLUSTERING_ADMINISTRATION_CLUSTER_CONFIG_HPP_ */ - diff --git a/src/clustering/administration/datum_adapter.cc b/src/clustering/administration/datum_adapter.cc deleted file mode 100644 index b0108cc8277..00000000000 --- a/src/clustering/administration/datum_adapter.cc +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/datum_adapter.hpp" - -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "clustering/administration/tables/database_metadata.hpp" -#include "clustering/administration/tables/table_metadata.hpp" -#include "clustering/table_manager/table_meta_client.hpp" -#include "rdb_protocol/pseudo_time.hpp" - -ql::datum_t convert_string_to_datum( - const std::string &value) { - return ql::datum_t(datum_string_t(value)); -} - -bool convert_string_from_datum( - const ql::datum_t &datum, - std::string *value_out, - admin_err_t *error_out) { - if (datum.get_type() != ql::datum_t::R_STR) { - *error_out = admin_err_t{ - "Expected a string; got " + datum.print(), - query_state_t::FAILED}; - return false; - } - *value_out = datum.as_str().to_std(); - return true; -} - -ql::datum_t convert_name_to_datum( - const name_string_t &value) { - return ql::datum_t(value.c_str()); -} - -bool convert_name_from_datum( - ql::datum_t datum, - const std::string &what, - name_string_t *value_out, - admin_err_t *error_out) { - if (datum.get_type() != ql::datum_t::R_STR) { - *error_out = admin_err_t{ - "Expected a " + what + "; got " + datum.print(), - query_state_t::FAILED}; - return false; - } - if (!value_out->assign_value(datum.as_str())) { - *error_out = admin_err_t{ - datum.print() + " is not a valid " + what + "; " - + std::string(name_string_t::valid_char_msg), - query_state_t::FAILED}; - return false; - } - return true; -} - -ql::datum_t convert_uuid_to_datum( - const uuid_u &value) { - return ql::datum_t(datum_string_t(uuid_to_str(value))); -} - -ql::datum_t convert_server_id_to_datum( - const server_id_t &value) { - return ql::datum_t(datum_string_t(value.print())); -} - -bool convert_uuid_from_datum( - ql::datum_t datum, - uuid_u *value_out, - admin_err_t *error_out) { - if (datum.get_type() != ql::datum_t::R_STR) { - *error_out = admin_err_t{ - "Expected a UUID; got " + datum.print(), - query_state_t::FAILED}; - return false; - } - if (!str_to_uuid(datum.as_str().to_std(), value_out)) { - *error_out = admin_err_t{ - "Expected a UUID; got " + datum.print(), - query_state_t::FAILED}; - return false; - } - return true; -} - -bool convert_server_id_from_datum( - ql::datum_t datum, - server_id_t *value_out, - admin_err_t *error_out) { - if (datum.get_type() != ql::datum_t::R_STR) { - *error_out = admin_err_t{ - "Expected a server ID; got " + datum.print(), - query_state_t::FAILED}; - return false; - } - if (!str_to_server_id(datum.as_str().to_std(), value_out)) { - *error_out = admin_err_t{ - "Expected a server ID; got " + datum.print(), - query_state_t::FAILED}; - return false; - } - return true; -} - -ql::datum_t convert_name_or_uuid_to_datum( - const name_string_t &name, - const uuid_u &uuid, - admin_identifier_format_t identifier_format) { - if (identifier_format == admin_identifier_format_t::name) { - return convert_name_to_datum(name); - } else { - return convert_uuid_to_datum(uuid); - } -} - -ql::datum_t convert_name_or_server_id_to_datum( - const name_string_t &name, - const server_id_t &sid, - admin_identifier_format_t identifier_format) { - if (identifier_format == admin_identifier_format_t::name) { - return convert_name_to_datum(name); - } else { - return convert_server_id_to_datum(sid); - } -} - -bool convert_connected_server_id_to_datum( - const server_id_t &server_id, - admin_identifier_format_t identifier_format, - server_config_client_t *server_config_client, - ql::datum_t *server_name_or_uuid_out, - name_string_t *server_name_out) { - optional name; - if (server_id.is_proxy()) { - // Fill in a temporary proxy server name. - // We assume that proxies only appear anywhere if they are currently - // connected, so we don't need a separate check for connectedness. - name.set(name_string_t::guarantee_valid(server_id.print().c_str())); - } else { - server_config_client->get_server_config_map()->read_key(server_id, - [&](const server_config_versioned_t *config) { - if (config != nullptr) { - name = make_optional(config->config.name); - } - }); - if (!static_cast(name)) { - // No entry in the server config map means that the server is not - // currently connected. - return false; - } - } - - if (server_name_or_uuid_out != nullptr) { - *server_name_or_uuid_out = convert_name_or_uuid_to_datum( - *name, server_id.get_uuid(), identifier_format); - } - if (server_name_out != nullptr) *server_name_out = *name; - return true; -} - -bool convert_table_id_to_datums( - const namespace_id_t &table_id, - admin_identifier_format_t identifier_format, - const cluster_semilattice_metadata_t &metadata, - table_meta_client_t *table_meta_client, - /* Any of these can be `nullptr` if they are not needed */ - ql::datum_t *table_name_or_uuid_out, - name_string_t *table_name_out, - ql::datum_t *db_name_or_uuid_out, - name_string_t *db_name_out) { - table_basic_config_t basic_config; - try { - table_meta_client->get_name(table_id, &basic_config); - } catch (const no_such_table_exc_t &) { - return false; - } - if (table_name_or_uuid_out != nullptr) { - *table_name_or_uuid_out = convert_name_or_uuid_to_datum( - basic_config.name, table_id, identifier_format); - } - if (table_name_out != nullptr) *table_name_out = basic_config.name; - name_string_t db_name; - auto jt = metadata.databases.databases.find(basic_config.database); - if (jt == metadata.databases.databases.end() || jt->second.is_deleted()) { - db_name = name_string_t::guarantee_valid("__deleted_database__"); - } else { - db_name = jt->second.get_ref().name.get_ref(); - } - if (db_name_or_uuid_out != nullptr) { - *db_name_or_uuid_out = convert_name_or_uuid_to_datum( - db_name, basic_config.database, identifier_format); - } - if (db_name_out != nullptr) *db_name_out = db_name; - return true; -} - -bool convert_database_id_to_datum( - const database_id_t &db_id, - admin_identifier_format_t identifier_format, - const cluster_semilattice_metadata_t &metadata, - ql::datum_t *db_name_or_uuid_out, - name_string_t *db_name_out) { - auto it = metadata.databases.databases.find(db_id); - guarantee(it != metadata.databases.databases.end()); - if (it->second.is_deleted()) { - return false; - } - name_string_t db_name = it->second.get_ref().name.get_ref(); - if (db_name_or_uuid_out != nullptr) { - *db_name_or_uuid_out = - convert_name_or_uuid_to_datum(db_name, db_id, identifier_format); - } - if (db_name_out != nullptr) *db_name_out = db_name; - return true; -} - -bool convert_database_id_from_datum( - const ql::datum_t &db_name_or_uuid, - admin_identifier_format_t identifier_format, - const cluster_semilattice_metadata_t &metadata, - database_id_t *db_id_out, - name_string_t *db_name_out, - admin_err_t *error_out) { - if (identifier_format == admin_identifier_format_t::name) { - name_string_t name; - if (!convert_name_from_datum(db_name_or_uuid, "database name", - &name, error_out)) { - return false; - } - database_id_t id; - if (!search_db_metadata_by_name(metadata.databases, name, &id, error_out)) { - return false; - } - if (db_id_out != nullptr) *db_id_out = id; - if (db_name_out != nullptr) *db_name_out = name; - return true; - } else { - database_id_t db_id; - if (!convert_uuid_from_datum(db_name_or_uuid, &db_id, error_out)) { - return false; - } - auto it = metadata.databases.databases.find(db_id); - if (it == metadata.databases.databases.end() || it->second.is_deleted()) { - *error_out = admin_err_t{ - strprintf("There is no database with UUID `%s`.", - uuid_to_str(db_id).c_str()), - query_state_t::FAILED}; - return false; - } - if (db_id_out != nullptr) *db_id_out = db_id; - if (db_name_out != nullptr) *db_name_out = it->second.get_ref().name.get_ref(); - return true; - } -} - -ql::datum_t convert_port_to_datum( - uint16_t value) { - return ql::datum_t(static_cast(value)); -} - -ql::datum_t convert_microtime_to_datum( - microtime_t value) { - return ql::pseudo::make_time(value / 1.0e6, "+00:00"); -} - -bool converter_from_datum_object_t::init( - ql::datum_t _datum, - admin_err_t *error_out) { - if (_datum.get_type() != ql::datum_t::R_OBJECT) { - *error_out = admin_err_t{ - "Expected an object; got " + _datum.print(), - query_state_t::FAILED}; - return false; - } - datum = _datum; - for (size_t i = 0; i < datum.obj_size(); ++i) { - std::pair pair = datum.get_pair(i); - extra_keys.insert(pair.first); - } - return true; -} - -bool converter_from_datum_object_t::get( - const char *key, - ql::datum_t *value_out, - admin_err_t *error_out) { - extra_keys.erase(datum_string_t(key)); - *value_out = datum.get_field(key, ql::NOTHROW); - if (!value_out->has()) { - *error_out = admin_err_t{ - strprintf("Expected a field named `%s`.", key), - query_state_t::FAILED}; - return false; - } - return true; -} - -void converter_from_datum_object_t::get_optional( - const char *key, - ql::datum_t *value_out) { - extra_keys.erase(datum_string_t(key)); - *value_out = datum.get_field(key, ql::NOTHROW); -} - -bool converter_from_datum_object_t::has(const char *key) { - return datum.get_field(key, ql::NOTHROW).has(); -} - -bool converter_from_datum_object_t::check_no_extra_keys(admin_err_t *error_out) { - if (!extra_keys.empty()) { - *error_out = admin_err_t{"Unexpected key(s):", query_state_t::FAILED}; - for (const datum_string_t &key : extra_keys) { - error_out->msg += " " + key.to_std(); - } - return false; - } - return true; -} - diff --git a/src/clustering/administration/datum_adapter.hpp b/src/clustering/administration/datum_adapter.hpp deleted file mode 100644 index 5da3b13d8e9..00000000000 --- a/src/clustering/administration/datum_adapter.hpp +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_DATUM_ADAPTER_HPP_ -#define CLUSTERING_ADMINISTRATION_DATUM_ADAPTER_HPP_ - -#include -#include -#include - -#include "clustering/administration/admin_op_exc.hpp" -#include "containers/name_string.hpp" -#include "rdb_protocol/context.hpp" -#include "rdb_protocol/datum.hpp" -#include "rpc/connectivity/server_id.hpp" -#include "time.hpp" - -class cluster_semilattice_metadata_t; -class server_config_client_t; -class table_meta_client_t; - -/* Note that we generally use `ql::configured_limits_t::unlimited` when converting -things to datum, rather than using a user-specified limit. This is mostly for consistency -with reading from a B-tree; if a value read from the B-tree contains an array larger than -the user-specified limit, then we don't throw an exception unless the user tries to grow -the array. Since these functions are used to construct values that the user will "read", -we use the same behavior here. This has the nice side effect that we don't have to worry -about threading `configured_limits_t` through these functions, or about handling -exceptions if the limit is violated. */ - -ql::datum_t convert_string_to_datum( - const std::string &value); -bool convert_string_from_datum( - const ql::datum_t &datum, - std::string *value_out, - admin_err_t *error_out); - -ql::datum_t convert_name_to_datum( - const name_string_t &value); -bool convert_name_from_datum( - ql::datum_t datum, - const std::string &what, /* e.g. "server name" or "table name" */ - name_string_t *value_out, - admin_err_t *error_out); - -ql::datum_t convert_uuid_to_datum( - const uuid_u &value); -bool convert_uuid_from_datum( - ql::datum_t datum, - uuid_u *value_out, - admin_err_t *error_out); - -ql::datum_t convert_name_or_uuid_to_datum( - const name_string_t &name, - const uuid_u &uuid, - admin_identifier_format_t identifier_format); - -ql::datum_t convert_server_id_to_datum( - const server_id_t &value); -bool convert_server_id_from_datum( - ql::datum_t datum, - server_id_t *value_out, - admin_err_t *error_out); - -ql::datum_t convert_name_or_server_id_to_datum( - const name_string_t &name, - const server_id_t &sid, - admin_identifier_format_t identifier_format); - -/* If the given server is connected, sets `*server_name_or_uuid_out` to a datum -representation of the server and returns `true`. If it's not connected, returns `false`. -*/ -bool convert_connected_server_id_to_datum( - const server_id_t &server_id, - admin_identifier_format_t identifier_format, - server_config_client_t *server_config_client, - ql::datum_t *server_name_or_uuid_out, - name_string_t *server_name_out); - -/* `convert_table_id_to_datums()` will return `false` if the table ID corresponds to a -deleted table. If the table still exists but the database does not, it will return `true` -but set the database to `__deleted_database__`. */ -bool convert_table_id_to_datums( - const namespace_id_t &table_id, - admin_identifier_format_t identifier_format, - const cluster_semilattice_metadata_t &metadata, - table_meta_client_t *table_meta_client, - /* Any of these can be `nullptr` if they are not needed */ - ql::datum_t *table_name_or_uuid_out, - name_string_t *table_name_out, - ql::datum_t *db_name_or_uuid_out, - name_string_t *db_name_out); - -/* `convert_database_id_to_datum()` will return `false` if the database ID corresponds to -a deleted database. */ -bool convert_database_id_to_datum( - const database_id_t &db_id, - admin_identifier_format_t identifier_format, - const cluster_semilattice_metadata_t &metadata, - ql::datum_t *db_name_or_uuid_out, - name_string_t *db_name_out); -bool convert_database_id_from_datum( - const ql::datum_t &db_name_or_uuid, - admin_identifier_format_t identifier_format, - const cluster_semilattice_metadata_t &metadata, - database_id_t *db_id_out, - name_string_t *db_name_out, - admin_err_t *error_out); - -ql::datum_t convert_port_to_datum( - uint16_t value); - -ql::datum_t convert_microtime_to_datum( - microtime_t value); - -template -ql::datum_t convert_vector_to_datum( - const std::function &conv, - const std::vector &vector) { - ql::datum_array_builder_t builder((ql::configured_limits_t::unlimited)); - builder.reserve(vector.size()); - for (const T &elem : vector) { - builder.add(conv(elem)); - } - return std::move(builder).to_datum(); -} - -template -bool convert_vector_from_datum( - const std::function &conv, - ql::datum_t datum, - std::vector *vector_out, - admin_err_t *error_out) { - if (datum.get_type() != ql::datum_t::R_ARRAY) { - *error_out = admin_err_t{ - "Expected an array, got " + datum.print(), - query_state_t::FAILED}; - return false; - } - vector_out->resize(datum.arr_size()); - for (size_t i = 0; i < datum.arr_size(); ++i) { - if (!conv(datum.get(i), &(*vector_out)[i], error_out)) { - return false; - } - } - return true; -} - -template -ql::datum_t convert_set_to_datum( - const std::function &conv, - const std::set &set) { - ql::datum_array_builder_t builder((ql::configured_limits_t::unlimited)); - builder.reserve(set.size()); - for (const T &elem : set) { - builder.add(conv(elem)); - } - return std::move(builder).to_datum(); -} - -template -bool convert_set_from_datum( - const std::function &conv, - bool allow_duplicates, - ql::datum_t datum, - std::set *set_out, - admin_err_t *error_out) { - if (datum.get_type() != ql::datum_t::R_ARRAY) { - *error_out = admin_err_t{ - "Expected an array, got " + datum.print(), - query_state_t::FAILED}; - return false; - } - set_out->clear(); - for (size_t i = 0; i < datum.arr_size(); ++i) { - T value; - if (!conv(datum.get(i), &value, error_out)) { - return false; - } - auto res = set_out->insert(value); - if (!allow_duplicates && !res.second) { - *error_out = admin_err_t{ - datum.get(i).print() + " was specified more than once.", - query_state_t::FAILED}; - return false; - } - } - return true; -} - -/* `converter_from_datum_object_t` is a helper for converting a `datum_t` to some other -type when the type's datum representation is an object with a fixed set of fields. -Construct a `converter_from_datum_object_t` and call `init()` with your datum. `init()` -will fail if the input isn't an object. Next, call `get()` or `get_optional()` for each -field of the object; `get()` will fail if the field isn't present. Finally, call -`check_no_extra_keys()`, which will fail if the object has any keys that you didn't call -`get()` or `get_optional()` for. This way, it will produce a nice error message if the -user passes an object with an invalid key. */ -class converter_from_datum_object_t { -public: - bool init(ql::datum_t datum, - admin_err_t *error_out); - bool get(const char *key, - ql::datum_t *value_out, - admin_err_t *error_out); - void get_optional(const char *key, - ql::datum_t *value_out); - bool has(const char *key); - bool check_no_extra_keys(admin_err_t *error_out); -private: - ql::datum_t datum; - std::set extra_keys; -}; - -#endif /* CLUSTERING_ADMINISTRATION_DATUM_ADAPTER_HPP_ */ - diff --git a/src/clustering/administration/http/cyanide.hpp b/src/clustering/administration/http/cyanide.hpp deleted file mode 100644 index 8a59f2716ef..00000000000 --- a/src/clustering/administration/http/cyanide.hpp +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_HTTP_CYANIDE_HPP_ -#define CLUSTERING_ADMINISTRATION_HTTP_CYANIDE_HPP_ - -#include "http/http.hpp" - -/* This is an `http_app_t` whose jobs is to give us a way to kill a server over - * http. */ -class cyanide_http_app_t : public http_app_t { - void handle(const http_req_t &, http_res_t *result, signal_t *) { - crash("Goodbye sweet world\n"); - *result = http_res_t(http_status_code_t::OK); - } -}; - -#endif /* CLUSTERING_ADMINISTRATION_HTTP_CYANIDE_HPP_ */ diff --git a/src/clustering/administration/http/server.cc b/src/clustering/administration/http/server.cc deleted file mode 100644 index a1cf91e3539..00000000000 --- a/src/clustering/administration/http/server.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "clustering/administration/http/server.hpp" - -#include "clustering/administration/http/cyanide.hpp" -#include "http/file_app.hpp" -#include "http/http.hpp" -#include "http/routing_app.hpp" -#include "rpc/semilattice/view/field.hpp" - -administrative_http_server_manager_t::administrative_http_server_manager_t( - const std::set &local_addresses, - int port, - http_app_t *reql_app, - std::string path, - tls_ctx_t *tls_ctx) -{ - - file_app.init(new file_http_app_t(path)); - -#ifndef NDEBUG - cyanide_app.init(new cyanide_http_app_t); -#endif - - std::map ajax_routes; - ajax_routes["reql"] = reql_app; - DEBUG_ONLY_CODE(ajax_routes["cyanide"] = cyanide_app.get()); - ajax_routing_app.init(new routing_http_app_t(nullptr, ajax_routes)); - - std::map root_routes; - root_routes["ajax"] = ajax_routing_app.get(); - root_routing_app.init(new routing_http_app_t(file_app.get(), root_routes)); - - server.init(new http_server_t(tls_ctx, local_addresses, port, root_routing_app.get())); -} - -administrative_http_server_manager_t::~administrative_http_server_manager_t() { - /* This must be declared in the `.cc` file because the definitions of the - destructors for the things in `scoped_ptr_t`s are not available from - the `.hpp` file. */ -} - -int administrative_http_server_manager_t::get_port() const { - return server->get_port(); -} diff --git a/src/clustering/administration/http/server.hpp b/src/clustering/administration/http/server.hpp deleted file mode 100644 index cd3057e1aa7..00000000000 --- a/src/clustering/administration/http/server.hpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_HTTP_SERVER_HPP_ -#define CLUSTERING_ADMINISTRATION_HTTP_SERVER_HPP_ - -#include -#include -#include - -#include "arch/io/openssl.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/namespace_interface_repository.hpp" -#include "http/http.hpp" -#include "rpc/semilattice/view.hpp" - -class http_server_t; -class routing_http_app_t; -class file_http_app_t; -class cyanide_http_app_t; - -class real_reql_cluster_interface_t; - -class administrative_http_server_manager_t { - -public: - administrative_http_server_manager_t( - const std::set &local_addresses, - int port, - http_app_t *reql_app, - std::string _path, - tls_ctx_t *tls_ctx); - ~administrative_http_server_manager_t(); - - int get_port() const; -private: - - scoped_ptr_t file_app; -#ifndef NDEBUG - scoped_ptr_t cyanide_app; -#endif - scoped_ptr_t ajax_routing_app; - scoped_ptr_t root_routing_app; - scoped_ptr_t server; - - DISABLE_COPYING(administrative_http_server_manager_t); // kind of redundant with the scoped_ptrs but too bad. -}; - -#endif /* CLUSTERING_ADMINISTRATION_HTTP_SERVER_HPP_ */ diff --git a/src/clustering/administration/issues/issue.cc b/src/clustering/administration/issues/issue.cc deleted file mode 100644 index 2657c59bc78..00000000000 --- a/src/clustering/administration/issues/issue.cc +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/issues/issue.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/datum_adapter.hpp" - -bool issue_t::to_datum(const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t identifier_format, - ql::datum_t *datum_out) const { - ql::datum_object_builder_t builder; - builder.overwrite("id", convert_uuid_to_datum(issue_id)); - builder.overwrite("type", ql::datum_t(get_name())); - builder.overwrite("critical", ql::datum_t::boolean(is_critical())); - ql::datum_t info; - datum_string_t description; - if (!build_info_and_description(metadata, server_config_client, table_meta_client, - identifier_format, &info, &description)) { - return false; - } - builder.overwrite("info", info); - builder.overwrite("description", ql::datum_t(description)); - *datum_out = std::move(builder).to_datum(); - return true; -} - -std::string issue_t::item_to_str(const name_string_t &str) { - return item_to_str(str.str()); -} - -std::string issue_t::item_to_str(const std::string &str) { - return 'S' + str + '\0'; -} - -std::string issue_t::item_to_str(const uuid_u &id) { - return 'U' + std::string(reinterpret_cast(id.data()), - id.static_size()) + '\0'; -} diff --git a/src/clustering/administration/issues/issue.hpp b/src/clustering/administration/issues/issue.hpp deleted file mode 100644 index 82f8e38bd70..00000000000 --- a/src/clustering/administration/issues/issue.hpp +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_ISSUES_ISSUE_HPP_ -#define CLUSTERING_ADMINISTRATION_ISSUES_ISSUE_HPP_ - -#include -#include -#include -#include - -#include "containers/scoped.hpp" -#include "rpc/semilattice/view.hpp" -#include "rdb_protocol/context.hpp" -#include "rdb_protocol/datum.hpp" -#include "rdb_protocol/datum_string.hpp" - -class cluster_semilattice_metadata_t; -class issue_multiplexer_t; -class name_string_t; -class server_config_client_t; -class table_meta_client_t; - -class issue_t { -public: - explicit issue_t(const issue_id_t &_issue_id) : issue_id(_issue_id) { } - virtual ~issue_t() { } - - /* `to_datum()` has to generate info and description data, which depends on the - current cluster configuration, so it takes the cluster configuration as a parameter. - Based on the cluster configuration, it may decide that the issue is no longer - relevant, in which case `to_datum()` will return `false`. */ - typedef cluster_semilattice_metadata_t metadata_t; - bool to_datum( - const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t identifier_format, - ql::datum_t *datum_out) const; - - virtual bool is_critical() const = 0; - virtual const datum_string_t &get_name() const = 0; - issue_id_t get_id() const { return issue_id; } - - // Utility function for generating deterministic UUIDs via a hash of the args - template - static uuid_u from_hash(const uuid_u &base, const Args&... args) { - return uuid_u::from_hash(base, concat({ item_to_str(args)... })); - } - - issue_id_t issue_id; - -protected: - /* `build_info_and_description()` can return `false` if it decides based on the - contents of the metadata that the issue is no longer relevant. */ - virtual bool build_info_and_description( - const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const = 0; - -private: - template - static std::string concat_internal(T items) { - return std::accumulate(items.begin(), items.end(), std::string(), - [](const std::string &acc, typename T::value_type const &val) { - return acc + item_to_str(val); - }); - } - - template - static std::string concat(const std::initializer_list &list) { - return concat_internal(list); - } - - template - static std::string item_to_str(const std::vector &vec) { - return concat_internal(vec); - } - - static std::string item_to_str(const name_string_t &str); - static std::string item_to_str(const std::string &str); - static std::string item_to_str(const uuid_u &id); -}; - -class issue_tracker_t { -public: - issue_tracker_t() { } - virtual ~issue_tracker_t() { } - virtual std::vector > get_issues(signal_t *interruptor) const = 0; -private: - DISABLE_COPYING(issue_tracker_t); -}; - -#endif /* CLUSTERING_ADMINISTRATION_ISSUES_ISSUE_HPP_ */ diff --git a/src/clustering/administration/issues/issues_backend.cc b/src/clustering/administration/issues/issues_backend.cc deleted file mode 100644 index 640e84814cd..00000000000 --- a/src/clustering/administration/issues/issues_backend.cc +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/issues/issues_backend.hpp" - -#include "clustering/administration/admin_op_exc.hpp" -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/main/watchable_fields.hpp" -#include "concurrency/cross_thread_signal.hpp" - -issues_artificial_table_backend_t::issues_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - mailbox_manager_t *mailbox_manager, - std::shared_ptr > - _cluster_sl_view, - watchable_map_t *directory_view, - server_config_client_t *_server_config_client, - table_meta_client_t *_table_meta_client, - namespace_repo_t *_namespace_repo, - admin_identifier_format_t _identifier_format) - : timer_cfeed_artificial_table_backend_t( - name_string_t::guarantee_valid("current_issues"), rdb_context, name_resolver), - identifier_format(_identifier_format), - cluster_sl_view(_cluster_sl_view), - server_config_client(_server_config_client), - table_meta_client(_table_meta_client), - local_issue_client(mailbox_manager, directory_view), - name_collision_issue_tracker( - server_config_client, cluster_sl_view, table_meta_client), - table_issue_tracker(server_config_client, table_meta_client, _namespace_repo), - outdated_index_issue_tracker(table_meta_client), - non_transitive_issue_tracker(_server_config_client) { - trackers.insert(&local_issue_client); - trackers.insert(&name_collision_issue_tracker); - trackers.insert(&table_issue_tracker); - trackers.insert(&outdated_index_issue_tracker); - trackers.insert(&non_transitive_issue_tracker); -} - -issues_artificial_table_backend_t::~issues_artificial_table_backend_t() { - begin_changefeed_destruction(); -} - -std::string issues_artificial_table_backend_t::get_primary_key_name() { - return "id"; -} - -bool issues_artificial_table_backend_t::read_all_rows_as_vector( - UNUSED auth::user_context_t const &user_context, - signal_t *interruptor, - std::vector *rows_out, - UNUSED admin_err_t *error_out) { - cross_thread_signal_t ct_interruptor(interruptor, home_thread()); - on_thread_t rethreader(home_thread()); - rows_out->clear(); - - cluster_semilattice_metadata_t metadata = cluster_sl_view->get(); - for (auto const &tracker : trackers) { - for (auto const &issue : tracker->get_issues(&ct_interruptor)) { - ql::datum_t row; - bool still_valid = issue->to_datum(cluster_sl_view->get(), - server_config_client, table_meta_client, identifier_format, &row); - if (!still_valid) { - /* Based on `metadata`, the issue decided it is no longer relevant. */ - continue; - } - rows_out->push_back(row); - } - } - - if (ct_interruptor.is_pulsed()) { - throw interrupted_exc_t(); - } - - return true; -} - -bool issues_artificial_table_backend_t::read_row( - UNUSED auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor, - ql::datum_t *row_out, - UNUSED admin_err_t *error_out) { - cross_thread_signal_t ct_interruptor(interruptor, home_thread()); - on_thread_t rethreader(home_thread()); - *row_out = ql::datum_t(); - - uuid_u issue_id; - admin_err_t dummy_error; - if (convert_uuid_from_datum(primary_key, &issue_id, &dummy_error)) { - std::vector > issues = all_issues(&ct_interruptor); - - for (auto const &issue : issues) { - if (issue->get_id() == issue_id) { - ql::datum_t row; - bool still_valid = issue->to_datum(cluster_sl_view->get(), - server_config_client, table_meta_client, identifier_format, &row); - if (still_valid) { - *row_out = row; - } - break; - } - } - } - return true; -} - -std::vector > issues_artificial_table_backend_t::all_issues( - signal_t *interruptor) const { - std::vector > res; - - for (auto const &tracker : trackers) { - std::vector > issues = tracker->get_issues(interruptor); - std::move(issues.begin(), issues.end(), std::back_inserter(res)); - } - - return res; -} - -bool issues_artificial_table_backend_t::write_row( - UNUSED auth::user_context_t const &user_context, - UNUSED ql::datum_t primary_key, - UNUSED bool pkey_was_autogenerated, - UNUSED ql::datum_t *new_value_inout, - UNUSED signal_t *interruptor, - admin_err_t *error_out) { - *error_out = admin_err_t{ - "It's illegal to write to the `rethinkdb.current_issues` table.", - query_state_t::FAILED}; - return false; -} - diff --git a/src/clustering/administration/issues/issues_backend.hpp b/src/clustering/administration/issues/issues_backend.hpp deleted file mode 100644 index 6514f73bab1..00000000000 --- a/src/clustering/administration/issues/issues_backend.hpp +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_ISSUES_ISSUES_BACKEND_HPP_ -#define CLUSTERING_ADMINISTRATION_ISSUES_ISSUES_BACKEND_HPP_ - -#include -#include -#include - -#include "rdb_protocol/artificial_table/caching_cfeed_backend.hpp" -#include "concurrency/watchable.hpp" -#include "clustering/administration/issues/local.hpp" -#include "clustering/administration/issues/name_collision.hpp" -#include "clustering/administration/issues/non_transitive.hpp" -#include "clustering/administration/issues/outdated_index.hpp" -#include "clustering/administration/issues/table.hpp" - -class namespace_repo_t; - -class issues_artificial_table_backend_t : - public timer_cfeed_artificial_table_backend_t -{ -public: - issues_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - mailbox_manager_t *mailbox_manager, - std::shared_ptr > - _cluster_sl_view, - watchable_map_t *directory_view, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - namespace_repo_t *namespace_repo, - admin_identifier_format_t identifier_format); - ~issues_artificial_table_backend_t(); - - std::string get_primary_key_name(); - - bool read_all_rows_as_vector( - auth::user_context_t const &user_context, - signal_t *interruptor, - std::vector *rows_out, - admin_err_t *error_out); - - bool read_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor, - ql::datum_t *row_out, - admin_err_t *error_out); - - bool write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor, - admin_err_t *error_out); - -private: - std::vector > all_issues(signal_t *interruptor) const; - - admin_identifier_format_t identifier_format; - - std::shared_ptr > - cluster_sl_view; - - server_config_client_t *server_config_client; - table_meta_client_t *table_meta_client; - - std::set trackers; - - // Global issues are tracked here, local issues are collected from other servers by - // the local_issue_client_t. - local_issue_client_t local_issue_client; - name_collision_issue_tracker_t name_collision_issue_tracker; - table_issue_tracker_t table_issue_tracker; - outdated_index_issue_tracker_t outdated_index_issue_tracker; - non_transitive_issue_tracker_t non_transitive_issue_tracker; -}; - -#endif /* CLUSTERING_ADMINISTRATION_ISSUES_ISSUES_BACKEND_HPP_ */ diff --git a/src/clustering/administration/issues/local.cc b/src/clustering/administration/issues/local.cc deleted file mode 100644 index 113b88eaf0c..00000000000 --- a/src/clustering/administration/issues/local.cc +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/issues/local.hpp" - -#include "arch/runtime/runtime_utils.hpp" -#include "clustering/administration/metadata.hpp" -#include "rpc/mailbox/disconnect_watcher.hpp" -#include "utils.hpp" - -RDB_MAKE_SERIALIZABLE_2_FOR_CLUSTER(local_issues_t, log_write_issues, memory_issues); -RDB_MAKE_SERIALIZABLE_1_FOR_CLUSTER(local_issue_bcard_t, get_mailbox); - -local_issue_server_t::local_issue_server_t( - mailbox_manager_t *mm, - log_write_issue_tracker_t *_log_write_issue_tracker, - memory_issue_tracker_t *_memory_issue_tracker) : - mailbox_manager(mm), - log_write_issue_tracker(_log_write_issue_tracker), - memory_issue_tracker(_memory_issue_tracker), - get_mailbox(mailbox_manager, - std::bind(&local_issue_server_t::on_get, this, ph::_1, ph::_2)) - { } - -void local_issue_server_t::on_get( - signal_t *, const mailbox_t::address_t &reply) { - local_issues_t issues; - issues.log_write_issues = log_write_issue_tracker->get_issues(); - issues.memory_issues = memory_issue_tracker->get_issues(); - send(mailbox_manager, reply, issues); -} - -local_issue_client_t::local_issue_client_t( - mailbox_manager_t *_mailbox_manager, - watchable_map_t *_directory) : - mailbox_manager(_mailbox_manager), directory(_directory) - { } - -std::vector > local_issue_client_t::get_issues( - signal_t *interruptor) const { - std::vector > bcards; - directory->read_all( - [&](const peer_id_t &, const cluster_directory_metadata_t *md) { - if (md->peer_type == SERVER_PEER) { - bcards.push_back(std::make_pair(md->server_id, md->local_issue_bcard)); - } - }); - local_issues_t aggregator; - static const int max_concurrent_fetches = 16; - throttled_pmap(bcards.size(), [&](size_t i) { - try { - cond_t got_reply; - mailbox_t reply_mailbox( - mailbox_manager, - [&](signal_t *, const local_issues_t &issues) { - for (const auto &issue : issues.log_write_issues) { - log_write_issue_t copy = issue; - copy.add_server(bcards[i].first); - aggregator.log_write_issues.push_back(copy); - } - for (const auto &issue : issues.memory_issues) { - memory_issue_t copy = issue; - copy.add_server(bcards[i].first); - aggregator.memory_issues.push_back(copy); - } - got_reply.pulse(); - }); - disconnect_watcher_t disconnect_watcher( - mailbox_manager, bcards[i].second.get_mailbox.get_peer()); - send(mailbox_manager, bcards[i].second.get_mailbox, - reply_mailbox.get_address()); - wait_any_t waiter(&got_reply, &disconnect_watcher); - wait_interruptible(&waiter, interruptor); - } catch (const interrupted_exc_t &) { - /* We'll check outside the `throttled_pmap()` */ - } - }, max_concurrent_fetches); - if (interruptor->is_pulsed()) { - throw interrupted_exc_t(); - } - std::vector > res; - log_write_issue_tracker_t::combine( - std::move(aggregator.log_write_issues), &res); - memory_issue_tracker_t::combine( - std::move(aggregator.memory_issues), &res); - return res; -} - diff --git a/src/clustering/administration/issues/local.hpp b/src/clustering/administration/issues/local.hpp deleted file mode 100644 index 9686f982b72..00000000000 --- a/src/clustering/administration/issues/local.hpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_ISSUES_LOCAL_HPP_ -#define CLUSTERING_ADMINISTRATION_ISSUES_LOCAL_HPP_ - -#include -#include - -#include "clustering/administration/issues/log_write.hpp" -#include "clustering/administration/issues/memory.hpp" -#include "concurrency/watchable.hpp" -#include "concurrency/watchable_map.hpp" -#include "containers/clone_ptr.hpp" -#include "rpc/semilattice/joins/macros.hpp" -#include "rpc/serialize_macros.hpp" - -class cluster_directory_metadata_t; - -/* "Local issues" are issues that originate on a particular server. When the user queries -the `rethinkdb.current_issues` system table, each server polls the other servers to -retrieve their local issues. */ -class local_issues_t { -public: - std::vector log_write_issues; - std::vector memory_issues; -}; - -RDB_DECLARE_SERIALIZABLE(local_issues_t); - -class local_issue_bcard_t { -public: - typedef mailbox_t> get_mailbox_t; - get_mailbox_t::address_t get_mailbox; -}; - -RDB_DECLARE_SERIALIZABLE(local_issue_bcard_t); - -class local_issue_server_t : public home_thread_mixin_t { -public: - local_issue_server_t( - mailbox_manager_t *mm, - log_write_issue_tracker_t *log_write_issue_tracker, - memory_issue_tracker_t *memory_issue_tracker); - - local_issue_bcard_t get_bcard() { - return local_issue_bcard_t { get_mailbox.get_address() }; - } - -private: - void on_get(signal_t *, const mailbox_t::address_t &reply); - - mailbox_manager_t *const mailbox_manager; - log_write_issue_tracker_t *const log_write_issue_tracker; - memory_issue_tracker_t *const memory_issue_tracker; - local_issue_bcard_t::get_mailbox_t get_mailbox; - DISABLE_COPYING(local_issue_server_t); -}; - -class local_issue_client_t : public issue_tracker_t { -public: - local_issue_client_t( - mailbox_manager_t *_mailbox_manager, - watchable_map_t *_directory); - std::vector > get_issues(signal_t *interruptor) const; - -private: - mailbox_manager_t *const mailbox_manager; - watchable_map_t *const directory; -}; - -#endif /* CLUSTERING_ADMINISTRATION_ISSUES_LOCAL_HPP_ */ diff --git a/src/clustering/administration/issues/log_write.cc b/src/clustering/administration/issues/log_write.cc deleted file mode 100644 index 3b28f1c8ae6..00000000000 --- a/src/clustering/administration/issues/log_write.cc +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/issues/log_write.hpp" -#include "clustering/administration/datum_adapter.hpp" - -const datum_string_t log_write_issue_t::log_write_issue_type = - datum_string_t("log_write_error"); -const uuid_u log_write_issue_t::base_issue_id = - str_to_uuid("a0c0dc6d-aa7f-4119-a29d-4c717c343445"); - -log_write_issue_t::log_write_issue_t() : issue_t(nil_uuid()) { } - -log_write_issue_t::log_write_issue_t(const std::string &_message) : - issue_t(from_hash(base_issue_id, _message)), - message(_message) { } - -bool log_write_issue_t::build_info_and_description( - UNUSED const metadata_t &metadata, - server_config_client_t *server_config_client, - UNUSED table_meta_client_t *table_meta_client, - admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const { - ql::datum_object_builder_t info_builder; - ql::datum_array_builder_t servers_builder(ql::configured_limits_t::unlimited); - std::string servers_string; - for (auto const &server_id : reporting_server_ids) { - ql::datum_t server_name_or_uuid; - name_string_t server_name; - if (!convert_connected_server_id_to_datum(server_id, identifier_format, - server_config_client, &server_name_or_uuid, &server_name)) { - continue; - } - servers_builder.add(server_name_or_uuid); - if (!servers_string.empty()) { - servers_string += ", "; - } - servers_string += server_name.str(); - } - info_builder.overwrite("servers", std::move(servers_builder).to_datum()); - info_builder.overwrite("message", convert_string_to_datum(message)); - *info_out = std::move(info_builder).to_datum(); - *description_out = datum_string_t(strprintf( - "The following server%s encountered an error while writing log statements: %s.\n" - "\nThe error message reported is: %s\n\nPlease fix the problem that is " - "preventing the server%s from writing to their log file. This issue will go " - "away the next time the server successfully writes to the log file.", - reporting_server_ids.size() == 1 ? "" : "s", - servers_string.c_str(), - message.c_str(), - reporting_server_ids.size() == 1 ? "" : "s")); - return true; -} - -RDB_IMPL_SERIALIZABLE_3_FOR_CLUSTER(log_write_issue_t, - issue_id, reporting_server_ids, message); -RDB_IMPL_EQUALITY_COMPARABLE_3(log_write_issue_t, - issue_id, reporting_server_ids, message); - -std::vector log_write_issue_tracker_t::get_issues() { - std::vector issues; - if (static_cast(error_message)) { - issues.push_back(log_write_issue_t(*error_message)); - } - return issues; -} - -void log_write_issue_tracker_t::report_success() { - assert_thread(); - error_message = r_nullopt; -} - -void log_write_issue_tracker_t::report_error(const std::string &message) { - assert_thread(); - error_message = make_optional(message); -} - -void log_write_issue_tracker_t::combine( - std::vector &&issues, - std::vector > *issues_out) { - std::map combined_issues; - for (auto &issue : issues) { - auto combined_it = combined_issues.find(issue.message); - if (combined_it == combined_issues.end()) { - combined_issues.insert(std::make_pair(issue.message, &issue)); - } else { - rassert(issue.reporting_server_ids.size() == 1); - combined_it->second->add_server(*issue.reporting_server_ids.begin()); - } - } - - for (auto const &it : combined_issues) { - issues_out->push_back(scoped_ptr_t(new log_write_issue_t(*it.second))); - } -} diff --git a/src/clustering/administration/issues/log_write.hpp b/src/clustering/administration/issues/log_write.hpp deleted file mode 100644 index 5662959325b..00000000000 --- a/src/clustering/administration/issues/log_write.hpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_ISSUES_LOG_WRITE_HPP_ -#define CLUSTERING_ADMINISTRATION_ISSUES_LOG_WRITE_HPP_ - -#include - -#include "clustering/administration/issues/issue.hpp" -#include "containers/incremental_lenses.hpp" -#include "rpc/connectivity/server_id.hpp" -#include "rpc/mailbox/typed.hpp" - -class local_issue_server_t; - -class log_write_issue_t : public issue_t { -public: - log_write_issue_t(); - explicit log_write_issue_t(const std::string &_message); - - const datum_string_t &get_name() const { return log_write_issue_type; } - bool is_critical() const { return false; } - - void add_server(const server_id_t &server) { - reporting_server_ids.insert(server); - } - - std::string message; - std::set reporting_server_ids; - -private: - bool build_info_and_description( - const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const; - - static const datum_string_t log_write_issue_type; - static const uuid_u base_issue_id; -}; - -RDB_DECLARE_SERIALIZABLE(log_write_issue_t); -RDB_DECLARE_EQUALITY_COMPARABLE(log_write_issue_t); - -class log_write_issue_tracker_t : - public home_thread_mixin_t { -public: - log_write_issue_tracker_t() { } - - std::vector get_issues(); - - void report_success(); - void report_error(const std::string &message); - - static void combine(std::vector &&issues, - std::vector > *issues_out); - -private: - void do_update(); - - optional error_message; - - DISABLE_COPYING(log_write_issue_tracker_t); -}; - -#endif /* CLUSTERING_ADMINISTRATION_ISSUES_LOG_WRITE_HPP_ */ diff --git a/src/clustering/administration/issues/memory.cc b/src/clustering/administration/issues/memory.cc deleted file mode 100644 index 7f9f3794b84..00000000000 --- a/src/clustering/administration/issues/memory.cc +++ /dev/null @@ -1,101 +0,0 @@ -//Copyright 2010-2016 RethinkDB, all rights reserved. -#include "clustering/administration/issues/memory.hpp" -#include "clustering/administration/datum_adapter.hpp" - -const datum_string_t memory_issue_t::memory_issue_type = - datum_string_t("memory_error"); -const uuid_u memory_issue_t::base_issue_id = - str_to_uuid("e013d7af-dd63-4fc0-a021-d9b57591685b"); - -memory_issue_t::memory_issue_t() : issue_t(nil_uuid()) { } - -memory_issue_t::memory_issue_t(const std::string &_message) : - issue_t(from_hash(base_issue_id, _message)), - message(_message) { } - -bool memory_issue_t::build_info_and_description( - const metadata_t &, - server_config_client_t *server_config_client, - table_meta_client_t *, - admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const { - - ql::datum_object_builder_t info_builder; - ql::datum_array_builder_t servers_builder(ql::configured_limits_t::unlimited); - std::string servers_string; - - for (auto const &server_id : reporting_server_ids) { - ql::datum_t server_name_or_uuid; - name_string_t server_name; - if (!convert_connected_server_id_to_datum(server_id, - identifier_format, - server_config_client, - &server_name_or_uuid, - &server_name)) { - continue; - } - servers_builder.add(server_name_or_uuid); - if (!servers_string.empty()) { - servers_string += ", "; - } - servers_string += server_name.str(); - } - info_builder.overwrite("servers", std::move(servers_builder).to_datum()); - info_builder.overwrite("message", convert_string_to_datum(message)); - *info_out = std::move(info_builder).to_datum(); - *description_out = datum_string_t(strprintf( - "The following server%s encountered a memory problem: %s. \n" - "\n The message reported is: %s\n\n", - reporting_server_ids.size() == 1 ? "" : "s", - servers_string.c_str(), - message.c_str())); - return true; -} - -RDB_IMPL_SERIALIZABLE_3_FOR_CLUSTER(memory_issue_t, - issue_id, - reporting_server_ids, - message); - -RDB_IMPL_EQUALITY_COMPARABLE_3(memory_issue_t, - issue_id, - reporting_server_ids, - message); - -std::vector memory_issue_tracker_t::get_issues() { - std::vector issues; - if (static_cast(error_message)) { - issues.push_back(memory_issue_t(*error_message)); - } - return issues; -} - -void memory_issue_tracker_t::report_success() { - assert_thread(); - error_message = r_nullopt; -} - -void memory_issue_tracker_t::report_error(const std::string &message) { - assert_thread(); - error_message = make_optional(message); -} - -void memory_issue_tracker_t::combine( - std::vector &&issues, - std::vector > *issues_out) { - std::map combined_issues; - for (auto &issue : issues) { - auto combined_it = combined_issues.find(issue.message); - if (combined_it == combined_issues.end()) { - combined_issues.insert(std::make_pair(issue.message, &issue)); - } else { - rassert(issue.reporting_server_ids.size() == 1); - combined_it->second->add_server(*issue.reporting_server_ids.begin()); - } - } - - for (auto const &it : combined_issues) { - issues_out->push_back(scoped_ptr_t(new memory_issue_t(*it.second))); - } -} diff --git a/src/clustering/administration/issues/memory.hpp b/src/clustering/administration/issues/memory.hpp deleted file mode 100644 index 902437ed706..00000000000 --- a/src/clustering/administration/issues/memory.hpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved -#ifndef CLUSTERING_ADMINISTRATION_ISSUES_MEMORY_HPP_ -#define CLUSTERING_ADMINISTRATION_ISSUES_MEMORY_HPP_ - -#include - -#include "clustering/administration/issues/issue.hpp" -#include "containers/incremental_lenses.hpp" -#include "rpc/connectivity/server_id.hpp" -#include "rpc/mailbox/typed.hpp" - -class local_issue_server_t; - -class memory_issue_t : public issue_t { -public: - memory_issue_t(); - explicit memory_issue_t(const std::string &_message); - - const datum_string_t &get_name() const { return memory_issue_type; } - bool is_critical() const final { return false; } - - void add_server(const server_id_t &server) { - reporting_server_ids.insert(server); - } - - std::string message; - std::set reporting_server_ids; - -private: - bool build_info_and_description( - const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const; - - static const datum_string_t memory_issue_type; - static const uuid_u base_issue_id; -}; - -RDB_DECLARE_SERIALIZABLE(memory_issue_t); -RDB_DECLARE_EQUALITY_COMPARABLE(memory_issue_t); - -class memory_issue_tracker_t : - public home_thread_mixin_t { -public: - memory_issue_tracker_t() { } - - std::vector get_issues(); - - void report_success(); - void report_error(const std::string &message); - - static void combine(std::vector &&issues, - std::vector > *issues_out); - -private: - void do_update(); - - optional error_message; - - DISABLE_COPYING(memory_issue_tracker_t); -}; - -#endif // CLUSTERING_ADMINISTRATION_ISSUES_MEMORY_HPP_ diff --git a/src/clustering/administration/issues/name_collision.cc b/src/clustering/administration/issues/name_collision.cc deleted file mode 100644 index 3a0478e156b..00000000000 --- a/src/clustering/administration/issues/name_collision.cc +++ /dev/null @@ -1,311 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/issues/name_collision.hpp" - -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "clustering/administration/tables/database_metadata.hpp" -#include "clustering/administration/tables/table_metadata.hpp" -#include "clustering/table_manager/table_meta_client.hpp" -#include "rdb_protocol/configured_limits.hpp" - -// Base issue for all name collisions (server, database, and table) -class name_collision_issue_t : public issue_t { -public: - name_collision_issue_t(const issue_id_t &_issue_id, - const name_string_t &_name, - const std::vector &_collided_ids); - bool is_critical() const { return true; } - -protected: - const name_string_t name; - const std::vector collided_ids; -}; - -// Issue for server name collisions -class server_name_collision_issue_t : public name_collision_issue_t { -public: - explicit server_name_collision_issue_t( - const name_string_t &_name, - const std::vector &_collided_ids); - const datum_string_t &get_name() const { return server_name_collision_issue_type; } - -private: - static const datum_string_t server_name_collision_issue_type; - static const issue_id_t base_issue_id; - bool build_info_and_description( - const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const; -}; - -// Issue for database name collisions -class db_name_collision_issue_t : public name_collision_issue_t { -public: - explicit db_name_collision_issue_t( - const name_string_t &_name, - const std::vector &_collided_ids); - const datum_string_t &get_name() const { return db_name_collision_issue_type; } - -private: - static const datum_string_t db_name_collision_issue_type; - static const issue_id_t base_issue_id; - bool build_info_and_description( - const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const; -}; - -// Issue for table name collisions -class table_name_collision_issue_t : public name_collision_issue_t { -public: - table_name_collision_issue_t( - const name_string_t &_name, - const database_id_t &_db_id, - const std::vector &_collided_ids); - const datum_string_t &get_name() const { return table_name_collision_issue_type; } - -private: - static const datum_string_t table_name_collision_issue_type; - static const issue_id_t base_issue_id; - const database_id_t db_id; - bool build_info_and_description( - const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const; -}; - -const datum_string_t server_name_collision_issue_t::server_name_collision_issue_type = - datum_string_t("server_name_collision"); -const uuid_u server_name_collision_issue_t::base_issue_id = - str_to_uuid("a8bf71c6-a178-43ea-b25d-73a13619c8f0"); - -const datum_string_t db_name_collision_issue_t::db_name_collision_issue_type = - datum_string_t("db_name_collision"); -const uuid_u db_name_collision_issue_t::base_issue_id = - str_to_uuid("a6996d3c-418d-4ab1-85b5-455dcd8721d8"); - -const datum_string_t table_name_collision_issue_t::table_name_collision_issue_type = - datum_string_t("table_name_collision"); -const uuid_u table_name_collision_issue_t::base_issue_id = - str_to_uuid("d610286a-a374-49e6-8d92-032794ed578f"); - -name_collision_issue_t::name_collision_issue_t( - const issue_id_t &_issue_id, - const name_string_t &_name, - const std::vector &_collided_ids) : - issue_t(_issue_id), - name(_name), - collided_ids(_collided_ids) { } - -void generic_build_info_and_description( - const std::string &long_type_plural, - const char *short_type_singular, - const char *system_table_name, - const name_string_t &name, - const std::vector &ids, - ql::datum_t *info_out, - datum_string_t *description_out) { - ql::datum_object_builder_t builder; - ql::datum_array_builder_t ids_builder(ql::configured_limits_t::unlimited); - std::string ids_str; - for (auto const &id : ids) { - ids_builder.add(ql::datum_t(datum_string_t(id))); - if (!ids_str.empty()) { - ids_str += ", "; - } - ids_str += id; - } - builder.overwrite("name", convert_name_to_datum(name)); - builder.overwrite("ids", std::move(ids_builder).to_datum()); - *info_out = std::move(builder).to_datum(); - *description_out = datum_string_t(strprintf( - "There are multiple %s named `%s`. RethinkDB requires that every %s have a " - "unique name. Please update the `name` field of the conflicting %ss' documents " - "in the `rethinkdb.%s` system table.\n\nThe UUIDs of the conflicting %ss are as " - "follows: %s", - long_type_plural.c_str(), name.c_str(), short_type_singular, short_type_singular, - system_table_name, short_type_singular, ids_str.c_str())); -} - -// Helper functions for converting IDs to strings -std::vector server_ids_to_str_ids(const std::vector &ids) { - std::vector res; - res.reserve(ids.size()); - for (const auto &id : ids) { - res.push_back(id.print()); - } - return res; -} -std::vector uuids_to_str_ids(const std::vector &ids) { - std::vector res; - res.reserve(ids.size()); - for (const auto &id : ids) { - res.push_back(uuid_to_str(id)); - } - return res; -} - -server_name_collision_issue_t::server_name_collision_issue_t( - const name_string_t &_name, - const std::vector &_collided_ids) : - name_collision_issue_t(from_hash(base_issue_id, _name), - _name, - server_ids_to_str_ids(_collided_ids)) { } - -bool server_name_collision_issue_t::build_info_and_description( - UNUSED const metadata_t &metadata, - UNUSED server_config_client_t *server_config_client, - UNUSED table_meta_client_t *table_meta_client, - UNUSED admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const { - generic_build_info_and_description( - "servers", "server", "server_config", name, collided_ids, - info_out, description_out); - return true; -} - -db_name_collision_issue_t::db_name_collision_issue_t( - const name_string_t &_name, - const std::vector &_collided_ids) : - name_collision_issue_t(from_hash(base_issue_id, _name), - _name, - uuids_to_str_ids(_collided_ids)) { } - -bool db_name_collision_issue_t::build_info_and_description( - UNUSED const metadata_t &metadata, - UNUSED server_config_client_t *server_config_client, - UNUSED table_meta_client_t *table_meta_client, - UNUSED admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const { - generic_build_info_and_description( - "databases", "database", "db_config", name, collided_ids, - info_out, description_out); - return true; -} - -table_name_collision_issue_t::table_name_collision_issue_t( - const name_string_t &_name, - const database_id_t &_db_id, - const std::vector &_collided_ids) : - name_collision_issue_t(from_hash(base_issue_id, _db_id, _name), - _name, - uuids_to_str_ids(_collided_ids)), - db_id(_db_id) { } - -bool table_name_collision_issue_t::build_info_and_description( - const metadata_t &metadata, - UNUSED server_config_client_t *server_config_client, - UNUSED table_meta_client_t *table_meta_client, - admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const { - ql::datum_t db_name_or_uuid; - name_string_t db_name; - if (!convert_database_id_to_datum(db_id, identifier_format, metadata, - &db_name_or_uuid, &db_name)) { - db_name_or_uuid = ql::datum_t("__deleted_database__"); - db_name = name_string_t::guarantee_valid("__deleted_database__"); - } - ql::datum_t partial_info; - generic_build_info_and_description( - strprintf("tables in database `%s`", db_name.c_str()), - "table", "table_config", name, collided_ids, - &partial_info, description_out); - ql::datum_object_builder_t info_rebuilder(partial_info); - info_rebuilder.overwrite("db", db_name_or_uuid); - *info_out = std::move(info_rebuilder).to_datum(); - return true; -} - -name_collision_issue_tracker_t::name_collision_issue_tracker_t( - server_config_client_t *_server_config_client, - std::shared_ptr > - _cluster_sl_view, - table_meta_client_t *_table_meta_client) : - server_config_client(_server_config_client), - cluster_sl_view(_cluster_sl_view), - table_meta_client(_table_meta_client) { } - -name_collision_issue_tracker_t::~name_collision_issue_tracker_t() { } - -void find_server_duplicates( - watchable_map_t *servers, - std::vector > *issues_out) { - std::map > name_counts; - servers->read_all( - [&](const server_id_t &sid, const server_config_versioned_t *config) { - name_counts[config->config.name].push_back(sid); - }); - for (auto const &it : name_counts) { - if (it.second.size() > 1) { - issues_out->push_back(scoped_ptr_t( - new server_name_collision_issue_t(it.first, it.second))); - } - } -} - -void find_db_duplicates( - const databases_semilattice_metadata_t &dbs, - std::vector > *issues_out) { - std::map > name_counts; - for (auto const &it : dbs.databases) { - if (!it.second.is_deleted()) { - name_string_t name = it.second.get_ref().name.get_ref(); - name_counts[name].push_back(it.first); - } - } - - for (auto const &it : name_counts) { - if (it.second.size() > 1) { - issues_out->push_back(scoped_ptr_t( - new db_name_collision_issue_t(it.first, it.second))); - } - } -} - -void find_table_duplicates( - table_meta_client_t *table_meta_client, - std::vector > *issues_out) { - std::map table_names; - table_meta_client->list_names(&table_names); - - std::map, std::vector > - name_counts; - for (auto const &table_name : table_names) { - auto pair = std::make_pair(table_name.second.database, table_name.second.name); - name_counts[pair].push_back(table_name.first); - } - - for (auto const &name_count : name_counts) { - if (name_count.second.size() > 1) { - issues_out->push_back(scoped_ptr_t( - new table_name_collision_issue_t(name_count.first.second, - name_count.first.first, - name_count.second))); - } - } -} - -std::vector > name_collision_issue_tracker_t::get_issues( - UNUSED signal_t *interruptor) const { - cluster_semilattice_metadata_t metadata = cluster_sl_view->get(); - std::vector > issues; - - find_server_duplicates(server_config_client->get_server_config_map(), &issues); - find_db_duplicates(metadata.databases, &issues); - find_table_duplicates(table_meta_client, &issues); - - return issues; -} diff --git a/src/clustering/administration/issues/name_collision.hpp b/src/clustering/administration/issues/name_collision.hpp deleted file mode 100644 index 9352d5fde89..00000000000 --- a/src/clustering/administration/issues/name_collision.hpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_ISSUES_NAME_COLLISION_HPP_ -#define CLUSTERING_ADMINISTRATION_ISSUES_NAME_COLLISION_HPP_ - -#include -#include -#include - -#include "clustering/administration/issues/issue.hpp" -#include "rpc/connectivity/server_id.hpp" -#include "rpc/semilattice/view.hpp" - -class name_collision_issue_tracker_t : public issue_tracker_t { -public: - name_collision_issue_tracker_t( - server_config_client_t *_server_config_client, - std::shared_ptr > - _cluster_sl_view, - table_meta_client_t *_table_meta_client); - - ~name_collision_issue_tracker_t(); - - std::vector > get_issues(signal_t *interruptor) const; - -private: - server_config_client_t *server_config_client; - std::shared_ptr > - cluster_sl_view; - table_meta_client_t *table_meta_client; - - DISABLE_COPYING(name_collision_issue_tracker_t); -}; - -#endif /* CLUSTERING_ADMINISTRATION_ISSUES_NAME_COLLISION_HPP_ */ diff --git a/src/clustering/administration/issues/non_transitive.cc b/src/clustering/administration/issues/non_transitive.cc deleted file mode 100644 index 81238078783..00000000000 --- a/src/clustering/administration/issues/non_transitive.cc +++ /dev/null @@ -1,83 +0,0 @@ -#include "clustering/administration/issues/non_transitive.hpp" - -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/servers/config_client.hpp" - -bool non_transitive_issue_t::build_info_and_description( - const metadata_t &, - server_config_client_t *config_client, - table_meta_client_t *, - admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const { - ql::datum_object_builder_t info_builder; - info_builder.overwrite("message", - convert_string_to_datum(message)); - - ql::datum_array_builder_t servers_builder(ql::configured_limits_t::unlimited); - for (auto const &server_id : reporting_server_ids) { - ql::datum_t server_name_or_uuid; - if (!convert_connected_server_id_to_datum( - server_id, - identifier_format, - config_client, - &server_name_or_uuid, - nullptr)) { - continue; - } - servers_builder.add(server_name_or_uuid); - } - info_builder.overwrite("servers", std::move(servers_builder).to_datum()); - *info_out = std::move(info_builder).to_datum(); - *description_out = datum_string_t( - "There are currently servers that cannot see every server" - " in the cluster. This may cause table availability issues."); - return true; -} - -const datum_string_t non_transitive_issue_t::non_transitive_issue_type = - datum_string_t("non_transitive_error"); -const uuid_u non_transitive_issue_t::base_type_id = - str_to_uuid("ab2a4f6d-c623-4c11-9982-8e5cc653737d"); - -non_transitive_issue_tracker_t::non_transitive_issue_tracker_t( - server_config_client_t *_server_config_client) : - server_config_client(_server_config_client) { } - -non_transitive_issue_tracker_t::~non_transitive_issue_tracker_t() { } - -std::vector > non_transitive_issue_tracker_t::get_issues( - signal_t *interruptor) const { - std::vector > issues; - - const server_connectivity_t &server_connectivity = - server_config_client->get_server_connectivity(); - - bool is_transitive = true; - int total_servers = server_connectivity.all_servers.size(); - - non_transitive_issue_t issue("Server connectivity is non-transitive."); - // Check if all the servers can see each other - for (const auto &server_pair : server_connectivity.all_servers) { - // Proxies might not be connected to each other, so we don't want to raise an - // issue based on their connectivity. We will still get an issue if some of the - // servers are missing a connection to the proxy. - if (server_pair.first.is_proxy()) { - continue; - } - if (server_pair.second != total_servers * 2) { - is_transitive = false; - issue.add_server(server_pair.first); - } - } - - if (is_transitive == false) { - ql::datum_t row; - issues.push_back(make_scoped(issue)); - } - - if (interruptor->is_pulsed()) { - throw interrupted_exc_t(); - } - return issues; -} diff --git a/src/clustering/administration/issues/non_transitive.hpp b/src/clustering/administration/issues/non_transitive.hpp deleted file mode 100644 index e59ff4cc7cc..00000000000 --- a/src/clustering/administration/issues/non_transitive.hpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_ISSUES_NON_TRANSITIVE_HPP_ -#define CLUSTERING_ADMINISTRATION_ISSUES_NON_TRANSITIVE_HPP_ - -#include "clustering/administration/issues/issue.hpp" -#include "rpc/connectivity/server_id.hpp" - -class non_transitive_issue_t : public issue_t { -public: - non_transitive_issue_t() : issue_t(nil_uuid()) { } - explicit non_transitive_issue_t(const std::string &_message) : - issue_t(from_hash(base_type_id, _message)), - message(_message) { } - bool build_info_and_description( - const metadata_t &, - server_config_client_t *config_client, - table_meta_client_t *, - admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const final; - - bool is_critical() const final { return false; } - - void add_server(const server_id_t &server) { - reporting_server_ids.insert(server); - } - - const datum_string_t &get_name() const final { - return non_transitive_issue_type; - } - - std::string message; - static const datum_string_t non_transitive_issue_type; - static const uuid_u base_type_id;; - std::set reporting_server_ids; -}; - -class non_transitive_issue_tracker_t : public issue_tracker_t { -public: - non_transitive_issue_tracker_t( - server_config_client_t *server_config_client); - ~non_transitive_issue_tracker_t(); - - std::vector > get_issues(signal_t *interruptor) const; - -private: - server_config_client_t *server_config_client; - - DISABLE_COPYING(non_transitive_issue_tracker_t); -}; - -#endif // CLUSTERING_ADMINISTRATION_ISSUES_NON_TRANSITIVE_HPP_ diff --git a/src/clustering/administration/issues/outdated_index.cc b/src/clustering/administration/issues/outdated_index.cc deleted file mode 100644 index 7068a751d23..00000000000 --- a/src/clustering/administration/issues/outdated_index.cc +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/issues/outdated_index.hpp" - -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/datum_adapter.hpp" -#include "rdb_protocol/configured_limits.hpp" - -const datum_string_t outdated_index_issue_t::outdated_index_issue_type = - datum_string_t("outdated_index"); -const uuid_u outdated_index_issue_t::base_issue_id = - str_to_uuid("528f5239-096e-47a3-b263-7a06a256ecc3"); - -outdated_index_issue_t::outdated_index_issue_t() : issue_t(nil_uuid()) { } - -outdated_index_issue_t::outdated_index_issue_t( - outdated_index_issue_t::index_map_t _indexes) : - issue_t(base_issue_id), - indexes(std::move(_indexes)) { } - -bool outdated_index_issue_t::build_info_and_description( - const metadata_t &metadata, - UNUSED server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const { - ql::datum_object_builder_t info_builder; - ql::datum_array_builder_t tables_builder(ql::configured_limits_t::unlimited); - std::string tables_str; - for (auto const &pair : indexes) { - ql::datum_t table_name_or_uuid; - name_string_t table_name; - ql::datum_t db_name_or_uuid; - name_string_t db_name; - if (!convert_table_id_to_datums(pair.first, identifier_format, metadata, - table_meta_client, &table_name_or_uuid, &table_name, &db_name_or_uuid, - &db_name)) { - /* No point in showing an outdated_index issue for a deleted table. */ - continue; - } - ql::datum_array_builder_t indexes_builder(ql::configured_limits_t::unlimited); - std::string indexes_str; - for (auto const &index : pair.second) { - indexes_builder.add(convert_string_to_datum(index)); - if (!indexes_str.empty()) { - indexes_str += ", "; - } - indexes_str += "`" + index + "`"; - } - ql::datum_object_builder_t table_builder; - table_builder.overwrite("table", table_name_or_uuid); - table_builder.overwrite("db", db_name_or_uuid); - table_builder.overwrite("indexes", std::move(indexes_builder).to_datum()); - tables_builder.add(std::move(table_builder).to_datum()); - tables_str += strprintf("\nFor table `%s.%s`: %s", - table_name.c_str(), db_name.c_str(), indexes_str.c_str()); - } - info_builder.overwrite("tables", std::move(tables_builder).to_datum()); - *info_out = std::move(info_builder).to_datum(); - *description_out = datum_string_t(strprintf( - "The cluster contains indexes that were created with a previous version of the " - "query language which contained some bugs. These should be remade to avoid " - "relying on broken behavior. See " - "http://www.rethinkdb.com/docs/troubleshooting/#my-secondary-index-is-outdated " - "for details.%s", tables_str.c_str())); - /* If all the tables were deleted by the time we get here, don't show the user an - issue */ - return !tables_str.empty(); -} - -outdated_index_issue_tracker_t::outdated_index_issue_tracker_t( - table_meta_client_t *_table_meta_client) : - table_meta_client(_table_meta_client) { } - -bool outdated_index_issue_tracker_t::is_acceptable_outdated(const sindex_config_t &sindex_config) { - if (sindex_config.func.is_simple_selector()) { - switch(sindex_config.func_version) { - // A version should return false if it breaks compatibility for even simple sindexes. - case reql_version_t::v1_16: - case reql_version_t::v2_0: - case reql_version_t::v2_1: - case reql_version_t::v2_2: - case reql_version_t::v2_3: - case reql_version_t::v2_4_is_latest: - default: - return true; - } - } - return false; -} - -void outdated_index_issue_tracker_t::log_outdated_indexes( - multi_table_manager_t *multi_table_manager, - const cluster_semilattice_metadata_t &metadata, - signal_t *interruptor) { - try { - multi_table_manager->visit_tables(interruptor, access_t::read, - [&] (const namespace_id_t &table_id, - multistore_ptr_t *, - table_manager_t *table_manager) { - table_config_and_shards_t config = - table_manager->get_raft()->get_committed_state()->get().state.config; - - std::string indexes; - for (auto const &sindex_pair : config.config.sindexes) { - if (sindex_pair.second.func_version != reql_version_t::LATEST - && !is_acceptable_outdated(sindex_pair.second )) { - indexes += (indexes.empty() ? "'" : ", '") + sindex_pair.first + "'"; - } - } - - if (!indexes.empty()) { - name_string_t db_name; - if (!convert_database_id_to_datum(config.config.basic.database, - admin_identifier_format_t::name, - metadata, - nullptr, - &db_name)) { - db_name = name_string_t::guarantee_valid("__deleted_database__"); - } - - logWRN("Table %s.%s (%s) contains these outdated indexes which should be " - "recreated: %s", - db_name.c_str(), - config.config.basic.name.c_str(), - uuid_to_str(table_id).c_str(), - indexes.c_str()); - } - }); - } catch (const interrupted_exc_t &) { - // Do nothing - } -} - -std::vector > outdated_index_issue_tracker_t::get_issues( - signal_t *interruptor) const { - std::vector > res; - outdated_index_issue_t::index_map_t index_map; - - std::map configs; - std::map disconnected_configs; - table_meta_client->list_configs(interruptor, &configs, &disconnected_configs); - - for (auto const &table_pair : configs) { - std::set indexes; - for (auto const &sindex_pair : table_pair.second.config.sindexes) { - if (sindex_pair.second.func_version != reql_version_t::LATEST - && !is_acceptable_outdated(sindex_pair.second)) { - indexes.insert(sindex_pair.first); - } - } - - if (!indexes.empty()) { - index_map.insert(std::make_pair(table_pair.first, indexes)); - } - } - - if (!index_map.empty()) { - res.push_back(scoped_ptr_t( - new outdated_index_issue_t(std::move(index_map)))); - } - return res; -} diff --git a/src/clustering/administration/issues/outdated_index.hpp b/src/clustering/administration/issues/outdated_index.hpp deleted file mode 100644 index ec61cb1cfb0..00000000000 --- a/src/clustering/administration/issues/outdated_index.hpp +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_ISSUES_OUTDATED_INDEX_HPP_ -#define CLUSTERING_ADMINISTRATION_ISSUES_OUTDATED_INDEX_HPP_ - -#include -#include -#include - -#include "clustering/administration/issues/issue.hpp" -#include "clustering/table_manager/multi_table_manager.hpp" -#include "concurrency/one_per_thread.hpp" -#include "concurrency/pump_coro.hpp" -#include "containers/scoped.hpp" - -class outdated_index_issue_t : public issue_t { -public: - typedef std::map > index_map_t; - - outdated_index_issue_t(); - explicit outdated_index_issue_t(index_map_t indexes); - const datum_string_t &get_name() const { return outdated_index_issue_type; } - bool is_critical() const { return false; } - - index_map_t indexes; - -private: - bool build_info_and_description( - const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const; - - static const datum_string_t outdated_index_issue_type; - static const uuid_u base_issue_id; -}; - -class outdated_index_issue_tracker_t : public issue_tracker_t { -public: - explicit outdated_index_issue_tracker_t(table_meta_client_t *_table_meta_client); - - std::vector > get_issues(signal_t *interruptor) const; - - static void log_outdated_indexes(multi_table_manager_t *multi_table_manager, - const cluster_semilattice_metadata_t &metadata, - signal_t *interruptor); - - static bool is_acceptable_outdated(const sindex_config_t &sindex_config); -private: - table_meta_client_t *table_meta_client; - DISABLE_COPYING(outdated_index_issue_tracker_t); -}; - -#endif /* CLUSTERING_ADMINISTRATION_ISSUES_OUTDATED_INDEX_HPP_ */ diff --git a/src/clustering/administration/issues/table.cc b/src/clustering/administration/issues/table.cc deleted file mode 100644 index 34e32bfa279..00000000000 --- a/src/clustering/administration/issues/table.cc +++ /dev/null @@ -1,166 +0,0 @@ -#include "clustering/administration/issues/table.hpp" - -#include - -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/tables/table_status.hpp" -#include "clustering/table_manager/table_meta_client.hpp" - -class table_availability_issue_t : public issue_t { -public: - table_availability_issue_t( - const namespace_id_t &_table_id, - table_status_t &&_status) : - issue_t(from_hash(base_issue_id, _table_id)), - table_id(_table_id), status(std::move(_status)) { } - - bool is_critical() const { return status.readiness < table_readiness_t::writes; } - const datum_string_t &get_name() const { return table_availability_issue_type; } - -private: - static const datum_string_t table_availability_issue_type; - static const uuid_u base_issue_id; - - bool build_info_and_description( - const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const; - - const namespace_id_t table_id; - const table_status_t status; -}; - -bool table_availability_issue_t::build_info_and_description( - const metadata_t &metadata, - UNUSED server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t identifier_format, - ql::datum_t *info_out, - datum_string_t *description_out) const { - ql::datum_t db_name_or_uuid; - name_string_t db_name; - ql::datum_t table_name_or_uuid; - name_string_t table_name; - if (!convert_table_id_to_datums(table_id, identifier_format, metadata, - table_meta_client, &table_name_or_uuid, &table_name, - &db_name_or_uuid, &db_name)) { - return false; - } - - ql::datum_t status_datum = convert_table_status_to_datum(status, identifier_format); - ql::datum_object_builder_t builder(status_datum); - builder.overwrite("table", table_name_or_uuid); - builder.overwrite("db", db_name_or_uuid); - *info_out = std::move(builder).to_datum(); - - // Provide a textual description of the problem as well - if (!status.total_loss) { - const char *blurb; - switch (status.readiness) { - case table_readiness_t::unavailable: - blurb = "not available for any operations"; - break; - case table_readiness_t::outdated_reads: - blurb = "available for outdated reads, but not up-to-date reads or writes"; - break; - case table_readiness_t::reads: - blurb = "available for outdated and up-to-date reads, but not writes"; - break; - case table_readiness_t::writes: - blurb = "available for all operations, but some replicas are not ready"; - break; - case table_readiness_t::finished: // fallthrough intentional - default: - unreachable(); - } - std::string text = strprintf( - "Table `%s.%s` is %s. The following servers are not reachable: ", - db_name.c_str(), table_name.c_str(), blurb); - bool first = true; - for (const server_id_t &server : status.disconnected) { - if (first) { - first = false; - } else { - text += ", "; - } - text += status.server_names.get(server).c_str(); - } - *description_out = datum_string_t(text); - } else { - *description_out = datum_string_t(strprintf( - "Table `%s.%s` is not available for any operations. No servers are " - "reachable for this table.", - db_name.c_str(), table_name.c_str())); - } - - return true; -} - -const datum_string_t table_availability_issue_t::table_availability_issue_type = - datum_string_t("table_availability"); -const uuid_u table_availability_issue_t::base_issue_id = - str_to_uuid("4ae8c575-b103-3335-a79a-121089e34339"); - -table_issue_tracker_t::table_issue_tracker_t( - server_config_client_t *_server_config_client, - table_meta_client_t *_table_meta_client, - namespace_repo_t *_namespace_repo) : - server_config_client(_server_config_client), - table_meta_client(_table_meta_client), - namespace_repo(_namespace_repo) { } - -table_issue_tracker_t::~table_issue_tracker_t() { } - -std::vector > table_issue_tracker_t::get_issues( - signal_t *interruptor) const { - std::map table_names; - table_meta_client->list_names(&table_names); - - // Convert to a vector of ids so we can index it in pmap - std::vector table_ids; - table_ids.reserve(table_names.size()); - for (auto const &pair : table_names) { - table_ids.push_back(pair.first); - } - - std::vector > issues; - throttled_pmap(table_ids.size(), [&] (int64_t i) { - check_table(table_ids[i], &issues, interruptor); - }, 16); - if (interruptor->is_pulsed()) { - throw interrupted_exc_t(); - } - - return issues; -} - -void table_issue_tracker_t::check_table(const namespace_id_t &table_id, - std::vector > *issues_out, - signal_t *interruptor) const { - table_status_t status; - try { - table_config_and_shards_t config; - table_meta_client->get_config(table_id, interruptor, &config); - get_table_status(table_id, config, namespace_repo, table_meta_client, - server_config_client, interruptor, &status); - } catch (const interrupted_exc_t &) { - // Ignore interruption - can't be passed through pmap - return; - } catch (const no_such_table_exc_t &) { - // Ignore any tables that have stopped existing - return; - } catch (const failed_table_op_exc_t &) { - status.readiness = table_readiness_t::unavailable; - status.total_loss = true; - } - - if (status.readiness != table_readiness_t::finished && - (status.total_loss || !status.disconnected.empty())) { - issues_out->emplace_back(make_scoped( - table_id, std::move(status))); - } -} diff --git a/src/clustering/administration/issues/table.hpp b/src/clustering/administration/issues/table.hpp deleted file mode 100644 index ab72c423931..00000000000 --- a/src/clustering/administration/issues/table.hpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_ISSUES_TABLE_HPP_ -#define CLUSTERING_ADMINISTRATION_ISSUES_TABLE_HPP_ - -#include - -#include "clustering/administration/issues/issue.hpp" -#include "containers/scoped.hpp" - -class namespace_repo_t; -class server_config_client_t; -class table_meta_client_t; -class table_basic_config_t; - -class table_issue_tracker_t : public issue_tracker_t { -public: - table_issue_tracker_t( - server_config_client_t *server_config_client, - table_meta_client_t *_table_meta_client, - namespace_repo_t *_namespace_repo); - ~table_issue_tracker_t(); - - std::vector > get_issues(signal_t *interruptor) const; - -private: - void check_table(const namespace_id_t &table_id, - std::vector > *issues_out, - signal_t *interruptor) const; - - server_config_client_t *server_config_client; - table_meta_client_t *table_meta_client; - namespace_repo_t *namespace_repo; - - DISABLE_COPYING(table_issue_tracker_t); -}; - -#endif /* CLUSTERING_ADMINISTRATION_ISSUES_TABLE_HPP_ */ diff --git a/src/clustering/administration/jobs/backend.cc b/src/clustering/administration/jobs/backend.cc deleted file mode 100644 index 23cb5d307ca..00000000000 --- a/src/clustering/administration/jobs/backend.cc +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. - -#include "clustering/administration/jobs/backend.hpp" - -#include - -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/jobs/manager.hpp" -#include "clustering/administration/jobs/report.hpp" -#include "clustering/administration/main/watchable_fields.hpp" -#include "clustering/administration/metadata.hpp" -#include "concurrency/cross_thread_signal.hpp" -#include "rpc/mailbox/disconnect_watcher.hpp" - -jobs_artificial_table_backend_t::jobs_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - mailbox_manager_t *_mailbox_manager, - std::shared_ptr< semilattice_readwrite_view_t< - cluster_semilattice_metadata_t> > _semilattice_view, - const clone_ptr_t > > &_directory_view, - server_config_client_t *_server_config_client, - table_meta_client_t *_table_meta_client, - admin_identifier_format_t _identifier_format) - : timer_cfeed_artificial_table_backend_t( - name_string_t::guarantee_valid("jobs"), rdb_context, name_resolver), - mailbox_manager(_mailbox_manager), - semilattice_view(_semilattice_view), - directory_view(_directory_view), - server_config_client(_server_config_client), - table_meta_client(_table_meta_client), - identifier_format(_identifier_format) { -} - -jobs_artificial_table_backend_t::~jobs_artificial_table_backend_t() { - begin_changefeed_destruction(); -} - -std::string jobs_artificial_table_backend_t::get_primary_key_name() { - return "id"; -} - -template -void insert_or_merge_jobs(std::vector const &jobs, std::map *jobs_out) { - for (auto const &job : jobs) { - auto result = jobs_out->insert(std::make_pair(job.id, job)); - if (result.second == false) { - result.first->second.merge(job); - } - } -} - -template -void jobs_to_datums( - std::map const &jobs, - admin_identifier_format_t identifier_format, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - cluster_semilattice_metadata_t const &metadata, - std::map *jobs_out) { - ql::datum_t job_out; - for (auto const &job : jobs) { - if (job.second.to_datum(identifier_format, server_config_client, - table_meta_client, metadata, &job_out)) { - jobs_out->insert(std::make_pair(job.first, std::move(job_out))); - } - } -} - -void jobs_artificial_table_backend_t::get_all_job_reports( - auth::user_context_t const &user_context, - signal_t *interruptor, - std::map *jobs_out) { - assert_thread(); // Accessing `directory_view` - - std::map query_jobs_map; - std::map disk_compaction_jobs_map; - std::map index_construction_jobs_map; - std::map backfill_jobs_map; - - typedef std::map peers_t; - peers_t peers = directory_view->get().get_inner(); - pmap(peers.begin(), peers.end(), [&](peers_t::value_type const &peer) { - cond_t returned_job_reports; - disconnect_watcher_t disconnect_watcher(mailbox_manager, peer.first); - - jobs_manager_business_card_t::return_mailbox_t return_mailbox( - mailbox_manager, - [&](UNUSED signal_t *, - std::vector const & query_jobs, - std::vector const &disk_compaction_jobs, - std::vector const &index_construction_jobs, - std::vector const &backfill_jobs) { - - insert_or_merge_jobs(query_jobs, &query_jobs_map); - insert_or_merge_jobs(disk_compaction_jobs, &disk_compaction_jobs_map); - insert_or_merge_jobs( - index_construction_jobs, &index_construction_jobs_map); - insert_or_merge_jobs(backfill_jobs, &backfill_jobs_map); - - returned_job_reports.pulse(); - }); - send(mailbox_manager, - peer.second.jobs_mailbox.get_job_reports_mailbox_address, - return_mailbox.get_address()); - - wait_any_t waiter(&returned_job_reports, &disconnect_watcher, interruptor); - waiter.wait(); - }); - - if (interruptor->is_pulsed()) { - throw interrupted_exc_t(); - } - - // FIXME This can be done more efficiently by not fetching the data - - for (auto query_job = query_jobs_map.begin(); query_job != query_jobs_map.end(); ) { - if (!user_context.is_admin_user() && - query_job->second.user_context != user_context) { - query_job = query_jobs_map.erase(query_job); - } else { - query_job++; - } - } - - if (!user_context.is_admin_user()) { - disk_compaction_jobs_map.clear(); - index_construction_jobs_map.clear(); - backfill_jobs_map.clear(); - } - - cluster_semilattice_metadata_t metadata = semilattice_view->get(); - jobs_to_datums(query_jobs_map, identifier_format, server_config_client, - table_meta_client, metadata, jobs_out); - jobs_to_datums(disk_compaction_jobs_map, identifier_format, server_config_client, - table_meta_client, metadata, jobs_out); - jobs_to_datums(index_construction_jobs_map, identifier_format, server_config_client, - table_meta_client, metadata, jobs_out); - jobs_to_datums(backfill_jobs_map, identifier_format, server_config_client, - table_meta_client, metadata, jobs_out); -} - -bool jobs_artificial_table_backend_t::read_all_rows_as_vector( - auth::user_context_t const &user_context, - signal_t *interruptor_on_caller, - std::vector *rows_out, - UNUSED admin_err_t *error_out) { - rows_out->clear(); - - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t rethreader(home_thread()); - - std::map job_reports; - get_all_job_reports( - user_context, - &interruptor_on_home, - &job_reports); - - rows_out->reserve(job_reports.size()); - for (auto &&job_report : job_reports) { - rows_out->push_back(std::move(job_report.second)); - } - - return true; -} - -bool jobs_artificial_table_backend_t::read_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor_on_caller, - ql::datum_t *row_out, - UNUSED admin_err_t *error_out) { - *row_out = ql::datum_t(); - - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t rethreader(home_thread()); - - std::string job_type; - uuid_u job_id; - if (convert_job_type_and_id_from_datum(primary_key, &job_type, &job_id)) { - std::map job_reports; - get_all_job_reports( - user_context, - &interruptor_on_home, - &job_reports); - - auto const iterator = job_reports.find(job_id); - if (iterator != job_reports.end() && - iterator->second.get_field("type").as_str().to_std() == job_type) { - *row_out = std::move(iterator->second); - } - } - - return true; -} - -bool jobs_artificial_table_backend_t::write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - UNUSED signal_t *interruptor_on_caller, - admin_err_t *error_out) { - on_thread_t rethreader(home_thread()); - - if (new_value_inout->has()) { - *error_out = admin_err_t{ - "The `rethinkdb.jobs` system table only allows deletions, " - "not inserts or updates.", - query_state_t::FAILED}; - return false; - } - guarantee(!pkey_was_autogenerated); - - std::string type; - uuid_u id; - if (convert_job_type_and_id_from_datum(primary_key, &type, &id)) { - if (type != "query") { - *error_out = admin_err_t{ - strprintf("Jobs of type `%s` cannot be interrupted.", type.c_str()), - query_state_t::FAILED}; - return false; - } - - typedef std::map peers_t; - peers_t peers = directory_view->get().get_inner(); - pmap(peers.begin(), peers.end(), [&](peers_t::value_type const &peer) { - send(mailbox_manager, - peer.second.jobs_mailbox.job_interrupt_mailbox_address, - id, - user_context); - }); - } - - return true; -} diff --git a/src/clustering/administration/jobs/backend.hpp b/src/clustering/administration/jobs/backend.hpp deleted file mode 100644 index 229108df660..00000000000 --- a/src/clustering/administration/jobs/backend.hpp +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_JOBS_BACKEND_HPP_ -#define CLUSTERING_ADMINISTRATION_JOBS_BACKEND_HPP_ - -#include -#include -#include - -#include "rdb_protocol/artificial_table/caching_cfeed_backend.hpp" -#include "rdb_protocol/context.hpp" -#include "clustering/administration/auth/user_context.hpp" -#include "concurrency/watchable.hpp" - -class cluster_directory_metadata_t; -class cluster_semilattice_metadata_t; -class server_config_client_t; -class table_meta_client_t; -template class semilattice_readwrite_view_t; -template class change_tracking_map_t; - -class jobs_artificial_table_backend_t : - public timer_cfeed_artificial_table_backend_t -{ -public: - jobs_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - mailbox_manager_t *_mailbox_manager, - std::shared_ptr > _semilattice_view, - const clone_ptr_t > > &_directory_view, - server_config_client_t *_server_config_client, - table_meta_client_t *_table_meta_client, - admin_identifier_format_t _identifier_format); - ~jobs_artificial_table_backend_t(); - - std::string get_primary_key_name(); - - bool read_all_rows_as_vector( - auth::user_context_t const &user_context, - signal_t *interruptor, - std::vector *rows_out, - admin_err_t *error_out); - - bool read_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor, - ql::datum_t *row_out, - admin_err_t *error_out); - - bool write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor, - admin_err_t *error_out); - -private: - void get_all_job_reports( - auth::user_context_t const &user_context, - signal_t *interruptor, - std::map *jobs_out); - - mailbox_manager_t *mailbox_manager; - - std::shared_ptr< semilattice_readwrite_view_t< - cluster_semilattice_metadata_t> > semilattice_view; - - clone_ptr_t > > directory_view; - - server_config_client_t *server_config_client; - table_meta_client_t *table_meta_client; - - admin_identifier_format_t identifier_format; -}; - -#endif /* CLUSTERING_ADMINISTRATION_JOBS_BACKEND_HPP_ */ diff --git a/src/clustering/administration/jobs/manager.cc b/src/clustering/administration/jobs/manager.cc deleted file mode 100644 index 3fc44ca67ee..00000000000 --- a/src/clustering/administration/jobs/manager.cc +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/jobs/manager.hpp" - -#include -#include - -#include "concurrency/watchable.hpp" -#include "pprint/js_pprint.hpp" -#include "rdb_protocol/context.hpp" -#include "rdb_protocol/query_cache.hpp" - -const size_t jobs_manager_t::printed_query_columns = 89; - -const uuid_u jobs_manager_t::base_sindex_id = - str_to_uuid("74d855a5-0c40-4930-a451-d1ce508ef2d2"); - -const uuid_u jobs_manager_t::base_disk_compaction_id = - str_to_uuid("b8766ece-d15c-4f96-bee5-c0edacf10c9c"); - -const uuid_u jobs_manager_t::base_backfill_id = - str_to_uuid("a5e1b38d-c712-42d7-ab4c-f177a3fb0d20"); - -jobs_manager_t::jobs_manager_t(mailbox_manager_t *_mailbox_manager, - server_id_t const &_server_id, - rdb_context_t *_rdb_context, - real_table_persistence_interface_t - *_table_persistence_interface, - multi_table_manager_t *_multi_table_manager) : - mailbox_manager(_mailbox_manager), - server_id(_server_id), - rdb_context(_rdb_context), - table_persistence_interface(_table_persistence_interface), - multi_table_manager(_multi_table_manager), - get_job_reports_mailbox(_mailbox_manager, - std::bind(&jobs_manager_t::on_get_job_reports, - this, ph::_1, ph::_2)), - job_interrupt_mailbox(_mailbox_manager, - std::bind(&jobs_manager_t::on_job_interrupt, - this, ph::_1, ph::_2, ph::_3)) { } - -jobs_manager_business_card_t jobs_manager_t::get_business_card() { - business_card_t business_card; - business_card.get_job_reports_mailbox_address = - get_job_reports_mailbox.get_address(); - business_card.job_interrupt_mailbox_address = job_interrupt_mailbox.get_address(); - return business_card; -} - -void jobs_manager_t::on_get_job_reports( - signal_t *interruptor, - const business_card_t::return_mailbox_t::address_t &reply_address) { - std::vector query_job_reports; - std::vector disk_compaction_job_reports; - std::vector index_construction_job_reports; - std::vector backfill_job_reports; - - if (drainer.is_draining()) { - // We're shutting down, send an empty reponse since we can't acquire a `drainer` - // lock any more. Note the `mailbox_manager` is destructed after we are, thus - // the pointer remains valid. - send(mailbox_manager, - reply_address, - query_job_reports, - disk_compaction_job_reports, - index_construction_job_reports, - backfill_job_reports); - return; - } - - auto lock = drainer.lock(); - - // Note, as `time` is retrieved here a job may actually report to be started after - // fetching the time, leading to a negative duration which we round to zero. - microtime_t time = current_microtime(); - - pmap(get_num_threads(), [&](int32_t threadnum) { - // Here we need to store `query_job_report_t` locally to prevent multiple threads - // from inserting into the outer `job_reports`. - std::vector query_job_reports_inner; - { - on_thread_t thread((threadnum_t(threadnum))); - - for (const auto &query_cache - : *rdb_context->get_query_caches_for_this_thread()) { - for (const auto &pair : *query_cache) { - if (pair.second->persistent_interruptor.is_pulsed()) { - continue; - } - - auto render = pprint::render_as_javascript( - pair.second->term_storage->root_term()); - - query_job_reports_inner.emplace_back( - pair.second->job_id, - time - std::min(pair.second->start_time, time), - server_id, - query_cache->get_client_addr_port(), - pretty_print(printed_query_columns, render), - query_cache->get_user_context()); - } - } - } - query_job_reports.insert( - query_job_reports.end(), - std::make_move_iterator(query_job_reports_inner.begin()), - std::make_move_iterator(query_job_reports_inner.end())); - }); - - if (table_persistence_interface != nullptr && - table_persistence_interface->is_gc_active()) { - // Note that "disk_compaction" jobs do not have a duration. - disk_compaction_job_reports.emplace_back( - uuid_u::from_hash(base_disk_compaction_id, uuid_to_str(server_id.get_uuid())), - -1, - server_id); - } - - try { - multi_table_manager->visit_tables(interruptor, access_t::read, - [&](const namespace_id_t &table_id, - UNUSED multistore_ptr_t *multistore_ptr, - table_manager_t *table_manager) { - std::map > statuses = - table_manager->get_sindex_manager().get_status(interruptor); - for (const auto &status : statuses) { - uuid_u id = uuid_u::from_hash( - base_sindex_id, uuid_to_str(table_id) + status.first); - - /* Note that we only calculate the duration if the index construction is - still in progress. */ - double duration = status.second.second.ready - ? 0.0 - : time - std::min(status.second.second.start_time, time); - - index_construction_job_reports.emplace_back( - id, - duration, - server_id, - table_id, - status.first, - status.second.second.ready, - status.second.second.progress_numerator, - status.second.second.progress_denominator); - } - - std::map backfills = - table_manager->get_backfill_progress_tracker().get_progress_trackers(); - std::string base_str = uuid_to_str(server_id.get_uuid()) + uuid_to_str(table_id); - for (const auto &backfill : backfills) { - uuid_u id = uuid_u::from_hash( - base_backfill_id, - base_str + uuid_to_str(backfill.second.source_server_id.get_uuid())); - - /* As above we only calculate the duration if the backfill is still in - progress. */ - double duration = backfill.second.is_ready - ? 0.0 - : time - std::min(backfill.second.start_time, time); - - backfill_job_reports.emplace_back( - id, - duration, - server_id, - table_id, - backfill.second.is_ready, - backfill.second.progress, - backfill.second.source_server_id, - server_id); - } - }); - - send(mailbox_manager, - reply_address, - query_job_reports, - disk_compaction_job_reports, - index_construction_job_reports, - backfill_job_reports); - } catch (const interrupted_exc_t &) { - // Do nothing - } -} - -void jobs_manager_t::on_job_interrupt( - UNUSED signal_t *interruptor, - uuid_u const &id, - auth::user_context_t const &user_context) { - if (drainer.is_draining()) { - // We're shutting down, the query job will be interrupted regardless. - return; - } - - auto lock = drainer.lock(); - - pmap(get_num_threads(), [&](int32_t threadnum) { - on_thread_t thread((threadnum_t(threadnum))); - - if (rdb_context != nullptr) { - for (auto &&query_cache : *rdb_context->get_query_caches_for_this_thread()) { - if (!user_context.is_admin_user() && - user_context != query_cache->get_user_context()) { - continue; - } - - for (auto &&pair : *query_cache) { - if (pair.second->job_id == id) { - query_cache->terminate_internal(pair.second.get()); - pair.second->interrupt_reason = - ql::query_cache_t::interrupt_reason_t::DELETE; - return; - } - } - } - } - }); -} diff --git a/src/clustering/administration/jobs/manager.hpp b/src/clustering/administration/jobs/manager.hpp deleted file mode 100644 index e3d68b2c91b..00000000000 --- a/src/clustering/administration/jobs/manager.hpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_JOBS_MANAGER_HPP_ -#define CLUSTERING_ADMINISTRATION_JOBS_MANAGER_HPP_ - -#include -#include -#include - -#include "clustering/administration/auth/user_context.hpp" -#include "clustering/administration/jobs/report.hpp" -#include "clustering/administration/persist/table_interface.hpp" -#include "clustering/table_manager/multi_table_manager.hpp" -#include "concurrency/auto_drainer.hpp" -#include "concurrency/one_per_thread.hpp" -#include "containers/archive/stl_types.hpp" -#include "containers/uuid.hpp" -#include "rpc/mailbox/typed.hpp" -#include "rpc/serialize_macros.hpp" - -class rdb_context_t; -class reactor_driver_t; - -class jobs_manager_t { -public: - explicit jobs_manager_t(mailbox_manager_t *mailbox_manager, - server_id_t const &server_id, - rdb_context_t *rdb_context, - real_table_persistence_interface_t - *table_persistence_interface, - multi_table_manager_t *multi_table_manager); - - typedef jobs_manager_business_card_t business_card_t; - jobs_manager_business_card_t get_business_card(); - -private: - static const size_t printed_query_columns; - - static const uuid_u base_sindex_id; - static const uuid_u base_disk_compaction_id; - static const uuid_u base_backfill_id; - - void on_get_job_reports( - UNUSED signal_t *interruptor, - business_card_t::return_mailbox_t::address_t const &reply_address); - - void on_job_interrupt( - UNUSED signal_t *interruptor, - uuid_u const &id, - auth::user_context_t const &user_context); - - mailbox_manager_t *mailbox_manager; - server_id_t server_id; - - rdb_context_t *rdb_context; - real_table_persistence_interface_t *table_persistence_interface; - multi_table_manager_t *multi_table_manager; - - auto_drainer_t drainer; - - business_card_t::get_job_reports_mailbox_t get_job_reports_mailbox; - business_card_t::job_interrupt_mailbox_t job_interrupt_mailbox; - - DISABLE_COPYING(jobs_manager_t); -}; - -#endif /* CLUSTERING_ADMINISTRATION_JOBS_MANAGER_HPP_ */ diff --git a/src/clustering/administration/jobs/report.cc b/src/clustering/administration/jobs/report.cc deleted file mode 100644 index fb8507b1822..00000000000 --- a/src/clustering/administration/jobs/report.cc +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/jobs/report.hpp" - -#include "clustering/administration/servers/config_client.hpp" - -bool convert_job_type_and_id_from_datum(ql::datum_t primary_key, - std::string *type_out, - uuid_u *id_out) { - admin_err_t error; - return primary_key.get_type() == ql::datum_t::R_ARRAY && - primary_key.arr_size() == 2 && - convert_string_from_datum(primary_key.get(0), type_out, &error) && - convert_uuid_from_datum(primary_key.get(1), id_out, &error); -} - -ql::datum_t convert_job_type_and_id_to_datum(std::string const &type, uuid_u const &id) { - ql::datum_array_builder_t primary_key_builder(ql::configured_limits_t::unlimited); - primary_key_builder.add(convert_string_to_datum(type)); - primary_key_builder.add(convert_uuid_to_datum(id)); - return std::move(primary_key_builder).to_datum(); -} - -disk_compaction_job_report_t::disk_compaction_job_report_t() - : job_report_base_t() { } - -disk_compaction_job_report_t::disk_compaction_job_report_t( - uuid_u const &_id, - double _duration, - server_id_t const &_server_id) - : job_report_base_t( - "disk_compaction", _id, _duration, _server_id) { } - -void disk_compaction_job_report_t::merge_derived( - disk_compaction_job_report_t const &) { } - -bool disk_compaction_job_report_t::info_derived( - UNUSED admin_identifier_format_t identifier_format, - UNUSED server_config_client_t *server_config_client, - UNUSED table_meta_client_t *table_meta_client, - UNUSED cluster_semilattice_metadata_t const &metadata, - UNUSED ql::datum_object_builder_t *info_builder_out) const { - return true; -} - -RDB_IMPL_SERIALIZABLE_4_FOR_CLUSTER( - disk_compaction_job_report_t, type, id, duration, servers); - -backfill_job_report_t::backfill_job_report_t() - : job_report_base_t() { } - -backfill_job_report_t::backfill_job_report_t( - uuid_u const &_id, - double _duration, - server_id_t const &_server_id, - namespace_id_t const &_table, - bool _is_ready, - double _progress, - server_id_t const &_source_server, - server_id_t const &_destination_server) - : job_report_base_t("backfill", _id, _duration, _server_id), - table(_table), - is_ready(_is_ready), - progress_numerator(_progress), - progress_denominator(1.0), - source_server(_source_server), - destination_server(_destination_server) { - servers.insert({source_server, destination_server}); -} - -void backfill_job_report_t::merge_derived( - backfill_job_report_t const &job_report) { - is_ready &= job_report.is_ready; - progress_numerator += job_report.progress_numerator; - progress_denominator += job_report.progress_denominator; -} - -bool backfill_job_report_t::info_derived( - admin_identifier_format_t identifier_format, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - cluster_semilattice_metadata_t const &metadata, - ql::datum_object_builder_t *info_builder_out) const { - if (is_ready) { - return false; // All shards are ready, skip this job. - } - - ql::datum_t table_name_or_uuid; - ql::datum_t db_name_or_uuid; - if (!convert_table_id_to_datums( - table, - identifier_format, - metadata, - table_meta_client, - &table_name_or_uuid, - nullptr, - &db_name_or_uuid, - nullptr)) { - return false; - } - info_builder_out->overwrite("table", table_name_or_uuid); - info_builder_out->overwrite("db", db_name_or_uuid); - - ql::datum_t source_server_name_or_uuid; - if (convert_connected_server_id_to_datum( - source_server, - identifier_format, - server_config_client, - &source_server_name_or_uuid, - nullptr)) { - info_builder_out->overwrite("source_server", source_server_name_or_uuid); - } else { - return false; - } - - ql::datum_t destination_server_name_or_uuid; - if (convert_connected_server_id_to_datum( - destination_server, - identifier_format, - server_config_client, - &destination_server_name_or_uuid, - nullptr)) { - info_builder_out->overwrite( - "destination_server", destination_server_name_or_uuid); - } else { - return false; - } - - info_builder_out->overwrite("progress", - ql::datum_t(progress_numerator / progress_denominator)); - - return true; -} - -RDB_IMPL_SERIALIZABLE_10_FOR_CLUSTER( - backfill_job_report_t, - type, - id, - duration, - servers, - table, - is_ready, - progress_numerator, - progress_denominator, - source_server, - destination_server); - -index_construction_job_report_t::index_construction_job_report_t() - : job_report_base_t() { } - -index_construction_job_report_t::index_construction_job_report_t( - uuid_u const &_id, - double _duration, - server_id_t const &_server_id, - namespace_id_t const &_table, - std::string const &_index, - bool _is_ready, - double _progress_numerator, - double _progress_denominator) - : job_report_base_t( - "index_construction", _id, _duration, _server_id), - table(_table), - index(_index), - is_ready(_is_ready), - progress_numerator(_progress_numerator), - progress_denominator(_progress_denominator) { } - -void index_construction_job_report_t::merge_derived( - index_construction_job_report_t const &job_report) { - is_ready &= job_report.is_ready; - progress_numerator += job_report.progress_numerator; - progress_denominator += job_report.progress_denominator; -} - -bool index_construction_job_report_t::info_derived( - admin_identifier_format_t identifier_format, - UNUSED server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - cluster_semilattice_metadata_t const &metadata, - ql::datum_object_builder_t *info_builder_out) const { - if (is_ready) { - return false; // All shards are ready, skip this job. - } - - ql::datum_t table_name_or_uuid; - ql::datum_t db_name_or_uuid; - if (!convert_table_id_to_datums( - table, - identifier_format, - metadata, - table_meta_client, - &table_name_or_uuid, - nullptr, - &db_name_or_uuid, - nullptr)) { - return false; - } - info_builder_out->overwrite("table", table_name_or_uuid); - info_builder_out->overwrite("db", db_name_or_uuid); - - info_builder_out->overwrite("index", convert_string_to_datum(index)); - info_builder_out->overwrite("progress", - ql::datum_t(progress_denominator == 0 - ? 0 - : progress_numerator / progress_denominator)); - - return true; -} - -RDB_IMPL_SERIALIZABLE_9_FOR_CLUSTER( - index_construction_job_report_t, - type, - id, - duration, - servers, - table, - index, - is_ready, - progress_numerator, - progress_denominator); - -query_job_report_t::query_job_report_t() - : job_report_base_t() { } - -query_job_report_t::query_job_report_t( - uuid_u const &_id, - double _duration, - server_id_t const &_server_id, - ip_and_port_t const &_client_addr_port, - std::string const &_query, - auth::user_context_t const &_user_context) - : job_report_base_t("query", _id, _duration, _server_id), - client_addr_port(_client_addr_port), - query(_query), - user_context(_user_context) { } - -void query_job_report_t::merge_derived(query_job_report_t const &) { } - -bool query_job_report_t::info_derived( - UNUSED admin_identifier_format_t identifier_format, - UNUSED server_config_client_t *server_config_client, - UNUSED table_meta_client_t *table_meta_client, - UNUSED cluster_semilattice_metadata_t const &metadata, - ql::datum_object_builder_t *info_builder_out) const { - info_builder_out->overwrite("client_address", - convert_string_to_datum(client_addr_port.ip().to_string())); - info_builder_out->overwrite("client_port", - convert_port_to_datum(client_addr_port.port().value())); - info_builder_out->overwrite("query", convert_string_to_datum(query)); - info_builder_out->overwrite( - "user", convert_string_to_datum(user_context.to_string())); - - return true; -} - -RDB_IMPL_SERIALIZABLE_7_FOR_CLUSTER( - query_job_report_t, type, id, duration, servers, client_addr_port, query, user_context); - -RDB_IMPL_SERIALIZABLE_2_FOR_CLUSTER(jobs_manager_business_card_t, - get_job_reports_mailbox_address, - job_interrupt_mailbox_address); diff --git a/src/clustering/administration/jobs/report.hpp b/src/clustering/administration/jobs/report.hpp deleted file mode 100644 index d65f53c0251..00000000000 --- a/src/clustering/administration/jobs/report.hpp +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_JOBS_REPORT_HPP_ -#define CLUSTERING_ADMINISTRATION_JOBS_REPORT_HPP_ - -#include - -#include "arch/address.hpp" -#include "btree/secondary_operations.hpp" -#include "clustering/administration/auth/user_context.hpp" -#include "clustering/administration/datum_adapter.hpp" -#include "concurrency/signal.hpp" -#include "containers/archive/stl_types.hpp" -#include "containers/uuid.hpp" -#include "rdb_protocol/datum.hpp" -#include "rpc/mailbox/typed.hpp" -#include "rpc/serialize_macros.hpp" -#include "time.hpp" - -bool convert_job_type_and_id_from_datum(ql::datum_t primary_key, - std::string *type_out, - uuid_u *id_out); - -ql::datum_t convert_job_type_and_id_to_datum(std::string const &type, uuid_u const &id); - -// `job_report_base_t` is a CRTP base class allowing static polymorphism without virtual -// functions, see http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern. -template -class job_report_base_t { -public: - job_report_base_t(); - job_report_base_t( - std::string const &type, - uuid_u const &id, - double duration, - server_id_t const &server_id); - - void merge(T const & job_report); - - bool to_datum( - admin_identifier_format_t identifier_format, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - cluster_semilattice_metadata_t const &metadata, - ql::datum_t *row_out) const; - - // `type` can be derived from the derived type, but having it as a string is easier. - std::string type; - uuid_u id; - double duration; - std::set servers; -}; - -class backfill_job_report_t : public job_report_base_t { -public: - backfill_job_report_t(); - backfill_job_report_t( - uuid_u const &id, - double duration, - server_id_t const &server_id, - namespace_id_t const &table, - bool is_ready, - double progress, - server_id_t const &source_server, - server_id_t const &destination_server); - - void merge_derived(backfill_job_report_t const &job_report); - - bool info_derived( - admin_identifier_format_t identifier_format, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - cluster_semilattice_metadata_t const &metadata, - ql::datum_object_builder_t *info_builder_out) const; - - namespace_id_t table; - bool is_ready; - double progress_numerator; - double progress_denominator; - server_id_t source_server; - server_id_t destination_server; -}; -RDB_DECLARE_SERIALIZABLE_FOR_CLUSTER(backfill_job_report_t); - -class disk_compaction_job_report_t - : public job_report_base_t { -public: - disk_compaction_job_report_t(); - disk_compaction_job_report_t( - uuid_u const &id, - double duration, - server_id_t const &server_id); - - void merge_derived(disk_compaction_job_report_t const &job_report); - - bool info_derived( - admin_identifier_format_t identifier_format, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - cluster_semilattice_metadata_t const &metadata, - ql::datum_object_builder_t *info_builder_out) const; -}; -RDB_DECLARE_SERIALIZABLE_FOR_CLUSTER(disk_compaction_job_report_t); - -class index_construction_job_report_t - : public job_report_base_t { -public: - index_construction_job_report_t(); - index_construction_job_report_t( - uuid_u const &id, - double duration, - server_id_t const &server_id, - namespace_id_t const &table, - std::string const &index, - bool is_ready, - double progress_numerator, - double progress_denominator); - - void merge_derived(index_construction_job_report_t const &job_report); - - bool info_derived( - admin_identifier_format_t identifier_format, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - cluster_semilattice_metadata_t const &metadata, - ql::datum_object_builder_t *info_builder_out) const; - - namespace_id_t table; - std::string index; - bool is_ready; - double progress_numerator; - double progress_denominator; -}; -RDB_DECLARE_SERIALIZABLE_FOR_CLUSTER(index_construction_job_report_t); - -class query_job_report_t : public job_report_base_t { -public: - query_job_report_t(); - query_job_report_t( - uuid_u const &id, - double duration, - server_id_t const &server_id, - ip_and_port_t const &client_addr_port, - std::string const &query, - auth::user_context_t const &user_context); - - void merge_derived(query_job_report_t const &job_report); - - bool info_derived( - admin_identifier_format_t identifier_format, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - cluster_semilattice_metadata_t const &metadata, - ql::datum_object_builder_t *info_builder_out) const; - - ip_and_port_t client_addr_port; - std::string query; - auth::user_context_t user_context; -}; -RDB_DECLARE_SERIALIZABLE_FOR_CLUSTER(query_job_report_t); - -class jobs_manager_business_card_t { -public: - typedef mailbox_t, - std::vector, - std::vector, - std::vector> return_mailbox_t; - typedef mailbox_t get_job_reports_mailbox_t; - typedef mailbox_t job_interrupt_mailbox_t; - - get_job_reports_mailbox_t::address_t get_job_reports_mailbox_address; - job_interrupt_mailbox_t::address_t job_interrupt_mailbox_address; -}; -RDB_DECLARE_SERIALIZABLE(jobs_manager_business_card_t); - -#include "clustering/administration/jobs/report.tcc" - -#endif /* CLUSTERING_ADMINISTRATION_JOBS_REPORT_HPP_ */ diff --git a/src/clustering/administration/jobs/report.tcc b/src/clustering/administration/jobs/report.tcc deleted file mode 100644 index 259e98c7f92..00000000000 --- a/src/clustering/administration/jobs/report.tcc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. - -template -job_report_base_t::job_report_base_t() { -} - -template -job_report_base_t::job_report_base_t( - std::string const &_type, - uuid_u const &_id, - double _duration, - server_id_t const &_server_id) - : type(_type), - id(_id), - duration(_duration), - servers({_server_id}) { } - -template -void job_report_base_t::merge(T const & job_report) { - duration = std::max(duration, job_report.duration); - servers.insert(job_report.servers.begin(), job_report.servers.end()); - - static_cast(this)->merge_derived(job_report); -} - -template -bool job_report_base_t::to_datum( - admin_identifier_format_t identifier_format, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - cluster_semilattice_metadata_t const &metadata, - ql::datum_t *row_out) const { - ql::datum_array_builder_t servers_builder(ql::configured_limits_t::unlimited); - for (server_id_t const &server : servers) { - ql::datum_t server_name_or_uuid; - if (convert_connected_server_id_to_datum( - server, - identifier_format, - server_config_client, - &server_name_or_uuid, - nullptr)) { - servers_builder.add(server_name_or_uuid); - } - } - if (servers_builder.empty()) { - return false; - } - - ql::datum_object_builder_t info_builder; - if (static_cast(this)->info_derived( - identifier_format, server_config_client, table_meta_client, - metadata, &info_builder) == false) { - return false; - } - - ql::datum_object_builder_t builder; - builder.overwrite("type", convert_string_to_datum(type)); - builder.overwrite("id", convert_job_type_and_id_to_datum(type, id)); - builder.overwrite("duration_sec", - duration >= 0 ? ql::datum_t(duration / 1e6) : ql::datum_t::null()); - builder.overwrite("servers", std::move(servers_builder).to_datum()); - builder.overwrite("info", std::move(info_builder).to_datum()); - *row_out = std::move(builder).to_datum(); - - return true; -} diff --git a/src/clustering/administration/logs/log_transfer.cc b/src/clustering/administration/logs/log_transfer.cc deleted file mode 100644 index 4f267ab9551..00000000000 --- a/src/clustering/administration/logs/log_transfer.cc +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "clustering/administration/logs/log_transfer.hpp" - -#include - -#include "concurrency/promise.hpp" -#include "containers/archive/boost_types.hpp" -#include "rpc/mailbox/disconnect_watcher.hpp" - -RDB_IMPL_SERIALIZABLE_1_SINCE_v1_13(log_server_business_card_t, address); - - -log_server_t::log_server_t(mailbox_manager_t *mm, thread_pool_log_writer_t *lw) : - mailbox_manager(mm), writer(lw), - request_mailbox(mailbox_manager, std::bind(&log_server_t::handle_request, this, ph::_1, ph::_2, ph::_3, ph::_4, ph::_5)) - { } - -log_server_business_card_t log_server_t::get_business_card() { - return log_server_business_card_t(request_mailbox.get_address()); -} - -void log_server_t::handle_request( - signal_t *interruptor, - int max_lines, - timespec min_timestamp, - timespec max_timestamp, - log_server_business_card_t::result_mailbox_t::address_t cont) { - std::string error; - try { - std::vector messages = - writer->tail(max_lines, min_timestamp, max_timestamp, interruptor); - send(mailbox_manager, cont, boost::variant, std::string>(messages)); - return; - } catch (const log_read_exc_t &e) { - error = e.what(); - } catch (const interrupted_exc_t &) { - /* don't respond; we'll shut down in a moment */ - return; - } - /* Hack around the fact that we can't call a blocking function (e.g. - `send()` from within a `catch`-block. */ - send(mailbox_manager, cont, boost::variant, std::string>(error)); -} // NOLINT(whitespace/indent) (cpplint is confused by the 'struct timespec') - -std::vector fetch_log_file( - mailbox_manager_t *mm, - const log_server_business_card_t &bcard, - int max_lines, timespec min_timestamp, timespec max_timestamp, - signal_t *interruptor) THROWS_ONLY(log_transfer_exc_t, log_read_exc_t, interrupted_exc_t) { - promise_t, std::string> > promise; - log_server_business_card_t::result_mailbox_t reply_mailbox( - mm, - [&](signal_t *, - const boost::variant, std::string> &r) { - promise.pulse(r); - }); - send(mm, bcard.address, max_lines, min_timestamp, max_timestamp, reply_mailbox.get_address()); - { - disconnect_watcher_t dw(mm, bcard.address.get_peer()); - wait_any_t waiter(promise.get_ready_signal(), &dw); - wait_interruptible(&waiter, interruptor); - } - - boost::variant, std::string> res; - if (promise.try_get_value(&res)) { - if (std::vector *messages = boost::get >(&res)) { - return *messages; - } else { - throw log_read_exc_t(boost::get(res)); - } - } else { - throw log_transfer_exc_t(); - } -} diff --git a/src/clustering/administration/logs/log_transfer.hpp b/src/clustering/administration/logs/log_transfer.hpp deleted file mode 100644 index aa0c2726b92..00000000000 --- a/src/clustering/administration/logs/log_transfer.hpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_LOGS_LOG_TRANSFER_HPP_ -#define CLUSTERING_ADMINISTRATION_LOGS_LOG_TRANSFER_HPP_ - -#include -#include - -#include "clustering/administration/logs/log_writer.hpp" - -class log_server_business_card_t { -public: - typedef mailbox_t, std::string>> result_mailbox_t; - typedef mailbox_t request_mailbox_t; - - log_server_business_card_t() { } - explicit log_server_business_card_t(const request_mailbox_t::address_t &a) : address(a) { } - - request_mailbox_t::address_t address; -}; - -RDB_DECLARE_SERIALIZABLE(log_server_business_card_t); - -RDB_MAKE_EQUALITY_COMPARABLE_1(log_server_business_card_t, address); - -class log_server_t { -public: - log_server_t(mailbox_manager_t *mm, thread_pool_log_writer_t *w); - log_server_business_card_t get_business_card(); -private: - void handle_request( - signal_t *interruptor, - int max_lines, struct timespec min_timestamp, struct timespec max_timestamp, - log_server_business_card_t::result_mailbox_t::address_t cont); - mailbox_manager_t *mailbox_manager; - thread_pool_log_writer_t *writer; - log_server_business_card_t::request_mailbox_t request_mailbox; - -private: - DISABLE_COPYING(log_server_t); -}; - -/* Fetches a block of entries from another server's log file. It examines the last -`max_entries` entries from the server's log, and filters out those whose timestamps are -not between `min_timestamp` and `max_timestamp`, inclusive. It returns the results in -reverse chronological order. Throws `log_transfer_exc_t` if the server disconnected or -`log_read_exc_t` if there's a problem with reading the log file. */ -class log_transfer_exc_t : public std::runtime_error { -public: - log_transfer_exc_t() : std::runtime_error( - "Lost contact with server while trying to transfer log messages") { } -}; -std::vector fetch_log_file( - mailbox_manager_t *mailbox_manager, - const log_server_business_card_t &server_bcard, - int max_entries, - struct timespec min_timestamp, - struct timespec max_timestamp, - signal_t *interruptor) - THROWS_ONLY(log_transfer_exc_t, log_read_exc_t, interrupted_exc_t); - -#endif /* CLUSTERING_ADMINISTRATION_LOGS_LOG_TRANSFER_HPP_ */ diff --git a/src/clustering/administration/logs/log_writer.cc b/src/clustering/administration/logs/log_writer.cc deleted file mode 100644 index 4d39e2775df..00000000000 --- a/src/clustering/administration/logs/log_writer.cc +++ /dev/null @@ -1,747 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "clustering/administration/logs/log_writer.hpp" - -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#endif - -#include "arch/runtime/thread_pool.hpp" -#include "arch/io/disk/filestat.hpp" -#include "arch/io/disk.hpp" -#include "concurrency/promise.hpp" -#include "containers/scoped.hpp" -#include "paths.hpp" -#include "thread_local.hpp" - - -RDB_IMPL_SERIALIZABLE_4_SINCE_v1_13(log_message_t, timestamp, uptime, level, message); - -// `struct timestamp` has platform-dependent integers, which is not good for -// serialization. We serialize them as fixed types that are compatible with the types -// we have on AMD64 Linux. -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" -#endif -template -void serialize(write_message_t *wm, const struct timespec &ts) { - static_assert( - std::numeric_limits::min() - >= std::numeric_limits::min() - && std::numeric_limits::max() - <= std::numeric_limits::max(), - "incompatible timespec type"); - serialize(wm, static_cast(ts.tv_sec)); - static_assert( - std::numeric_limits::min() - >= std::numeric_limits::min() - && std::numeric_limits::max() - <= std::numeric_limits::max(), - "incompatible timespec type"); - serialize(wm, static_cast(ts.tv_nsec)); -} -template -archive_result_t deserialize(read_stream_t *s, struct timespec *ts_out) { - archive_result_t res = archive_result_t::SUCCESS; - - int64_t tv_sec; - res = deserialize(s, &tv_sec); - if (bad(res)) { return res; } - if(!(tv_sec >= std::numeric_limitstv_sec)>::min() - && tv_sec <= std::numeric_limitstv_sec)>::max())) { - return archive_result_t::RANGE_ERROR; - } - ts_out->tv_sec = tv_sec; - - int64_t tv_nsec; - res = deserialize(s, &tv_nsec); - if (bad(res)) { return res; } - if(!(tv_nsec >= std::numeric_limitstv_nsec)>::min() - && tv_nsec <= std::numeric_limitstv_nsec)>::max())) { - return archive_result_t::RANGE_ERROR; - } - ts_out->tv_nsec = tv_nsec; - - return res; -} -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -INSTANTIATE_SERIALIZABLE_SINCE_v1_13(struct timespec); - -std::string format_log_level(log_level_t l) { - switch (l) { - case log_level_debug: return "debug"; - case log_level_info: return "info"; - case log_level_notice: return "notice"; - case log_level_warn: return "warn"; - case log_level_error: return "error"; - default: unreachable(); - } -} - -log_level_t parse_log_level(const std::string &s) THROWS_ONLY(log_read_exc_t) { - if (s == "debug") - return log_level_debug; - else if (s == "info") - return log_level_info; - else if (s == "notice") - return log_level_notice; - else if (s == "warn") - return log_level_warn; - else if (s == "error") - return log_level_error; - else - throw log_read_exc_t("cannot parse '" + s + "' as log level"); -} - -std::string format_log_message(const log_message_t &m, bool for_console) { - // never write an info level message to console - guarantee(!(for_console && m.level == log_level_info)); - - std::string message = m.message; - std::string message_reformatted; - - std::string prepend; - if (!for_console) { - prepend = strprintf("%s %ld.%06llds %s: ", - format_time(m.timestamp, local_or_utc_time_t::local).c_str(), - m.uptime.tv_sec, - m.uptime.tv_nsec / THOUSAND, - format_log_level(m.level).c_str()); - } else { - if (m.level != log_level_info && m.level != log_level_notice) { - prepend = strprintf("%s: ", format_log_level(m.level).c_str()); - } - } - ssize_t prepend_length = prepend.length(); - - ssize_t start = 0, end = message.length() - 1; - while (start < static_cast(message.length()) && message[start] == '\n') { - ++start; - } - while (end >= 0 && (message[end] == '\n' || message[end] == '\r')) { - end--; - } - for (int i = start; i <= end; i++) { - if (message[i] == '\n') { - if (for_console) { - message_reformatted.push_back('\n'); - message_reformatted.append(prepend_length, ' '); - } else { - message_reformatted.append("\\n"); - } - } else if (message[i] == '\t') { - if (for_console) { - message_reformatted.push_back('\t'); - } else { - message_reformatted.append("\\t"); - } - } else if (message[i] == '\\') { - if (for_console) { - message_reformatted.push_back(message[i]); - } else { - message_reformatted.append("\\\\"); - } - } else if (message[i] < ' ' || message[i] > '~') { -#if !defined(NDEBUG) - crash("We can't have special characters in log messages because then it " - "would be difficult to parse the log file. Message: %s", - message.c_str()); -#else - message_reformatted.push_back('?'); -#endif - } else { - message_reformatted.push_back(message[i]); - } - } - - return prepend + message_reformatted; -} - - - -log_message_t parse_log_message(const std::string &s) THROWS_ONLY(log_read_exc_t) { - const char *p = s.c_str(); - const char *start_timestamp = p; - while (*p && *p != ' ') p++; - if (!*p) throw log_read_exc_t("cannot parse log message (1)"); - const char *end_timestamp = p; - p++; - const char *start_uptime_ipart = p; - while (*p && *p != '.') p++; - if (!*p) throw log_read_exc_t("cannot parse log message (2)"); - const char *end_uptime_ipart = p; - p++; - const char *start_uptime_fpart = p; - while (*p && *p != 's') p++; - if (!*p) throw log_read_exc_t("cannot parse log message (3)"); - const char *end_uptime_fpart = p; - p++; - if (*p != ' ') throw log_read_exc_t("cannot parse log message (4)"); - p++; - const char *start_level = p; - while (*p && *p != ':') p++; - if (*p != ':') throw log_read_exc_t("cannot parse log message (5)"); - const char *end_level = p; - p++; - if (*p != ' ') throw log_read_exc_t("cannot parse log message (6)"); - p++; - const char *start_message = p; - while (*p) p++; - const char *end_message = p; - - struct timespec timestamp; - { - std::string errmsg; - if (!parse_time(std::string(start_timestamp, end_timestamp - start_timestamp), - local_or_utc_time_t::local, ×tamp, &errmsg)) { - throw log_read_exc_t(errmsg); - } - } - struct timespec uptime; - - { - std::string tv_sec_str(start_uptime_ipart, end_uptime_ipart - start_uptime_ipart); - uint64_t tv_sec; - if (!strtou64_strict(tv_sec_str, 10, &tv_sec)) { - throw log_read_exc_t("cannot parse log message (9)"); - } - - uptime.tv_sec = tv_sec; - - std::string tv_nsec_str(start_uptime_fpart, end_uptime_fpart - start_uptime_fpart); - uint64_t tv_nsec; - if (!strtou64_strict(tv_nsec_str, 10, &tv_nsec)) { - throw log_read_exc_t("cannot parse log message (10)"); - } - - // TODO: Seriously? We assume three decimal places? - uptime.tv_nsec = THOUSAND * tv_nsec; - } - - log_level_t level = parse_log_level(std::string(start_level, end_level - start_level)); - std::string message = std::string(start_message, end_message - start_message); - - return log_message_t(timestamp, uptime, level, message); -} - -void throw_unless(bool condition, const std::string &where) { - if (!condition) { - throw log_read_exc_t("file IO error: " + where + " (errno = " + errno_string(get_errno()).c_str() + ")"); - } -} - -file_reverse_reader_t::file_reverse_reader_t(scoped_fd_t &&_fd) : - fd(std::move(_fd)), - current_chunk(chunk_size) { - int64_t fd_filesize = get_file_size(fd.get()); - if (fd_filesize == 0) { - remaining_in_current_chunk = current_chunk_start = 0; - } else { - remaining_in_current_chunk = fd_filesize % chunk_size; - if (remaining_in_current_chunk == 0) { - /* We landed right on a chunk boundary; set ourself to read the - previous whole chunk. */ - remaining_in_current_chunk = chunk_size; - } - current_chunk_start = fd_filesize - remaining_in_current_chunk; - int res = pread(fd.get(), current_chunk.data(), remaining_in_current_chunk, current_chunk_start); - throw_unless(res == remaining_in_current_chunk, "could not read from file"); - - // Ignore the newline and any unfinished statement at the end of the file - while (current_chunk[remaining_in_current_chunk - 1] != '\n') { - --remaining_in_current_chunk; - if (remaining_in_current_chunk == 0) { - read_chunk(); - } - } - --remaining_in_current_chunk; - if (remaining_in_current_chunk == 0) { - read_chunk(); - } - } -} - -bool file_reverse_reader_t::get_next(std::string *out) { - if (remaining_in_current_chunk == 0) { - guarantee(current_chunk_start == 0); - return false; - } - - *out = ""; - while (true) { - for (int p = remaining_in_current_chunk - 1; p >= 0; --p) { - if (current_chunk[p] == '\n') { - *out = std::string(¤t_chunk[p] + 1, remaining_in_current_chunk - p - 1) + *out; - - remaining_in_current_chunk = p; - if (remaining_in_current_chunk == 0) { - read_chunk(); - } - return true; - } - } - - *out = std::string(current_chunk.data(), remaining_in_current_chunk) + *out; - remaining_in_current_chunk = 0; - if (!read_chunk()) { - return true; // This is the last (first) line - } - } -} - -bool file_reverse_reader_t::read_chunk() { - if (current_chunk_start == 0) { - return false; - } - - current_chunk_start -= chunk_size; - int res = pread(fd.get(), current_chunk.data(), chunk_size, current_chunk_start); - throw_unless(res == chunk_size, "could not read from file"); - remaining_in_current_chunk = chunk_size; - return true; -} - -/* Most of the logging we do will be through thread_pool_log_writer_t. However, -thread_pool_log_writer_t depends on the existence of a thread pool, which is -not always the case. Thus, fallback_log_writer_t exists to perform logging -operations when thread_pool_log_writer_t cannot be used. */ - -class fallback_log_writer_t { -public: - fallback_log_writer_t(); - void install(const std::string &logfile_name); - friend class thread_pool_log_writer_t; - - log_message_t assemble_log_message(log_level_t level, const std::string &m); - -private: - friend void log_coro(thread_pool_log_writer_t *writer, log_level_t level, const std::string &message, auto_drainer_t::lock_t); - friend void log_internal(const char *src_file, int src_line, log_level_t level, const char *format, ...); - friend void vlog_internal(const char *src_file, int src_line, log_level_t level, const char *format, va_list args); - - bool write(const log_message_t &msg, std::string *error_out); - void initiate_write(log_level_t level, const std::string &message); - base_path_t filename; - struct timespec uptime_reference; - struct timespec last_msg_timestamp; - spinlock_t last_msg_timestamp_lock; - -#ifdef _WIN32 - // TODO WINDOWS: log locking -#else - struct flock filelock, fileunlock; -#endif - scoped_fd_t fd; - - DISABLE_COPYING(fallback_log_writer_t); -} fallback_log_writer; - -fallback_log_writer_t::fallback_log_writer_t() : - filename("-") { - uptime_reference = clock_monotonic(); - last_msg_timestamp = clock_realtime(); - -#ifndef _WIN32 - filelock.l_type = F_WRLCK; - filelock.l_whence = SEEK_SET; - filelock.l_start = 0; - filelock.l_len = 0; - filelock.l_pid = getpid(); - - fileunlock.l_type = F_UNLCK; - fileunlock.l_whence = SEEK_SET; - fileunlock.l_start = 0; - fileunlock.l_len = 0; - fileunlock.l_pid = getpid(); -#endif -} - -void fallback_log_writer_t::install(const std::string &logfile_name) { - guarantee(filename.path() == "-", "Attempted to install a fallback_log_writer_t that was already installed."); - filename = base_path_t(logfile_name); - -#ifdef _WIN32 - HANDLE h = CreateFile(filename.path().c_str(), FILE_APPEND_DATA, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - fd.reset(h); - - if (fd.get() == INVALID_FD) { - throw std::runtime_error(strprintf("Failed to open log file '%s': %s", - logfile_name.c_str(), - winerr_string(GetLastError()).c_str()).c_str()); - } -#else - int res; - do { - res = open(filename.path().c_str(), O_WRONLY|O_APPEND|O_CREAT, 0644); - } while (res == INVALID_FD && get_errno() == EINTR); - - fd.reset(res); - - if (fd.get() == INVALID_FD) { - throw std::runtime_error(strprintf("Failed to open log file '%s': %s", - logfile_name.c_str(), - errno_string(errno).c_str()).c_str()); - } -#endif - - // Get the absolute path for the log file, so it will still be valid if - // the working directory changes - filename.make_absolute(); - - // For the case that the log file was newly created, - // call fsync() on the parent directory to guarantee that its - // directory entry is persisted to disk. - int sync_res = fsync_parent_directory(filename.path().c_str()); - if (sync_res != 0) { - char errno_str_buf[250]; - const char *errno_str = errno_string_maybe_using_buffer(sync_res, - errno_str_buf, sizeof(errno_str_buf)); - logWRN("Parent directory of log file (%s) could not be synced. (%s)\n", - filename.path().c_str(), errno_str); - } -} - -log_message_t fallback_log_writer_t::assemble_log_message( - log_level_t level, const std::string &m) { - struct timespec timestamp = clock_realtime(); - - { - /* Make sure timestamps on log messages are unique. */ - - /* We grab the spinlock to avoid races if multiple threads get here at once. - There's usually no possibility of lock contention because log messages - originating from within the thread pool will all get sent to thread 0 before - being written; but the `log*()` functions are supposed to work even outside of - the thread pool, including in the blocker pool. */ - spinlock_acq_t lock_acq(&last_msg_timestamp_lock); - struct timespec last_plus = last_msg_timestamp; - add_to_timespec(&last_plus, 1); - if (last_plus > timestamp) { - timestamp = last_plus; - } - last_msg_timestamp = timestamp; - } - - struct timespec uptime = subtract_timespecs(clock_monotonic(), uptime_reference); - - return log_message_t(timestamp, uptime, level, m); -} - -// WINDOWS TODO: this function could benefit from some refactoring -bool fallback_log_writer_t::write(const log_message_t &msg, std::string *error_out) { - std::string formatted = format_log_message(msg) + "\n"; - - FILE* write_stream = nullptr; - fd_t filefd = INVALID_FD; - switch (msg.level) { - case log_level_info: - // no message on stdout/stderr - break; - case log_level_notice: - write_stream = stdout; - filefd = STDOUT_FD; - break; - case log_level_debug: - case log_level_warn: - case log_level_error: - write_stream = stderr; - filefd = STDERR_FD; - break; - default: - unreachable(); - } - - if (msg.level != log_level_info) { - // Write to stdout/stderr for all log levels but info (#3040) - std::string console_formatted = format_log_message(msg, true) + "\n"; -#ifdef _WIN32 - // WINDOWS TODO - (void) write_stream; -#else - flockfile(write_stream); -#endif - - bool write_failure = false; - size_t write_res = ::fwrite(console_formatted.data(), 1, console_formatted.length(), write_stream); - if (write_res == console_formatted.length()) { - int fflush_res = ::fflush(write_stream); - write_failure = (fflush_res != 0); - } else { - write_failure = true; - } - if (write_failure) { - error_out->assign("cannot write to stdout/stderr: " + errno_string(get_errno())); - return false; - } - -#ifdef _WIN32 - // WINDOWS TODO -#else - int fsync_res = fsync(filefd); - if (fsync_res != 0 && !(get_errno() == EROFS || get_errno() == EINVAL || - get_errno() == ENOTSUP)) { - error_out->assign("cannot flush stdout/stderr: " + errno_string(get_errno())); - return false; - } - - funlockfile(write_stream); -#endif - } - - if (fd.get() == INVALID_FD) { - error_out->assign("logging module is not yet initialized"); - return false; - } -#ifndef _WIN32 - int fcntl_res = fcntl(fd.get(), F_SETLKW, &filelock); - if (fcntl_res != 0) { - error_out->assign("cannot lock log file: " + errno_string(get_errno())); - return false; - } -#endif - -#ifdef _WIN32 - DWORD bytes_written; - BOOL res = WriteFile(fd.get(), formatted.data(), formatted.length(), &bytes_written, nullptr); - if (!res) { - error_out->assign("cannot write to log file: " + winerr_string(GetLastError())); - return false; - } -#else - ssize_t write_res = ::write(fd.get(), formatted.data(), formatted.length()); - if (write_res != static_cast(formatted.length())) { - error_out->assign("cannot write to log file: " + errno_string(get_errno())); - return false; - } -#endif - -#ifndef _WIN32 - fcntl_res = fcntl(fd.get(), F_SETLK, &fileunlock); - if (fcntl_res != 0) { - error_out->assign("cannot unlock log file: " + errno_string(get_errno())); - return false; - } -#endif - - return true; -} - -void fallback_log_writer_t::initiate_write(log_level_t level, const std::string &message) { - log_message_t log_msg = assemble_log_message(level, message); - std::string error_message; - if (!write(log_msg, &error_message)) { - fprintf(stderr, "Previous message may not have been written to the log file (%s).\n", error_message.c_str()); - } -} - -typedef auto_drainer_t * type; - -TLS_with_init(thread_pool_log_writer_t *, global_log_writer, nullptr); -TLS_with_init(auto_drainer_t *, global_log_drainer, nullptr); -TLS_with_init(int, log_writer_block, 0); - -thread_pool_log_writer_t::thread_pool_log_writer_t() - : has_parse_error(false) { - pmap( - get_num_threads(), - std::bind(&thread_pool_log_writer_t::install_on_thread, this, ph::_1)); -} - -thread_pool_log_writer_t::~thread_pool_log_writer_t() { - pmap(get_num_threads(), std::bind(&thread_pool_log_writer_t::uninstall_on_thread, this, ph::_1)); -} - -std::vector thread_pool_log_writer_t::tail( - int max_lines, - struct timespec min_timestamp, - struct timespec max_timestamp, - signal_t *interruptor) THROWS_ONLY(log_read_exc_t, interrupted_exc_t) { - assert_thread(); - - volatile bool cancel = false; - class cancel_subscription_t : public signal_t::subscription_t { - public: - explicit cancel_subscription_t(volatile bool *c) : cancel(c) { } - void run() { - *cancel = true; - } - volatile bool *cancel; - } subscription(&cancel); - subscription.reset(interruptor); - - std::vector log_messages; - std::string error_message; - - - bool ok; - thread_pool_t::run_in_blocker_pool( - std::bind( - &thread_pool_log_writer_t::tail_blocking, - this, - max_lines, - min_timestamp, - max_timestamp, - &cancel, - &log_messages, - &error_message, - &ok)); - if (ok) { - if (cancel) { - throw interrupted_exc_t(); - } else { - if (has_parse_error == false && error_message.empty() == false) { - logERR("%s", error_message.c_str()); - has_parse_error = true; - } - return log_messages; - } - } else { - throw log_read_exc_t(error_message); - } -} - -void thread_pool_log_writer_t::install_on_thread(int i) { - on_thread_t thread_switcher((threadnum_t(i))); - guarantee(TLS_get_global_log_writer() == nullptr); - TLS_set_global_log_drainer(new auto_drainer_t); - TLS_set_global_log_writer(this); -} - -void thread_pool_log_writer_t::uninstall_on_thread(int i) { - on_thread_t thread_switcher((threadnum_t(i))); - guarantee(TLS_get_global_log_writer() == this); - TLS_set_global_log_writer(nullptr); - delete TLS_get_global_log_drainer(); - TLS_set_global_log_drainer(nullptr); -} - -void thread_pool_log_writer_t::write(const log_message_t &lm) { - mutex_t::acq_t write_mutex_acq(&write_mutex); - std::string error_message; - bool ok; - thread_pool_t::run_in_blocker_pool(std::bind(&thread_pool_log_writer_t::write_blocking, this, lm, &error_message, &ok)); - if (ok) { - log_write_issue_tracker.report_success(); - } else { - log_write_issue_tracker.report_error(error_message); - } -} - -void thread_pool_log_writer_t::write_blocking(const log_message_t &msg, std::string *error_out, bool *ok_out) { - *ok_out = fallback_log_writer.write(msg, error_out); - return; -} - -void thread_pool_log_writer_t::tail_blocking( - int max_lines, - timespec min_timestamp, - timespec max_timestamp, - volatile bool *cancel, - std::vector *messages_out, - std::string *error_out, - bool *ok_out) { - try { - scoped_fd_t fd; -#ifdef _WIN32 - fd.reset(CreateFile(fallback_log_writer.filename.path().c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); - if (fd.get() == INVALID_FD) { - logWRN("CreateFile failed: %s", winerr_string(GetLastError()).c_str()); - set_errno(EIO); - } -#else - do { - fd.reset(open(fallback_log_writer.filename.path().c_str(), O_RDONLY)); - } while (fd.get() == INVALID_FD && get_errno() == EINTR); -#endif - throw_unless(fd.get() != INVALID_FD, - strprintf("could not open '%s' for reading.", - fallback_log_writer.filename.path().c_str())); - - file_reverse_reader_t reader(std::move(fd)); - std::string line; - while (max_lines-- > 0 && reader.get_next(&line) && !*cancel) { - if (line.empty()) { - continue; - } - log_message_t lm; - try { - lm = parse_log_message(line); - } catch (const log_read_exc_t &exc) { - *error_out = strprintf( - "Failed to parse one or more lines from the log file, the contents " - "of the `logs` system table will be incomplete. The following parse " - "error occurred: %s while parsing \"%s\"", - exc.what(), - line.c_str()); - continue; - } - if (lm.timestamp > max_timestamp) { - continue; - } - if (lm.timestamp < min_timestamp) { - break; - } - messages_out->push_back(lm); - } - *ok_out = true; - return; - } catch (const log_read_exc_t &e) { - // Any parse errors are handled above, this is for reader-related failures. - *error_out = e.what(); - *ok_out = false; - return; - } -} - -void log_coro(thread_pool_log_writer_t *writer, log_level_t level, const std::string &message, auto_drainer_t::lock_t) { - on_thread_t thread_switcher(writer->home_thread()); - - log_message_t log_msg = fallback_log_writer.assemble_log_message(level, message); - writer->write(log_msg); -} - -/* Declared in `logger.hpp`, not `clustering/administration/logs/logger.hpp` like the -other things in this file. */ -void log_internal(const char *src_file, int src_line, log_level_t level, const char *format, ...) { - va_list args; - va_start(args, format); - vlog_internal(src_file, src_line, level, format, args); - va_end(args); -} - -void vlog_internal(UNUSED const char *src_file, UNUSED int src_line, log_level_t level, const char *format, va_list args) { - thread_pool_log_writer_t *writer; - if ((writer = TLS_get_global_log_writer()) && TLS_get_log_writer_block() == 0) { - auto_drainer_t::lock_t lock(TLS_get_global_log_drainer()); - - std::string message = vstrprintf(format, args); - coro_t::spawn_sometime(std::bind(&log_coro, writer, level, message, lock)); - - } else { - std::string message = vstrprintf(format, args); - - fallback_log_writer.initiate_write(level, message); - } -} - -thread_log_writer_disabler_t::thread_log_writer_disabler_t() { - TLS_set_log_writer_block(TLS_get_log_writer_block() + 1); -} - -thread_log_writer_disabler_t::~thread_log_writer_disabler_t() { - TLS_set_log_writer_block(TLS_get_log_writer_block() - 1); - guarantee(TLS_get_log_writer_block() >= 0); -} - -void install_fallback_log_writer(const std::string &logfile_name) { - fallback_log_writer.install(logfile_name); -} diff --git a/src/clustering/administration/logs/log_writer.hpp b/src/clustering/administration/logs/log_writer.hpp deleted file mode 100644 index 01cf5ece372..00000000000 --- a/src/clustering/administration/logs/log_writer.hpp +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_LOGS_LOG_WRITER_HPP_ -#define CLUSTERING_ADMINISTRATION_LOGS_LOG_WRITER_HPP_ - -#include -#include - -#include -#include -#include - -#include "arch/io/io_utils.hpp" -#include "clustering/administration/issues/log_write.hpp" -#include "concurrency/mutex.hpp" -#include "containers/scoped.hpp" -#include "logger.hpp" -#include "rpc/mailbox/typed.hpp" -#include "utils.hpp" - -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE(log_level_t, int, log_level_debug, log_level_error); -RDB_DECLARE_SERIALIZABLE(struct timespec); - -class log_message_t { -public: - log_message_t() { } - log_message_t(struct timespec t, struct timespec u, log_level_t l, std::string m) : - timestamp(t), uptime(u), level(l), message(m) { } - struct timespec timestamp; - struct timespec uptime; - log_level_t level; - std::string message; -}; - -RDB_DECLARE_SERIALIZABLE(log_message_t); - -class log_read_exc_t : public std::runtime_error { -public: - explicit log_read_exc_t(const std::string &s) : std::runtime_error(s) { } -}; - -std::string format_log_level(log_level_t l); -log_level_t parse_log_level(const std::string &s) THROWS_ONLY(log_read_exc_t); - -std::string format_log_message(const log_message_t &m, bool for_console = false); -log_message_t parse_log_message(const std::string &s) THROWS_ONLY(log_read_exc_t); - - -class file_reverse_reader_t { -public: - explicit file_reverse_reader_t(scoped_fd_t &&fd); - bool get_next(std::string *out); - -private: - bool read_chunk(); - - static const int chunk_size = 4096; - scoped_fd_t fd; - scoped_array_t current_chunk; - int remaining_in_current_chunk; - int64_t current_chunk_start; - - DISABLE_COPYING(file_reverse_reader_t); -}; - -void log_internal(const char *src_file, int src_line, log_level_t level, const char *format, ...) ATTR_FORMAT(printf, 4, 5); -void vlog_internal(const char *src_file, int src_line, log_level_t level, const char *format, va_list args) ATTR_FORMAT(printf, 4, 0); - -class thread_pool_log_writer_t : public home_thread_mixin_t { -public: - thread_pool_log_writer_t(); - ~thread_pool_log_writer_t(); - - std::vector tail(int max_lines, struct timespec min_timestamp, struct timespec max_timestamp, signal_t *interruptor) THROWS_ONLY(log_read_exc_t, interrupted_exc_t); - - log_write_issue_tracker_t *get_log_write_issue_tracker() { - return &log_write_issue_tracker; - } - -private: - friend void log_coro(thread_pool_log_writer_t *writer, log_level_t level, const std::string &message, auto_drainer_t::lock_t lock); - friend void log_internal(const char *src_file, int src_line, log_level_t level, const char *format, ...); - friend void vlog_internal(const char *src_file, int src_line, log_level_t level, const char *format, va_list args); - void install_on_thread(int i); - void uninstall_on_thread(int i); - void write(const log_message_t &msg); - void write_blocking(const log_message_t &msg, std::string *error_out, bool *ok_out); - void tail_blocking(int max_lines, - struct timespec min_timestamp, - struct timespec max_timestamp, - volatile bool *cancel, - std::vector *messages_out, - std::string *error_out, - bool *ok_out); - - mutex_t write_mutex; - log_write_issue_tracker_t log_write_issue_tracker; - bool has_parse_error; - - DISABLE_COPYING(thread_pool_log_writer_t); -}; - -void install_fallback_log_writer(const std::string &logfile_name); - -class thread_log_writer_disabler_t { -public: - thread_log_writer_disabler_t(); - ~thread_log_writer_disabler_t(); -}; - -#endif /* CLUSTERING_ADMINISTRATION_LOGS_LOG_WRITER_HPP_ */ diff --git a/src/clustering/administration/logs/logs_backend.cc b/src/clustering/administration/logs/logs_backend.cc deleted file mode 100644 index a22b883bd2d..00000000000 --- a/src/clustering/administration/logs/logs_backend.cc +++ /dev/null @@ -1,601 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/logs/logs_backend.hpp" - -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/logs/log_writer.hpp" -#include "clustering/administration/logs/log_transfer.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "concurrency/watchable_map.hpp" -#include "rdb_protocol/pseudo_time.hpp" - -static const int entries_per_server = 1000; - - -ql::datum_t convert_timespec_to_datum(const timespec &t) { - return ql::pseudo::make_time( - t.tv_sec + static_cast(t.tv_nsec) / BILLION, "+00:00"); -} - -ql::datum_t convert_timespec_duration_to_datum(const timespec &t) { - return ql::datum_t(t.tv_sec + static_cast(t.tv_nsec) / BILLION); -} - -ql::datum_t convert_log_key_to_datum(const timespec &ts, const server_id_t &si) { - ql::datum_array_builder_t id_builder(ql::configured_limits_t::unlimited); - id_builder.add(ql::datum_t(datum_string_t( - format_time(ts, local_or_utc_time_t::utc)))); - id_builder.add(convert_server_id_to_datum(si)); - return std::move(id_builder).to_datum(); -} - -bool convert_log_key_from_datum(const ql::datum_t &d, - timespec *ts_out, server_id_t *si_out, admin_err_t *error_out) { - if (d.get_type() != ql::datum_t::R_ARRAY || d.arr_size() != 2) { - *error_out = admin_err_t{ - "Expected two-element array, got:" + d.print(), - query_state_t::FAILED}; - return false; - } - if (d.get(0).get_type() != ql::datum_t::R_STR) { - *error_out = admin_err_t{ - "Expected string, got:" + d.print(), - query_state_t::FAILED}; - return false; - } - std::string err; - if (!parse_time(d.get(0).as_str().to_std(), local_or_utc_time_t::utc, - ts_out, &err)) { - *error_out = admin_err_t{"In timestamp: " + err, query_state_t::FAILED}; - return false; - } - if (!convert_server_id_from_datum(d.get(1), si_out, error_out)) { - return false; - } - return true; -} - -ql::datum_t convert_log_message_to_datum( - const log_message_t &msg, const server_id_t &server_id, - const ql::datum_t &server_datum) { - ql::datum_object_builder_t builder; - builder.overwrite("id", convert_log_key_to_datum(msg.timestamp, server_id)); - builder.overwrite("server", server_datum); - builder.overwrite("timestamp", convert_timespec_to_datum(msg.timestamp)); - builder.overwrite("uptime", convert_timespec_duration_to_datum(msg.uptime)); - builder.overwrite("level", ql::datum_t(datum_string_t(format_log_level(msg.level)))); - builder.overwrite("message", ql::datum_t(datum_string_t(msg.message))); - return std::move(builder).to_datum(); -} - -logs_artificial_table_backend_t::logs_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - mailbox_manager_t *_mailbox_manager, - watchable_map_t *_directory, - server_config_client_t *_server_config_client, - admin_identifier_format_t _identifier_format) : - cfeed_artificial_table_backend_t( - name_string_t::guarantee_valid("logs"), rdb_context, name_resolver), - mailbox_manager(_mailbox_manager), - directory(_directory), - server_config_client(_server_config_client), - identifier_format(_identifier_format) { -} - -logs_artificial_table_backend_t::~logs_artificial_table_backend_t() { - begin_changefeed_destruction(); -} - -std::string logs_artificial_table_backend_t::get_primary_key_name() { - return "id"; -} - -bool logs_artificial_table_backend_t::read_all_rows_as_vector( - UNUSED auth::user_context_t const &user_context, - signal_t *interruptor, - std::vector *rows_out, - admin_err_t *error_out) { - return read_all_rows_raw( - [&](const log_message_t &message, - const peer_id_t &, - const server_id_t &server_id, - const ql::datum_t &server_name_datum) { - rows_out->push_back(convert_log_message_to_datum( - message, server_id, server_name_datum)); - }, - interruptor, - error_out); -} - -bool logs_artificial_table_backend_t::read_row( - UNUSED auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor, - ql::datum_t *row_out, - admin_err_t *error_out) { - timespec timestamp; - server_id_t server_id; - admin_err_t dummy_error; - if (!convert_log_key_from_datum(primary_key, ×tamp, &server_id, &dummy_error)) { - *row_out = ql::datum_t(); - return true; - } - - ql::datum_t server_datum; - name_string_t server_name; - if (!convert_connected_server_id_to_datum(server_id, identifier_format, - server_config_client, &server_datum, &server_name)) { - /* The server is not currently connected */ - *row_out = ql::datum_t(); - return true; - } - - optional peer_id = - server_config_client->get_server_to_peer_map()->get_key(server_id); - if (!peer_id.has_value()) { - /* Disconnected or nonexistent server, so log entries shouldn't be present in the - table */ - *row_out = ql::datum_t(); - return true; - } - - optional bcard; - directory->read_key(*peer_id, - [&](const cluster_directory_metadata_t *metadata) { - if (metadata != nullptr) { - bcard.set(metadata->log_mailbox); - } - }); - if (!static_cast(bcard)) { - /* Server is not present in directory. This can happen due to a race condition, - if the server is connecting or disconnecting, and the `server_config_client` is - out of sync with the directory. */ - *row_out = ql::datum_t(); - return true; - } - - std::vector messages; - try { - /* The timestamp filter is set so that we'll only get messages with the exact - timestamp we're looking for, and there should be at most one such message. */ - messages = fetch_log_file(mailbox_manager, *bcard, - entries_per_server, timestamp, timestamp, interruptor); - } catch (const log_transfer_exc_t &) { - /* Server disconnected during the query. */ - *row_out = ql::datum_t(); - return true; - } catch (const log_read_exc_t &e) { - *error_out = admin_err_t{ - strprintf("Problem when reading log file on server `%s`: %s", - server_name.c_str(), e.what()), - query_state_t::FAILED}; - return false; - } - - if (messages.size() == 0) { - /* There is no log entry with that timestamp */ - *row_out = ql::datum_t(); - return true; - } else if (messages.size() >= 2) { - /* This shouldn't happen unless the user tampered with the log file or the server - clock ran backwards while the server was shut down (and even then it's very - unlikely) */ - *error_out = admin_err_t{ - strprintf("Problem when reading log file on server `%s`: Found " - "multiple log entries with identical timestamps.", - server_name.c_str()), - query_state_t::FAILED}; - return false; - } - - *row_out = convert_log_message_to_datum(messages[0], server_id, server_datum); - - /* The `id` field should be present and mostly correct. But since the conversion - between `timespec` and ReQL time objects is not perfect, the timestamp in the `id` - field might not be exactly the same as the timestamp on the row the user requested. - So we set the `id` field manually here. */ - ql::datum_object_builder_t builder(std::move(*row_out)); - builder.overwrite("id", primary_key); - *row_out = std::move(builder).to_datum(); - - return true; -} - -bool logs_artificial_table_backend_t::write_row( - UNUSED auth::user_context_t const &user_context, - UNUSED ql::datum_t primary_key, - UNUSED bool pkey_was_autogenerated, - UNUSED ql::datum_t *new_value_inout, - UNUSED signal_t *interruptor, - admin_err_t *error_out) { - *error_out = admin_err_t{ - "It's illegal to write to the `rethinkdb.logs` system table.", - query_state_t::FAILED}; - return false; -} - -namespace logs_backend { -class cfeed_machinery_t : public cfeed_artificial_table_backend_t::machinery_t { -public: - cfeed_machinery_t( - namespace_id_t const &namespace_id, - lifetime_t name_resolver, - auth::user_context_t const &user_context, - logs_artificial_table_backend_t *_parent); - - /* `on_change()` checks for newly-connected peers. If it finds one, it puts an - entry in `peers_handled` and spawns an instance of `run()`. */ - void on_change(const peer_id_t &peer, const cluster_directory_metadata_t *dir); - - /* One instance of `run` will be running for each server we're in contact with - that hasn't been permanently removed. It first fetches the latest entry of each - server's log, then repeatedly checks for newer log entries at a regular interval. - If it sees that the server is disconnected, then it removes itself from - `peers_handled` and stops. */ - void run( - const peer_id_t &peer, - const server_id_t &server_id, - const log_server_business_card_t &bcard, - bool is_a_starter, - auto_drainer_t::lock_t keepalive); - - /* Helper function for `run()`. Checks if the server is no longer present in the - directory; if so, removes the entry from `peers_handled` and returns `false`. */ - bool check_disconnected(const peer_id_t &peer); - - /* Called by `cfeed_artificial_table_backend_t` to fetch the initial values for a - new changefeed */ - bool get_initial_values( - const new_mutex_acq_t *proof, - std::vector *initial_values_out, - signal_t *interruptor); - - logs_artificial_table_backend_t *parent; - std::set peers_handled; - std::map last_timestamps; - - /* `all_starters_done` is pulsed when we've fetched logs from every peer that was - connected to the `cfeed_machinery_t` when it was first created. When the - `cfeed_machinery_t` is first created, `starting` is true, and any instance of - `run()` that are spawned in the first group have `is_a_starter` set to `true`. - `num_starters_left` is initially the number of such coroutines. As soon as the - initial batch are spawned, `starting` is set to `false`, so any further instances - that are spawned for newly-connected servers will have `is_a_starter` set to - `false`. As each instance with `is_a_starter` set to `true` finishes fetching the - initial timestamp, it decrements `num_starters_left`. The last one pulses - `all_starters_done`.*/ - bool starting; - int num_starters_left; - cond_t all_starters_done; - - auto_drainer_t drainer; - watchable_map_t::all_subs_t dir_subs; -}; - -cfeed_machinery_t::cfeed_machinery_t( - namespace_id_t const &table_id, - lifetime_t name_resolver, - auth::user_context_t const &user_context, - logs_artificial_table_backend_t *_parent) - : cfeed_artificial_table_backend_t::machinery_t( - table_id, name_resolver, user_context), - parent(_parent), - starting(true), - num_starters_left(0), - dir_subs( - parent->directory, - std::bind(&cfeed_machinery_t::on_change, this, ph::_1, ph::_2), - initial_call_t::YES) -{ - starting = false; - /* In the unlikely event that we're not connected to any servers (not even ourself) - there will be nothing to pulse `all_starters_done`, so we have to do it here. */ - if (num_starters_left == 0) { - all_starters_done.pulse_if_not_already_pulsed(); - } -} - -void cfeed_machinery_t::on_change( - const peer_id_t &peer, - const cluster_directory_metadata_t *dir) { - if (dir == nullptr || peers_handled.count(peer) != 0) { - return; - } - peers_handled.insert(peer); - if (starting) { - ++num_starters_left; - } - coro_t::spawn_sometime(std::bind( - &cfeed_machinery_t::run, this, - peer, dir->server_id, dir->log_mailbox, starting, - auto_drainer_t::lock_t(&drainer))); -} - -void cfeed_machinery_t::run( - const peer_id_t &peer, - const server_id_t &server_id, - const log_server_business_card_t &bcard, - bool is_a_starter, - auto_drainer_t::lock_t keepalive) { - guarantee(!starting, "starting should be set to false before run() actually starts"); - - /* `poll_interval_ms` is how long to wait between polling for new messages. */ - static const int poll_interval_ms = 1000; - - try { - /* First, fetch the initial value of the latest timestamp in the log. The reason - this is in a loop is so that we keep retrying if something goes wrong: the log - file is empty, we can't read it for some reason, etc. */ - timespec initial_latest_timestamp; - while (true) { - if (!check_disconnected(peer)) { - /* The peer is disconnected, so this instance of `run()` is going to - exit. So we have to check this here because we're not going to get to it - later. */ - if (is_a_starter) { - guarantee(num_starters_left > 0); - --num_starters_left; - if (num_starters_left == 0) { - all_starters_done.pulse(); - } - } - return; - } - - /* Fetch the last message in the server's log file */ - std::vector messages; - try { - timespec min_time = { 0, 0 }; - timespec max_time = { std::numeric_limits::max(), 0 }; - messages = fetch_log_file( - parent->mailbox_manager, - bcard, - 1, /* only fetch latest entry */ - min_time, - max_time, - keepalive.get_drain_signal()); - } catch (const log_transfer_exc_t &) { - /* The server disconnected. However, to avoid race conditions, we can't - exit unless we see that the server is absent from the directory, which we - check above. So we ignore the error and go around the loop again. */ - } catch (const log_read_exc_t &) { - /* Something went wrong reading the log file on the other server. Go - around the loop again. */ - } - - if (messages.empty()) { - /* This could mean that the log file is empty, or that an error - occurred. In either case, retry after a short delay. */ - nap(poll_interval_ms, keepalive.get_drain_signal()); - continue; - } - - guarantee(messages.size() == 1, "We asked for at most 1 log message."); - initial_latest_timestamp = messages[0].timestamp; - break; - } - - map_insertion_sentry_t last_timestamp( - &last_timestamps, peer, initial_latest_timestamp); - - /* Now that we've fetched the initial timestamp, we can let the call to - `.changes()` return */ - if (is_a_starter) { - guarantee(num_starters_left > 0); - --num_starters_left; - if (num_starters_left == 0) { - all_starters_done.pulse(); - } - } - - /* Loop forever to check for new messages. */ - while (true) { - if (!check_disconnected(peer)) { - return; - } - - /* Fetch messages since our last request */ - std::vector messages; - try { - /* We choose `min_time` so as to exclude the last message from before */ - timespec min_time = *last_timestamp.get_value(); - add_to_timespec(&min_time, 1); - timespec max_time = { std::numeric_limits::max(), 0 }; - messages = fetch_log_file( - parent->mailbox_manager, - bcard, - /* We might miss some notifications if more than `entries_per_server` - entries are appended to the log file in one iteration of the loop. - But this table already "cheats" regarding the relationship between - the contents of the table and the changefeed, so it's no big deal. */ - entries_per_server, - min_time, - max_time, - keepalive.get_drain_signal()); - } catch (const log_transfer_exc_t &) { - /* Just like in the earlier loop, we ignore the error and rely on - `check_disconnected()` to do the work. */ - } catch (const log_read_exc_t &) { - /* Just like in the earlier loop. */ - } - - if (!messages.empty()) { - /* Compute the server name to attach to the log messages */ - ql::datum_t server_datum; - if (!convert_connected_server_id_to_datum(server_id, - parent->identifier_format, parent->server_config_client, - &server_datum, nullptr)) { - /* The server is disconnected. Don't retrieve log messages until it - reconnects. */ - peers_handled.erase(peer); - return; - } - - new_mutex_acq_t mutex_acq(&mutex, keepalive.get_drain_signal()); - - for (auto it = messages.rbegin(); it != messages.rend(); ++it) { - /* It's possible that `get_initial_values()` has changed - `last_timestamp` since we started fetching the log file. We need to - filter out any entries that started after `last_timestamp`. */ - if (it->timestamp <= *last_timestamp.get_value()) { - continue; - } - *last_timestamp.get_value() = it->timestamp; - - ql::datum_t row = convert_log_message_to_datum( - *it, server_id, server_datum); - store_key_t key( - convert_log_key_to_datum( - it->timestamp, - server_id - ).print_primary()); - send_all_change(&mutex_acq, key, ql::datum_t(), row); - } - } - - nap(poll_interval_ms, keepalive.get_drain_signal()); - } - - } catch (const interrupted_exc_t &) { - /* we're shutting down, do nothing */ - } -} - -bool cfeed_machinery_t::check_disconnected( - const peer_id_t &peer) { - /* We have to do this atomically. If we don't, then we would lose the guarantee that - there is exactly one instance of `run()` for each connected peer. For example, if we - delayed between checking the directory and removing `peer` from `peers_handled`, and - the server reconnected in that time, then `on_change()` wouldn't spawn a new instance - of `run()`, but we would still end up returning `false`, so there would be no - instance of `run()` for that server. */ - ASSERT_FINITE_CORO_WAITING; - - bool connected; - parent->directory->read_key( - peer, - [&](const cluster_directory_metadata_t *metadata) { - connected = (metadata != nullptr); - }); - if (!connected) { - peers_handled.erase(peer); - } - return connected; -} - -bool cfeed_machinery_t::get_initial_values( - const new_mutex_acq_t *proof, - std::vector *initial_values_out, - signal_t *interruptor) { - admin_err_t dummy_error; - return parent->read_all_rows_raw( - [&](const log_message_t &message, - const peer_id_t &peer_id, - const server_id_t &server_id, - const ql::datum_t &server_name_datum) { - ql::datum_t row = convert_log_message_to_datum( - message, server_id, server_name_datum); - initial_values_out->push_back(row); - - auto it = last_timestamps.find(peer_id); - if (it != last_timestamps.end() && it->second < message.timestamp) { - it->second = message.timestamp; - store_key_t key(convert_log_key_to_datum( - message.timestamp, server_id).print_primary()); - send_all_change(proof, key, ql::datum_t(), row); - } - }, - interruptor, - &dummy_error); -} - -} // namespace logs_backend - -bool logs_artificial_table_backend_t::read_all_rows_raw( - const std::function &callback, - signal_t *interruptor, - admin_err_t *error_out) { - std::map> server_names; - std::map server_business_cards; - directory->read_all( - [&](const peer_id_t &peer_id, const cluster_directory_metadata_t *value) { - if (value->peer_type == SERVER_PEER) { - server_names.insert(std::make_pair( - peer_id, - std::make_pair(value->server_config.config.name, value->server_id))); - server_business_cards.insert(std::make_pair( - peer_id, value->log_mailbox)); - } - }); - - optional error; - pmap(server_business_cards.begin(), server_business_cards.end(), - [&](const std::pair &pair) { - auto server_name = server_names.find(pair.first); - guarantee(server_name != server_names.end()); - - std::vector messages; - try { - struct timespec min_time = { 0, 0 }; - struct timespec max_time = { std::numeric_limits::max(), 0 }; - messages = fetch_log_file( - mailbox_manager, - pair.second, - entries_per_server, - min_time, - max_time, - interruptor); - } catch (const interrupted_exc_t &) { - /* We'll deal with it outside the `pmap()` */ - return; - } catch (const log_transfer_exc_t &) { - /* The server disconnected. Ignore it. */ - return; - } catch (const log_read_exc_t &e) { - /* We'll deal with it outside the `pmap()` */ - error.set(strprintf("Problem with reading log file on server `%s`: %s", - server_name->second.first.c_str(), e.what())); - return; - } - - ql::datum_t server_name_datum = convert_name_or_server_id_to_datum( - server_name->second.first, - server_name->second.second, - identifier_format); - for (const log_message_t &message : messages) { - callback( - message, - pair.first, - server_name->second.second, - server_name_datum); - } - }); - - /* We can't throw exceptions or `return false` from within the `pmap()`, so we have - to do it here instead. */ - if (interruptor->is_pulsed()) { - throw interrupted_exc_t(); - } else if (static_cast(error)) { - *error_out = admin_err_t{*error, query_state_t::FAILED}; - return false; - } - return true; -} - -scoped_ptr_t -logs_artificial_table_backend_t::construct_changefeed_machinery( - lifetime_t name_resolver, - auth::user_context_t const &user_context, - signal_t *interruptor) { - scoped_ptr_t machinery( - new logs_backend::cfeed_machinery_t(get_table_id(), name_resolver, user_context, this)); - wait_interruptible(&machinery->all_starters_done, interruptor); - return scoped_ptr_t( - machinery.release()); -} - diff --git a/src/clustering/administration/logs/logs_backend.hpp b/src/clustering/administration/logs/logs_backend.hpp deleted file mode 100644 index be48a7faa82..00000000000 --- a/src/clustering/administration/logs/logs_backend.hpp +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_LOGS_LOGS_BACKEND_HPP_ -#define CLUSTERING_ADMINISTRATION_LOGS_LOGS_BACKEND_HPP_ - -#include -#include -#include -#include - -#include "rdb_protocol/artificial_table/caching_cfeed_backend.hpp" -#include "rdb_protocol/context.hpp" - -class cluster_directory_metadata_t; -class log_message_t; -class log_server_business_card_t; -class server_config_client_t; -class server_id_t; - -template class watchable_map_t; - -/* This backend assumes that the entries in the log file have timestamps that are unique -and monotonically increasing. These assumptions can be broken if the system clock runs -backwards while the server is turned off, or if the user manually edits the log file. If -these assumptions are broken, the system shouldn't crash, but the contents of -`rethinkdb.logs` are undefined. In particular, the following things will go wrong: - * The `rethinkdb.logs` table might have multiple entries with the same primary key. - * Log entries might appear in `r.db("rethinkdb").table("logs")` but not if the user - runs `r.db("rethinkdb").table("logs").get(primary_key)` - * Changefeeds on `rethinkdb.logs` might skip some changes. -*/ - -namespace logs_backend { -class cfeed_machinery_t; -} - -class logs_artificial_table_backend_t : - public cfeed_artificial_table_backend_t -{ -public: - logs_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - mailbox_manager_t *_mailbox_manager, - watchable_map_t *_directory, - server_config_client_t *_server_config_client, - admin_identifier_format_t _identifier_format); - ~logs_artificial_table_backend_t(); - - std::string get_primary_key_name(); - - bool read_all_rows_as_vector( - auth::user_context_t const &user_context, - signal_t *interruptor, - std::vector *rows_out, - admin_err_t *error_out); - - bool read_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor, - ql::datum_t *row_out, - admin_err_t *error_out); - - bool write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor, - admin_err_t *error_out); - -private: - friend class logs_backend::cfeed_machinery_t; - - bool read_all_rows_raw( - const std::function &callback, - signal_t *interruptor, - admin_err_t *error_out); - - scoped_ptr_t - construct_changefeed_machinery( - lifetime_t name_resolver, - auth::user_context_t const &user_context, - signal_t *interruptor); - - mailbox_manager_t *mailbox_manager; - watchable_map_t *directory; - server_config_client_t *server_config_client; - admin_identifier_format_t identifier_format; -}; - -#endif /* CLUSTERING_ADMINISTRATION_LOGS_LOGS_BACKEND_HPP_ */ diff --git a/src/clustering/administration/main/cache_size.cc b/src/clustering/administration/main/cache_size.cc deleted file mode 100644 index 97daf09105a..00000000000 --- a/src/clustering/administration/main/cache_size.cc +++ /dev/null @@ -1,370 +0,0 @@ -#include "clustering/administration/main/cache_size.hpp" - -#include -#include -#include - -#if defined(__MACH__) -#include -#endif - -#include -#include -#include - -#if defined(__MACH__) -#include -#include -#include -#include -#endif - -#include "arch/runtime/thread_pool.hpp" -#include "arch/types.hpp" -#include "logger.hpp" -#include "paths.hpp" -#include "utils.hpp" - -#ifndef __MACH__ - -#if defined(_WIN32) -#include -#include -#endif -size_t skip_spaces(const std::string &s, size_t i) { - while (i < s.size() && (s[i] == ' ' || s[i] == '\t')) { - ++i; - } - return i; -} - -bool parse_status_line(const std::string &contents, - uint64_t *value_out, - std::string *unit_out) { - const size_t line_begin = contents.find("VmSwap"); - const size_t line_end = contents.find('\n', line_begin); - if (line_end == std::string::npos) { - return false; - } - - const std::string line = contents.substr(line_begin, line_end - line_begin); - - const size_t colon = line.find(':'); - if (colon == std::string::npos) { - return false; - } - size_t i = skip_spaces(line, colon + 1); - - const size_t number_begin = i; - while (i < line.size() && '0' <= line[i] && '9' >= line[i]) { - ++i; - } - const size_t number_end = i; - - if (number_begin == number_end) { - return false; - } - - const size_t unit_begin = skip_spaces(line, i); - size_t unit_end = line.find_first_of(" \t", unit_begin); - if (unit_end == std::string::npos) { - unit_end = line.size(); - } - - const size_t end = skip_spaces(line, unit_end); - if (end != line.size()) { - return false; - } - - if (!strtou64_strict(line.substr(number_begin, number_end - number_begin), - 10, - value_out)) { - return false; - } - *unit_out = line.substr(unit_begin, unit_end - unit_begin); - return true; -} - -bool parse_meminfo_line(const std::string &contents, size_t *offset_ref, - std::string *name_out, - uint64_t *value_out, - std::string *unit_out) { -#if defined(_WIN32) - return false; -#else - const size_t line_begin = *offset_ref; - size_t line_end = contents.find('\n', line_begin); - if (line_end == std::string::npos) { - line_end = contents.size(); - *offset_ref = line_end; - } else { - *offset_ref = line_end + 1; - } - - const std::string line = contents.substr(line_begin, line_end - line_begin); - - const size_t colon = line.find(':'); - if (colon == std::string::npos) { - return false; - } - size_t i = skip_spaces(line, colon + 1); - - const size_t number_begin = i; - while (i < line.size() && '0' <= line[i] && '9' >= line[i]) { - ++i; - } - const size_t number_end = i; - - if (number_begin == number_end) { - return false; - } - - const size_t unit_begin = skip_spaces(line, i); - size_t unit_end = line.find_first_of(" \t", unit_begin); - if (unit_end == std::string::npos) { - unit_end = line.size(); - } - - const size_t end = skip_spaces(line, unit_end); - if (end != line.size()) { - return false; - } - - *name_out = line.substr(0, colon); - if (!strtou64_strict(line.substr(number_begin, number_end - number_begin), - 10, - value_out)) { - return false; - } - *unit_out = line.substr(unit_begin, unit_end - unit_begin); - return true; -#endif -} - -bool parse_status_file(const std::string &contents, uint64_t *swap_usage_out) { - std::string name; - uint64_t value; - std::string unit; - if (parse_status_line(contents, &value, &unit)) { - *swap_usage_out = value * KILOBYTE; - return true; - } - return false; -} - - -bool parse_meminfo_file(const std::string &contents, uint64_t *mem_avail_out) { -#if defined(_WIN32) - return false; -#else - uint64_t memfree = 0; - uint64_t cached = 0; - - bool seen_memfree = false; - bool seen_cached = false; - - size_t offset = 0; - std::string name; - uint64_t value; - std::string unit; - while (parse_meminfo_line(contents, &offset, &name, &value, &unit)) { - if (name == "MemFree") { - if (seen_memfree) { - return false; - } - if (unit != "kB") { - return false; - } - seen_memfree = true; - memfree = value * KILOBYTE; - } else if (name == "Cached") { - if (seen_cached) { - return false; - } - if (unit != "kB") { - return false; - } - seen_cached = true; - cached = value * KILOBYTE; - } - - if (seen_memfree && seen_cached) { - break; - } - } - - if (seen_memfree && seen_cached) { - *mem_avail_out = memfree + cached; - return true; - } else { - return false; - } -#endif -} - - -bool get_proc_status_current_swap_usage(uint64_t *swap_usage_out) { -#if defined(_WIN32) - return false; -#else - std::string contents; - bool ok; - pid_t our_pid = getpid(); - if (our_pid == -1) { - return false; - } - thread_pool_t::run_in_blocker_pool([&]() { - ok = blocking_read_file("/proc/self/status", &contents); - }); - if (!ok) { - return false; - } - return parse_status_file(contents, swap_usage_out); -#endif -} - -bool get_proc_meminfo_available_memory_size(uint64_t *mem_avail_out) { - std::string contents; - bool ok; - thread_pool_t::run_in_blocker_pool([&]() { - ok = blocking_read_file("/proc/meminfo", &contents); - }); - if (!ok) { - return false; - } - return parse_meminfo_file(contents, mem_avail_out); -} - -#endif // __MACH_ - -#if defined(__MACH__) - -bool osx_runtime_version_check() { - char str[256]; - size_t size = sizeof(str); - int ret = sysctlbyname("kern.osrelease", str, &size, nullptr, 0); - if (ret == -1) { - return false; - } - int kernel_major_version; - ret = sscanf(str, "%d.", &kernel_major_version); - if (!ret) { - return false; - } - if (kernel_major_version >= 13) { - // This corresponds to 10.9.x or higher - return true; - } - return false; -} - -#endif //__MACH___ - -uint64_t get_avail_mem_size() { -#if defined(_WIN32) - MEMORYSTATUSEX ms; - ms.dwLength = sizeof(ms); - BOOL res = GlobalMemoryStatusEx(&ms); - guarantee_winerr(res, "GlobalMemoryStatusEx failed"); - return ms.ullAvailPhys; -#elif defined(__MACH__) - uint64_t page_size = sysconf(_SC_PAGESIZE); - mach_msg_type_number_t count = HOST_VM_INFO64_COUNT; - vm_statistics64_data_t vmstat; - // We memset this struct to zero because of zero-knowledge paranoia that some old - // system might use a shorter version of the struct, where it would not set the - // vmstat.external_page_count field (which is relatively new) that we use below. - // (Probably, instead, the host_statistics64 call will fail, because count would - // be wrong.) - memset(&vmstat, 0, sizeof(vmstat)); - if (KERN_SUCCESS != host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t)&vmstat, &count)) { - logERR("Could not determine available RAM for the default cache size " - "(errno=%d).\n", get_errno()); - return 1024 * MEGABYTE; - } -#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED - // We know the field we want showed up in 10.9. It may have shown - // up in 10.8, but is definitely not in 10.7. Per Availability.h, - // we use a raw number rather than the corresponding #define. - uint64_t ret; - if (!osx_runtime_version_check()) { - ret = vmstat.free_count * page_size; - } else { - // external_page_count is the number of pages that are file-backed (non-swap) -- - // see /usr/include/mach/vm_statistics.h, see also vm_stat.c, the implementation - // of vm_stat, in Darwin. - // We use an offset here, rather than using the struct, to let us compile on 10.7, - // where the field doesn't exist in the struct. - const size_t external_page_count_offset = 136; -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1900 - static_assert(offsetof(struct vm_statistics64, external_page_count) - == external_page_count_offset, - "OS X ABI changed."); -#endif - ret = (vmstat.free_count + - *(reinterpret_cast((reinterpret_cast(&vmstat) - + external_page_count_offset)))) * page_size; - } -#else -#error "We don't support Mach kernels other than OS X, sorry." -#endif // __MAC_OS_X_VERSION_MIN_REQUIRED - return ret; -#else - uint64_t page_size = sysconf(_SC_PAGESIZE); - { - uint64_t memory; - if (get_proc_meminfo_available_memory_size(&memory)) { - return memory; - } else { - logERR("Could not parse /proc/meminfo, so we will treat cached file memory " - "as if it were unavailable."); - - // This just returns what /proc/meminfo would report as "MemFree". - uint64_t avail_mem_pages = sysconf(_SC_AVPHYS_PAGES); - return avail_mem_pages * page_size; - } - } -#endif -} - -uint64_t get_max_total_cache_size() { - /* We're checking for two things here: - 1. That the cache size is not larger than the amount of RAM that a pointer can - address. This is for 32-bit systems. - 2. That the cache size is not larger than a petabyte. This is partially to catch - people who think the cache size is specified in bytes rather than megabytes. But - it also serves the purpose of making it so that the max total cache size easily - fits into a 64-bit integer, so that we can test the code path where - `validate_total_cache_size()` fails even on a 64-bit system. When hardware - advances to the point where a petabyte of RAM is plausible for a server, we'll - increase the limit. */ - return std::min( - static_cast(std::numeric_limits::max()), - static_cast(MEGABYTE) * static_cast(GIGABYTE)); -} - -uint64_t get_default_total_cache_size() { - const int64_t available_mem = get_avail_mem_size(); - - // Default to half the available memory minus a gigabyte, to leave room for server - // and query overhead, but never default to less than 100 megabytes - const int64_t signed_res = - std::min(available_mem - GIGABYTE, get_max_total_cache_size()) / - DEFAULT_MAX_CACHE_RATIO; - const uint64_t res = std::max(signed_res, 100 * MEGABYTE); - return res; -} - -void log_warnings_for_cache_size(uint64_t bytes) { - const uint64_t available_memory = get_avail_mem_size(); - if (bytes > available_memory) { - logWRN("Cache size is larger than available memory."); - } else if (bytes + GIGABYTE > available_memory) { - logWRN("Cache size does not leave much memory for server and query " - "overhead (available memory: %" PRIu64 " MB).", - available_memory / static_cast(MEGABYTE)); - } - if (bytes <= 100 * MEGABYTE) { - logWRN("Cache size is very low and may impact performance."); - } -} diff --git a/src/clustering/administration/main/cache_size.hpp b/src/clustering/administration/main/cache_size.hpp deleted file mode 100644 index 9086b4e6a10..00000000000 --- a/src/clustering/administration/main/cache_size.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef CLUSTERING_ADMINISTRATION_MAIN_CACHE_SIZE_HPP_ -#define CLUSTERING_ADMINISTRATION_MAIN_CACHE_SIZE_HPP_ - -#include - -#include - -uint64_t get_max_total_cache_size(); -uint64_t get_default_total_cache_size(); -void log_warnings_for_cache_size(uint64_t); - -#endif // CLUSTERING_ADMINISTRATION_MAIN_CACHE_SIZE_HPP_ - diff --git a/src/clustering/administration/main/command_line.cc b/src/clustering/administration/main/command_line.cc deleted file mode 100644 index a82eddf09a9..00000000000 --- a/src/clustering/administration/main/command_line.cc +++ /dev/null @@ -1,2876 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#include "clustering/administration/main/command_line.hpp" - -#include -#include -#include -#include -#include -#include -#include -#ifdef ENABLE_TLS -#include -#include -#include -#include -#endif - -#ifndef _WIN32 -#include -#include -#endif - -#ifdef _WIN32 -#include "errors.hpp" -#include -#include -#endif - -// Needed for determining rethinkdb binary path below -#if defined(__MACH__) -#include -#elif defined(__FreeBSD_version) -#include -#endif - -#include -#include - -#include - -#include "arch/io/disk.hpp" -#include "arch/io/openssl.hpp" -#include "arch/os_signal.hpp" -#include "arch/runtime/starter.hpp" -#include "arch/filesystem.hpp" - -#include "extproc/extproc_spawner.hpp" -#include "clustering/administration/main/cache_size.hpp" -#include "clustering/administration/main/names.hpp" -#include "clustering/administration/main/options.hpp" -#include "clustering/administration/main/ports.hpp" -#include "clustering/administration/main/serve.hpp" -#include "clustering/administration/main/directory_lock.hpp" -#include "clustering/administration/main/windows_service.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/logs/log_writer.hpp" -#include "clustering/administration/main/path.hpp" -#include "clustering/administration/persist/file.hpp" -#include "clustering/administration/persist/file_keys.hpp" -#include "clustering/administration/persist/migrate/migrate_v1_16.hpp" -#include "clustering/administration/persist/migrate/migrate_v2_1.hpp" -#include "clustering/administration/servers/server_metadata.hpp" -#include "containers/scoped.hpp" -#include "crypto/random.hpp" -#include "logger.hpp" - -#define RETHINKDB_EXPORT_SCRIPT "rethinkdb-export" -#define RETHINKDB_IMPORT_SCRIPT "rethinkdb-import" -#define RETHINKDB_DUMP_SCRIPT "rethinkdb-dump" -#define RETHINKDB_RESTORE_SCRIPT "rethinkdb-restore" -#define RETHINKDB_INDEX_REBUILD_SCRIPT "rethinkdb-index-rebuild" -#define RETHINKDB_REPL_SCRIPT "rethinkdb-repl" - -namespace cluster_defaults { -const int reconnect_timeout = (24 * 60 * 60); // 24 hours (in secs) -} // namespace cluster_defaults - -MUST_USE bool numwrite(const char *path, int number) { - // Try to figure out what this function does. - FILE *fp1 = fopen(path, "w"); - if (fp1 == nullptr) { - return false; - } - fprintf(fp1, "%d", number); - fclose(fp1); - return true; -} - -static std::string pid_file; - -void remove_pid_file() { - if (!pid_file.empty()) { - remove(pid_file.c_str()); - pid_file.clear(); - } -} - -int check_pid_file(const std::string &pid_filepath) { - guarantee(pid_filepath.size() > 0); - - if (access(pid_filepath.c_str(), F_OK) == 0) { - logERR("The pid-file specified already exists. This might mean that an instance is already running."); - return EXIT_FAILURE; - } - - if (access(strdirname(pid_filepath).c_str(), W_OK) == -1) { - logERR("Cannot access the pid-file directory."); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - -int write_pid_file(const std::string &pid_filepath) { - guarantee(pid_filepath.size() > 0); - - // Be very careful about modifying this. It is important that the code that removes the - // pid-file only run if the checks here pass. Right now, this is guaranteed by the return on - // failure here. - if (!pid_file.empty()) { - logERR("Attempting to write pid-file twice."); - return EXIT_FAILURE; - } - - if (check_pid_file(pid_filepath) == EXIT_FAILURE) { - return EXIT_FAILURE; - } - - if (!numwrite(pid_filepath.c_str(), getpid())) { - logERR("Writing to the specified pid-file failed."); - return EXIT_FAILURE; - } - pid_file = pid_filepath; - atexit(remove_pid_file); - - return EXIT_SUCCESS; -} - -// Extracts an option that appears either zero or once. Multiple appearances are not allowed (or -// expected). -optional get_optional_option(const std::map &opts, - const std::string &name, - std::string *source_out) { - auto it = opts.find(name); - if (it == opts.end()) { - *source_out = "nowhere"; - return optional(); - } - - if (it->second.values.empty()) { - *source_out = it->second.source; - return optional(); - } - - if (it->second.values.size() == 1) { - *source_out = it->second.source; - return make_optional(it->second.values[0]); - } - - throw std::logic_error("Option '%s' appears multiple times (when it should only appear once.)"); -} - -optional get_optional_option(const std::map &opts, - const std::string &name) { - std::string source; - return get_optional_option(opts, name, &source); -} - -#ifndef _WIN32 -// Returns false if the group was not found. This function replaces a call to -// getgrnam(3). That's right, getgrnam_r's interface is such that you have to -// go through these shenanigans. -bool get_group_id(const char *name, gid_t *group_id_out) { - // On Linux we can use sysconf to learn what the bufsize should be but on OS - // X we just have to guess. - size_t bufsize = 4096; - // I think 128 MB ought to be enough. - while (bufsize <= (1 << 17)) { - scoped_array_t buf(bufsize); - - struct group g; - struct group *result; - int res; - do { - res = getgrnam_r(name, &g, buf.data(), bufsize, &result); - } while (res == EINTR); - - if (res == 0) { - if (result == nullptr) { - return false; - } else { - *group_id_out = result->gr_gid; - return true; - } - } else if (res == ERANGE) { - bufsize *= 2; - } else { - // Here's what the man page says about error codes of getgrnam_r: - // 0 or ENOENT or ESRCH or EBADF or EPERM or ... - // The given name or uid was not found. - // - // So let's just return false here. I'm sure EMFILE and ENFILE and - // EIO will turn up again somewhere else. - return false; - } - } - - rassert(false, "get_group_id bufsize overflow"); - return false; -} - -// Returns false if the user was not found. This function replaces a call to -// getpwnam(3). That's right, getpwnam_r's interface is such that you have to -// go through these shenanigans. -bool get_user_ids(const char *name, uid_t *user_id_out, gid_t *user_group_id_out) { - // On Linux we can use sysconf to learn what the bufsize should be but on OS - // X we just have to guess. - size_t bufsize = 4096; - // I think 128 MB ought to be enough. - while (bufsize <= (1 << 17)) { - scoped_array_t buf(bufsize); - - struct passwd p; - struct passwd *result; - int res; - do { - res = getpwnam_r(name, &p, buf.data(), bufsize, &result); - } while (res == EINTR); - - if (res == 0) { - if (result == nullptr) { - return false; - } else { - *user_id_out = result->pw_uid; - *user_group_id_out = result->pw_gid; - return true; - } - } else if (res == ERANGE) { - bufsize *= 2; - } else { - // Here's what the man page says about error codes of getpwnam_r: - // 0 or ENOENT or ESRCH or EBADF or EPERM or ... - // The given name or uid was not found. - // - // So let's just return false here. I'm sure EMFILE and ENFILE and - // EIO will turn up again somewhere else. - // - // (Yes, this is the same situation as with getgrnam_r, not some - // copy/paste.) - return false; - } - } - - rassert(false, "get_user_ids bufsize overflow"); - return false; -} - -void get_user_group(const std::map &opts, - gid_t *group_id_out, std::string *group_name_out, - uid_t *user_id_out, std::string *user_name_out) { - optional rungroup = get_optional_option(opts, "--rungroup"); - optional runuser = get_optional_option(opts, "--runuser"); - group_name_out->clear(); - user_name_out->clear(); - - if (rungroup) { - group_name_out->assign(*rungroup); - if (!get_group_id(rungroup->c_str(), group_id_out)) { - throw std::runtime_error(strprintf("Group '%s' not found: %s", - rungroup->c_str(), - errno_string(get_errno()).c_str()).c_str()); - } - } else { - *group_id_out = INVALID_GID; - } - - if (runuser) { - user_name_out->assign(*runuser); - gid_t user_group_id; - if (!get_user_ids(runuser->c_str(), user_id_out, &user_group_id)) { - throw std::runtime_error(strprintf("User '%s' not found: %s", - runuser->c_str(), - errno_string(get_errno()).c_str()).c_str()); - } - if (!rungroup) { - // No group specified, use the user's group - group_name_out->assign(*runuser); - *group_id_out = user_group_id; - } - } else { - *user_id_out = INVALID_UID; - } -} - -void set_user_group(gid_t group_id, const std::string &group_name, - uid_t user_id, const std::string user_name) { - if (group_id != INVALID_GID) { - if (setgid(group_id) != 0) { - throw std::runtime_error(strprintf("Could not set group to '%s': %s", - group_name.c_str(), - errno_string(get_errno()).c_str()).c_str()); - } - } - - if (user_id != INVALID_UID) { - if (setuid(user_id) != 0) { - throw std::runtime_error(strprintf("Could not set user account to '%s': %s", - user_name.c_str(), - errno_string(get_errno()).c_str()).c_str()); - } - } -} - -void get_and_set_user_group(const std::map &opts) { - std::string group_name, user_name; - gid_t group_id; - uid_t user_id; - - get_user_group(opts, &group_id, &group_name, &user_id, &user_name); - set_user_group(group_id, group_name, user_id, user_name); -} - -void get_and_set_user_group_and_directory( - const std::map &opts, - directory_lock_t *directory_lock) { - std::string group_name, user_name; - gid_t group_id; - uid_t user_id; - - get_user_group(opts, &group_id, &group_name, &user_id, &user_name); - directory_lock->change_ownership(group_id, group_name, user_id, user_name); - set_user_group(group_id, group_name, user_id, user_name); -} -#endif - -int check_pid_file(const std::map &opts) { - optional pid_filepath = get_optional_option(opts, "--pid-file"); - if (!pid_filepath || pid_filepath->empty()) { - return EXIT_SUCCESS; - } - - return check_pid_file(*pid_filepath); -} - -// Maybe writes a pid file, using the --pid-file option, if it's present. -int write_pid_file(const std::map &opts) { - optional pid_filepath = get_optional_option(opts, "--pid-file"); - if (!pid_filepath || pid_filepath->empty()) { - return EXIT_SUCCESS; - } - - return write_pid_file(*pid_filepath); -} - -// Extracts an option that must appear exactly once. (This is often used for optional arguments -// that have a default value.) -std::string get_single_option(const std::map &opts, - const std::string &name, - std::string *source_out) { - std::string source; - optional value = get_optional_option(opts, name, &source); - - if (!value) { - throw std::logic_error(strprintf("Missing option '%s'. (It does not have a default value.)", name.c_str())); - } - - *source_out = source; - return *value; -} - -std::string get_single_option(const std::map &opts, - const std::string &name) { - std::string source; - return get_single_option(opts, name, &source); -} - -// Used for options that don't take parameters, such as --help or --exit-failure, tells whether the -// option exists. -bool exists_option(const std::map &opts, const std::string &name) { - auto it = opts.find(name); - return it != opts.end() && !it->second.values.empty(); -} - -void print_version_message() { - printf("%s\n", RETHINKDB_VERSION_STR); -} - -bool handle_help_or_version_option(const std::map &opts, - void (*help_fn)()) { - if (exists_option(opts, "--help")) { - help_fn(); - return true; - } else if (exists_option(opts, "--version")) { - print_version_message(); - return true; - } - return false; -} - -void initialize_logfile(const std::map &opts, - const base_path_t& dirpath) { - std::string filename; - if (exists_option(opts, "--log-file")) { - filename = get_single_option(opts, "--log-file"); - } else { - filename = dirpath.path() + "/log_file"; - } - install_fallback_log_writer(filename); -} - -std::string get_web_path(optional web_static_directory) { - path_t result; - - if (web_static_directory) { - result = parse_as_path(*web_static_directory); - } else { - return std::string(); - } - - // Make sure we return an absolute path - base_path_t abs_path(render_as_path(result)); - - if (!check_existence(abs_path)) { - throw std::runtime_error(strprintf("ERROR: web assets directory not found '%s'", - abs_path.path().c_str()).c_str()); - } - - abs_path.make_absolute(); - return abs_path.path(); -} - -std::string get_web_path(const std::map &opts) { - if (!exists_option(opts, "--no-http-admin")) { - optional web_static_directory = get_optional_option(opts, "--web-static-directory"); - return get_web_path(web_static_directory); - } - return std::string(); -} - -optional parse_join_delay_secs_option( - const std::map &opts) { - if (exists_option(opts, "--join-delay")) { - const std::string delay_opt = get_single_option(opts, "--join-delay"); - uint64_t join_delay_secs; - if (!strtou64_strict(delay_opt, 10, &join_delay_secs)) { - throw std::runtime_error(strprintf( - "ERROR: join-delay should be a number, got '%s'", - delay_opt.c_str())); - } - if (join_delay_secs > static_cast(std::numeric_limits::max())) { - throw std::runtime_error(strprintf( - "ERROR: join-delay is too large. Must be at most %d", - std::numeric_limits::max())); - } - return optional(static_cast(join_delay_secs)); - } else { - return optional(); - } -} - -optional parse_node_reconnect_timeout_secs_option( - const std::map &opts) { - if (exists_option(opts, "--cluster-reconnect-timeout")) { - const std::string timeout_opt = get_single_option(opts, "--cluster-reconnect-timeout"); - uint64_t node_reconnect_timeout_secs; - if (!strtou64_strict(timeout_opt, 10, &node_reconnect_timeout_secs)) { - throw std::runtime_error(strprintf( - "ERROR: cluster-reconnect-timeout should be a number, got '%s'", - timeout_opt.c_str())); - } - if (node_reconnect_timeout_secs > std::numeric_limits::max() || - node_reconnect_timeout_secs * 1000 > std::numeric_limits::max()) { - throw std::runtime_error(strprintf( - "ERROR: cluster-reconnect-timeout is too large. Must be at most %d", - std::numeric_limits::max() / 1000)); - } - return optional(static_cast(node_reconnect_timeout_secs)); - } - - return optional(); -} - -/* An empty outer `optional` means the `--cache-size` parameter is not present. An -empty inner `optional` means the cache size is set to `auto`. */ -optional > parse_total_cache_size_option( - const std::map &opts) { - if (exists_option(opts, "--cache-size")) { - const std::string cache_size_opt = get_single_option(opts, "--cache-size"); - if (cache_size_opt == "auto") { - return optional >( - optional()); - } else { - uint64_t cache_size_megs; - if (!strtou64_strict(cache_size_opt, 10, &cache_size_megs)) { - throw std::runtime_error(strprintf( - "ERROR: cache-size should be a number or 'auto', got '%s'", - cache_size_opt.c_str())); - } - const uint64_t res = cache_size_megs * MEGABYTE; - if (res > get_max_total_cache_size()) { - throw std::runtime_error(strprintf( - "ERROR: cache-size is %" PRIu64 " MB, which is larger than the " - "maximum legal cache size for this platform (%" PRIu64 " MB)", - res, get_max_total_cache_size())); - } - return optional >( - optional(res)); - } - } else { - return make_optional(optional()); - } -} - -// Note that this defaults to the peer port if no port is specified -// (at the moment, this is only used for parsing --join directives) -// Possible formats: -// host only: newton -// host and port: newton:60435 -// IPv4 addr only: 192.168.0.1 -// IPv4 addr and port: 192.168.0.1:60435 -// IPv6 addr only: ::dead:beef -// IPv6 addr only: [::dead:beef] -// IPv6 addr and port: [::dead:beef]:60435 -// IPv4-mapped IPv6 addr only: ::ffff:192.168.0.1 -// IPv4-mapped IPv6 addr only: [::ffff:192.168.0.1] -// IPv4-mapped IPv6 addr and port: [::ffff:192.168.0.1]:60435 -host_and_port_t parse_host_and_port(const std::string &source, const std::string &option_name, - const std::string &value, int default_port) { - // First disambiguate IPv4 vs IPv6 - size_t colon_count = std::count(value.begin(), value.end(), ':'); - - if (colon_count < 2) { - // IPv4 will have 1 or less colons - size_t colon_loc = value.find_last_of(':'); - if (colon_loc != std::string::npos) { - std::string host = value.substr(0, colon_loc); - int port = atoi(value.substr(colon_loc + 1).c_str()); - if (host.size() != 0 && port != 0 && port <= MAX_PORT) { - return host_and_port_t(host, port_t(port)); - } - } else if (value.size() != 0) { - return host_and_port_t(value, port_t(default_port)); - } - } else { - // IPv6 will have 2 or more colons - size_t last_colon_loc = value.find_last_of(':'); - size_t start_bracket_loc = value.find_first_of('['); - size_t end_bracket_loc = value.find_last_of(']'); - - if (start_bracket_loc > end_bracket_loc) { - // Error condition fallthrough - } else if (start_bracket_loc == std::string::npos || end_bracket_loc == std::string::npos) { - // No brackets, therefore no port, just parse the whole thing as a hostname - return host_and_port_t(value, port_t(default_port)); - } else if (last_colon_loc < end_bracket_loc) { - // Brackets, but no port, verify no other characters outside the brackets - if (value.find_last_not_of(" \t\r\n[", start_bracket_loc) == std::string::npos && - value.find_first_not_of(" \t\r\n]", end_bracket_loc) == std::string::npos) { - std::string host = value.substr(start_bracket_loc + 1, end_bracket_loc - start_bracket_loc - 1); - return host_and_port_t(host, port_t(default_port)); - } - } else { - // Brackets and port - std::string host = value.substr(start_bracket_loc + 1, end_bracket_loc - start_bracket_loc - 1); - std::string remainder = value.substr(end_bracket_loc + 1); - size_t remainder_colon_loc = remainder.find_first_of(':'); - int port = atoi(remainder.substr(remainder_colon_loc + 1).c_str()); - - // Verify no characters before the brackets and up to the port colon - if (port != 0 && port <= MAX_PORT && remainder_colon_loc == 0 && - value.find_last_not_of(" \t\r\n[", start_bracket_loc) == std::string::npos) { - return host_and_port_t(host, port_t(port)); - } - } - } - - - throw options::value_error_t(source, option_name, - strprintf("Option '%s' has invalid host and port format '%s'", - option_name.c_str(), value.c_str())); -} - -class address_lookup_exc_t : public std::exception { -public: - explicit address_lookup_exc_t(const std::string& data) : info(data) { } - ~address_lookup_exc_t() throw () { } - const char *what() const throw () { return info.c_str(); } -private: - std::string info; -}; - -std::set get_local_addresses( - const std::vector &specific_options, - const std::vector &default_options, - local_ip_filter_t filter_type) { - std::set set_filter; - - const std::vector &bind_options = - specific_options.size() > 0 ? - specific_options : - default_options; - - // Scan through specified bind options - for (size_t i = 0; i < bind_options.size(); ++i) { - if (bind_options[i] == "all") { - filter_type = local_ip_filter_t::ALL; - } else { - // Verify that all specified addresses are valid ip addresses - try { - ip_address_t addr(bind_options[i]); - if (addr.is_any()) { - filter_type = local_ip_filter_t::ALL; - } else { - set_filter.insert(addr); - } - } catch (const std::exception &ex) { - throw address_lookup_exc_t(strprintf("bind ip address '%s' could not be parsed: %s", - bind_options[i].c_str(), - ex.what())); - } - } - } - - std::set result = get_local_ips(set_filter, filter_type); - - // Make sure that all specified addresses were found - for (std::set::iterator i = set_filter.begin(); i != set_filter.end(); ++i) { - if (result.find(*i) == result.end()) { - std::string errmsg = strprintf("Could not find bind ip address '%s'", i->to_string().c_str()); - if (i->is_ipv6_link_local()) { - errmsg += strprintf(", this is an IPv6 link-local address, make sure the scope is correct"); - } - throw address_lookup_exc_t(errmsg); - } - } - - if (result.empty()) { - throw address_lookup_exc_t("No local addresses found to bind to."); - } - - // If we will use all local addresses, return an empty set, which is how tcp_listener_t does it - if (filter_type == local_ip_filter_t::ALL) { - return std::set(); - } - - return result; -} - -// Returns the options vector for a given option name. The option must *exist*! Typically this is -// for OPTIONAL_REPEAT options with a default value being the empty vector. -const std::vector &all_options(const std::map &opts, - const std::string &name, - std::string *source_out) { - auto it = opts.find(name); - if (it == opts.end()) { - throw std::logic_error(strprintf("option '%s' not found", name.c_str())); - } - *source_out = it->second.source; - return it->second.values; -} - -const std::vector &all_options(const std::map &opts, - const std::string &name) { - std::string source; - return all_options(opts, name, &source); -} - -// Gets a single integer option, often an optional integer option with a default value. -int get_single_int(const std::map &opts, const std::string &name) { - const std::string value = get_single_option(opts, name); - int64_t x; - if (strtoi64_strict(value, 10, &x)) { - if (INT_MIN <= x && x <= INT_MAX) { - return x; - } - } - throw std::runtime_error(strprintf("Option '%s' (with value '%s') not a valid integer", - name.c_str(), value.c_str())); -} - -int offseted_port(const int port, const int port_offset) { - return port == 0 ? 0 : port + port_offset; -} - -peer_address_t get_canonical_addresses(const std::map &opts, - int default_port) { - std::string source; - std::vector canonical_options = all_options(opts, "--canonical-address", &source); - // Verify that all specified addresses are valid ip addresses - std::set result; - for (size_t i = 0; i < canonical_options.size(); ++i) { - host_and_port_t host_port = parse_host_and_port(source, "--canonical-address", - canonical_options[i], default_port); - - if (host_port.port().value() == 0 && default_port != 0) { - // The cluster layer would probably swap this out with whatever port we were - // actually listening on, but since the user explicitly specified 0, it doesn't make sense - throw std::logic_error("cannot specify a port of 0 in --canonical-address"); - } - result.insert(host_port); - } - return peer_address_t(result); -} - -service_address_ports_t get_service_address_ports(const std::map &opts) { - const int port_offset = get_single_int(opts, "--port-offset"); - const int cluster_port = offseted_port(get_single_int(opts, "--cluster-port"), port_offset); - - local_ip_filter_t filter = - exists_option(opts, "--no-default-bind") ? - local_ip_filter_t::MATCH_FILTER : - local_ip_filter_t::MATCH_FILTER_OR_LOOPBACK; - - const std::vector &default_options = - all_options(opts, "--bind"); - return service_address_ports_t( - get_local_addresses(default_options, default_options, filter), - get_local_addresses(all_options(opts, "--bind-cluster"), - default_options, - filter), - get_local_addresses(all_options(opts, "--bind-driver"), - default_options, - filter), - get_local_addresses(all_options(opts, "--bind-http"), - default_options, - filter), - get_canonical_addresses(opts, cluster_port), - cluster_port, - get_single_int(opts, "--client-port"), - exists_option(opts, "--no-http-admin"), - offseted_port(get_single_int(opts, "--http-port"), port_offset), - offseted_port(get_single_int(opts, "--driver-port"), port_offset), - port_offset); -} - -#ifdef ENABLE_TLS -bool load_tls_key_and_cert( - SSL_CTX *tls_ctx, const std::string &key_file, const std::string &cert_file) { - if(SSL_CTX_use_PrivateKey_file(tls_ctx, key_file.c_str(), SSL_FILETYPE_PEM) <= 0) { - ERR_print_errors_fp(stderr); - return false; - } - - if(SSL_CTX_use_certificate_file(tls_ctx, cert_file.c_str(), SSL_FILETYPE_PEM) <= 0) { - ERR_print_errors_fp(stderr); - return false; - } - - if(1 != SSL_CTX_check_private_key(tls_ctx)) { - logERR("The private key and certificate do not match."); - return false; - } - - return true; -} - -bool configure_web_tls( - const std::map &opts, SSL_CTX *web_tls) { - optional key_file = get_optional_option(opts, "--http-tls-key"); - optional cert_file = get_optional_option(opts, "--http-tls-cert"); - - if (!(key_file && cert_file)) { - logERR("--http-tls-key and --http-tls-cert must be specified together."); - return false; - } - - return load_tls_key_and_cert(web_tls, *key_file, *cert_file); -} - -bool configure_driver_tls( - const std::map &opts, SSL_CTX *driver_tls) { - optional key_file = get_optional_option( - opts, "--driver-tls-key"); - optional cert_file = get_optional_option( - opts, "--driver-tls-cert"); - optional ca_file = get_optional_option(opts, "--driver-tls-ca"); - - if (!(key_file && cert_file)) { - if (key_file || cert_file) { - logERR("--driver-tls-key and --driver-tls-cert must be specified together."); - } else { - rassert(ca_file); - logERR("--driver-tls-key and --driver-tls-cert must be specified if " - "--driver-tls-ca is specified."); - } - return false; - } - - if (!load_tls_key_and_cert(driver_tls, *key_file, *cert_file)) { - return false; - } - - if (ca_file) { - if (!SSL_CTX_load_verify_locations(driver_tls, ca_file->c_str(), nullptr)) { - ERR_print_errors_fp(stderr); - return false; - } - - // Mutual authentication. - SSL_CTX_set_verify( - driver_tls, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); - } - - return true; -} - -bool configure_cluster_tls( - const std::map &opts, SSL_CTX *cluster_tls) { - optional key_file = get_optional_option( - opts, "--cluster-tls-key"); - optional cert_file = get_optional_option( - opts, "--cluster-tls-cert"); - optional ca_file = get_optional_option(opts, "--cluster-tls-ca"); - - if (!(key_file && cert_file && ca_file)) { - logERR("--cluster-tls-key, --cluster-tls-cert, and --cluster-tls-ca must be " - "specified together."); - return false; - } - - if (!load_tls_key_and_cert(cluster_tls, *key_file, *cert_file)) { - return false; - } - - if (!SSL_CTX_load_verify_locations(cluster_tls, ca_file->c_str(), nullptr)) { - ERR_print_errors_fp(stderr); - return false; - } - - // Mutual authentication. - SSL_CTX_set_verify( - cluster_tls, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); - - return true; -} - -class fp_wrapper_t { -public: - fp_wrapper_t(const char *filename, const char *mode) { - fp = fopen(filename, mode); - } - - ~fp_wrapper_t() { - if (fp != nullptr) { - fclose(fp); - } - } - - FILE *get() { - return fp; - } - -private: - FILE *fp; -}; - -bool initialize_tls_ctx( - const std::map &opts, - shared_ssl_ctx_t *tls_ctx_out) { - - tls_ctx_out->reset(SSL_CTX_new(SSLv23_method()), SSL_CTX_free); - if (nullptr == tls_ctx_out->get()) { - ERR_print_errors_fp(stderr); - return false; - } - - optional min_protocol_opt = get_optional_option( - opts, "--tls-min-protocol"); - long protocol_flags = // NOLINT(runtime/int) - SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1; - if (min_protocol_opt) { - if (*min_protocol_opt == "TLSv1") { - protocol_flags ^= SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1; - } else if (*min_protocol_opt == "TLSv1.1") { - protocol_flags ^= SSL_OP_NO_TLSv1_1; - } else if (*min_protocol_opt == "TLSv1.2") { - // Already the default - } else { - logERR("Unrecognized TLS protocol version '%s'.", min_protocol_opt->c_str()); - return false; - } - } - SSL_CTX_set_options(tls_ctx_out->get(), protocol_flags); - - // Prefer server ciphers, and always generate new keys for DHE or ECDHE. - SSL_CTX_set_options( - tls_ctx_out->get(), - SSL_OP_CIPHER_SERVER_PREFERENCE|SSL_OP_SINGLE_DH_USE|SSL_OP_SINGLE_ECDH_USE); - - /* This is pretty important. We want to use the most secure TLS cipher - suite that we can. Our default list only allows ciphers suites which employ - ECDHE (Elliptic Curve Diffie-Hellman with Ephemeral keys) for encryption - key agreement for Perfect Forward Secrecy and we want to use AESGCM for - efficient authenticated encryption. An option exists for system admins to - configure their own ciphers list if their SSL/TLS lib doesn't support these - options or if they want to use other cipher suites. */ - optional ciphers_opt = get_optional_option( - opts, "--tls-ciphers"); - /* NOTE: I normally prefer the abreviation 'ECDHE', but older versions of - OpenSSL do not seem to like 'ECDHE' even though the matching cipher suites - have that prefix. Yeah, I know... it's very frustrating. Both older (1.0.1) - and newer (1.0.2) versions seem to be okay with 'EECDH' though. */ - std::string ciphers = ciphers_opt ? *ciphers_opt : "EECDH+AESGCM"; - if (0 == SSL_CTX_set_cipher_list(tls_ctx_out->get(), ciphers.c_str())) { - logERR("No secure cipher suites available.\n"); - return false; - } - - /* The default curve name corresponds to a standard NIST/FIPS curve and is - also commonly known as P-256. Some people speculate that the NIST/FIPS - curves are compromised in some way by the NSA but most people agree that - they are safe to use. Any real risk in using elliptic curves is associated - with ECDSA (Elliptic Curve Digital Signature Algorithm) where you must be - careful to generate signatures correctly using a secure PRNG. As we are - using them only for ECDHE, this should be safe. This is just our default - ECDHE curve and is widley supported by TLS libraries, but a system admin - may specify any preferred curve which is supported by their local version - of libssl with a command line option - for example 'secp256k1' (widely used - and popularized by the Bitcoin cryptocurrency) and Daniel J. Bernstein's - 'Curve25519' (though not currently supported by OpenSSL) would be other - good choices. - NOTE: For compatibility reasons, we can only support a single elliptic - curve since OpenSSL is ridiculous. */ - optional curve_opt = get_optional_option( - opts, "--tls-ecdh-curve"); - std::string curve_name = curve_opt ? *curve_opt : "prime256v1"; - - int curve_nid = OBJ_txt2nid(curve_name.c_str()); - if (NID_undef == curve_nid) { - logERR("No elliptic curve found corresponding to name '%s'.", curve_name.c_str()); - return false; - } - - EC_KEY *ec_key = EC_KEY_new_by_curve_name(curve_nid); - if (nullptr == ec_key) { - logERR("Unable to get elliptic curve by name '%s'.", curve_name.c_str()); - return false; - } - - SSL_CTX_set_tmp_ecdh(tls_ctx_out->get(), ec_key); - EC_KEY_free(ec_key); - - - /* If we only supported OpenSSL 1.0.2 then we could have multiple ECDHE - curves with something like this: - - if (0 == SSL_CTX_set1_curves_list(tls_ctx_out->get(), curves.c_str())) { - logERR("Not able to set ECDHE curves for perfect forward secrecy\n"); - return false; - } - - // Enabling this option allows the server to select the appropriate curve - // that is shared with the client. - if (0 == SSL_CTX_set_ecdh_auto(tls_ctx_out->get(), 1)) { - logERR("Not able to set ECDHE curve selection mode\n"); - return false; - } - - */ - - /* If the client and server do not support ECDHE but do support DHE, an - admin must specify a file containing parameters for DHE. */ - optional dhparams_filename = get_optional_option( - opts, "--tls-dhparams"); - if (dhparams_filename) { - fp_wrapper_t dhparams_fp(dhparams_filename->c_str(), "r"); - if (nullptr == dhparams_fp.get()) { - logERR( - "Unable to open '%s' for reading: %s", - dhparams_filename->c_str(), - errno_string(get_errno()).c_str()); - return false; - } - - DH *dhparams = PEM_read_DHparams( - dhparams_fp.get(), nullptr, nullptr, nullptr); - if (nullptr == dhparams) { - unsigned long err_code = ERR_get_error(); // NOLINT(runtime/int) - const char *err_str = ERR_reason_error_string(err_code); - logERR( - "Unable to read DH parameters from '%s': %s (OpenSSL error %lu)", - dhparams_filename->c_str(), - err_str == nullptr ? "unknown error" : err_str, - err_code); - return false; - } - - if (1 != SSL_CTX_set_tmp_dh(tls_ctx_out->get(), dhparams)) { - unsigned long err_code = ERR_get_error(); // NOLINT(runtime/int) - const char *err_str = ERR_reason_error_string(err_code); - logERR( - "Unable to set DH parameters: %s (OpenSSL error %lu)", - err_str == nullptr ? "unknown error" : err_str, - err_code); - return false; - } - } - - return true; -} - -bool configure_tls( - const std::map &opts, - tls_configs_t *tls_configs_out) { - - if(!exists_option(opts, "--no-http-admin") && - (exists_option(opts, "--http-tls-key") - || exists_option(opts, "--http-tls-cert"))) { - if (!(initialize_tls_ctx(opts, &(tls_configs_out->web)) && - configure_web_tls(opts, tls_configs_out->web.get()) - )) { - return false; - } - } - - if (exists_option(opts, "--driver-tls-key") - || exists_option(opts, "--driver-tls-cert") - || exists_option(opts, "--driver-tls-ca")) { - if (!(initialize_tls_ctx(opts, &(tls_configs_out->driver)) && - configure_driver_tls(opts, tls_configs_out->driver.get()) - )) { - return false; - } - } - - if (exists_option(opts, "--cluster-tls-key") - || exists_option(opts, "--cluster-tls-cert") - || exists_option(opts, "--cluster-tls-ca")) { - if (!(initialize_tls_ctx(opts, &(tls_configs_out->cluster)) && - configure_cluster_tls(opts, tls_configs_out->cluster.get()) - )) { - return false; - } - } - - return true; -} -#endif /* ENABLE_TLS */ - -void run_rethinkdb_create(const base_path_t &base_path, - const name_string_t &server_name, - const std::set &server_tags, - const std::string &initial_password, - optional total_cache_size, - const file_direct_io_mode_t direct_io_mode, - const int max_concurrent_io_requests, - bool *const result_out) { - server_id_t our_server_id = server_id_t::generate_server_id(); - - cluster_semilattice_metadata_t cluster_metadata; - auth_semilattice_metadata_t auth_metadata; - heartbeat_semilattice_metadata_t heartbeat_metadata; - - server_config_versioned_t server_config; - server_config.config.name = server_name; - server_config.config.tags = server_tags; - server_config.config.cache_size_bytes = total_cache_size; - server_config.version = 1; - - io_backender_t io_backender(direct_io_mode, max_concurrent_io_requests); - - perfmon_collection_t metadata_perfmon_collection; - perfmon_membership_t metadata_perfmon_membership(&get_global_perfmon_collection(), &metadata_perfmon_collection, "metadata"); - - try { - cond_t non_interruptor; - metadata_file_t metadata_file( - &io_backender, - base_path, - &metadata_perfmon_collection, - [&](metadata_file_t::write_txn_t *write_txn, signal_t *interruptor) { - write_txn->write(mdkey_server_id(), - our_server_id, interruptor); - write_txn->write(mdkey_server_config(), - server_config, interruptor); - write_txn->write(mdkey_cluster_semilattices(), - cluster_metadata, interruptor); - write_txn->write(mdkey_auth_semilattices(), - auth_semilattice_metadata_t(initial_password), interruptor); - write_txn->write(mdkey_heartbeat_semilattices(), - heartbeat_semilattice_metadata_t(), interruptor); - }, - &non_interruptor); - logINF("Created directory '%s' and a metadata file inside it.\n", base_path.path().c_str()); - *result_out = true; - } catch (const file_in_use_exc_t &ex) { - logNTC("Directory '%s' is in use by another rethinkdb process.\n", base_path.path().c_str()); - *result_out = false; - } -} - -#ifdef _WIN32 -std::string windows_version_string() { - // TODO WINDOWS: the return value of GetVersion may be capped, - // see https://msdn.microsoft.com/en-us/library/dn481241(v=vs.85).aspx - DWORD version = GetVersion(); - int major = LOBYTE(LOWORD(version)); - int minor = HIBYTE(LOWORD(version)); - int build = version < 0x80000000 ? HIWORD(version) : 0; - std::string name; - switch (LOWORD(version)) { - case 0x000A: name ="Windows 10, Server 2016"; break; - case 0x0306: name ="Windows 8.1, Server 2012"; break; - case 0x0206: name ="Windows 8, Server 2012"; break; - case 0x0106: name ="Windows 7, Server 2008 R2"; break; - case 0x0006: name ="Windows Vista, Server 2008"; break; - case 0x0205: name ="Windows XP 64-bit, Server 2003"; break; - case 0x0105: name ="Windows XP"; break; - case 0x0005: name ="Windows 2000"; break; - default: name = "Unknown"; - } - return strprintf("%d.%d.%d (%s)", major, minor, build, name.c_str()); -} -#else -// WARNING WARNING WARNING blocking -// if in doubt, DO NOT USE. -std::string run_uname(const std::string &flags) { - char buf[1024]; - static const std::string unknown = "unknown operating system\n"; - const std::string combined = "uname -" + flags; - FILE *out = popen(combined.c_str(), "r"); - if (!out) return unknown; - if (!fgets(buf, sizeof(buf), out)) { - pclose(out); - return unknown; - } - pclose(out); - return buf; -} - -std::string uname_msr() { - return run_uname("msr"); -} -#endif - -void run_rethinkdb_serve(const base_path_t &base_path, - serve_info_t *serve_info, - const std::string &initial_password, - const file_direct_io_mode_t direct_io_mode, - const int max_concurrent_io_requests, - const optional > - &total_cache_size, - const server_id_t *our_server_id, - const server_config_versioned_t *server_config, - const cluster_semilattice_metadata_t *cluster_metadata, - directory_lock_t *data_directory_lock, - bool *const result_out) { - logNTC("Running %s...\n", RETHINKDB_VERSION_STR); -#ifdef _WIN32 - logNTC("Running on %s", windows_version_string().c_str()); -#else - logNTC("Running on %s", uname_msr().c_str()); -#endif - os_signal_cond_t sigint_cond; - - logNTC("Loading data from directory %s\n", base_path.path().c_str()); - - io_backender_t io_backender(direct_io_mode, max_concurrent_io_requests); - - perfmon_collection_t metadata_perfmon_collection; - perfmon_membership_t metadata_perfmon_membership(&get_global_perfmon_collection(), &metadata_perfmon_collection, "metadata"); - - try { - scoped_ptr_t metadata_file; - cond_t non_interruptor; - if (our_server_id != nullptr && cluster_metadata != nullptr) { - metadata_file.init(new metadata_file_t( - &io_backender, - base_path, - &metadata_perfmon_collection, - [&](metadata_file_t::write_txn_t *write_txn, signal_t *interruptor) { - write_txn->write(mdkey_server_id(), - *our_server_id, interruptor); - write_txn->write(mdkey_server_config(), - *server_config, interruptor); - write_txn->write(mdkey_cluster_semilattices(), - *cluster_metadata, interruptor); - write_txn->write(mdkey_auth_semilattices(), - auth_semilattice_metadata_t(initial_password), interruptor); - write_txn->write(mdkey_heartbeat_semilattices(), - heartbeat_semilattice_metadata_t(), interruptor); - }, - &non_interruptor)); - guarantee(!static_cast(total_cache_size), "rethinkdb porcelain should " - "have already set up total_cache_size"); - } else { - metadata_file.init(new metadata_file_t( - &io_backender, - base_path, - &metadata_perfmon_collection, - &non_interruptor)); - /* The `metadata_file_t` constructor will migrate the main metadata if it - exists, but we need to migrate the auth metadata separately */ - serializer_filepath_t auth_path(base_path, "auth_metadata"); - if (access(auth_path.permanent_path().c_str(), F_OK) == 0) { - { - metadata_file_t::write_txn_t txn(metadata_file.get(), - &non_interruptor); - logNTC("Migrating auth metadata to v2.1"); - migrate_auth_metadata_to_v2_1(&io_backender, auth_path, &txn, - &non_interruptor); - logNTC("Migrating auth metadata to v2.3"); - migrate_auth_metadata_v2_1_to_v2_3(&txn, &non_interruptor); - /* Commit the transaction here so we flush the new metadata file before - we delete the old auth file */ - txn.commit(); - } - if (remove(auth_path.permanent_path().c_str()) != 0) { - fail_due_to_user_error( - "Failed to remove legacy 'auth_metadata' configuration file at %s. " - "If this file remains, it could overwrite future changes to user " - "and password configurations. Please remove it and try again.", - auth_path.permanent_path().c_str()); - } - } - if (static_cast(total_cache_size)) { - /* Apply change to cache size */ - metadata_file_t::write_txn_t txn(metadata_file.get(), &non_interruptor); - server_config_versioned_t config = - txn.read(mdkey_server_config(), &non_interruptor); - if (config.config.cache_size_bytes != *total_cache_size) { - config.config.cache_size_bytes = *total_cache_size; - ++config.version; - txn.write(mdkey_server_config(), config, &non_interruptor); - } - txn.commit(); - } - if (!initial_password.empty()) { - /* Apply the initial password if there isn't one already. */ - metadata_file_t::write_txn_t txn(metadata_file.get(), &non_interruptor); - auth_semilattice_metadata_t auth_data = - txn.read(mdkey_auth_semilattices(), &non_interruptor); - auto admin_pair = auth_data.m_users.find(auth::username_t("admin")); - /* `admin_pair` should always exist. But in case it somehow doesn't, - we still create a new admin user so that people can recover from - corrupted user metadata. */ - if (admin_pair == auth_data.m_users.end() - || !static_cast(admin_pair->second.get_ref()) - || admin_pair->second.get_ref()->get_password().is_empty()) { - auto new_admin_pair = - auth_semilattice_metadata_t::create_initial_admin_pair( - initial_password); - /* Note that the `versioned_t` timestmap of the new admin pair might - be smaller than the existing one if the password was previously - explicitly set to empty. This is probably ok. It just means that - if we connect to another node, the configured (non-initial) password - is going to win again. This is consistent with how - `--initial-password` behaves in general. */ - auth_data.m_users[new_admin_pair.first] = new_admin_pair.second; - - txn.write(mdkey_auth_semilattices(), auth_data, &non_interruptor); - } else { - logNTC("Ignoring --initial-password option because the admin " - "password is already configured."); - } - txn.commit(); - } - } - - // Tell the directory lock that the directory is now good to go, as it will - // otherwise delete an uninitialized directory - data_directory_lock->directory_initialized(); - - serve_info->look_up_peers(); - *result_out = serve(&io_backender, - base_path, - metadata_file.get(), - *serve_info, - &sigint_cond); - - } catch (const file_in_use_exc_t &ex) { - logNTC("Directory '%s' is in use by another rethinkdb process.\n", base_path.path().c_str()); - *result_out = false; - } catch (const host_lookup_exc_t &ex) { - logERR("%s\n", ex.what()); - *result_out = false; - } -} - -void run_rethinkdb_porcelain(const base_path_t &base_path, - const name_string_t &server_name, - const std::set &server_tag_names, - const std::string &initial_password, - const file_direct_io_mode_t direct_io_mode, - const int max_concurrent_io_requests, - const optional > - &total_cache_size, - const bool new_directory, - serve_info_t *serve_info, - directory_lock_t *data_directory_lock, - bool *const result_out) { - if (!new_directory) { - run_rethinkdb_serve(base_path, serve_info, initial_password, direct_io_mode, - max_concurrent_io_requests, total_cache_size, - nullptr, nullptr, nullptr, data_directory_lock, - result_out); - } else { - logNTC("Initializing directory %s\n", base_path.path().c_str()); - - server_id_t our_server_id = server_id_t::generate_server_id(); - - cluster_semilattice_metadata_t cluster_metadata; - if (serve_info->joins.empty()) { - logINF("Creating a default database for your convenience. (This is because you ran 'rethinkdb' " - "without 'create', 'serve', or '--join', and the directory '%s' did not already exist or is empty.)\n", - base_path.path().c_str()); - - /* Create a test database. */ - database_id_t database_id = generate_uuid(); - database_semilattice_metadata_t database_metadata; - name_string_t db_name; - bool db_name_success = db_name.assign_value("test"); - guarantee(db_name_success); - database_metadata.name = - versioned_t(db_name); - cluster_metadata.databases.databases.insert(std::make_pair( - database_id, - deletable_t(database_metadata))); - } - - server_config_versioned_t server_config; - server_config.config.name = server_name; - server_config.config.tags = server_tag_names; - server_config.config.cache_size_bytes = static_cast(total_cache_size) - ? *total_cache_size - : optional(); /* default to 'auto' */ - server_config.version = 1; - - run_rethinkdb_serve(base_path, serve_info, initial_password, direct_io_mode, - max_concurrent_io_requests, - optional >(), - &our_server_id, &server_config, &cluster_metadata, - data_directory_lock, result_out); - } -} - -void run_rethinkdb_proxy( - serve_info_t *serve_info, - const std::string &initial_password, - bool *const result_out) { - - os_signal_cond_t sigint_cond; - guarantee(!serve_info->joins.empty()); - - try { - serve_info->look_up_peers(); - *result_out = serve_proxy(*serve_info, - initial_password, - &sigint_cond); - } catch (const host_lookup_exc_t &ex) { - logERR("%s\n", ex.what()); - *result_out = false; - } -} - -options::help_section_t get_server_options(std::vector *options_out) { - options::help_section_t help("Server options"); - options_out->push_back(options::option_t(options::names_t("--server-name", "-n"), - options::OPTIONAL)); - help.add("-n [ --server-name ] arg", - "the name for this server (as will appear in the metadata). If not" - " specified, one will be generated from the hostname and a random " - "alphanumeric string."); - - options_out->push_back(options::option_t(options::names_t("--server-tag", "-t"), - options::OPTIONAL_REPEAT)); - help.add("-t [ --server-tag ] arg", - "a tag for this server. Can be specified multiple times."); - - return help; -} - -options::help_section_t get_auth_options(std::vector *options_out) { - options::help_section_t help("Authentication options"); - - options_out->push_back(options::option_t(options::names_t("--initial-password"), - options::OPTIONAL)); - help.add("--initial-password {auto | password}", - "sets an initial password for the \"admin\" user on a new server. If set " - "to auto, a random password will be generated."); - - return help; -} - -options::help_section_t get_log_options(std::vector *options_out) { - options::help_section_t help("Log options"); - options_out->push_back(options::option_t(options::names_t("--log-file"), - options::OPTIONAL)); - help.add("--log-file file", "specify the file to log to, defaults to 'log_file'"); - options_out->push_back(options::option_t(options::names_t("--no-update-check"), - options::OPTIONAL_NO_PARAMETER)); - help.add("--no-update-check", "obsolete. Update checking has been removed."); - return help; -} - - -options::help_section_t get_file_options(std::vector *options_out) { - options::help_section_t help("File path options"); - options_out->push_back(options::option_t(options::names_t("--directory", "-d"), - options::OPTIONAL, - "rethinkdb_data")); - help.add("-d [ --directory ] path", "specify directory to store data and metadata"); - options_out->push_back(options::option_t(options::names_t("--io-threads"), - options::OPTIONAL, - strprintf("%d", DEFAULT_MAX_CONCURRENT_IO_REQUESTS))); - help.add("--io-threads n", - "how many simultaneous I/O operations can happen at the same time"); -#ifndef _WIN32 - // TODO WINDOWS: accept this option, but error out if it is passed - options_out->push_back(options::option_t(options::names_t("--direct-io"), - options::OPTIONAL_NO_PARAMETER)); - help.add("--direct-io", "use direct I/O for file access"); -#endif - options_out->push_back(options::option_t(options::names_t("--cache-size"), - options::OPTIONAL)); - help.add("--cache-size mb", "total cache size (in megabytes) for the process. Can " - "be 'auto'."); - return help; -} - -options::help_section_t get_config_file_options(std::vector *options_out) { - options::help_section_t help("Configuration file options"); - options_out->push_back(options::option_t(options::names_t("--config-file"), - options::OPTIONAL)); - help.add("--config-file", "take options from a configuration file"); - return help; -} - -std::vector parse_join_options(const std::map &opts, - int default_port) { - std::string source; - const std::vector join_strings = all_options(opts, "--join", &source); - std::vector joins; - for (auto it = join_strings.begin(); it != join_strings.end(); ++it) { - joins.push_back(parse_host_and_port(source, "--join", *it, default_port)); - } - return joins; -} - -name_string_t parse_server_name_option( - const std::map &opts) { - optional server_name_str = - get_optional_option(opts, "--server-name"); - if (static_cast(server_name_str)) { - name_string_t server_name; - if (!server_name.assign_value(*server_name_str)) { - throw std::runtime_error(strprintf("server-name '%s' is invalid. (%s)\n", - server_name_str->c_str(), name_string_t::valid_char_msg)); - } - return server_name; - } else { - return get_default_server_name(); - } -} - -std::set parse_server_tag_options( - const std::map &opts) { - std::set server_tag_names; - for (const std::string &tag_str : opts.at("--server-tag").values) { - name_string_t tag; - if (!tag.assign_value(tag_str)) { - throw std::runtime_error(strprintf("--server_tag %s is invalid. (%s)\n", - tag_str.c_str(), name_string_t::valid_char_msg)); - } - /* We silently accept tags that appear multiple times. */ - server_tag_names.insert(tag); - } - server_tag_names.insert(name_string_t::guarantee_valid("default")); - return server_tag_names; -} - -std::string parse_initial_password_option( - const std::map &opts) { - optional initial_password_str = - get_optional_option(opts, "--initial-password"); - if (static_cast(initial_password_str)) { - if (*initial_password_str == "auto") { - std::array random_data = crypto::random_bytes<16>(); - const uuid_u base_uuid = str_to_uuid("4a3a5542-6a45-4668-a09a-d775e63a52cd"); - std::string random_pw = uuid_to_str(uuid_u::from_hash( - base_uuid, - std::string(reinterpret_cast(random_data.data()), 16))); - printf("Generated random admin password: %s\n", random_pw.c_str()); - return random_pw; - } - return *initial_password_str; - } else { - // The default is an empty admin password - return ""; - } -} - -std::string get_reql_http_proxy_option(const std::map &opts) { - std::string source; - optional proxy = get_optional_option(opts, "--reql-http-proxy", &source); - if (!proxy.has_value()) { - return std::string(); - } - - // We verify the correct format here, as we won't be configuring libcurl until later, - // in the extprocs. At the moment, we do not support specifying IPv6 addresses. - // - // protocol = proxy protocol recognized by libcurl: (http, socks4, socks4a, socks5, socks5h) - // host = hostname or ip address - // port = integer in range [0-65535] - // [protocol://]host[:port] - // - // The chunks in the regex used to parse and verify the format are: - // protocol - (?:([A-z][A-z0-9+-.]*)(?:://))? - captures the protocol, adhering to - // RFC 3986, discarding the '://' from the end - // host - ([A-z0-9.-]+) - captures the hostname or ip address, consisting of letters, - // numbers, dashes, and dots - // port - (?::(\d+))? - captures the numeric port, discarding the preceding ':' - RE2 re2_parser("(?:([A-z][A-z0-9+-.]*)(?:://))?([A-z0-9_.-]+)(?::(\\d+))?", - RE2::Quiet); - std::string protocol, host, port_str; - if (!RE2::FullMatch(proxy.get(), re2_parser, &protocol, &host, &port_str)) { - throw std::runtime_error(strprintf("--reql-http-proxy format unrecognized, " - "expected [protocol://]host[:port]: %s", - proxy.get().c_str())); - } - - if (!protocol.empty() && - protocol != "http" && - protocol != "socks4" && - protocol != "socks4a" && - protocol != "socks5" && - protocol != "socks5h") { - throw std::runtime_error(strprintf("--reql-http-proxy protocol unrecognized (%s), " - "must be one of http, socks4, socks4a, socks5, " - "and socks5h", protocol.c_str())); - } - - if (!port_str.empty()) { - int port = atoi(port_str.c_str()); - if (port_str.length() > 5 || port <= 0 || port > MAX_PORT) { - throw std::runtime_error(strprintf("--reql-http-proxy port (%s) is not in " - "the valid range (0-65535)", - port_str.c_str())); - } - } - return proxy.get(); -} - -options::help_section_t get_web_options(std::vector *options_out) { - options::help_section_t help("Web options"); - options_out->push_back(options::option_t(options::names_t("--web-static-directory"), - options::OPTIONAL)); - help.add("--web-static-directory directory", "the directory containing web resources for the http interface"); - options_out->push_back(options::option_t(options::names_t("--http-port"), - options::OPTIONAL, - strprintf("%d", port_defaults::http_port))); - help.add("--http-port port", "port for web administration console"); - options_out->push_back(options::option_t(options::names_t("--no-http-admin"), - options::OPTIONAL_NO_PARAMETER)); - help.add("--no-http-admin", "disable web administration console"); - return help; -} - -options::help_section_t get_network_options(const bool join_required, std::vector *options_out) { - options::help_section_t help("Network options"); - options_out->push_back(options::option_t(options::names_t("--bind"), - options::OPTIONAL_REPEAT)); - options_out->push_back(options::option_t(options::names_t("--bind-cluster"), - options::OPTIONAL_REPEAT)); - options_out->push_back(options::option_t(options::names_t("--bind-driver"), - options::OPTIONAL_REPEAT)); - options_out->push_back(options::option_t(options::names_t("--bind-http"), - options::OPTIONAL_REPEAT)); - help.add("--bind {all | addr}", "add the address of a local interface to listen on when accepting connections, loopback addresses are enabled by default. Can be overridden by the following three options."); - help.add("--bind-cluster {all | addr}", "override the behavior specified by --bind for cluster connections."); - help.add("--bind-driver {all | addr}", "override the behavior specified by --bind for client driver connections."); - help.add("--bind-http {all | addr}", "override the behavior specified by --bind for web console connections."); - options_out->push_back(options::option_t(options::names_t("--no-default-bind"), - options::OPTIONAL_NO_PARAMETER)); - help.add("--no-default-bind", "disable automatic listening on loopback addresses"); - - options_out->push_back(options::option_t(options::names_t("--cluster-port"), - options::OPTIONAL, - strprintf("%d", port_defaults::peer_port))); - help.add("--cluster-port port", "port for receiving connections from other servers"); - - options_out->push_back(options::option_t(options::names_t("--client-port"), - options::OPTIONAL, - strprintf("%d", port_defaults::client_port))); -#ifndef NDEBUG - help.add("--client-port port", "port to use when connecting to other servers (for development)"); -#endif // NDEBUG - - options_out->push_back(options::option_t(options::names_t("--driver-port"), - options::OPTIONAL, - strprintf("%d", port_defaults::reql_port))); - help.add("--driver-port port", "port for rethinkdb protocol client drivers"); - - options_out->push_back(options::option_t(options::names_t("--port-offset", "-o"), - options::OPTIONAL, - strprintf("%d", port_defaults::port_offset))); - help.add("-o [ --port-offset ] offset", "all ports used locally will have this value added"); - - options_out->push_back(options::option_t(options::names_t("--join", "-j"), - join_required ? options::MANDATORY_REPEAT : options::OPTIONAL_REPEAT)); - help.add("-j [ --join ] host[:port]", "host and port of a rethinkdb server to connect to"); - - options_out->push_back(options::option_t(options::names_t("--reql-http-proxy"), - options::OPTIONAL)); - help.add("--reql-http-proxy [protocol://]host[:port]", "HTTP proxy to use for performing `r.http(...)` queries, default port is 1080"); - - options_out->push_back(options::option_t(options::names_t("--canonical-address"), - options::OPTIONAL_REPEAT)); - help.add("--canonical-address host[:port]", "address that other rethinkdb instances will use to connect to us, can be specified multiple times"); - - options_out->push_back(options::option_t(options::names_t("--join-delay"), - options::OPTIONAL)); - help.add("--join-delay seconds", "hold the TCP connection open for these many " - "seconds before joining with another server"); - - options_out->push_back(options::option_t(options::names_t("--cluster-reconnect-timeout"), - options::OPTIONAL, - strprintf("%d", cluster_defaults::reconnect_timeout))); - help.add("--cluster-reconnect-timeout seconds", "maximum number of seconds to " - "attempt reconnecting to a server " - "before giving up, the default is " - "24 hours"); - - return help; -} - -options::help_section_t get_cpu_options(std::vector *options_out) { - options::help_section_t help("CPU options"); - options_out->push_back(options::option_t(options::names_t("--cores", "-c"), - options::OPTIONAL, - strprintf("%d", get_cpu_count()))); - help.add("-c [ --cores ] n", "the number of cores to use"); - return help; -} - -MUST_USE bool parse_cores_option(const std::map &opts, - int *num_workers_out) { - int num_workers = get_single_int(opts, "--cores"); - if (num_workers <= 0 || num_workers > MAX_THREADS) { - fprintf(stderr, "ERROR: number specified for cores to use must be between 1 and %d\n", MAX_THREADS); - return false; - } - *num_workers_out = num_workers; - return true; -} - -options::help_section_t get_service_options(std::vector *options_out) { - options::help_section_t help("Service options"); - options_out->push_back(options::option_t(options::names_t("--pid-file"), - options::OPTIONAL)); - help.add("--pid-file path", "a file in which to write the process id when the process is running"); - options_out->push_back(options::option_t(options::names_t("--daemon"), - options::OPTIONAL_NO_PARAMETER)); - help.add("--daemon", "daemonize this rethinkdb process"); - return help; -} - -options::help_section_t get_setuser_options(std::vector *options_out) { - options::help_section_t help("Set User/Group options"); -#ifndef _WIN32 - options_out->push_back(options::option_t(options::names_t("--runuser"), - options::OPTIONAL)); - help.add("--runuser user", "run as the specified user"); - options_out->push_back(options::option_t(options::names_t("--rungroup"), - options::OPTIONAL)); - help.add("--rungroup group", "run with the specified group"); -#endif - return help; -} - -#ifdef ENABLE_TLS -options::help_section_t get_tls_options(std::vector *options_out) { - options::help_section_t help("TLS options"); - - // Web TLS options. - options_out->push_back(options::option_t(options::names_t("--http-tls-key"), - options::OPTIONAL)); - options_out->push_back(options::option_t(options::names_t("--http-tls-cert"), - options::OPTIONAL)); - help.add( - "--http-tls-key key_filename", - "private key to use for web administration console TLS"); - help.add( - "--http-tls-cert cert_filename", - "certificate to use for web administration console TLS"); - - // Client Driver TLS options. - options_out->push_back(options::option_t(options::names_t("--driver-tls-key"), - options::OPTIONAL)); - options_out->push_back(options::option_t(options::names_t("--driver-tls-cert"), - options::OPTIONAL)); - options_out->push_back(options::option_t(options::names_t("--driver-tls-ca"), - options::OPTIONAL)); - help.add( - "--driver-tls-key key_filename", - "private key to use for client driver connection TLS"); - help.add( - "--driver-tls-cert cert_filename", - "certificate to use for client driver connection TLS"); - help.add( - "--driver-tls-ca ca_filename", - "CA certificate bundle used to verify client certificates; TLS client authentication disabled if omitted"); - - // Client Driver TLS options. - options_out->push_back(options::option_t(options::names_t("--cluster-tls-key"), - options::OPTIONAL)); - options_out->push_back(options::option_t(options::names_t("--cluster-tls-cert"), - options::OPTIONAL)); - options_out->push_back(options::option_t(options::names_t("--cluster-tls-ca"), - options::OPTIONAL)); - help.add( - "--cluster-tls-key key_filename", - "private key to use for intra-cluster connection TLS"); - help.add( - "--cluster-tls-cert cert_filename", - "certificate to use for intra-cluster connection TLS"); - help.add( - "--cluster-tls-ca ca_filename", - "CA certificate bundle used to verify cluster peer certificates"); - - // Generic TLS options, for customizing the supported protocols and cipher suites. - options_out->push_back(options::option_t(options::names_t("--tls-min-protocol"), - options::OPTIONAL)); - options_out->push_back(options::option_t(options::names_t("--tls-ciphers"), - options::OPTIONAL)); - options_out->push_back(options::option_t(options::names_t("--tls-ecdh-curve"), - options::OPTIONAL)); - options_out->push_back(options::option_t(options::names_t("--tls-dhparams"), - options::OPTIONAL)); - help.add( - "--tls-min-protocol protocol", - "the minimum TLS protocol version that the server accepts; options are " - "'TLSv1', 'TLSv1.1', 'TLSv1.2'; default is 'TLSv1.2'"); - help.add( - "--tls-ciphers cipher_list", - "specify a list of TLS ciphers to use; default is 'EECDH+AESGCM'"); - help.add( - "--tls-ecdh-curve curve_name", - "specify a named elliptic curve to use for ECDHE; default is 'prime256v1'"); - help.add( - "--tls-dhparams dhparams_filename", - "provide parameters for DHE key agreement; REQUIRED if using DHE cipher suites; " - "at least 2048-bit recommended"); - - return help; -} -#endif - -options::help_section_t get_help_options(std::vector *options_out) { - options::help_section_t help("Help options"); - options_out->push_back(options::option_t(options::names_t("--help", "-h"), - options::OPTIONAL_NO_PARAMETER)); - options_out->push_back(options::option_t(options::names_t("--version", "-v"), - options::OPTIONAL_NO_PARAMETER)); - help.add("-h [ --help ]", "print this help"); - help.add("-v [ --version ]", "print the version number of rethinkdb"); - return help; -} - -void get_rethinkdb_create_options(std::vector *help_out, - std::vector *options_out) { - help_out->push_back(get_file_options(options_out)); - help_out->push_back(get_server_options(options_out)); - help_out->push_back(get_auth_options(options_out)); - help_out->push_back(get_setuser_options(options_out)); - help_out->push_back(get_help_options(options_out)); - help_out->push_back(get_log_options(options_out)); - help_out->push_back(get_config_file_options(options_out)); -} - -void get_rethinkdb_serve_options(std::vector *help_out, - std::vector *options_out) { - help_out->push_back(get_file_options(options_out)); - help_out->push_back(get_network_options(false, options_out)); -#ifdef ENABLE_TLS - help_out->push_back(get_tls_options(options_out)); -#endif - help_out->push_back(get_auth_options(options_out)); - help_out->push_back(get_web_options(options_out)); - help_out->push_back(get_cpu_options(options_out)); - help_out->push_back(get_service_options(options_out)); - help_out->push_back(get_setuser_options(options_out)); - help_out->push_back(get_help_options(options_out)); - help_out->push_back(get_log_options(options_out)); - help_out->push_back(get_config_file_options(options_out)); -} - -void get_rethinkdb_proxy_options(std::vector *help_out, - std::vector *options_out) { - help_out->push_back(get_network_options(true, options_out)); -#ifdef ENABLE_TLS - help_out->push_back(get_tls_options(options_out)); -#endif - help_out->push_back(get_auth_options(options_out)); - help_out->push_back(get_web_options(options_out)); - help_out->push_back(get_service_options(options_out)); - help_out->push_back(get_setuser_options(options_out)); - help_out->push_back(get_help_options(options_out)); - help_out->push_back(get_log_options(options_out)); - help_out->push_back(get_config_file_options(options_out)); -} - -void get_rethinkdb_porcelain_options(std::vector *help_out, - std::vector *options_out) { - help_out->push_back(get_file_options(options_out)); - help_out->push_back(get_server_options(options_out)); - help_out->push_back(get_network_options(false, options_out)); -#ifdef ENABLE_TLS - help_out->push_back(get_tls_options(options_out)); -#endif - help_out->push_back(get_auth_options(options_out)); - help_out->push_back(get_web_options(options_out)); - help_out->push_back(get_cpu_options(options_out)); - help_out->push_back(get_service_options(options_out)); - help_out->push_back(get_setuser_options(options_out)); - help_out->push_back(get_help_options(options_out)); - help_out->push_back(get_log_options(options_out)); - help_out->push_back(get_config_file_options(options_out)); -} - -std::map parse_config_file_flat(const std::string &config_filepath, - const std::vector &options) { - std::string file; - if (!blocking_read_file(config_filepath.c_str(), &file)) { - throw std::runtime_error(strprintf("Trouble reading config file '%s'", config_filepath.c_str())); - } - - std::vector options_superset; - std::vector helps_superset; - - // There will be some duplicates in here, but it shouldn't be a problem - get_rethinkdb_create_options(&helps_superset, &options_superset); - get_rethinkdb_serve_options(&helps_superset, &options_superset); - get_rethinkdb_proxy_options(&helps_superset, &options_superset); - get_rethinkdb_porcelain_options(&helps_superset, &options_superset); - - return options::parse_config_file(file, config_filepath, - options, options_superset); -} - -std::map parse_commands_deep(int argc, char **argv, - std::vector options) { - std::map opts = options::parse_command_line(argc, argv, options); - const optional config_file_name = get_optional_option(opts, "--config-file"); - if (config_file_name) { - opts = options::merge(opts, parse_config_file_flat(*config_file_name, options)); - } - opts = options::merge(opts, default_values_map(options)); - return opts; -} - -void output_sourced_error(const options::option_error_t &ex) { - fprintf(stderr, "Error in %s: %s\n", ex.source().c_str(), ex.what()); -} - -void output_named_error(const options::named_error_t &ex, const std::vector &help) { - output_sourced_error(ex); - - for (auto section = help.begin(); section != help.end(); ++section) { - for (auto line = section->help_lines.begin(); line != section->help_lines.end(); ++line) { - if (line->syntax_description.find(ex.option_name()) != std::string::npos) { - std::vector one_help_line; - one_help_line.push_back(*line); - std::vector one_help_section; - one_help_section.push_back(options::help_section_t("Usage", one_help_line)); - fprintf(stderr, "%s", - options::format_help(one_help_section).c_str()); - break; - } - } - } -} - -MUST_USE bool parse_io_threads_option(const std::map &opts, - int *max_concurrent_io_requests_out) { - int max_concurrent_io_requests = get_single_int(opts, "--io-threads"); - if (max_concurrent_io_requests <= 0 - || max_concurrent_io_requests > MAXIMUM_MAX_CONCURRENT_IO_REQUESTS) { - fprintf(stderr, "ERROR: io-threads must be between 1 and %d\n", - MAXIMUM_MAX_CONCURRENT_IO_REQUESTS); - return false; - } - *max_concurrent_io_requests_out = max_concurrent_io_requests; - return true; -} - -file_direct_io_mode_t parse_direct_io_mode_option(const std::map &opts) { - return exists_option(opts, "--direct-io") ? - file_direct_io_mode_t::direct_desired : - file_direct_io_mode_t::buffered_desired; -} - -int main_rethinkdb_create(int argc, char *argv[]) { - std::vector options; - std::vector help; - get_rethinkdb_create_options(&help, &options); - - try { - std::map opts = parse_commands_deep(argc - 2, argv + 2, options); - - if (handle_help_or_version_option(opts, &help_rethinkdb_create)) { - return EXIT_SUCCESS; - } - - options::verify_option_counts(options, opts); - - base_path_t base_path(get_single_option(opts, "--directory")); - - name_string_t server_name = parse_server_name_option(opts); - std::set server_tag_names = parse_server_tag_options(opts); - std::string initial_password = parse_initial_password_option(opts); - optional total_cache_size; - if (optional > x = - parse_total_cache_size_option(opts)) { - total_cache_size = *x; - } - - int max_concurrent_io_requests; - if (!parse_io_threads_option(opts, &max_concurrent_io_requests)) { - return EXIT_FAILURE; - } - - const int num_workers = get_cpu_count(); - - bool is_new_directory = false; - directory_lock_t data_directory_lock(base_path, true, &is_new_directory); - - if (!is_new_directory) { - fprintf(stderr, "The path '%s' already exists. Delete it and try again.\n", base_path.path().c_str()); - return EXIT_FAILURE; - } - -#ifndef _WIN32 - get_and_set_user_group_and_directory(opts, &data_directory_lock); -#endif - - initialize_logfile(opts, base_path); - - recreate_temporary_directory(base_path); - - const file_direct_io_mode_t direct_io_mode = parse_direct_io_mode_option(opts); - - bool result; - run_in_thread_pool(std::bind(&run_rethinkdb_create, - base_path, - server_name, - server_tag_names, - initial_password, - total_cache_size, - direct_io_mode, - max_concurrent_io_requests, - &result), - num_workers); - - if (result) { - // Tell the directory lock that the directory is now good to go, as it will - // otherwise delete an uninitialized directory - data_directory_lock.directory_initialized(); - return EXIT_SUCCESS; - } - } catch (const options::named_error_t &ex) { - output_named_error(ex, help); - fprintf(stderr, "Run 'rethinkdb help create' for help on the command\n"); - } catch (const options::option_error_t &ex) { - output_sourced_error(ex); - fprintf(stderr, "Run 'rethinkdb help create' for help on the command\n"); - } catch (const std::exception &ex) { - fprintf(stderr, "%s\n", ex.what()); - } - return EXIT_FAILURE; -} - -bool maybe_daemonize(const std::map &opts) { - if (exists_option(opts, "--daemon")) { -#ifdef _WIN32 - // TODO WINDOWS - fail_due_to_user_error("--daemon not implemented on windows"); -#else - pid_t pid = fork(); - if (pid < 0) { - throw std::runtime_error(strprintf("Failed to fork daemon: %s\n", errno_string(get_errno()).c_str()).c_str()); - } - - if (pid > 0) { - return false; - } - - umask(0); - - pid_t sid = setsid(); - if (sid == 0) { - throw std::runtime_error(strprintf("Failed to create daemon session: %s\n", errno_string(get_errno()).c_str()).c_str()); - } - - if (chdir("/") < 0) { - throw std::runtime_error(strprintf("Failed to change directory: %s\n", errno_string(get_errno()).c_str()).c_str()); - } - - if (freopen("/dev/null", "r", stdin) == nullptr) { - throw std::runtime_error(strprintf("Failed to redirect stdin for daemon: %s\n", errno_string(get_errno()).c_str()).c_str()); - } - if (freopen("/dev/null", "w", stdout) == nullptr) { - throw std::runtime_error(strprintf("Failed to redirect stdin for daemon: %s\n", errno_string(get_errno()).c_str()).c_str()); - } - if (freopen("/dev/null", "w", stderr) == nullptr) { - throw std::runtime_error(strprintf("Failed to redirect stderr for daemon: %s\n", errno_string(get_errno()).c_str()).c_str()); - } -#endif - } - return true; -} - -int main_rethinkdb_serve(int argc, char *argv[]) { - std::vector options; - std::vector help; - get_rethinkdb_serve_options(&help, &options); - - try { - std::map opts = parse_commands_deep(argc - 2, argv + 2, options); - - if (handle_help_or_version_option(opts, &help_rethinkdb_serve)) { - return EXIT_SUCCESS; - } - - options::verify_option_counts(options, opts); - -#ifndef _WIN32 - get_and_set_user_group(opts); -#endif - - base_path_t base_path(get_single_option(opts, "--directory")); - - std::string initial_password = parse_initial_password_option(opts); - - std::vector joins = parse_join_options(opts, port_defaults::peer_port); - - service_address_ports_t address_ports = get_service_address_ports(opts); - - std::string web_path = get_web_path(opts); - - int num_workers; - if (!parse_cores_option(opts, &num_workers)) { - return EXIT_FAILURE; - } - - int max_concurrent_io_requests; - if (!parse_io_threads_option(opts, &max_concurrent_io_requests)) { - return EXIT_FAILURE; - } - - optional > total_cache_size = - parse_total_cache_size_option(opts); - - optional join_delay_secs = parse_join_delay_secs_option(opts); - optional node_reconnect_timeout_secs = - parse_node_reconnect_timeout_secs_option(opts); - - // Open and lock the directory, but do not create it - bool is_new_directory = false; - directory_lock_t data_directory_lock(base_path, false, &is_new_directory); - guarantee(!is_new_directory); - - base_path.make_absolute(); - initialize_logfile(opts, base_path); - - recreate_temporary_directory(base_path); - - if (check_pid_file(opts) != EXIT_SUCCESS) { - return EXIT_FAILURE; - } - - if (!maybe_daemonize(opts)) { - // This is the parent process of the daemon, just exit - return EXIT_SUCCESS; - } - - if (write_pid_file(opts) != EXIT_SUCCESS) { - return EXIT_FAILURE; - } - - extproc_spawner_t extproc_spawner; - - tls_configs_t tls_configs; -#ifdef ENABLE_TLS - if (!configure_tls(opts, &tls_configs)) { - return EXIT_FAILURE; - } -#endif - - serve_info_t serve_info(std::move(joins), - get_reql_http_proxy_option(opts), - std::move(web_path), - address_ports, - get_optional_option(opts, "--config-file"), - std::vector(argv, argv + argc), - join_delay_secs.value_or(0), - node_reconnect_timeout_secs.value_or(cluster_defaults::reconnect_timeout), - tls_configs); - - const file_direct_io_mode_t direct_io_mode = parse_direct_io_mode_option(opts); - - bool result; - run_in_thread_pool(std::bind(&run_rethinkdb_serve, - base_path, - &serve_info, - initial_password, - direct_io_mode, - max_concurrent_io_requests, - total_cache_size, - static_cast(nullptr), - static_cast(nullptr), - static_cast(nullptr), - &data_directory_lock, - &result), - num_workers); - return result ? EXIT_SUCCESS : EXIT_FAILURE; - } catch (const options::named_error_t &ex) { - output_named_error(ex, help); - fprintf(stderr, "Run 'rethinkdb help serve' for help on the command\n"); - } catch (const options::option_error_t &ex) { - output_sourced_error(ex); - fprintf(stderr, "Run 'rethinkdb help serve' for help on the command\n"); - } catch (const std::exception& ex) { - fprintf(stderr, "%s\n", ex.what()); - } - return EXIT_FAILURE; -} - -int main_rethinkdb_proxy(int argc, char *argv[]) { - std::vector options; - std::vector help; - get_rethinkdb_proxy_options(&help, &options); - - try { - std::map opts = parse_commands_deep(argc - 2, argv + 2, options); - - if (handle_help_or_version_option(opts, &help_rethinkdb_proxy)) { - return EXIT_SUCCESS; - } - - options::verify_option_counts(options, opts); - - std::vector joins = parse_join_options(opts, port_defaults::peer_port); - - service_address_ports_t address_ports = get_service_address_ports(opts); - - std::string initial_password = parse_initial_password_option(opts); - - if (joins.empty()) { - fprintf(stderr, "No --join option(s) given. A proxy needs to connect to something!\n" - "Run 'rethinkdb help proxy' for more information.\n"); - return EXIT_FAILURE; - } - - optional join_delay_secs = parse_join_delay_secs_option(opts); - optional node_reconnect_timeout_secs = - parse_node_reconnect_timeout_secs_option(opts); - -#ifndef _WIN32 - get_and_set_user_group(opts); -#endif - - // Default to putting the log file in the current working directory - base_path_t base_path("."); - initialize_logfile(opts, base_path); - - std::string web_path = get_web_path(opts); - const int num_workers = get_cpu_count(); - - if (check_pid_file(opts) != EXIT_SUCCESS) { - return EXIT_FAILURE; - } - - if (!maybe_daemonize(opts)) { - // This is the parent process of the daemon, just exit - return EXIT_SUCCESS; - } - - if (write_pid_file(opts) != EXIT_SUCCESS) { - return EXIT_FAILURE; - } - - extproc_spawner_t extproc_spawner; - - tls_configs_t tls_configs; -#ifdef ENABLE_TLS - if (!configure_tls(opts, &tls_configs)) { - return EXIT_FAILURE; - } -#endif - - serve_info_t serve_info(std::move(joins), - get_reql_http_proxy_option(opts), - std::move(web_path), - address_ports, - get_optional_option(opts, "--config-file"), - std::vector(argv, argv + argc), - join_delay_secs.value_or(0), - node_reconnect_timeout_secs.value_or(cluster_defaults::reconnect_timeout), - tls_configs); - - bool result; - run_in_thread_pool( - std::bind(&run_rethinkdb_proxy, &serve_info, initial_password, &result), - num_workers); - return result ? EXIT_SUCCESS : EXIT_FAILURE; - } catch (const options::named_error_t &ex) { - output_named_error(ex, help); - fprintf(stderr, "Run 'rethinkdb help proxy' for help on the command\n"); - } catch (const options::option_error_t &ex) { - output_sourced_error(ex); - fprintf(stderr, "Run 'rethinkdb help proxy' for help on the command\n"); - } catch (const std::exception& ex) { - fprintf(stderr, "%s\n", ex.what()); - } - return EXIT_FAILURE; -} - -MUST_USE bool split_db_table(const std::string &db_table, std::string *db_name_out, std::string *table_name_out) { - size_t first_pos = db_table.find_first_of('.'); - if (first_pos == std::string::npos || db_table.find_last_of('.') != first_pos) { - return false; - } - - if (first_pos == 0 || first_pos + 1 == db_table.size()) { - return false; - } - - db_name_out->assign(db_table.data(), first_pos); - table_name_out->assign(db_table.data() + first_pos + 1, db_table.data() + db_table.size()); - guarantee(db_name_out->size() > 0); - guarantee(table_name_out->size() > 0); - return true; -} - -void run_backup_script(const std::string& script_name, char * const arguments[]) { - int res = execvp(script_name.c_str(), arguments); - if (res == -1) { - - fprintf(stderr, "Error when launching '%s': %s\n", - script_name.c_str(), errno_string(get_errno()).c_str()); - fprintf(stderr, "The %s command depends on the RethinkDB Python driver, which must be installed.\n", - script_name.c_str()); - fprintf(stderr, "If the Python driver is already installed, make sure that the PATH environment variable\n" - "includes the location of the backup scripts, and that the current user has permission to\n" - "access and run the scripts.\n" - "Instructions for installing the RethinkDB Python driver are available here:\n" - "http://www.rethinkdb.com/docs/install-drivers/python/\n"); - } -} - -int main_rethinkdb_export(int, char *argv[]) { - run_backup_script(RETHINKDB_EXPORT_SCRIPT, argv + 1); - return EXIT_FAILURE; -} - -int main_rethinkdb_import(int, char *argv[]) { - run_backup_script(RETHINKDB_IMPORT_SCRIPT, argv + 1); - return EXIT_FAILURE; -} - -int main_rethinkdb_dump(int, char *argv[]) { - run_backup_script(RETHINKDB_DUMP_SCRIPT, argv + 1); - return EXIT_FAILURE; -} - -int main_rethinkdb_restore(int, char *argv[]) { - run_backup_script(RETHINKDB_RESTORE_SCRIPT, argv + 1); - return EXIT_FAILURE; -} - -int main_rethinkdb_index_rebuild(int, char *argv[]) { - run_backup_script(RETHINKDB_INDEX_REBUILD_SCRIPT, argv + 1); - return EXIT_FAILURE; -} - -int main_rethinkdb_repl(int, char *argv[]) { - run_backup_script(RETHINKDB_REPL_SCRIPT, argv + 1); - return EXIT_FAILURE; -} - -int main_rethinkdb_porcelain(int argc, char *argv[]) { - std::vector options; - std::vector help; - get_rethinkdb_porcelain_options(&help, &options); - - try { - std::map opts = parse_commands_deep(argc - 1, argv + 1, options); - - if (handle_help_or_version_option(opts, help_rethinkdb_porcelain)) { - return EXIT_SUCCESS; - } - - options::verify_option_counts(options, opts); - - base_path_t base_path(get_single_option(opts, "--directory")); - - std::set server_tag_names = parse_server_tag_options(opts); - - std::string initial_password = parse_initial_password_option(opts); - - std::vector joins = parse_join_options(opts, port_defaults::peer_port); - - const service_address_ports_t address_ports = get_service_address_ports(opts); - - std::string web_path = get_web_path(opts); - - int num_workers; - if (!parse_cores_option(opts, &num_workers)) { - return EXIT_FAILURE; - } - - int max_concurrent_io_requests; - if (!parse_io_threads_option(opts, &max_concurrent_io_requests)) { - return EXIT_FAILURE; - } - - optional join_delay_secs = parse_join_delay_secs_option(opts); - optional node_reconnect_timeout_secs = - parse_node_reconnect_timeout_secs_option(opts); - - // Attempt to create the directory early so that the log file can use it. - // If we create the file, it will be cleaned up unless directory_initialized() - // is called on it. This will be done after the metadata files have been created. - bool is_new_directory = false; - directory_lock_t data_directory_lock(base_path, true, &is_new_directory); - -#ifndef _WIN32 - if (is_new_directory) { - get_and_set_user_group_and_directory(opts, &data_directory_lock); - } else { - get_and_set_user_group(opts); - } -#endif - - base_path.make_absolute(); - initialize_logfile(opts, base_path); - - recreate_temporary_directory(base_path); - - name_string_t server_name; - if (is_new_directory) { - server_name = parse_server_name_option(opts); - } else { - if (static_cast(get_optional_option(opts, "--server-name"))) { - fprintf(stderr, "WARNING: ignoring --server-name because this server " - "already has a name.\n"); - } - } - - optional > total_cache_size = - parse_total_cache_size_option(opts); - - if (check_pid_file(opts) != EXIT_SUCCESS) { - return EXIT_FAILURE; - } - - if (!maybe_daemonize(opts)) { - // This is the parent process of the daemon, just exit - // Make sure the parent process doesn't remove the directory, - // the child process will handle it from here - data_directory_lock.directory_initialized(); - return EXIT_SUCCESS; - } - - if (write_pid_file(opts) != EXIT_SUCCESS) { - return EXIT_FAILURE; - } - - extproc_spawner_t extproc_spawner; - - tls_configs_t tls_configs; -#ifdef ENABLE_TLS - if (!configure_tls(opts, &tls_configs)) { - return EXIT_FAILURE; - } -#endif - - serve_info_t serve_info(std::move(joins), - get_reql_http_proxy_option(opts), - std::move(web_path), - address_ports, - get_optional_option(opts, "--config-file"), - std::vector(argv, argv + argc), - join_delay_secs.value_or(0), - node_reconnect_timeout_secs.value_or(cluster_defaults::reconnect_timeout), - tls_configs); - - const file_direct_io_mode_t direct_io_mode = parse_direct_io_mode_option(opts); - - bool result; - run_in_thread_pool(std::bind(&run_rethinkdb_porcelain, - base_path, - server_name, - server_tag_names, - initial_password, - direct_io_mode, - max_concurrent_io_requests, - total_cache_size, - is_new_directory, - &serve_info, - &data_directory_lock, - &result), - num_workers); - - return result ? EXIT_SUCCESS : EXIT_FAILURE; - } catch (const options::named_error_t &ex) { - output_named_error(ex, help); - fprintf(stderr, "Run 'rethinkdb help' for help on the command\n"); - } catch (const options::option_error_t &ex) { - output_sourced_error(ex); - fprintf(stderr, "Run 'rethinkdb help' for help on the command\n"); - } catch (const std::exception& ex) { - fprintf(stderr, "%s\n", ex.what()); - } - - return EXIT_FAILURE; -} - -void help_rethinkdb_porcelain() { - std::vector help_sections; - { - std::vector options; - get_rethinkdb_porcelain_options(&help_sections, &options); - } - - printf("Running 'rethinkdb' will create a new data directory or use an existing one,\n"); - printf(" and serve as a RethinkDB server.\n"); - printf("%s", format_help(help_sections).c_str()); - printf("\n"); - printf("There are a number of subcommands for more specific tasks:\n"); - printf(" 'rethinkdb create': prepare files on disk for a new server instance\n"); - printf(" 'rethinkdb serve': use an existing data directory to host data and serve queries\n"); - printf(" 'rethinkdb proxy': serve queries from an existing cluster but don't host data\n"); - printf(" 'rethinkdb export': export data from an existing cluster into a file or directory\n"); - printf(" 'rethinkdb import': import data from from a file or directory into an existing cluster\n"); - printf(" 'rethinkdb dump': export and compress data from an existing cluster\n"); - printf(" 'rethinkdb restore': import compressed data into an existing cluster\n"); - printf(" 'rethinkdb index-rebuild': rebuild outdated secondary indexes\n"); - printf(" 'rethinkdb repl': start a Python REPL with the RethinkDB driver\n"); -#ifdef _WIN32 - printf(" 'rethinkdb install-service': install RethinkDB as a Windows service\n"); - printf(" 'rethinkdb remove-service': remove a previously installed Windows service\n"); -#endif - printf("\n"); - printf("For more information, run 'rethinkdb help [subcommand]'.\n"); -} - -void help_rethinkdb_create() { - std::vector help_sections; - { - std::vector options; - get_rethinkdb_create_options(&help_sections, &options); - } - - printf("'rethinkdb create' is used to prepare a directory to act" - " as the storage location for a RethinkDB server.\n"); - printf("%s", format_help(help_sections).c_str()); -} - -void help_rethinkdb_serve() { - std::vector help_sections; - { - std::vector options; - get_rethinkdb_serve_options(&help_sections, &options); - } - - printf("'rethinkdb serve' is the actual process for a RethinkDB server.\n"); - printf("%s", format_help(help_sections).c_str()); -} - -void help_rethinkdb_proxy() { - std::vector help_sections; - { - std::vector options; - get_rethinkdb_proxy_options(&help_sections, &options); - } - - printf("'rethinkdb proxy' serves as a proxy to an existing RethinkDB cluster.\n"); - printf("%s", format_help(help_sections).c_str()); -} - -void help_rethinkdb_export() { - char help_arg[] = "--help"; - char dummy_arg[] = RETHINKDB_EXPORT_SCRIPT; - char* args[3] = { dummy_arg, help_arg, nullptr }; - run_backup_script(RETHINKDB_EXPORT_SCRIPT, args); -} - -void help_rethinkdb_import() { - char help_arg[] = "--help"; - char dummy_arg[] = RETHINKDB_IMPORT_SCRIPT; - char* args[3] = { dummy_arg, help_arg, nullptr }; - run_backup_script(RETHINKDB_IMPORT_SCRIPT, args); -} - -void help_rethinkdb_dump() { - char help_arg[] = "--help"; - char dummy_arg[] = RETHINKDB_DUMP_SCRIPT; - char* args[3] = { dummy_arg, help_arg, nullptr }; - run_backup_script(RETHINKDB_DUMP_SCRIPT, args); -} - -void help_rethinkdb_restore() { - char help_arg[] = "--help"; - char dummy_arg[] = RETHINKDB_RESTORE_SCRIPT; - char* args[3] = { dummy_arg, help_arg, nullptr }; - run_backup_script(RETHINKDB_RESTORE_SCRIPT, args); -} - -void help_rethinkdb_index_rebuild() { - char help_arg[] = "--help"; - char dummy_arg[] = RETHINKDB_INDEX_REBUILD_SCRIPT; - char* args[3] = { dummy_arg, help_arg, nullptr }; - run_backup_script(RETHINKDB_INDEX_REBUILD_SCRIPT, args); -} - -void help_rethinkdb_repl() { - char help_arg[] = "--help"; - char dummy_arg[] = RETHINKDB_REPL_SCRIPT; - char* args[3] = { dummy_arg, help_arg, nullptr }; - run_backup_script(RETHINKDB_REPL_SCRIPT, args); -} - -#ifdef _WIN32 - -int global_windows_service_argc; -scoped_array_t global_windows_service_argv; -int main_rethinkdb_run_service(int argc, char *argv[]) { - // Open stdin, stdout, stderr. We ignore errors here. If stderr or stdout fail to - // open, the log writer is going to raise an issue later. - freopen("NUL", "r", stdin); - freopen("NUL", "w", stdout); - freopen("NUL", "w", stderr); - - // We need to get our actual arguments into the main function callback. - // StartServiceCtrlDispatcher requires a C function pointer, so we can't - // use a C++ lambda with a clojure. Instead we preserve those values in - // a pair of global variables. - // We skip the second argument, which is just `run-service`. - guarantee(argc >= 2); - guarantee(strcmp(argv[1], "run-service") == 0); - global_windows_service_argc = argc - 1; - global_windows_service_argv.init(argc - 1); - int out_i = 0; - for (int i = 0; i < argc; ++i) { - if (i == 1) { - continue; - } - global_windows_service_argv[out_i] = argv[i]; - ++out_i; - } - - SERVICE_TABLE_ENTRY dispatch_table[] = { - { "", [](DWORD, char **) { - // Note that the arguments passed to this function are - // not the arguments specified when installing the service. - // Instead these are arguments that can be specified ad-hoc - // when *starting* the service. We don't particularly care - // about those, and ignore them here. - return windows_service_main_function( - main_rethinkdb_porcelain, - global_windows_service_argc, - global_windows_service_argv.data()); - } }, - { nullptr, nullptr } - }; - - if (!StartServiceCtrlDispatcher(dispatch_table)) { - return EXIT_FAILURE; - } - return EXIT_SUCCESS; -} - -void get_rethinkdb_install_service_options( - std::vector *help_out, - std::vector *options_out) { - options::help_section_t help("Service options"); - options_out->push_back(options::option_t(options::names_t("--instance-name"), - options::OPTIONAL)); - help.add("--instance-name name", "name under which the service will be installed. " - "Specify this if you want to run multiple instances " - "of RethinkDB on this server (default: \"default\")"); - options_out->push_back(options::option_t(options::names_t("--runuser"), - options::OPTIONAL)); - help.add("--runuser user", "run the service under the given user account, " - "specified in the format DomainName\\UserName. " - "If not specified, the LocalSystem account is used"); - options_out->push_back(options::option_t(options::names_t("--runuser-password"), - options::OPTIONAL)); - help.add("--runuser-password password", "password of the user specified in " - "the `--runuser` option"); - help_out->push_back(help); - help_out->push_back(get_config_file_options(options_out)); - help_out->push_back(get_help_options(options_out)); -} - -void help_rethinkdb_install_service() { - std::vector help_sections; - { - std::vector options; - get_rethinkdb_install_service_options(&help_sections, &options); - } - - printf("'rethinkdb install-service' installs RethinkDB as a Windows service.\n"); - printf("%s", format_help(help_sections).c_str()); -} - -void get_rethinkdb_remove_service_options( - std::vector *help_out, - std::vector *options_out) { - options::help_section_t help("Service options"); - options_out->push_back(options::option_t(options::names_t("--instance-name"), - options::OPTIONAL)); - help.add("--instance-name name", "name of the instance that will be removed. " - "The name must match the one used when installing the service " - "(default: \"default\")"); - help_out->push_back(help); - help_out->push_back(get_help_options(options_out)); -} - -void help_rethinkdb_remove_service() { - std::vector help_sections; - { - std::vector options; - get_rethinkdb_remove_service_options(&help_sections, &options); - } - - printf("'rethinkdb remove-service' removes a previously installed Windows service.\n"); - printf("%s", format_help(help_sections).c_str()); -} - -const char *ELEVATED_RUN_MARKER = "_rethinkdb_was_elevated"; - -bool was_elevated(int argc, char *argv[]) { - return argc > 0 && strcmp(argv[argc - 1], ELEVATED_RUN_MARKER) == 0; -} - -int restart_elevated(int argc, char *argv[]) { - SHELLEXECUTEINFO sei; - memset(&sei, 0, sizeof(sei)); - sei.cbSize = sizeof(sei); - sei.lpVerb = "runas"; - sei.fMask = SEE_MASK_NOCLOSEPROCESS; - sei.nShow = SW_NORMAL; - sei.lpFile = argv[0]; - std::string args; - for (int i = 1; i < argc; ++i) { - if (i > 1) { - args += " "; - } - args += escape_windows_shell_arg(argv[i]); - } - // Mark as elevated - args += " "; - args += ELEVATED_RUN_MARKER; - sei.lpParameters = args.c_str(); - - if (!ShellExecuteEx(&sei)) { - DWORD err = GetLastError(); - if (err == ERROR_CANCELLED) { - fprintf(stderr, "The request to elevate permissions was rejected.\n"); - } else { - fprintf(stderr, "ShellExecuteEx failed: %s\n", winerr_string(err).c_str()); - } - return EXIT_FAILURE; - } else { - if (sei.hProcess != nullptr) { - WaitForSingleObject(sei.hProcess, INFINITE); - DWORD exit_code; - if (GetExitCodeProcess(sei.hProcess, &exit_code)) { - CloseHandle(sei.hProcess); - return exit_code; - } - CloseHandle(sei.hProcess); - } - fprintf(stderr, "Could not get a handler on the child process.\n"); - return EXIT_FAILURE; - } -} - -int run_and_maybe_elevate( - bool already_elevated, - int argc, - char *argv[], - const std::function &f) { - try { - bool success = f(); - if (already_elevated) { - // Sleep a bit so that any output from the elevated shell window remains visible. - Sleep(5000); - } - return success ? EXIT_SUCCESS : EXIT_FAILURE; - } catch (const windows_privilege_exc_t &) { - if (!already_elevated) { - fprintf(stderr, "Permission denied. Trying again with elevated permissions...\n"); - return restart_elevated(argc, argv); - } else { - fprintf(stderr, "Permission denied\n"); - return EXIT_FAILURE; - } - } -} - -int main_rethinkdb_install_service(int argc, char *argv[]) { - bool already_elevated = was_elevated(argc, argv); - if (already_elevated) { - --argc; - } - - std::vector options; - std::vector help; - get_rethinkdb_install_service_options(&help, &options); - try { - std::map opts = parse_command_line(argc - 2, argv + 2, options); - - if (handle_help_or_version_option(opts, &help_rethinkdb_install_service)) { - return EXIT_SUCCESS; - } - - options::verify_option_counts(options, opts); - - const optional config_file_name_arg = - get_optional_option(opts, "--config-file"); - if (!config_file_name_arg) { - fprintf(stderr, "rethinkdb install-service requires the `--config-file` option.\n"); - fprintf(stderr, - "You can find a template for the configuration file at " - ".\n"); - return EXIT_FAILURE; - } - // Make the config file name absolute - TCHAR full_path[MAX_PATH]; - DWORD full_path_length = GetFullPathName( - config_file_name_arg->c_str(), - MAX_PATH, - full_path, - nullptr); - if (full_path_length >= MAX_PATH) { - fprintf( - stderr, - "The absolute path to the configuration file is too long. " - "It must be shorter than %zu.\n", - static_cast(MAX_PATH)); - return EXIT_FAILURE; - } else if (full_path_length == 0) { - fprintf( - stderr, - "Failed to convert the configuration file path to an absolute path: %s\n", - winerr_string(GetLastError()).c_str()); - return EXIT_FAILURE; - } - std::string config_file_name(full_path, full_path_length); - - // Validate the configuration file (we ignore the resulting options) - { - std::vector help; - std::vector valid_options; - get_rethinkdb_porcelain_options(&help, &valid_options); - parse_config_file_flat(config_file_name, valid_options); - } - - const char *runuser_ptr = nullptr; - const optional runuser = get_optional_option(opts, "--runuser"); - if (runuser) { - runuser_ptr = runuser->c_str(); - } - const char *runuser_password_ptr = nullptr; - const optional runuser_password - = get_optional_option(opts, "--runuser-password"); - if (runuser_password) { - runuser_password_ptr = runuser_password->c_str(); - } - - std::string instance_name = "default"; - const optional instance_name_arg = - get_optional_option(opts, "--instance-name"); - if (instance_name_arg) { - instance_name = *instance_name_arg; - } - - // Get our filename - TCHAR my_path[MAX_PATH]; - if (!GetModuleFileName(nullptr, my_path, MAX_PATH)) { - fprintf(stderr, "Unable to retrieve own path: %s\n", - winerr_string(GetLastError()).c_str()); - return EXIT_FAILURE; - } - - std::string service_name = "rethinkdb_" + instance_name; - std::string display_name = "RethinkDB (" + instance_name + ")"; - - std::vector service_args; - service_args.push_back("run-service"); - service_args.push_back("--config-file"); - service_args.push_back(config_file_name); - - return run_and_maybe_elevate(already_elevated, argc, argv, [&]() { - fprintf(stderr, "Installing service `%s`...\n", service_name.c_str()); - bool success = install_windows_service( - service_name, display_name, std::string(my_path), service_args, - runuser_ptr, runuser_password_ptr); - if (success) { - fprintf(stderr, "Service `%s` installed.\n", service_name.c_str()); - } - fprintf(stderr, "Starting service `%s`...\n", service_name.c_str()); - if (start_windows_service(service_name)) { - fprintf(stderr, "Service `%s` started.\n", service_name.c_str()); - } - return success; - }); - } catch (const options::named_error_t &ex) { - output_named_error(ex, help); - fprintf(stderr, "Run 'rethinkdb help install-service' for help on the command\n"); - } catch (const options::option_error_t &ex) { - output_sourced_error(ex); - fprintf(stderr, "Run 'rethinkdb help install-service' for help on the command\n"); - } catch (const std::exception& ex) { - fprintf(stderr, "%s\n", ex.what()); - } - return EXIT_FAILURE; -} - -int main_rethinkdb_remove_service(int argc, char *argv[]) { - bool already_elevated = was_elevated(argc, argv); - if (already_elevated) { - --argc; - } - - std::vector options; - std::vector help; - get_rethinkdb_remove_service_options(&help, &options); - try { - std::map opts = parse_command_line(argc - 2, argv + 2, options); - - if (handle_help_or_version_option(opts, &help_rethinkdb_remove_service)) { - return EXIT_SUCCESS; - } - - options::verify_option_counts(options, opts); - - std::string instance_name = "default"; - const optional instance_name_arg = - get_optional_option(opts, "--instance-name"); - if (instance_name_arg) { - instance_name = *instance_name_arg; - } - - std::string service_name = "rethinkdb_" + instance_name; - - return run_and_maybe_elevate(already_elevated, argc, argv, [&]() { - fprintf(stderr, "Stopping service `%s`...\n", service_name.c_str()); - if (stop_windows_service(service_name)) { - fprintf(stderr, "Service `%s` stopped.\n", service_name.c_str()); - } - fprintf(stderr, "Removing service `%s`...\n", service_name.c_str()); - bool success = remove_windows_service(service_name); - if (success) { - fprintf(stderr, "Service `%s` removed.\n", service_name.c_str()); - } - return success; - }); - } catch (const options::named_error_t &ex) { - output_named_error(ex, help); - fprintf(stderr, "Run 'rethinkdb help remove-service' for help on the command\n"); - } catch (const options::option_error_t &ex) { - output_sourced_error(ex); - fprintf(stderr, "Run 'rethinkdb help remove-service' for help on the command\n"); - } catch (const std::exception& ex) { - fprintf(stderr, "%s\n", ex.what()); - } - return EXIT_FAILURE; -} - -#endif /* _WIN32 */ diff --git a/src/clustering/administration/main/command_line.hpp b/src/clustering/administration/main/command_line.hpp deleted file mode 100644 index 8d1ecfd1c04..00000000000 --- a/src/clustering/administration/main/command_line.hpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_MAIN_COMMAND_LINE_HPP_ -#define CLUSTERING_ADMINISTRATION_MAIN_COMMAND_LINE_HPP_ - -void print_version_message(); - -int main_rethinkdb_create(int argc, char *argv[]); -int main_rethinkdb_serve(int argc, char *argv[]); -int main_rethinkdb_proxy(int argc, char *argv[]); -int main_rethinkdb_porcelain(int argc, char *argv[]); -int main_rethinkdb_export(int argc, char *argv[]); -int main_rethinkdb_import(int argc, char *argv[]); -int main_rethinkdb_dump(int argc, char *argv[]); -int main_rethinkdb_restore(int argc, char *argv[]); -int main_rethinkdb_index_rebuild(int argc, char *argv[]); -int main_rethinkdb_repl(int argc, char *argv[]); -#ifdef _WIN32 -int main_rethinkdb_run_service(int argc, char *argv[]); -int main_rethinkdb_install_service(int argc, char *argv[]); -int main_rethinkdb_remove_service(int argc, char *argv[]); -#endif /* _WIN32 */ - -void help_rethinkdb_create(); -void help_rethinkdb_serve(); -void help_rethinkdb_proxy(); -void help_rethinkdb_porcelain(); -void help_rethinkdb_export(); -void help_rethinkdb_import(); -void help_rethinkdb_dump(); -void help_rethinkdb_restore(); -void help_rethinkdb_index_rebuild(); -void help_rethinkdb_repl(); -#ifdef _WIN32 -void help_rethinkdb_install_service(); -void help_rethinkdb_remove_service(); -#endif /* _WIN32 */ - -#endif /* CLUSTERING_ADMINISTRATION_MAIN_COMMAND_LINE_HPP_ */ diff --git a/src/clustering/administration/main/directory_lock.cc b/src/clustering/administration/main/directory_lock.cc deleted file mode 100644 index ad37595cee8..00000000000 --- a/src/clustering/administration/main/directory_lock.cc +++ /dev/null @@ -1,150 +0,0 @@ -#include "clustering/administration/main/directory_lock.hpp" - -#ifdef _MSC_VER -#include -#include -#else -#include -#include -#endif - -#include -#include -#include -#include - -#include - -#include "arch/io/disk.hpp" - -#ifdef _WIN32 -#define LOCK_FILE_NAME "dirlock" -#endif - -bool check_existence(const base_path_t& base_path) { - return 0 == access(base_path.path().c_str(), F_OK); -} - -bool check_dir_emptiness(const base_path_t& base_path) { -#ifdef _MSC_VER - for (auto it : std::tr2::sys::directory_iterator(base_path.path())) { - return false; - } - return true; -#else - DIR *dp; - struct dirent *ep; - - dp = opendir(base_path.path().c_str()); - if (dp == nullptr) { - throw directory_open_failed_exc_t(get_errno(), base_path); - } - - set_errno(0); - - // cpplint is wrong about readdir here, because POSIX guarantees we're not - // sharing a static buffer with other threads on the system. Even better, OS X - // and Linux glibc both allocate per-directory buffers. readdir_r is unsafe - // because you can't specify the length of the struct dirent buffer you pass in - // to it. See http://elliotth.blogspot.com/2012/10/how-not-to-use-readdirr3.html - while ((ep = readdir(dp)) != nullptr) { // NOLINT(runtime/threadsafe_fn) - if (strcmp(ep->d_name, ".") != 0 && strcmp(ep->d_name, "..") != 0) { - closedir(dp); - return false; - } - } - guarantee_err(get_errno() == 0, "Error while reading directory"); - - closedir(dp); - return true; -#endif -} - -directory_lock_t::directory_lock_t(const base_path_t &path, bool create, bool *created_out) : - directory_path(path), - created(false), - initialize_done(false) { - - *created_out = false; - bool dir_exists = check_existence(directory_path); - - if (!dir_exists) { - if (!create) { - throw directory_missing_exc_t(directory_path); - } - int mkdir_res; - do { -#if defined(__MINGW32__) - mkdir_res = mkdir(directory_path.path().c_str()); -#elif defined(_WIN32) - mkdir_res = _mkdir(directory_path.path().c_str()); -#else - mkdir_res = mkdir(directory_path.path().c_str(), 0755); -#endif - } while (mkdir_res == -1 && get_errno() == EINTR); - if (mkdir_res != 0) { - throw directory_create_failed_exc_t(get_errno(), directory_path); - } - created = true; - *created_out = true; - - // Call fsync() on the parent directory to guarantee that the newly - // created directory's directory entry is persisted to disk. - warn_fsync_parent_directory(directory_path.path().c_str()); - } else if (create && check_dir_emptiness(directory_path)) { - created = true; - *created_out = true; - } - -#ifdef _WIN32 - directory_fd.reset(CreateFile((directory_path.path() + "\\" LOCK_FILE_NAME).c_str(), - GENERIC_WRITE, 0, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_DELETE_ON_CLOSE, NULL)); - if (directory_fd.get() == INVALID_FD) { - logERR("CreateFile failed: %s", winerr_string(GetLastError()).c_str()); - throw directory_open_failed_exc_t(EIO, directory_path); - } -#else - directory_fd.reset(::open(directory_path.path().c_str(), O_RDONLY)); - if (directory_fd.get() == INVALID_FD) { - throw directory_open_failed_exc_t(get_errno(), directory_path); - } - if (flock(directory_fd.get(), LOCK_EX | LOCK_NB) != 0) { - throw directory_locked_exc_t(directory_path); - } -#endif -} - -directory_lock_t::~directory_lock_t() { - // Only delete the directory if we created it and haven't finished initialization - if (created && !initialize_done) { -#ifdef _WIN32 - // TODO WINDOWS: the lock is a file inside the directory, - // so we can't delete the directory without deleting it, - // but by deleting it now we unlock the directory too early - directory_fd.reset(); -#endif - remove_directory_recursive(directory_path.path().c_str()); - } -} - -void directory_lock_t::directory_initialized() { - guarantee(directory_fd.get() != INVALID_FD); - initialize_done = true; -} - -#ifndef _WIN32 -void directory_lock_t::change_ownership(gid_t group_id, const std::string &group_name, - uid_t user_id, const std::string &user_name) { - if (group_id != INVALID_GID || user_id != INVALID_UID) { - if (fchown(directory_fd.get(), user_id, group_id) != 0) { - throw std::runtime_error(strprintf("Failed to change ownership of data " - "directory '%s' to '%s:%s': %s", - directory_path.path().c_str(), - user_name.c_str(), - group_name.c_str(), - errno_string(get_errno()).c_str())); - } - } -} -#endif diff --git a/src/clustering/administration/main/directory_lock.hpp b/src/clustering/administration/main/directory_lock.hpp deleted file mode 100644 index 642f9f220fb..00000000000 --- a/src/clustering/administration/main/directory_lock.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef CLUSTERING_ADMINISTRATION_MAIN_DIRECTORY_LOCK_HPP_ -#define CLUSTERING_ADMINISTRATION_MAIN_DIRECTORY_LOCK_HPP_ - -#include - -#include -#include - -#include "arch/io/io_utils.hpp" -#include "paths.hpp" -#include "utils.hpp" - -#ifndef _WIN32 -#define INVALID_GID (static_cast(-1)) -#define INVALID_UID (static_cast(-1)) -#endif - -bool check_existence(const base_path_t& base_path); -bool check_dir_emptiness(const base_path_t& base_path); - -class directory_lock_t { -public: - // Possibly creates, then opens and locks the specified directory - // Returns true if the directory was created, false otherwise - directory_lock_t(const base_path_t &path, bool create, bool *created_out); - ~directory_lock_t(); - - // Prevents deletion of the directory tree at destruction, if - // the directory was created in the constructor - void directory_initialized(); - - // Sets the ownership of the directory to the given user and group. - // INVALID_GID or INVALID_UID is a no-op for that field. - // The strings are for error messages. - void change_ownership(gid_t group_id, const std::string &group_name, - uid_t user_id, const std::string &user_name); - -private: - const base_path_t directory_path; - scoped_fd_t directory_fd; - bool created; - bool initialize_done; - - DISABLE_COPYING(directory_lock_t); -}; - -class directory_missing_exc_t : public std::exception { -public: - explicit directory_missing_exc_t(const base_path_t &path) { - info = strprintf("The directory '%s' does not exist, run 'rethinkdb create -d \"%s\"' and try again.", - path.path().c_str(), path.path().c_str()); - } - ~directory_missing_exc_t() throw () { } - const char *what() const throw () { - return info.c_str(); - } -private: - std::string info; -}; - -class directory_create_failed_exc_t : public std::exception { -public: - directory_create_failed_exc_t(int err, const base_path_t &path) { - info = strprintf("Could not create directory '%s': %s", - path.path().c_str(), errno_string(err).c_str()); - } - ~directory_create_failed_exc_t() throw () { } - const char *what() const throw () { - return info.c_str(); - } -private: - std::string info; -}; - -class directory_open_failed_exc_t : public std::exception { -public: - directory_open_failed_exc_t(int err, const base_path_t &path) { - info = strprintf("Could not open directory '%s': %s", - path.path().c_str(), errno_string(err).c_str()); - } - ~directory_open_failed_exc_t() throw () { } - const char *what() const throw () { - return info.c_str(); - } -private: - std::string info; -}; - -class directory_locked_exc_t : public std::exception { -public: - explicit directory_locked_exc_t(const base_path_t &path) { - info = strprintf("Directory '%s' is already in use, perhaps another instance of rethinkdb is using it.", - path.path().c_str()); - } - ~directory_locked_exc_t() throw () { } - const char *what() const throw () { - return info.c_str(); - } -private: - std::string info; -}; - -#endif // CLUSTERING_ADMINISTRATION_MAIN_DIRECTORY_LOCK_HPP_ diff --git a/src/clustering/administration/main/initial_join.cc b/src/clustering/administration/main/initial_join.cc deleted file mode 100644 index 20fd493fe70..00000000000 --- a/src/clustering/administration/main/initial_join.cc +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "clustering/administration/main/initial_join.hpp" - -#include "concurrency/wait_any.hpp" -#include "logger.hpp" - -// This is a helper function used by find_peer_address_in_set to avoid code duplication. -// It will return an iterator to the first peer_address_t in the set that matches any -// of the IP addresses in addr. -peer_address_set_t::iterator find_peer_address_internal(const peer_address_set_t &peers, - const std::set &addrs) { - for (peer_address_set_t::iterator other = peers.begin(); other != peers.end(); ++other) { - for (auto it = addrs.begin(); it != addrs.end(); ++it) { - for (auto jt = other->ips().begin(); - jt != other->ips().end(); ++jt) { - if (*it == *jt) { - return other; - } - } - } - } - - return peers.end(); -} - -// We need to make sure we find the *right* address in the set. -// Example: -// Set contains [127.0.1.1] and [192.168.0.15]. -// find is called with [127.0.0.1, 127.0.1.1, 192.168.0.15]. -// In this case, only 192.168.0.15 should be removed, because the loopback -// address is obviously talking about a different peer. -// So, first we create a peer_address_t without any loopback addresses -// then, if there are no matches for that, use the original. -peer_address_set_t::iterator find_peer_address_in_set(const peer_address_set_t &peers, - const peer_address_t &addr) { - // Compare non-loopback addresses first - const std::set &addrs = addr.ips(); - std::set addrs_no_loopback; - for (std::set::const_iterator it = addrs.begin(); - it != addrs.end(); ++it) { - if (!it->ip().is_loopback()) { - addrs_no_loopback.insert(*it); - } - } - - peer_address_set_t::iterator result = - find_peer_address_internal(peers, addrs_no_loopback); - - if (result == peers.end()) { - // No match found, compare including loopback addresses - result = find_peer_address_internal(peers, addr.ips()); - } - - return result; -} - -initial_joiner_t::initial_joiner_t( - connectivity_cluster_t *cluster_, - connectivity_cluster_t::run_t *cluster_run, - const peer_address_set_t &peers, - const int join_delay_secs_, - int timeout_ms) : - cluster(cluster_), - peers_not_heard_from(peers), - join_delay_secs(join_delay_secs_), - subs(cluster->get_connections(), - std::bind(&initial_joiner_t::on_connection_change, this, ph::_1, ph::_2), - initial_call_t::YES), - successful_connection(false) { - guarantee(!peers.empty()); - - auto_drainer_t::lock_t connection_keepalive; - connectivity_cluster_t::connection_t *loopback = - cluster->get_connection(cluster->get_me(), &connection_keepalive); - guarantee(loopback != nullptr); - peer_address_t self_addr = loopback->get_peer_address(); - for (peer_address_set_t::iterator join_self_it = find_peer_address_in_set(peers_not_heard_from, self_addr); - join_self_it != peers_not_heard_from.end(); - join_self_it = find_peer_address_in_set(peers_not_heard_from, self_addr)) { - peers_not_heard_from.erase(*join_self_it); - logWRN("Attempted to join self, peer ignored"); - } - - if (timeout_ms != -1) { - grace_period_timer.start(timeout_ms); - } - - coro_t::spawn_sometime(std::bind(&initial_joiner_t::main_coro, this, cluster_run, auto_drainer_t::lock_t(&drainer))); -} - -static const int initial_retry_interval_ms = 200; -static const int max_retry_interval_ms = 1000 * 15; -static const double retry_interval_growth_rate = 1.5; -static const int grace_period_before_warn_ms = 1000 * 5; - -void initial_joiner_t::main_coro(connectivity_cluster_t::run_t *cluster_run, - auto_drainer_t::lock_t keepalive) { - try { - int retry_interval_ms = initial_retry_interval_ms; - logINF("Attempting connection to %zu peer%s...", - peers_not_heard_from.size(), peers_not_heard_from.size() == 1 ? "" : "s"); - do { - for (peer_address_set_t::iterator it = peers_not_heard_from.begin(); it != peers_not_heard_from.end(); it++) { - cluster_run->join(*it, join_delay_secs); - } - signal_timer_t retry_timer; - retry_timer.start(retry_interval_ms); - wait_any_t waiter(&retry_timer); - if (grace_period_timer.is_running()) { - waiter.add(&grace_period_timer); - } - wait_interruptible(&waiter, keepalive.get_drain_signal()); - retry_interval_ms = std::min(static_cast(retry_interval_ms * retry_interval_growth_rate), max_retry_interval_ms); - } while (!peers_not_heard_from.empty() && !grace_period_timer.is_pulsed()); - if (!peers_not_heard_from.empty()) { - peer_address_set_t::iterator it = peers_not_heard_from.begin(); - printf_buffer_t buffer; - debug_print(&buffer, it->primary_host()); - for (it++; it != peers_not_heard_from.end(); it++) { - buffer.appendf(", "); - debug_print(&buffer, it->primary_host()); - } - logWRN("We were unable to connect to the following peer%s, or the --join address does not match the peer's canonical address: %s", peers_not_heard_from.size() > 1 ? "s" : "", buffer.c_str()); - } - } catch (const interrupted_exc_t &) { - /* ignore */ - } - - if (!done_signal.is_pulsed()) - done_signal.pulse(); -} - -void initial_joiner_t::on_connection_change( - const peer_id_t &peer, - const connectivity_cluster_t::connection_pair_t *pair) { - if (peer == cluster->get_me() || pair == nullptr) { - return; - } - successful_connection = true; - peer_address_t peer_addr = pair->first->get_peer_address(); - // We want to remove a peer address, find it in the set (if it's there at all, and - // remove it) - peer_address_set_t::iterator join_addr = - find_peer_address_in_set(peers_not_heard_from, peer_addr); - if (join_addr != peers_not_heard_from.end()) { - peers_not_heard_from.erase(*join_addr); - } - - if (!done_signal.is_pulsed()) { - done_signal.pulse(); - - if (!grace_period_timer.is_running()) { - grace_period_timer.start(grace_period_before_warn_ms); - } - } -} - diff --git a/src/clustering/administration/main/initial_join.hpp b/src/clustering/administration/main/initial_join.hpp deleted file mode 100644 index d9ec2b037e9..00000000000 --- a/src/clustering/administration/main/initial_join.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_MAIN_INITIAL_JOIN_HPP_ -#define CLUSTERING_ADMINISTRATION_MAIN_INITIAL_JOIN_HPP_ - -#include - -#include "arch/timing.hpp" -#include "rpc/connectivity/cluster.hpp" - -/* `initial_joiner_t` is the class that `rethinkdb serve` and `rethinkdb admin` -use to handle the `--join` flag. It takes a list of addresses; it repeatedly -tries to connect to the addresses until at least one connection has been made. -At that point, it stops trying to connect to the other addresses and pulses -`get_ready_signal()`. If after a certain period of time we have still not -successfully connected to one of the other addresses, it calls `logWRN()` with -a warning message. */ - -class initial_joiner_t { -public: - initial_joiner_t( - connectivity_cluster_t *cluster, - connectivity_cluster_t::run_t *cluster_run, - const peer_address_set_t &peers, - const int join_delay, - int timeout = -1); - - signal_t *get_ready_signal() { - return &done_signal; - } - - bool get_success() { - return successful_connection; - } - -private: - void main_coro(connectivity_cluster_t::run_t *cluster_run, - auto_drainer_t::lock_t keepalive); - void on_connection_change( - const peer_id_t &peer, - const connectivity_cluster_t::connection_pair_t *pair); - - connectivity_cluster_t *cluster; - peer_address_set_t peers_not_heard_from; - cond_t done_signal; - signal_timer_t grace_period_timer; - int join_delay_secs; - auto_drainer_t drainer; - watchable_map_t - ::all_subs_t subs; - bool successful_connection; - - DISABLE_COPYING(initial_joiner_t); -}; - - -#endif /* CLUSTERING_ADMINISTRATION_MAIN_INITIAL_JOIN_HPP_ */ - diff --git a/src/clustering/administration/main/memory_checker.cc b/src/clustering/administration/main/memory_checker.cc deleted file mode 100644 index b43c62de800..00000000000 --- a/src/clustering/administration/main/memory_checker.cc +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#include "clustering/administration/main/memory_checker.hpp" - -#include -#ifndef _WIN32 -#include -#endif - -#include "clustering/administration/metadata.hpp" -#include "clustering/table_manager/table_meta_client.hpp" -#include "logger.hpp" -#include "rdb_protocol/env.hpp" -#include "rdb_protocol/pseudo_time.hpp" - -static const int64_t delay_time = 60*1000; -static const int64_t reset_checks = 10; - -static const int64_t practice_runs = 2; - -memory_checker_t::memory_checker_t() : - checks_until_reset(0), - swap_usage(0), - print_log_message(true), - practice_runs_remaining(practice_runs), - timer(delay_time, this) -{ - coro_t::spawn_sometime(std::bind(&memory_checker_t::do_check, - this, - drainer.lock())); -} - -void memory_checker_t::do_check(UNUSED auto_drainer_t::lock_t keepalive) { -#if defined(__MACH__) || defined(_WIN32) - size_t new_swap_usage = 0; -#else - struct rusage current_usage; - - int res = getrusage(RUSAGE_SELF, ¤t_usage); - - if (res == -1) { - logWRN("Error in swap usage detection: %d\n", get_errno()); - return; - } - size_t new_swap_usage = current_usage.ru_majflt; -#endif - - if (practice_runs_remaining == practice_runs) { - swap_usage = new_swap_usage; - } - - const std::string error_message = - "RethinkDB has been accessing a lot of swap memory in the past ten" - " minutes. This may impact performace."; - - if (new_swap_usage > swap_usage + 200 && practice_runs_remaining == 0) { - // We've started using more swap - if (print_log_message) { - logWRN("RethinkDB has been accessing a lot of swap memory in the past ten" - " minutes. This may impact performace."); - - print_log_message = false; - } - checks_until_reset = reset_checks; - memory_issue_tracker.report_error(error_message); - } else if (checks_until_reset == 0) { - // We haven't had more than 200 major page faults per minute for the last 10m. - memory_issue_tracker.report_success(); - print_log_message = true; - } - - swap_usage = new_swap_usage; - - if (checks_until_reset > 0) { - --checks_until_reset; - } - if (practice_runs_remaining > 0) { - --practice_runs_remaining; - } -} - diff --git a/src/clustering/administration/main/memory_checker.hpp b/src/clustering/administration/main/memory_checker.hpp deleted file mode 100644 index ac25ffa8e4a..00000000000 --- a/src/clustering/administration/main/memory_checker.hpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_MAIN_MEMORY_CHECKER_HPP_ -#define CLUSTERING_ADMINISTRATION_MAIN_MEMORY_CHECKER_HPP_ - -#include - -#include "arch/runtime/coroutines.hpp" -#include "arch/timing.hpp" -#include "clustering/administration/issues/memory.hpp" -#include "clustering/administration/main/cache_size.hpp" -#include "concurrency/auto_drainer.hpp" -#include "rdb_protocol/context.hpp" - -class table_meta_client_t; - -// memory_checker_t is created in serve.cc, and calls a repeating timer to -// Periodically check if we're using swap by looking at the proc file or system calls. -// If we're using swap, it creates an issue in a local issue tracker, and logs an error. -class memory_checker_t : private repeating_timer_callback_t { -public: - memory_checker_t(); - - memory_issue_tracker_t *get_memory_issue_tracker() { - return &memory_issue_tracker; - } -private: - void do_check(auto_drainer_t::lock_t keepalive); - void on_ring() final { - coro_t::spawn_sometime(std::bind(&memory_checker_t::do_check, - this, - drainer.lock())); - } - memory_issue_tracker_t memory_issue_tracker; - - uint64_t checks_until_reset; - uint64_t swap_usage; - - bool print_log_message; - - int practice_runs_remaining; - - // Timer must be destructed before drainer, because on_ring aquires a lock on drainer. - auto_drainer_t drainer; - repeating_timer_t timer; -}; - -#endif // CLUSTERING_ADMINISTRATION_MAIN_MEMORY_CHECKER_HPP_ diff --git a/src/clustering/administration/main/names.cc b/src/clustering/administration/main/names.cc deleted file mode 100644 index b974a534003..00000000000 --- a/src/clustering/administration/main/names.cc +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "clustering/administration/main/names.hpp" - -#include -#include -#include - -#include -#include - -#include "random.hpp" -#include "utils.hpp" - -static const char* names[] = { - "Akasha", - "Alchemist", - "Azwraith", - "Bane", - "Batrider", - "Beastmaster", - "Bradwarden", - "Brewmaster", - "Bristleback", - "Chaosknight", - "Chen", - "Clinkz", - "Clockwerk", - "Courier", - "Dazzle", - "Dragonknight", - "Dragonus", - "Drow", - "Earthshaker", - "Enchantress", - "Enigma", - "Ezalor", - "Furion", - "Gondar", - "Gyrocopter", - "Huskar", - "Invoker", - "Jakiro", - "Juggernaut", - "Kaldr", - "Krobelus", - "Kunkka", - "Lanaya", - "LeDirge", - "Leshrac", - "Lina", - "Lion", - "Luna", - "Lycanthrope", - "Magnus", - "Medusa", - "Meepo", - "Mirana", - "Morphling", - "Mortred", - "Naix", - "Nevermore", - "NyxNyxNyx", - "Ogre", - "Omniknight", - "Ostarion", - "Outworld", - "Phoenix", - "Puck", - "Pudge", - "Pugna", - "Razor", - "Rhasta", - "Rikimaru", - "Roshan", - "Rubick", - "Rylai", - "Sandking", - "Silencer", - "Slardar", - "Slark", - "Slithice", - "Spectre", - "Stormspirit", - "Strygwyr", - "Sven", - "Sylla", - "Tidehunter", - "Tinker", - "Tiny", - "Treant", - "Tusk", - "Ursa", - "Viper", - "Visage", - "Weaver", - "Windrunner", - "Wisp", - "Worldsmith" -}; - -name_string_t get_random_server_name() { - int index = randint(sizeof(names) / sizeof(char *)); - return name_string_t::guarantee_valid(names[index]); -} - -bool is_invalid_char(char ch) { - return !(isalpha(ch) || isdigit(ch) || ch == '_'); -} - -char rand_alnum() { - int rand_val = randint(10 + 26); - return (rand_val < 10) ? ('0' + rand_val) : ('a' + rand_val - 10); -} - -name_string_t get_default_server_name() { - char h[64]; - h[sizeof(h) - 1] = '\0'; - - if (gethostname(h, sizeof(h) - 1) != 0 || strlen(h) == 0) { - return get_random_server_name(); - } - - std::string sanitized(h); - std::replace_if(sanitized.begin(), sanitized.end(), is_invalid_char, '_'); - - return name_string_t::guarantee_valid( - strprintf("%s_%c%c%c", - sanitized.c_str(), - rand_alnum(), rand_alnum(), rand_alnum()).c_str()); -} diff --git a/src/clustering/administration/main/names.hpp b/src/clustering/administration/main/names.hpp deleted file mode 100644 index 0666ccbbdf8..00000000000 --- a/src/clustering/administration/main/names.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_MAIN_NAMES_HPP_ -#define CLUSTERING_ADMINISTRATION_MAIN_NAMES_HPP_ - -#include "containers/name_string.hpp" - -name_string_t get_default_server_name(); - -#endif // CLUSTERING_ADMINISTRATION_MAIN_NAMES_HPP_ diff --git a/src/clustering/administration/main/options.cc b/src/clustering/administration/main/options.cc deleted file mode 100644 index 7c09c8c1f5c..00000000000 --- a/src/clustering/administration/main/options.cc +++ /dev/null @@ -1,473 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/main/options.hpp" - -#include - -#include -#include -#include - -#include "errors.hpp" -#include "utils.hpp" - -namespace options { - -option_count_error_t::option_count_error_t(std::string _source, std::string _option_name, - size_t min_appearances, size_t max_appearances, - size_t actual_appearances) - : named_error_t(_source, _option_name, - actual_appearances > max_appearances ? strprintf("Option '%s' specified too many times.", _option_name.c_str()) - : min_appearances == 1 ? strprintf("Option '%s' must be specified.", _option_name.c_str()) - : strprintf("Option '%s' specified too few times.", _option_name.c_str())) { - rassert(min_appearances <= max_appearances); - rassert(actual_appearances < min_appearances || actual_appearances > max_appearances); -} - -missing_parameter_error_t::missing_parameter_error_t(std::string _source, - std::string _option_name) - : named_error_t(_source, _option_name, - strprintf("Option '%s' is missing its parameter.", _option_name.c_str())) { } - -value_error_t::value_error_t(std::string _source, - std::string _option_name, - std::string msg) - : named_error_t(_source, _option_name, msg) { } - -unrecognized_option_error_t::unrecognized_option_error_t(std::string _source, - std::string _option_name) - : option_error_t(_source, strprintf("Unrecognized option '%s'.", _option_name.c_str())), - unrecognized_option_name_(_option_name) { } - -positional_parameter_error_t::positional_parameter_error_t(std::string _source, - std::string _parameter_value) - : option_error_t(_source, - strprintf("Unexpected positional parameter '%s' (did you forget the " - "option name, or forget to quote a parameter list?).", - _parameter_value.c_str())), - parameter_value_(_parameter_value) { } - - -option_t::option_t(const names_t _names, const appearance_t appearance) - : names(_names.names), - default_values() { - switch (appearance) { - case MANDATORY: - min_appearances = 1; - max_appearances = 1; - no_parameter = false; - break; - case MANDATORY_REPEAT: - min_appearances = 1; - max_appearances = SIZE_MAX; - no_parameter = false; - break; - case OPTIONAL: - min_appearances = 0; - max_appearances = 1; - no_parameter = false; - break; - case OPTIONAL_REPEAT: - min_appearances = 0; - max_appearances = SIZE_MAX; - no_parameter = false; - break; - case OPTIONAL_NO_PARAMETER: - min_appearances = 0; - max_appearances = 1; - no_parameter = true; - break; - default: - unreachable(); - } -} - - -option_t::option_t(const names_t _names, const appearance_t appearance, std::string default_value) - : names(_names.names), - default_values(1, default_value) { - switch (appearance) { - case OPTIONAL: - min_appearances = 0; - max_appearances = 1; - no_parameter = false; - break; - case OPTIONAL_REPEAT: - min_appearances = 0; - max_appearances = SIZE_MAX; - no_parameter = false; - break; - case MANDATORY: - case MANDATORY_REPEAT: - case OPTIONAL_NO_PARAMETER: - // fall through - default: - unreachable(); - } -} - -bool looks_like_option_name(const char *const str) { - return str[0] == '-'; -} - -const option_t *find_option(const char *const option_name, const std::vector &options) { - for (auto it = options.begin(); it != options.end(); ++it) { - for (auto name_it = it->names.begin(); name_it != it->names.end(); ++name_it) { - if (*name_it == option_name) { - return &*it; - } - } - } - return nullptr; -} - -std::map default_values_map(const std::vector &options) { - std::map names_by_values; - for (auto it = options.begin(); it != options.end(); ++it) { - if (it->min_appearances == 0) { - names_by_values.insert(std::make_pair(it->names[0], - values_t("the default value", it->default_values))); - } - } - return names_by_values; -} - -std::map merge(const std::map &high_precedence_values, - const std::map &low_precedence_values) { - std::map ret = low_precedence_values; - for (auto it = high_precedence_values.begin(); it != high_precedence_values.end(); ++it) { - auto res = ret.insert(*it); - if (!res.second) { - res.first->second = it->second; - } - } - return ret; -} - -std::map do_parse_command_line( - const int argc, const char *const *const argv, const std::vector &options, - std::vector *const unrecognized_out) { - guarantee(argc >= 0); - - const std::string source = "the command line"; - - std::map names_by_values; - std::vector unrecognized; - - for (int i = 0; i < argc; ) { - // The option name as seen _in the command line_. We output this in - // help messages (because it's what the user typed in) instead of the - // official name for the option. - const char *const option_name = argv[i]; - ++i; - - const option_t *const option = find_option(option_name, options); - if (!option) { - if (unrecognized_out != nullptr) { - unrecognized.push_back(option_name); - continue; - } else if (looks_like_option_name(option_name)) { - throw unrecognized_option_error_t(source, option_name); - } else { - throw positional_parameter_error_t(source, option_name); - } - } - - const std::string official_name = option->names[0]; - - if (option->no_parameter) { - // Push an empty parameter value -- in particular, this makes our - // duplicate checking work. - auto res = names_by_values.insert(std::make_pair(official_name, values_t(source, std::vector()))); - res.first->second.values.push_back(""); - } else { - if (i == argc) { - throw missing_parameter_error_t(source, option_name); - } - - const char *const option_parameter = argv[i]; - ++i; - - if (looks_like_option_name(option_parameter) || - strlen(option_parameter) == 0) { - throw missing_parameter_error_t(source, option_name); - } - - auto res = names_by_values.insert(std::make_pair(official_name, values_t(source, std::vector()))); - res.first->second.values.push_back(option_parameter); - } - } - - if (unrecognized_out != nullptr) { - unrecognized_out->swap(unrecognized); - } - - return names_by_values; -} - -std::map parse_command_line(const int argc, const char *const *const argv, const std::vector &options) { - return do_parse_command_line(argc, argv, options, nullptr); -} - -std::map parse_command_line_and_collect_unrecognized( - int argc, const char *const *argv, const std::vector &options, - std::vector *unrecognized_out) { - // We check that unrecognized_out is not NULL because do_parse_command_line - // throws some exceptions depending on the nullness of that value. - guarantee(unrecognized_out != nullptr); - - return do_parse_command_line(argc, argv, options, unrecognized_out); -} - -void verify_option_counts(const std::vector &options, - const std::map &names_by_values) { - for (auto option = options.begin(); option != options.end(); ++option) { - const std::string option_name = option->names[0]; - auto entry = names_by_values.find(option_name); - if (entry == names_by_values.end()) { - if (option->min_appearances > 0) { - throw option_count_error_t("command line", option_name, - option->min_appearances, option->max_appearances, 0); - } - } else { - if (entry->second.values.size() < option->min_appearances || entry->second.values.size() > option->max_appearances) { - throw option_count_error_t(entry->second.source, option_name, - option->min_appearances, option->max_appearances, - entry->second.values.size()); - } - - if (option->no_parameter && entry->second.values.size() == 1 && entry->second.values[0] != "") { - throw value_error_t(entry->second.source, option_name, - strprintf("Option '%s' should not have a value.", option_name.c_str())); - } - } - } -} - -std::vector split_by_lines(const std::string &s) { - std::vector ret; - auto it = s.begin(); - auto const end = s.end(); - - while (it != end) { - auto jt = it; - while (jt != end && *jt != '\n') { - ++jt; - } - - ret.push_back(std::string(it, jt)); - it = jt == end ? jt : jt + 1; - } - return ret; -} - -bool is_space_or_equal_sign(char ch) { - return isspace(ch) || ch == '='; -} - -bool is_equal_sign(char ch) { - return ch == '='; -} - -bool is_not_space(char ch) { - return !isspace(ch); -} - -std::map parse_config_file(const std::string &file_contents, - const std::string &filepath, - const std::vector &options, - const std::vector &options_superset) { - const std::string source = "the configuration file " + filepath; - - const std::vector lines = split_by_lines(file_contents); - std::set ignored_options; - - std::map ret; - - for (auto it = lines.begin(); it != lines.end(); ++it) { - std::string stripped_line = *it; - // strip comment - stripped_line.erase(std::find(stripped_line.begin(), stripped_line.end(), '#'), stripped_line.end()); - - // strip trailing whitespace. - while (!stripped_line.empty() && isspace(stripped_line[stripped_line.size() - 1])) { - stripped_line.resize(stripped_line.size() - 1); - } - - auto const config_name_beg = std::find_if(stripped_line.begin(), stripped_line.end(), is_not_space); - - if (config_name_beg == stripped_line.end()) { - // all-whitespace (or comment) line - continue; - } - - auto const config_name_end = std::find_if(config_name_beg, stripped_line.end(), is_space_or_equal_sign); - if (config_name_beg == config_name_end) { - throw file_parse_error_t(source, - strprintf("Config file %s: parse error at line %zu", - filepath.c_str(), - it - lines.begin())); - } - - const std::string name(config_name_beg, config_name_end); - const std::string option_name = "--" + name; - const option_t *option = find_option(option_name.c_str(), options); - - if (option == nullptr) { - // Ignore 'known' options that are not valid now, but exist in the superset - if (find_option(option_name.c_str(), options_superset) == nullptr) { - throw file_parse_error_t(source, - strprintf("Config file %s: parse error at line %zu: unrecognized option name '%s'", - filepath.c_str(), it - lines.begin(), name.c_str())); - } else { - ignored_options.insert(name); - } - } else if (option->no_parameter) { - // Parameterless option, make sure we only have the option itself, and add it - if (stripped_line != name) { - throw file_parse_error_t(source, - strprintf("Config file %s: parse error at line %zu: unexpected data after no-parameter option '%s'", - filepath.c_str(), - it - lines.begin(), - name.c_str())); - } - auto res = ret.insert(std::make_pair(option_name, values_t(source, std::vector()))); - res.first->second.values.push_back(""); - } else { - // Option requires a parameter, parse it out - auto const expected_equal_sign = std::find_if(config_name_end, stripped_line.end(), is_not_space); - if (expected_equal_sign == stripped_line.end() || *expected_equal_sign != '=') { - throw file_parse_error_t(source, - strprintf("Config file %s: parse error at line %zu", - filepath.c_str(), - it - lines.begin())); - } - - auto const beginning_of_value = std::find_if(expected_equal_sign + 1, stripped_line.end(), is_not_space); - - const std::string value(beginning_of_value, stripped_line.end()); - - if (value.empty()) { - throw file_parse_error_t(source, - strprintf("Config file %s: parse error at line %zu: no parameter for option '%s'", - filepath.c_str(), - it - lines.begin(), - name.c_str())); - } - - auto res = ret.insert(std::make_pair(option_name, values_t(source, std::vector()))); - res.first->second.values.push_back(value); - } - } - - if (!ignored_options.empty()) { - std::string ignored_string; - for (auto it = ignored_options.begin(); it != ignored_options.end(); ++it) { - if (!ignored_string.empty()) { - ignored_string += ", "; - } - ignored_string += *it; - } - fprintf(stderr, "Warning: The following options are not used by this invocation" - " of rethinkdb and will be ignored: %s\n", ignored_string.c_str()); - } - - return ret; -} - -std::vector word_wrap(const std::string &s, const size_t width) { - std::vector ret; - - auto it = s.begin(); - auto const end = s.end(); - - for (;;) { - // We need to begin a new line. - it = std::find_if(it, end, is_not_space); - if (it == end) { - break; - } - - // Find an end of this line that we _could_ truncate to. - auto const window = end - it <= static_cast(width) ? end : it + width; - auto jt = window; - if (jt != end) { - while (it < jt && !isspace(*jt)) { - --jt; - } - } - - // (We have a word that is longer than width.) - if (jt == it) { - jt = window; - while (jt < end && !isspace(*jt)) { - ++jt; - } - } - - // Now we _could_ truncate to jt, but we should not leave trailing whitespace. - auto truncation_point = jt; - while (it < truncation_point && isspace(*(truncation_point - 1))) { - --truncation_point; - } - - // Since `it` stopped at a non-whitespace character, there's no way this line is empty. - guarantee(it < truncation_point); - - ret.push_back(std::string(it, truncation_point)); - - // Update it to jt, avoiding pointless work. - it = jt; - } - - if (ret.empty()) { - ret.push_back(""); - } - - return ret; -} - -std::string format_help(const std::vector &help) { - size_t max_syntax_description_length = 0; - for (auto section = help.begin(); section != help.end(); ++section) { - for (auto line = section->help_lines.begin(); line != section->help_lines.end(); ++line) { - max_syntax_description_length = std::max(max_syntax_description_length, - line->syntax_description.size()); - } - } - - const size_t summary_width = std::max(30, 79 - static_cast(max_syntax_description_length)); - - // Two spaces before summary description, two spaces after. 2 + 2 = 4. - const size_t indent_width = 4 + max_syntax_description_length; - - std::string ret; - for (auto section = help.begin(); section != help.end(); ++section) { - ret += section->section_name; - ret += ":\n"; - - for (auto line = section->help_lines.begin(); line != section->help_lines.end(); ++line) { - std::vector parts = word_wrap(line->blurb, summary_width); - - for (size_t i = 0; i < parts.size(); ++i) { - if (i == 0) { - ret += " "; // 2 spaces - ret += line->syntax_description; - ret += std::string(indent_width - (2 + line->syntax_description.size()), ' '); - } else { - ret += std::string(indent_width, ' '); - } - - ret += parts[i]; - ret += "\n"; - } - } - ret += "\n"; - } - - return ret; -} - - - -} // namespace options - diff --git a/src/clustering/administration/main/options.hpp b/src/clustering/administration/main/options.hpp deleted file mode 100644 index ee9fd1c6f7c..00000000000 --- a/src/clustering/administration/main/options.hpp +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_MAIN_OPTIONS_HPP_ -#define CLUSTERING_ADMINISTRATION_MAIN_OPTIONS_HPP_ - -#include -#include -#include -#include - -namespace options { - -struct option_error_t : public std::runtime_error { - option_error_t(std::string _source, std::string msg) - : std::runtime_error(msg), source_(_source) { } - ~option_error_t() throw () { } - - std::string source() const { return source_; } - -private: - std::string source_; -}; - -struct named_error_t : public option_error_t { - named_error_t(std::string _source, std::string _option_name, std::string msg) - : option_error_t(_source, msg), option_name_(_option_name) { } - ~named_error_t() throw () { } - - std::string option_name() const { return option_name_; } - -private: - std::string option_name_; -}; - -struct option_count_error_t : public named_error_t { - option_count_error_t(std::string _source, std::string _option_name, size_t min_appearances, - size_t max_appearances, size_t actual_appearances); - ~option_count_error_t() throw () { } -}; - -struct missing_parameter_error_t : public named_error_t { - missing_parameter_error_t(std::string _source, std::string _option_name); - ~missing_parameter_error_t() throw () { } -}; - -struct value_error_t : public named_error_t { - value_error_t(std::string _source, std::string _option_name, std::string msg); - ~value_error_t() throw () { } -}; - -struct unrecognized_option_error_t : public option_error_t { - unrecognized_option_error_t(std::string _source, std::string _option_name); - ~unrecognized_option_error_t() throw () { } - - std::string unrecognized_option_name() const { return unrecognized_option_name_; } - -private: - std::string unrecognized_option_name_; -}; - -struct positional_parameter_error_t : public option_error_t { - positional_parameter_error_t(std::string _source, std::string _parameter_value); - ~positional_parameter_error_t() throw () { } - - std::string parameter_value() const { return parameter_value_; } - -private: - std::string parameter_value_; -}; - -struct file_parse_error_t : public option_error_t { - file_parse_error_t(std::string _source, std::string msg) : option_error_t(_source, msg) { } - ~file_parse_error_t() throw () { } -}; - -// A list of values provided for some option. (Multiple values means the option was specified -// multiple times.) Also included is a string description the *source* of the option values (e.g. a -// command line or the name of a config file) to help make friendly error messages. Typically this -// is a value in a `std::map`, where the key is the option name. -struct values_t { - explicit values_t(const std::string &_source, const std::vector &_values) - : source(_source), values(_values) { } - - // A string describing the source of the values. This should be usable in a help message as the - // object of a prepositional phrase -- e.g. "the command line", which could be used in phrases - // "from the command line" or "in the command line", or "the configuration file foo.cfg". - std::string source; - std::vector values; -}; - -// Represents an option's names. Be sure to include dashes! Usage: -// -// names_t("--max-foobars") // An option name. -// names_t("--cores", "-c") // An option name with an abbreviation -class names_t { -public: - // Include dashes. For example, name might be "--blah". - explicit names_t(const std::string &name) { - names.push_back(name); - } - // Include the right amount of dashes. For example, official_name might - // be "--help", and other_name might be "-h". - names_t(std::string official_name, std::string other_name) { - names.push_back(official_name); - names.push_back(other_name); - } -private: - friend class option_t; - std::vector names; -}; - -// Pass one of these to the option_t construct to tell what kind of argument you have. -enum appearance_t { - // A mandatory argument that can be passed once. - MANDATORY, - // A mandatory argument that may be repeated. - MANDATORY_REPEAT, - // An optional argument, that may be passed zero or one times. - OPTIONAL, - // An optional argument, that may be repeated. - OPTIONAL_REPEAT, - // An optional argument that doesn't take a parameter. Useful for "--help". - OPTIONAL_NO_PARAMETER -}; - -// A command line option with a name, specification of how many times it may appear, and whether it -// takes a parameter. -// -// Examples: -// // An option that may be used at most once, with no parameter. -// option_t(names_t("--help", "-h"), OPTIONAL_NO_PARAMETER) -// // An option that may be used at most once, with a default value. The user -// // could pass --cores 3 or -c 3, but not a naked -c. -// option_t(names_t("--cores", "-c"), OPTIONAL, strprintf("%d", get_cpu_count())); -// // An option that must appear one or more times. -// option_t(names_t("--join", "-j"), MANDATORY_REPEAT) -class option_t { -public: - // Creates an option with the appropriate name and appearance specifier, - // with a default value being the empty vector. - explicit option_t(names_t names, appearance_t appearance); - // Creates an option with the appropriate name and appearance specifier, - // with the default value being a vector of size 1. OPTIONAL and - // OPTIONAL_REPEAT are the only valid appearance specifiers. - explicit option_t(names_t names, appearance_t appearance, std::string default_value); - -private: - friend std::map default_values_map(const std::vector &options); - friend std::map do_parse_command_line( - const int argc, const char *const *const argv, const std::vector &options, - std::vector *const unrecognized_out); - friend const option_t *find_option(const char *const option_name, const std::vector &options); - friend void verify_option_counts(const std::vector &options, - const std::map &names_by_values); - friend std::map parse_config_file(const std::string &contents, - const std::string &filepath, - const std::vector &options, - const std::vector &options_superset); - - // Names for the option, e.g. "-j", "--join" - std::vector names; - - // How many times must the option appear? If an option appears zero times, - // and if min_appearances is zero, then `default_values` will be used as the - // value-list of the option. Typical combinations of (min_appearances, - // max_appearances) are (0, 1) (with a default_value), (0, SIZE_MAX) (with or - // without a default value), (1, 1) (for mandatory options), (1, SIZE_MAX) - // (for mandatory options with repetition). - // - // It must be the case that 0 <= min_appearances <= max_appearances <= - // SIZE_MAX. - size_t min_appearances; - size_t max_appearances; - - // True if an option doesn't take a parameter. For example, "--help" would - // take no parameter. - bool no_parameter; - - // The value(s) to use if no appearances of the command line option are - // available. This is only relevant if min_appearances == 0. - std::vector default_values; -}; - -// Parses options from a command line into a return value. Uses empty-string parameter values for -// appearances of OPTIONAL_NO_PARAMETER options. Uses the *official name* of the option (the first -// parameter passed to names_t) for map keys. Does not do any verification that we have the right -// number of option values. (That only happens in `verify_option_counts`.) -std::map parse_command_line(int argc, const char *const *argv, const std::vector &options); - -// Like `parse_command_line`, except that it tolerates unrecognized options, instead of throwing. -// Out-of-place positional parameters and unrecognized options are output to `*unrecognized_out`, in -// the same order that they appeared in the options list. This can lead to some weird situations, -// if you passed "--recognized-foo 3 --unrecognized --recognized-bar 4 5" on the command line. You -// would get ["--unrecognized", "5"] in `*unrecognized_out`. -std::map parse_command_line_and_collect_unrecognized( - int argc, const char *const *argv, const std::vector &options, - std::vector *unrecognized_out); - -// Merges option values from two different sources together, with higher precedence going to the -// left-hand argument. Example usage: -// -// opts = merge(command_line_values, config_file_values); -std::map merge( - const std::map &high_precedence_values, - const std::map &low_precedence_values); - -// Verifies that given options build the right amount of times. This is separate from option -// parsing because we need to accumulate options from both the command line and config file. -void verify_option_counts(const std::vector &options, - const std::map &names_by_values); - -// Constructs a map of default option values. -std::map default_values_map(const std::vector &options); - -// Parses the file contents, using filepath solely to help build error messages, retrieving some -// options. Ignore options that are in the superset, but not in the regular set. -std::map parse_config_file(const std::string &contents, - const std::string &filepath, - const std::vector &options, - const std::vector &options_superset); - -// A help_line_t is a syntax description and a blurb. When used in a help_section_t, the blurbs get -// aligned and word-wrapped. -struct help_line_t { - help_line_t(const std::string &_syntax_description, - const std::string &_blurb) - : syntax_description(_syntax_description), blurb(_blurb) { } - - std::string syntax_description; - std::string blurb; -}; - -// A help_section_t is a titled list of syntax descriptions and blurbs. When rendered with -// format_help, all the blurbs get aligned and word-wrapped. -struct help_section_t { - help_section_t() { } - explicit help_section_t(const std::string &_section_name) - : section_name(_section_name) { } - help_section_t(const std::string &_section_name, const std::vector &_help_lines) - : section_name(_section_name), help_lines(_help_lines) { } - - void add(const std::string &syntax_description, const std::string &blurb) { - help_lines.push_back(help_line_t(syntax_description, blurb)); - } - - std::string section_name; - std::vector help_lines; -}; - -// Word wraps a string (which presumably doesn't contain newlines or tab characters) to the -// specified width. -std::vector word_wrap(const std::string &s, size_t width); - -// Creates a help string suitable for output from --help, aligning and word-wrapping the blurbs. -std::string format_help(const std::vector &help); - - - - - -} // namespace options - -#endif // CLUSTERING_ADMINISTRATION_MAIN_OPTIONS_HPP_ diff --git a/src/clustering/administration/main/path.cc b/src/clustering/administration/main/path.cc deleted file mode 100644 index 37f86c97d8b..00000000000 --- a/src/clustering/administration/main/path.cc +++ /dev/null @@ -1,34 +0,0 @@ -#include "clustering/administration/main/path.hpp" - -#include "errors.hpp" -#include - -static const char *const unix_path_separator = "/"; - -path_t parse_as_path(const std::string &path) { - path_t res; - res.is_absolute = (path[0] == unix_path_separator[0]); - - typedef boost::tokenizer > tokenizer; - - boost::char_separator sep(unix_path_separator); - tokenizer tokens(path, sep); - - res.nodes.assign(tokens.begin(), tokens.end()); - - return res; -} - -std::string render_as_path(const path_t &path) { - std::string res; - for (std::vector::const_iterator it = path.nodes.begin(); - it != path.nodes.end(); - ++it) { - if (it != path.nodes.begin() || path.is_absolute) { - res += unix_path_separator; - } - res += *it; - } - - return res; -} diff --git a/src/clustering/administration/main/path.hpp b/src/clustering/administration/main/path.hpp deleted file mode 100644 index fd63627f0b6..00000000000 --- a/src/clustering/administration/main/path.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef CLUSTERING_ADMINISTRATION_MAIN_PATH_HPP_ -#define CLUSTERING_ADMINISTRATION_MAIN_PATH_HPP_ - -#include -#include - -struct path_t { - std::vector nodes; - bool is_absolute; -}; - -path_t parse_as_path(const std::string &); -std::string render_as_path(const path_t &); - -#endif // CLUSTERING_ADMINISTRATION_MAIN_PATH_HPP_ diff --git a/src/clustering/administration/main/ports.hpp b/src/clustering/administration/main/ports.hpp deleted file mode 100644 index c1654fd3212..00000000000 --- a/src/clustering/administration/main/ports.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_MAIN_PORTS_HPP_ -#define CLUSTERING_ADMINISTRATION_MAIN_PORTS_HPP_ - -namespace port_defaults { - -const int peer_port = 29015; -const int client_port = 0; -const int http_port = 8080; - -/* We currently spin up clusters with port offsets differing by 1, so - these ports should be reasonably far apart. */ -const int reql_port = 28015; - -const int port_offset = 0; - -} // namespace port_defaults - -#endif // CLUSTERING_ADMINISTRATION_MAIN_PORTS_HPP_ diff --git a/src/clustering/administration/main/serve.cc b/src/clustering/administration/main/serve.cc deleted file mode 100644 index 5f24f21cfe6..00000000000 --- a/src/clustering/administration/main/serve.cc +++ /dev/null @@ -1,707 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/main/serve.hpp" - -#include - -#include "arch/arch.hpp" -#include "arch/io/network.hpp" -#include "arch/os_signal.hpp" -#include "buffer_cache/cache_balancer.hpp" -#include "clustering/administration/artificial_reql_cluster_interface.hpp" -#include "clustering/administration/http/server.hpp" -#include "clustering/administration/issues/local.hpp" -#include "clustering/administration/jobs/manager.hpp" -#include "clustering/administration/logs/log_writer.hpp" -#include "clustering/administration/main/initial_join.hpp" -#include "clustering/administration/main/ports.hpp" -#include "clustering/administration/main/memory_checker.hpp" -#include "clustering/administration/main/watchable_fields.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/perfmon_collection_repo.hpp" -#include "clustering/administration/persist/file_keys.hpp" -#include "clustering/administration/persist/semilattice.hpp" -#include "clustering/administration/persist/table_interface.hpp" -#include "clustering/administration/real_reql_cluster_interface.hpp" -#include "clustering/administration/servers/auto_reconnect.hpp" -#include "clustering/administration/servers/config_server.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "clustering/administration/servers/network_logger.hpp" -#include "clustering/administration/tables/name_resolver.hpp" -#include "clustering/table_manager/table_meta_client.hpp" -#include "clustering/table_manager/multi_table_manager.hpp" -#include "containers/incremental_lenses.hpp" -#include "containers/lifetime.hpp" -#include "containers/optional.hpp" -#include "extproc/extproc_pool.hpp" -#include "rdb_protocol/query_server.hpp" -#include "rpc/connectivity/cluster.hpp" -#include "rpc/directory/map_read_manager.hpp" -#include "rpc/directory/map_write_manager.hpp" -#include "rpc/directory/read_manager.hpp" -#include "rpc/directory/write_manager.hpp" -#include "rpc/semilattice/semilattice_manager.hpp" -#include "rpc/semilattice/view/field.hpp" - -peer_address_set_t look_up_peers_addresses(const std::vector &names) { - peer_address_set_t peers; - for (size_t i = 0; i < names.size(); ++i) { - peer_address_t peer(std::set{names[i]}); - if (peers.find(peer) != peers.end()) { - logWRN("Duplicate peer in --join parameters, ignoring: '%s:%d'", - names[i].host().c_str(), names[i].port().value()); - } else { - peers.insert(peer); - } - } - return peers; -} - -std::string service_address_ports_t::get_addresses_string( - std::set actual_addresses) { - - bool first = true; - std::string result; - - // Get the actual list for printing if we're listening on all addresses. - if (is_bind_all(actual_addresses)) { - actual_addresses = get_local_ips(std::set(), - local_ip_filter_t::ALL); - } - - for (std::set::const_iterator i = actual_addresses.begin(); i != actual_addresses.end(); ++i) { - result += (first ? "" : ", " ) + i->to_string(); - first = false; - } - - return result; -} - -bool service_address_ports_t::is_bind_all(const std::set &addresses) { - // If the set is empty, it means we're listening on all addresses. - return addresses.empty(); -} - -#ifdef _WIN32 -std::string windows_version_string(); -#else -// Defined in command_line.cc; not in any header, because it is not -// safe to run in general. -std::string run_uname(const std::string &flags); -#endif - -bool do_serve(io_backender_t *io_backender, - bool i_am_a_server, - // NB. filepath & persistent_file are used only if i_am_a_server is true. - const base_path_t &base_path, - metadata_file_t *metadata_file, - const serve_info_t &serve_info, - os_signal_cond_t *stop_cond, - /* For regular servers, the initial password is already set in the - metadata and this will be empty. */ - const std::string &proxy_initial_password = "") { - /* This coroutine is responsible for creating and destroying most of the important - components of the server. */ - - // Do this here so we don't block on popen while pretending to serve. -#ifdef _WIN32 - std::string uname = windows_version_string(); -#else - std::string uname = run_uname("ms"); -#endif - try { - /* `extproc_pool` spawns several subprocesses that can be used to run tasks that - we don't want to run in the main RethinkDB process, such as Javascript - evaluations. */ - extproc_pool_t extproc_pool(get_num_threads()); - - /* `thread_pool_log_writer_t` automatically registers itself. While it exists, - log messages will be written using the event loop instead of blocking. */ - thread_pool_log_writer_t log_writer; - - cluster_semilattice_metadata_t cluster_metadata; - auth_semilattice_metadata_t auth_metadata; - heartbeat_semilattice_metadata_t heartbeat_metadata; - server_id_t server_id; - if (metadata_file != nullptr) { - guarantee(proxy_initial_password.empty()); - cond_t non_interruptor; - metadata_file_t::read_txn_t txn(metadata_file, &non_interruptor); - cluster_metadata = txn.read(mdkey_cluster_semilattices(), &non_interruptor); - auth_metadata = txn.read(mdkey_auth_semilattices(), &non_interruptor); - heartbeat_metadata = txn.read( - mdkey_heartbeat_semilattices(), &non_interruptor); - server_id = txn.read(mdkey_server_id(), &non_interruptor); - } else { - // We are a proxy, generate a temporary proxy server id. - server_id = server_id_t::generate_proxy_id(); - auth_metadata.m_users.insert( - auth_semilattice_metadata_t::create_initial_admin_pair( - proxy_initial_password)); - } - -#ifndef NDEBUG - logNTC("Our server ID is %s", server_id.print().c_str()); -#endif - - /* The `connectivity_cluster_t` maintains TCP connections to other servers in the - cluster. */ - connectivity_cluster_t connectivity_cluster; - - /* The `mailbox_manager_t` maintains a local index of mailboxes that exist on - this server, and routes mailbox messages received from other servers. */ - mailbox_manager_t mailbox_manager(&connectivity_cluster, 'M'); - - /* `semilattice_manager_cluster`, `semilattice_manager_auth`, and - `semilattice_manager_heartbeat` are responsible for syncing the semilattice - metadata between servers over the network. */ - semilattice_manager_t - semilattice_manager_cluster(&connectivity_cluster, 'S', cluster_metadata); - semilattice_manager_t - semilattice_manager_auth(&connectivity_cluster, 'A', auth_metadata); - semilattice_manager_t - semilattice_manager_heartbeat( - &connectivity_cluster, 'Y', heartbeat_metadata); - - /* The `directory_*_read_manager_t`s are responsible for receiving directory - updates over the network from other servers. */ - - /* The `cluster_directory_metadata_t` contains basic information about each - server (such as its name, server ID, version, PID, command line arguments, etc.) - and also many singleton mailboxes for various purposes. */ - directory_read_manager_t - directory_read_manager(&connectivity_cluster, 'D'); - - /* The `table_manager_bcard_t`s contain the mailboxes that allow the Raft members - on different servers to communicate with each other. */ - directory_map_read_manager_t - table_directory_read_manager(&connectivity_cluster, 'T'); - - /* The `table_query_bcard_t`s contain mailboxes that execute `read_t`s or - `write_t`s. The `table_query_client_t` reads this directory to know how to route - queries. */ - directory_map_read_manager_t< - std::pair, - table_query_bcard_t> - table_query_directory_read_manager(&connectivity_cluster, 'Q'); - - /* `server_connection_read_manager` gives us second-order connectivity - information; we can figure out which of the servers we're connected to are - connected to which other servers. */ - directory_map_read_manager_t - server_connection_read_manager(&connectivity_cluster, 'C'); - - /* `log_server` retrieves pieces of our local log file and sends them out over - the network via its mailbox. */ - log_server_t log_server(&mailbox_manager, &log_writer); - - /* `server_config_server` tracks this server's name, tags, etc. It takes care of - loading and persisting that information to disk, and also distributing it over - the network via the `cluster_directory_metadata_t`. */ - scoped_ptr_t server_config_server; - if (i_am_a_server) { - server_config_server.init(new server_config_server_t( - &mailbox_manager, metadata_file)); - } - - /* `server_config_client` is used to get a list of all connected servers and - request information about their names and tags. It can also be used to change - servers' names and tags over the network by sending messages to the servers' - `server_config_server_t`s. */ - server_config_client_t server_config_client( - &mailbox_manager, - directory_read_manager.get_root_map_view(), - server_connection_read_manager.get_root_view()); - - /* `network_logger` writes to the log file when another server connects or - disconnects. */ - network_logger_t network_logger( - connectivity_cluster.get_me(), - directory_read_manager.get_root_map_view()); - - /* `connectivity_cluster_run` is the other half of the `connectivity_cluster_t`. - Before it's created, the `connectivity_cluster_t` won't process any connections - or messages. So it's only safe to create now that we've set up all of our message - handlers. */ - scoped_ptr_t connectivity_cluster_run; - try { - connectivity_cluster_run.init(new connectivity_cluster_t::run_t( - &connectivity_cluster, - server_id, - serve_info.ports.local_addresses_cluster, - serve_info.ports.canonical_addresses, - serve_info.join_delay_secs, - serve_info.ports.port, - serve_info.ports.client_port, - semilattice_manager_heartbeat.get_root_view(), - semilattice_manager_auth.get_root_view(), - serve_info.tls_configs.cluster.get())); - } catch (const address_in_use_exc_t &ex) { - throw address_in_use_exc_t(strprintf("Could not bind to cluster port: %s", ex.what())); - } - - // If (0 == port), then we asked the OS to give us a port number. - if (serve_info.ports.port != 0) { - guarantee(serve_info.ports.port == connectivity_cluster_run->get_port()); - } - logNTC("Listening for intracluster connections on port %d\n", - connectivity_cluster_run->get_port()); - - /* `auto_reconnector` tries to reconnect to other servers if we lose the - connection to them. */ - auto_reconnector_t auto_reconnector( - &connectivity_cluster, - connectivity_cluster_run.get(), - &server_config_client, - serve_info.join_delay_secs, - serve_info.node_reconnect_timeout_secs * 1000); // in ms - - /* `initial_joiner` sets up the initial connections to the peers that were - specified with the `--join` flag on the command line. */ - scoped_ptr_t initial_joiner; - if (!serve_info.peers.empty()) { - initial_joiner.init(new initial_joiner_t(&connectivity_cluster, - connectivity_cluster_run.get(), - serve_info.peers, - serve_info.join_delay_secs)); - try { - wait_interruptible(initial_joiner->get_ready_signal(), stop_cond); - } catch (const interrupted_exc_t &) { - return false; - } - } - - perfmon_collection_repo_t perfmon_collection_repo( - &get_global_perfmon_collection()); - - /* We thread the `rdb_context_t` through every function that evaluates ReQL - terms. It contains pointers to all the things that the ReQL term evaluation code - needs. */ - rdb_context_t rdb_ctx(&extproc_pool, - &mailbox_manager, - nullptr, /* we'll fill this in later */ - semilattice_manager_auth.get_root_view(), - &get_global_perfmon_collection(), - serve_info.reql_http_proxy); - { - /* Extract a subview of the directory with all the table meta manager - business cards. */ - watchable_map_value_transform_t - multi_table_manager_directory( - directory_read_manager.get_root_map_view(), - [](const cluster_directory_metadata_t *cluster_md) { - return &cluster_md->multi_table_manager_bcard; - }); - - /* The `multi_table_manager_t` takes care of the actual business of setting - up tables and handling queries for them. The `table_persistence_interface_t` - helps it by constructing the B-trees and serializers, and also persisting - table-related metadata to disk. */ - scoped_ptr_t cache_balancer; - scoped_ptr_t - table_persistence_interface; - scoped_ptr_t multi_table_manager; - if (i_am_a_server) { - cache_balancer.init(new alt_cache_balancer_t( - server_config_server->get_actual_cache_size_bytes())); - table_persistence_interface.init( - new real_table_persistence_interface_t( - io_backender, - cache_balancer.get(), - base_path, - &rdb_ctx, - metadata_file)); - multi_table_manager.init(new multi_table_manager_t( - server_id, - &mailbox_manager, - &server_config_client, - &multi_table_manager_directory, - table_directory_read_manager.get_root_view(), - server_config_client.get_connections_map(), - table_persistence_interface.get(), - base_path, - io_backender, - &perfmon_collection_repo)); - } else { - /* Proxies still need a `multi_table_manager_t` because it takes care of - receiving table names, databases, and primary keys from other servers and - providing them to the `table_meta_client_t`. */ - multi_table_manager.init(new multi_table_manager_t( - &mailbox_manager, - &multi_table_manager_directory, - table_directory_read_manager.get_root_view())); - } - - artificial_reql_cluster_interface_t artificial_reql_cluster_interface( - semilattice_manager_auth.get_root_view(), - &rdb_ctx); - - /* The `table_meta_client_t` sends messages to the `multi_table_manager_t`s - on the other servers in the cluster to create, drop, and reconfigure tables, - as well as request information about them. */ - table_meta_client_t table_meta_client( - &mailbox_manager, - multi_table_manager.get(), - &multi_table_manager_directory, - table_directory_read_manager.get_root_view(), - &server_config_client); - - name_resolver_t name_resolver( - semilattice_manager_cluster.get_root_view(), - &table_meta_client, - make_lifetime(artificial_reql_cluster_interface)); - - /* The `real_reql_cluster_interface_t` is the interface that the ReQL logic - uses to create, destroy, and reconfigure databases and tables. */ - real_reql_cluster_interface_t real_reql_cluster_interface( - &mailbox_manager, - semilattice_manager_auth.get_root_view(), - semilattice_manager_cluster.get_root_view(), - &rdb_ctx, - &server_config_client, - &table_meta_client, - multi_table_manager.get(), - table_query_directory_read_manager.get_root_view(), - make_lifetime(name_resolver)); - - artificial_reql_cluster_interface.set_next_reql_cluster_interface( - &real_reql_cluster_interface); - - artificial_reql_cluster_backends_t artificial_reql_cluster_backends( - &artificial_reql_cluster_interface, - &real_reql_cluster_interface, - semilattice_manager_auth.get_root_view(), - semilattice_manager_cluster.get_root_view(), - semilattice_manager_heartbeat.get_root_view(), - directory_read_manager.get_root_view(), - directory_read_manager.get_root_map_view(), - &table_meta_client, - &server_config_client, - &mailbox_manager, - &rdb_ctx, - make_lifetime(name_resolver)); - - /* Kick off a coroutine to log any outdated indexes. */ - outdated_index_issue_tracker_t::log_outdated_indexes( - multi_table_manager.get(), - semilattice_manager_cluster.get_root_view()->get(), - stop_cond); - - /* `jobs_manager_t` keeps track of all of the running jobs on this server. - When the user reads the `rethinkdb.jobs` table, it sends messages to the - `jobs_manager_t` on each server to get information about running jobs. */ - jobs_manager_t jobs_manager( - &mailbox_manager, - server_id, - &rdb_ctx, - /* A `table_persistence_interface` is only instantiated when - `i_am_a_server` is true, and a `nullptr` otherwise. */ - table_persistence_interface.get_or_null(), - multi_table_manager.get()); - - /* When the user reads the `rethinkdb.stats` table, it sends messages to the - `stat_manager_t` on each server to get the stats information. */ - stat_manager_t stat_manager(&mailbox_manager, server_id); - - /* `real_reql_cluster_interface_t` needs access to the admin tables so that - it can return rows from the `table_status` and `table_config` artificial - tables when the user calls the corresponding porcelains. But - `admin_artificial_tables_t` needs access to the - `real_reql_cluster_interface_t` because `table_config` needs to be able to - run distribution queries. The simplest solution is for them to have - references to each other. This is the place where we "close the loop". */ - real_reql_cluster_interface.artificial_reql_cluster_interface = - &artificial_reql_cluster_interface; - - /* `rdb_context_t` needs access to the `reql_cluster_interface_t` so that it - can find tables and run meta-queries, but the `real_reql_cluster_interface_t` - needs access to the `rdb_context_t` so that it can construct instances of - `cluster_namespace_interface_t`. Again, we solve this problem by having a - circular reference. Note that the cluster interface is a chain of command, - the `artificial_reql_cluster_interface` proxies to the - `real_reql_cluster_interface`. */ - rdb_ctx.cluster_interface = &artificial_reql_cluster_interface; - - /* `memory_checker` periodically checks to see if we are using swap - memory, and will log a warning. */ - scoped_ptr_t memory_checker; - if (i_am_a_server) { - memory_checker.init(new memory_checker_t()); - } - - /* When the user reads the `rethinkdb.current_issues` table, it sends - messages to the `local_issue_server` on each server to get the issues - information. */ - scoped_ptr_t local_issue_server; - if (i_am_a_server) { - local_issue_server.init(new local_issue_server_t( - &mailbox_manager, - log_writer.get_log_write_issue_tracker(), - memory_checker->get_memory_issue_tracker())); - } - - proc_directory_metadata_t initial_proc_directory { - RETHINKDB_VERSION_STR, - current_microtime(), - getpid(), - str_gethostname(), - /* Note we'll update `reql_port` and `http_port` later, once final values - are available */ - static_cast(connectivity_cluster_run->get_port()), - static_cast(serve_info.ports.reql_port), - serve_info.ports.http_admin_is_disabled - ? optional() - : optional(serve_info.ports.http_port), - connectivity_cluster_run->get_canonical_addresses(), - serve_info.argv }; - cluster_directory_metadata_t initial_directory( - server_id, - connectivity_cluster.get_me(), - initial_proc_directory, - 0, /* we'll fill `actual_cache_size_bytes` in later */ - multi_table_manager->get_multi_table_manager_bcard(), - jobs_manager.get_business_card(), - stat_manager.get_address(), - log_server.get_business_card(), - i_am_a_server - ? local_issue_server->get_bcard() - : local_issue_bcard_t(), - i_am_a_server - ? server_config_server->get_config()->get() - : server_config_versioned_t(), - i_am_a_server - ? make_optional(server_config_server->get_business_card()) - : optional(), - i_am_a_server ? SERVER_PEER : PROXY_PEER); - - /* `our_root_directory_variable` is the value we'll send out over the network - in our directory to all the other servers. */ - watchable_variable_t - our_root_directory_variable(initial_directory); - - /* These will take care of updating the directory every time our cache size - or server config changes. They also fill in the initial values. */ - scoped_ptr_t > - actual_cache_size_directory_copier; - scoped_ptr_t > - server_config_directory_copier; - if (i_am_a_server) { - actual_cache_size_directory_copier.init( - new watchable_field_copier_t< - uint64_t, cluster_directory_metadata_t>( - &cluster_directory_metadata_t::actual_cache_size_bytes, - server_config_server->get_actual_cache_size_bytes(), - &our_root_directory_variable)); - server_config_directory_copier.init( - new watchable_field_copier_t< - server_config_versioned_t, cluster_directory_metadata_t>( - &cluster_directory_metadata_t::server_config, - server_config_server->get_config(), - &our_root_directory_variable)); - } - - /* These `directory_*_write_manager_t`s are the counterparts to the - `directory_*_read_manager_t`s earlier in this file. These are responsible for - sending directory information over the network; the `read_manager_t`s are - responsible for receiving the transmissions. */ - - directory_write_manager_t directory_write_manager( - &connectivity_cluster, 'D', our_root_directory_variable.get_watchable()); - - directory_map_write_manager_t server_connection_write_manager( - &connectivity_cluster, 'C', network_logger.get_local_connections_map()); - - scoped_ptr_t > - table_directory_write_manager; - scoped_ptr_t, table_query_bcard_t> > - table_query_directory_write_manager; - if (i_am_a_server) { - table_directory_write_manager.init( - new directory_map_write_manager_t< - namespace_id_t, table_manager_bcard_t>( - &connectivity_cluster, 'T', - multi_table_manager->get_table_manager_bcards())); - table_query_directory_write_manager.init( - new directory_map_write_manager_t< - std::pair, table_query_bcard_t>( - &connectivity_cluster, 'Q', - multi_table_manager->get_table_query_bcards())); - } - - { - /* The `rdb_query_server_t` listens for client requests and processes the - queries it receives. */ - rdb_query_server_t rdb_query_server( - serve_info.ports.local_addresses_driver, - serve_info.ports.reql_port, - &rdb_ctx, - &server_config_client, - server_id, - serve_info.tls_configs.driver.get()); - logNTC("Listening for client driver connections on port %d\n", - rdb_query_server.get_port()); - /* If `serve_info.ports.reql_port` was zero then the OS assigned us a - port, so we need to update the directory. */ - our_root_directory_variable.apply_atomic_op( - [&](cluster_directory_metadata_t *md) -> bool { - md->proc.reql_port = rdb_query_server.get_port(); - return (md->proc.reql_port != serve_info.ports.reql_port); - }); - - /* `cluster_metadata_persister`, `auth_metadata_persister`, and - `heartbeat_semilattice_metadata_t` are responsible for syncing the - semilattice metadata to disk. */ - scoped_ptr_t > - cluster_metadata_persister; - scoped_ptr_t > - auth_metadata_persister; - scoped_ptr_t > - heartbeat_metadata_persister; - - if (i_am_a_server) { - cluster_metadata_persister.init( - new semilattice_persister_t( - metadata_file, - mdkey_cluster_semilattices(), - semilattice_manager_cluster.get_root_view())); - auth_metadata_persister.init( - new semilattice_persister_t( - metadata_file, - mdkey_auth_semilattices(), - semilattice_manager_auth.get_root_view())); - heartbeat_metadata_persister.init( - new semilattice_persister_t( - metadata_file, - mdkey_heartbeat_semilattices(), - semilattice_manager_heartbeat.get_root_view())); - } - - { - /* The `administrative_http_server_manager_t` serves the web UI. */ - scoped_ptr_t admin_server_ptr; - if (serve_info.ports.http_admin_is_disabled) { - logNTC("Administrative HTTP connections are disabled.\n"); - } else { - // TODO: Pardon me what, but is this how we fail here? - guarantee(serve_info.ports.http_port < 65536); - admin_server_ptr.init( - new administrative_http_server_manager_t( - serve_info.ports.local_addresses_http, - serve_info.ports.http_port, - rdb_query_server.get_http_app(), - serve_info.web_assets, - serve_info.tls_configs.web.get())); - logNTC("Listening for administrative HTTP connections on port %d\n", - admin_server_ptr->get_port()); - /* If `serve_info.ports.http_port` was zero then the OS assigned - us a port, so we need to update the directory. */ - our_root_directory_variable.apply_atomic_op( - [&](cluster_directory_metadata_t *md) -> bool { - *md->proc.http_admin_port = admin_server_ptr->get_port(); - return (*md->proc.http_admin_port != - serve_info.ports.http_port); - }); - } - - std::string addresses_string = - serve_info.ports.get_addresses_string( - serve_info.ports.local_addresses_cluster); - logNTC("Listening on cluster address%s: %s\n", - serve_info.ports.local_addresses_cluster.size() == 1 ? "" : "es", - addresses_string.c_str()); - - addresses_string = - serve_info.ports.get_addresses_string( - serve_info.ports.local_addresses_driver); - logNTC("Listening on driver address%s: %s\n", - serve_info.ports.local_addresses_driver.size() == 1 ? "" : "es", - addresses_string.c_str()); - - addresses_string = - serve_info.ports.get_addresses_string( - serve_info.ports.local_addresses_http); - logNTC("Listening on http address%s: %s\n", - serve_info.ports.local_addresses_http.size() == 1 ? "" : "es", - addresses_string.c_str()); - - if (!service_address_ports_t::is_bind_all(serve_info.ports.local_addresses)) { - if(serve_info.config_file) { - logNTC("To fully expose RethinkDB on the network, bind to " - "all addresses by adding `bind=all' to the config " - "file (%s).", (*serve_info.config_file).c_str()); - } else { - logNTC("To fully expose RethinkDB on the network, bind to " - "all addresses by running rethinkdb with the `--bind " - "all` command line option."); - } - } - - if (i_am_a_server) { - logNTC("Server ready, \"%s\" %s\n", - server_config_server->get_config() - ->get().config.name.c_str(), - server_id.print().c_str()); - } else { - logNTC("Proxy ready, %s", server_id.print().c_str()); - } - - /* This is the end of the startup process. `stop_cond` will be pulsed - when it's time for the server to shut down. */ - stop_cond->wait_lazily_unordered(); - logNTC("Server got %s; shutting down...", stop_cond->format().c_str()); - } - - cond_t non_interruptor; - if (i_am_a_server) { - cluster_metadata_persister->stop_and_flush(&non_interruptor); - auth_metadata_persister->stop_and_flush(&non_interruptor); - } - - logNTC("Shutting down client connections...\n"); - } - logNTC("All client connections closed.\n"); - logNTC("Shutting down storage engine... (This may take a while if you had a lot of unflushed data in the writeback cache.)\n"); - } - logNTC("Storage engine shut down.\n"); - - } catch (const address_in_use_exc_t &ex) { - logERR("%s.\n", ex.what()); - return false; - } catch (const tcp_socket_exc_t &ex) { - logERR("%s.\n", ex.what()); - return false; - } - - return true; -} - -bool serve(io_backender_t *io_backender, - const base_path_t &base_path, - metadata_file_t *metadata_file, - const serve_info_t &serve_info, - os_signal_cond_t *stop_cond) { - return do_serve(io_backender, - true, - base_path, - metadata_file, - serve_info, - stop_cond); -} - -bool serve_proxy(const serve_info_t &serve_info, - const std::string &initial_password, - os_signal_cond_t *stop_cond) { - // TODO: filepath doesn't _seem_ ignored. - // filepath and persistent_file are ignored for proxies, so we use the empty string & NULL respectively. - return do_serve(nullptr, - false, - base_path_t(""), - nullptr, - serve_info, - stop_cond, - initial_password); -} diff --git a/src/clustering/administration/main/serve.hpp b/src/clustering/administration/main/serve.hpp deleted file mode 100644 index ecbd7945838..00000000000 --- a/src/clustering/administration/main/serve.hpp +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_MAIN_SERVE_HPP_ -#define CLUSTERING_ADMINISTRATION_MAIN_SERVE_HPP_ - -#include -#include -#include -#include -#include - -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/persist/file.hpp" -#include "arch/address.hpp" -#include "arch/io/openssl.hpp" - -class os_signal_cond_t; - -class invalid_port_exc_t : public std::exception { -public: - invalid_port_exc_t(const std::string& name, int port, int port_offset) { - if (port_offset == 0) { - info = strprintf("%s has a value (%d) above the maximum allowed port (%d).", - name.c_str(), port, MAX_PORT); - } else { - info = strprintf("%s has a value (%d) above the maximum allowed port (%d)." - " Note port_offset is set to %d which may cause this error.", - name.c_str(), port, MAX_PORT, port_offset); - } - } - ~invalid_port_exc_t() throw () { } - const char *what() const throw () { - return info.c_str(); - } -private: - std::string info; -}; - -inline void sanitize_port(int port, const char *name, int port_offset) { - if (port >= MAX_PORT) { - throw invalid_port_exc_t(name, port, port_offset); - } -} - -struct service_address_ports_t { - service_address_ports_t() : - port(0), - client_port(0), - http_port(0), - reql_port(0), - port_offset(0) { } - - service_address_ports_t(const std::set &_local_addresses, - const std::set &_local_addresses_cluster, - const std::set &_local_addresses_driver, - const std::set &_local_addresses_http, - const peer_address_t &_canonical_addresses, - int _port, - int _client_port, - bool _http_admin_is_disabled, - int _http_port, - int _reql_port, - int _port_offset) : - local_addresses(_local_addresses), - local_addresses_cluster(_local_addresses_cluster), - local_addresses_driver(_local_addresses_driver), - local_addresses_http(_local_addresses_http), - canonical_addresses(_canonical_addresses), - port(_port), - client_port(_client_port), - http_admin_is_disabled(_http_admin_is_disabled), - http_port(_http_port), - reql_port(_reql_port), - port_offset(_port_offset) - { - sanitize_port(port, "port", port_offset); - sanitize_port(client_port, "client_port", port_offset); - sanitize_port(http_port, "http_port", port_offset); - sanitize_port(reql_port, "reql_port", port_offset); - } - - static std::string get_addresses_string(std::set actual_addresses); - - static bool is_bind_all(const std::set &addresses); - - std::set local_addresses; - std::set local_addresses_cluster; - std::set local_addresses_driver; - std::set local_addresses_http; - - peer_address_t canonical_addresses; - int port; - int client_port; - bool http_admin_is_disabled; - int http_port; - int reql_port; - int port_offset; -}; - -typedef std::shared_ptr shared_ssl_ctx_t; - -class tls_configs_t { -public: - shared_ssl_ctx_t web; - shared_ssl_ctx_t driver; - shared_ssl_ctx_t cluster; -}; - -peer_address_set_t look_up_peers_addresses(const std::vector &names); - -class serve_info_t { -public: - serve_info_t(std::vector &&_joins, - std::string &&_reql_http_proxy, - std::string &&_web_assets, - service_address_ports_t _ports, - optional _config_file, - std::vector &&_argv, - const int _join_delay_secs, - const int _node_reconnect_timeout_secs, - tls_configs_t _tls_configs) : - joins(std::move(_joins)), - reql_http_proxy(std::move(_reql_http_proxy)), - web_assets(std::move(_web_assets)), - ports(_ports), - config_file(_config_file), - argv(std::move(_argv)), - join_delay_secs(_join_delay_secs), - node_reconnect_timeout_secs(_node_reconnect_timeout_secs) - { - tls_configs = _tls_configs; - } - - void look_up_peers() { - peers = look_up_peers_addresses(joins); - } - - const std::vector joins; - peer_address_set_t peers; - std::string reql_http_proxy; - std::string web_assets; - service_address_ports_t ports; - optional config_file; - /* The original arguments, so we can display them in `server_status`. All the - argument parsing has already been completed at this point. */ - std::vector argv; - int join_delay_secs; - int node_reconnect_timeout_secs; - tls_configs_t tls_configs; -}; - -/* This has been factored out from `command_line.hpp` because it takes a very -long time to compile. */ - -bool serve(io_backender_t *io_backender, - const base_path_t &base_path, - metadata_file_t *metadata_file, - const serve_info_t &serve_info, - os_signal_cond_t *stop_cond); - -bool serve_proxy(const serve_info_t& serve_info, - const std::string &initial_password, - os_signal_cond_t *stop_cond); - -#endif /* CLUSTERING_ADMINISTRATION_MAIN_SERVE_HPP_ */ diff --git a/src/clustering/administration/main/watchable_fields.hpp b/src/clustering/administration/main/watchable_fields.hpp deleted file mode 100644 index 14b6aec5814..00000000000 --- a/src/clustering/administration/main/watchable_fields.hpp +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_MAIN_WATCHABLE_FIELDS_HPP_ -#define CLUSTERING_ADMINISTRATION_MAIN_WATCHABLE_FIELDS_HPP_ - -#include - -#include "concurrency/watchable.hpp" -#include "containers/incremental_lenses.hpp" -#include "rpc/connectivity/peer_id.hpp" - -template -class field_getter_t { -public: - explicit field_getter_t(inner_t outer_t::*f) : field(f) { } - - std::map operator()(const std::map &outer) const { - std::map inner; - for (typename std::map::const_iterator it = outer.begin(); it != outer.end(); it++) { - inner[it->first] = it->second.*field; - } - return inner; - } - - /* Support `boost::result_of`'s protocol for indicating functor return types - so that we work with `watchable_t::subview()`. */ - template - class result { - public: - typedef std::map type; - }; - -private: - inner_t outer_t::*const field; -}; - -// `incremental_field_getter_t` does the same as field_getter_t, except that -// it is implemented as an incremental lens and operates on a -// `change_tracking_map_t` instead of an `std::map`. -// `inner_field_getter_t` provides a mapping function, which is applied by -// `incremental_field_getter_t` to every value of a given map. It simply -// extracts a given field from the provided value. -template -class inner_field_getter_t { -public: - explicit inner_field_getter_t(inner_t outer_t::*f) : field(f) { } - - inner_t operator()(const outer_t &outer) const { - return outer.*field; - } - - /* Support `boost::result_of`'s protocol for indicating functor return types - so that we work with `watchable_t::subview()`. */ - template - class result { - public: - typedef inner_t type; - }; - -private: - inner_t outer_t::*const field; -}; -template -class incremental_field_getter_t - : public incremental_map_lens_t > { -public: - explicit incremental_field_getter_t(inner_t outer_t::*f) : - incremental_map_lens_t >( - inner_field_getter_t(f)) { - } -}; - -#endif /* CLUSTERING_ADMINISTRATION_MAIN_WATCHABLE_FIELDS_HPP_ */ diff --git a/src/clustering/administration/main/windows_service.cc b/src/clustering/administration/main/windows_service.cc deleted file mode 100644 index 34178acae52..00000000000 --- a/src/clustering/administration/main/windows_service.cc +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2016 RethinkDB, all rights reserved. -#include "clustering/administration/main/windows_service.hpp" - -#ifdef _WIN32 -#include "arch/runtime/thread_pool.hpp" -#include "arch/types.hpp" - -// For some reason the DELETE symbol doesn't get defined properly. -// (it should get defined by Winnt.h which is part of Windows.h) -#define WIN_ACCESS_DELETE (0x00010000L) - -DWORD windows_service_control_handler( - DWORD dw_control, - DWORD dw_event_type, - LPVOID, - LPVOID) { - switch (dw_control) { - case SERVICE_CONTROL_INTERROGATE: - return NO_ERROR; - case SERVICE_CONTROL_SHUTDOWN: // fallthru - case SERVICE_CONTROL_STOP: - thread_pool_t::interrupt_handler(CTRL_SHUTDOWN_EVENT); - return NO_ERROR; - default: - return ERROR_CALL_NOT_IMPLEMENTED; - } -} - -void windows_service_main_function( - const std::function &main_fun, - DWORD argc, - char *argv[]) { - SERVICE_STATUS_HANDLE status_handle = - RegisterServiceCtrlHandlerEx("", windows_service_control_handler, nullptr); - SERVICE_STATUS status; - memset(&status, 0, sizeof(status)); - status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; - status.dwCurrentState = SERVICE_RUNNING; - status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP; - SetServiceStatus(status_handle, &status); - - int res = main_fun(argc, argv); - - status.dwCurrentState = SERVICE_STOPPED; - status.dwWin32ExitCode = res == 0 ? 0 : ERROR_SERVICE_SPECIFIC_ERROR; - status.dwServiceSpecificExitCode = res; - SetServiceStatus(status_handle, &status); -} - -std::string escape_windows_shell_arg(const std::string &arg) { - std::string res; - res += "\""; - for (char ch : arg) { - if (ch == '\\') { - res += "\\\\"; - } else if (ch == '"') { - res += "\\\""; - } else { - res += ch; - } - } - res += "\""; - return res; -} - -class service_manager_t { -public: - explicit service_manager_t(DWORD desired_access) { - sc_manager = OpenSCManager( - nullptr /* local computer */, - nullptr, - desired_access); - if (sc_manager == nullptr) { - DWORD err = GetLastError(); - if (err == ERROR_ACCESS_DENIED) { - throw windows_privilege_exc_t(); - } - fprintf(stderr, "Error opening service manager: %s\n", winerr_string(err).c_str()); - } - } - ~service_manager_t() { - if (sc_manager != nullptr) { - CloseServiceHandle(sc_manager); - } - } - SC_HANDLE get() { - return sc_manager; - } -private: - SC_HANDLE sc_manager; -}; - -class service_handle_t { -public: - explicit service_handle_t(SC_HANDLE existing_handle) { - service = existing_handle; - } - service_handle_t( - service_manager_t *sc_manager, - const std::string &service_name, - DWORD desired_access) { - service = OpenService( - sc_manager->get(), - service_name.c_str(), - desired_access); - if (service == nullptr) { - DWORD err = GetLastError(); - if (err == ERROR_ACCESS_DENIED) { - throw windows_privilege_exc_t(); - } - fprintf(stderr, "Opening the service `%s` failed: %s\n", - service_name.c_str(), winerr_string(err).c_str()); - } - } - ~service_handle_t() { - if (service != nullptr) { - CloseServiceHandle(service); - } - } - SC_HANDLE get() { - return service; - } -private: - SC_HANDLE service; -}; - -bool install_windows_service( - const std::string &service_name, - const std::string &display_name, - const std::string &bin_path, - const std::vector &arguments, - const char *account_name, - const char *account_password) { - - // The following code is loosely based on the example at - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683500(v=vs.85).aspx - - // Get a handle to the SCM database. - service_manager_t sc_manager(SC_MANAGER_CREATE_SERVICE); - if (sc_manager.get() == nullptr) { - return false; - } - - // Build the service path (escaped binary path + arguments) - std::string service_path = escape_windows_shell_arg(bin_path); - for (const auto &arg : arguments) { - service_path += " "; - service_path += escape_windows_shell_arg(arg); - } - - // Create the service - service_handle_t service(CreateService( - sc_manager.get(), - service_name.c_str(), - display_name.c_str(), - SERVICE_ALL_ACCESS, - SERVICE_WIN32_OWN_PROCESS, - SERVICE_AUTO_START /* start the service on system startup */, - SERVICE_ERROR_NORMAL, - service_path.c_str(), - nullptr /* no load ordering group */, - nullptr /* no tag identifier */, - nullptr /* no dependencies */, - account_name, - account_password)); - - if (service.get() == nullptr) { - DWORD err = GetLastError(); - if (err == ERROR_ACCESS_DENIED) { - throw windows_privilege_exc_t(); - } - fprintf(stderr, "Creating the service `%s` failed: %s\n", - service_name.c_str(), winerr_string(err).c_str()); - return false; - } - - // Set the service description - SERVICE_DESCRIPTION description; - std::string description_str = "RethinkDB database server (" + service_name + ")."; - description.lpDescription = const_cast(description_str.c_str()); - if (!ChangeServiceConfig2(service.get(), SERVICE_CONFIG_DESCRIPTION, &description)) { - DWORD err = GetLastError(); - fprintf(stderr, "Failed to change the service description for service `%s`: %s\n", - service_name.c_str(), winerr_string(err).c_str()); - return false; - } else { - return true; - } -} - -bool remove_windows_service(const std::string &service_name) { - - // Get a handle to the SCM database. - service_manager_t sc_manager(SC_MANAGER_CONNECT); - if (sc_manager.get() == nullptr) { - return false; - } - - // Open the service - service_handle_t service(&sc_manager, service_name, WIN_ACCESS_DELETE); - if (service.get() == nullptr) { - return false; - } - - // Delete the service - if (!DeleteService(service.get())) { - DWORD err = GetLastError(); - if (err == ERROR_ACCESS_DENIED) { - throw windows_privilege_exc_t(); - } - fprintf(stderr, "Deleting the service `%s` failed: %s\n", - service_name.c_str(), winerr_string(err).c_str()); - return false; - } else { - return true; - } -} - -bool start_windows_service(const std::string &service_name) { - // Get a handle to the SCM database. - service_manager_t sc_manager(SC_MANAGER_CONNECT); - if (sc_manager.get() == nullptr) { - return false; - } - - // Open the service - service_handle_t service(&sc_manager, service_name, SERVICE_START); - if (service.get() == nullptr) { - return false; - } - - // Start the service - if (!StartService(service.get(), 0, nullptr)) { - DWORD err = GetLastError(); - if (err == ERROR_ACCESS_DENIED) { - throw windows_privilege_exc_t(); - } else if (err == ERROR_SERVICE_ALREADY_RUNNING) { - fprintf(stderr, "The service `%s` is already running.\n", - service_name.c_str()); - return true; - } - fprintf(stderr, "Starting the service `%s` failed: %s\n", - service_name.c_str(), winerr_string(err).c_str()); - return false; - } else { - return true; - } -} - -bool stop_windows_service(const std::string &service_name) { - // Get a handle to the SCM database. - service_manager_t sc_manager(SC_MANAGER_CONNECT); - if (sc_manager.get() == nullptr) { - return false; - } - - // Open the service - service_handle_t service(&sc_manager, service_name, SERVICE_STOP); - if (service.get() == nullptr) { - return false; - } - - // Stop the service - SERVICE_STATUS last_status; - if (!ControlService(service.get(), SERVICE_CONTROL_STOP, &last_status)) { - DWORD err = GetLastError(); - if (err == ERROR_ACCESS_DENIED) { - throw windows_privilege_exc_t(); - } else if (err == ERROR_SERVICE_NOT_ACTIVE) { - fprintf(stderr, "The service `%s` is already stopped.\n", - service_name.c_str()); - return true; - } - fprintf(stderr, "Stopping the service `%s` failed: %s\n", - service_name.c_str(), winerr_string(err).c_str()); - return false; - } else { - return true; - } -} - -#endif /* _WIN32 */ diff --git a/src/clustering/administration/main/windows_service.hpp b/src/clustering/administration/main/windows_service.hpp deleted file mode 100644 index d4ab71c2a9b..00000000000 --- a/src/clustering/administration/main/windows_service.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2016 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_MAIN_WINDOWS_SERVICE_HPP_ -#define CLUSTERING_ADMINISTRATION_MAIN_WINDOWS_SERVICE_HPP_ -#ifdef _WIN32 - -#include -#include -#include -#include - -#include "errors.hpp" - -#include - - -class windows_privilege_exc_t : public std::exception { -}; - -// Helper function to escape a path or argument -// For example -// c:\test folder\test.exe -// gets turned into -// "c:\\test folder\\test.exe" -std::string escape_windows_shell_arg(const std::string &arg); - -void windows_service_main_function( - const std::function &main_fun, - DWORD argc, - char *argv[]); - -bool install_windows_service( - const std::string &service_name, - const std::string &display_name, - const std::string &bin_path, - const std::vector &arguments, - const char *account_name = nullptr /* LocalSystem account if nullptr */, - const char *account_password = nullptr); -bool remove_windows_service(const std::string &service_name); - -bool start_windows_service(const std::string &service_name); -bool stop_windows_service(const std::string &service_name); - -#endif /* _WIN32 */ -#endif /* CLUSTERING_ADMINISTRATION_MAIN_WINDOWS_SERVICE_HPP_ */ diff --git a/src/clustering/administration/metadata.cc b/src/clustering/administration/metadata.cc deleted file mode 100644 index d03a378c00d..00000000000 --- a/src/clustering/administration/metadata.cc +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/metadata.hpp" - -#include "clustering/administration/servers/server_metadata.hpp" -#include "containers/archive/archive.hpp" -#include "containers/archive/boost_types.hpp" -#include "containers/archive/stl_types.hpp" -#include "containers/archive/versioned.hpp" -#include "rdb_protocol/protocol.hpp" -#include "stl_utils.hpp" - -RDB_IMPL_SERIALIZABLE_1_SINCE_v2_1(cluster_semilattice_metadata_t, databases); -RDB_IMPL_SEMILATTICE_JOINABLE_1(cluster_semilattice_metadata_t, databases); -RDB_IMPL_EQUALITY_COMPARABLE_1(cluster_semilattice_metadata_t, databases); - -RDB_IMPL_SERIALIZABLE_1_SINCE_v2_3(auth_semilattice_metadata_t, m_users); -RDB_IMPL_SEMILATTICE_JOINABLE_1(auth_semilattice_metadata_t, m_users); -RDB_IMPL_EQUALITY_COMPARABLE_1(auth_semilattice_metadata_t, m_users); - -RDB_IMPL_SERIALIZABLE_1_SINCE_v2_1(heartbeat_semilattice_metadata_t, heartbeat_timeout); -RDB_IMPL_SEMILATTICE_JOINABLE_1(heartbeat_semilattice_metadata_t, heartbeat_timeout); -RDB_IMPL_EQUALITY_COMPARABLE_1(heartbeat_semilattice_metadata_t, heartbeat_timeout); - -RDB_IMPL_SERIALIZABLE_9_FOR_CLUSTER(proc_directory_metadata_t, - version, - time_started, - pid, - hostname, - cluster_port, - reql_port, - http_admin_port, - canonical_addresses, - argv); - -RDB_IMPL_SERIALIZABLE_12_FOR_CLUSTER(cluster_directory_metadata_t, - server_id, - peer_id, - proc, - actual_cache_size_bytes, - multi_table_manager_bcard, - jobs_mailbox, - get_stats_mailbox_address, - log_mailbox, - local_issue_bcard, - server_config, - server_config_business_card, - peer_type); - -bool search_db_metadata_by_name( - const databases_semilattice_metadata_t &metadata, - const name_string_t &name, - database_id_t *id_out, - admin_err_t *error_out) { - size_t found = 0; - for (const auto &pair : metadata.databases) { - if (!pair.second.is_deleted() && pair.second.get_ref().name.get_ref() == name) { - *id_out = pair.first; - ++found; - } - } - if (found == 0) { - *error_out = admin_err_t{ - strprintf("Database `%s` does not exist.", name.c_str()), - query_state_t::FAILED}; - return false; - } else if (found >= 2) { - *error_out = admin_err_t{ - strprintf("Database `%s` is ambiguous; there are multiple " - "databases with that name.", name.c_str()), - query_state_t::FAILED}; - return false; - } else { - return true; - } -} - - diff --git a/src/clustering/administration/metadata.hpp b/src/clustering/administration/metadata.hpp deleted file mode 100644 index 986a165e99b..00000000000 --- a/src/clustering/administration/metadata.hpp +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_METADATA_HPP_ -#define CLUSTERING_ADMINISTRATION_METADATA_HPP_ - -#include -#include -#include -#include - -#include "clustering/administration/auth/user.hpp" -#include "clustering/administration/auth/username.hpp" -#include "clustering/administration/issues/local.hpp" -#include "clustering/administration/jobs/report.hpp" -#include "clustering/administration/logs/log_transfer.hpp" -#include "clustering/administration/servers/server_metadata.hpp" -#include "clustering/administration/stats/stat_manager.hpp" -#include "clustering/administration/tables/database_metadata.hpp" -#include "containers/optional.hpp" -#include "clustering/table_manager/table_metadata.hpp" -#include "arch/address.hpp" -#include "rpc/connectivity/peer_id.hpp" -#include "rpc/semilattice/joins/macros.hpp" -#include "rpc/semilattice/joins/versioned.hpp" -#include "rpc/serialize_macros.hpp" - -class cluster_semilattice_metadata_t { -public: - cluster_semilattice_metadata_t() { } - - databases_semilattice_metadata_t databases; -}; - -RDB_DECLARE_SERIALIZABLE(cluster_semilattice_metadata_t); -RDB_DECLARE_SEMILATTICE_JOINABLE(cluster_semilattice_metadata_t); -RDB_DECLARE_EQUALITY_COMPARABLE(cluster_semilattice_metadata_t); - -class auth_semilattice_metadata_t { -public: - // For deserialization only - auth_semilattice_metadata_t() { } - - explicit auth_semilattice_metadata_t(const std::string &initial_password) - : m_users({create_initial_admin_pair(initial_password)}) { } - - static std::pair>> - create_initial_admin_pair(const std::string &initial_password) { - // Generate a timestamp that's minus our current time, so that the oldest - // initial password wins. Unless the initial password is empty, which - // should always lose. - time_t version_ts = std::numeric_limits::min(); - if (!initial_password.empty()) { - time_t current_time = time(nullptr); - if (current_time > 0) { - version_ts = -current_time; - } else { - logWRN("The system time seems to be incorrectly set. Metadata " - "versioning will behave unexpectedly."); - } - } - // Use a single iteration for better efficiency when starting out with an empty - // password. - uint32_t iterations = initial_password.empty() - ? 1 - : auth::password_t::default_iteration_count; - auth::password_t pw(initial_password, iterations); - return std::make_pair( - auth::username_t("admin"), - versioned_t>::make_with_manual_timestamp( - version_ts, - make_optional(auth::user_t(std::move(pw))))); - } - - std::map>> m_users; -}; - -RDB_DECLARE_SERIALIZABLE(auth_semilattice_metadata_t); -RDB_DECLARE_SEMILATTICE_JOINABLE(auth_semilattice_metadata_t); -RDB_DECLARE_EQUALITY_COMPARABLE(auth_semilattice_metadata_t); - -class heartbeat_semilattice_metadata_t { -public: - heartbeat_semilattice_metadata_t() - : heartbeat_timeout(10000) { } - - versioned_t heartbeat_timeout; -}; - -RDB_DECLARE_SERIALIZABLE(heartbeat_semilattice_metadata_t); -RDB_DECLARE_SEMILATTICE_JOINABLE(heartbeat_semilattice_metadata_t); -RDB_DECLARE_EQUALITY_COMPARABLE(heartbeat_semilattice_metadata_t); - -enum cluster_directory_peer_type_t { - SERVER_PEER, - PROXY_PEER -}; - -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE(cluster_directory_peer_type_t, int8_t, SERVER_PEER, PROXY_PEER); - -/* `proc_directory_metadata_t` is a part of `cluster_directory_metadata_t` that doesn't -change after the server starts and is only used for displaying in `server_status`. The -reason it's in a separate structure is to keep `cluster_directory_metadata_t` from being -too complicated. */ -class proc_directory_metadata_t { -public: - std::string version; /* server version string, e.g. "rethinkdb 1.X.Y ..." */ - microtime_t time_started; - int64_t pid; /* really a `pid_t`, but we need a platform-independent type */ - std::string hostname; - uint16_t cluster_port, reql_port; - optional http_admin_port; - std::set canonical_addresses; - std::vector argv; -}; - -RDB_DECLARE_SERIALIZABLE(proc_directory_metadata_t); - -class cluster_directory_metadata_t { -public: - cluster_directory_metadata_t() { } - cluster_directory_metadata_t( - server_id_t _server_id, - peer_id_t _peer_id, - const proc_directory_metadata_t &_proc, - uint64_t _actual_cache_size_bytes, - const multi_table_manager_bcard_t &mtmbc, - const jobs_manager_business_card_t& _jobs_mailbox, - const get_stats_mailbox_address_t& _stats_mailbox, - const log_server_business_card_t &lmb, - const local_issue_bcard_t &lib, - const server_config_versioned_t &sc, - const optional &scbc, - cluster_directory_peer_type_t _peer_type) : - server_id(_server_id), - peer_id(_peer_id), - proc(_proc), - actual_cache_size_bytes(_actual_cache_size_bytes), - multi_table_manager_bcard(mtmbc), - jobs_mailbox(_jobs_mailbox), - get_stats_mailbox_address(_stats_mailbox), - log_mailbox(lmb), - local_issue_bcard(lib), - server_config(sc), - server_config_business_card(scbc), - peer_type(_peer_type) { } - /* Move constructor */ - cluster_directory_metadata_t(const cluster_directory_metadata_t &) = default; - cluster_directory_metadata_t &operator=(const cluster_directory_metadata_t &) - = default; - - server_id_t server_id; - - /* this is redundant, since a directory entry is always associated with a peer */ - peer_id_t peer_id; - - /* This group of fields are for showing in `rethinkdb.server_status` */ - proc_directory_metadata_t proc; - uint64_t actual_cache_size_bytes; /* might be user-set or automatically picked */ - - multi_table_manager_bcard_t multi_table_manager_bcard; - jobs_manager_business_card_t jobs_mailbox; - get_stats_mailbox_address_t get_stats_mailbox_address; - log_server_business_card_t log_mailbox; - local_issue_bcard_t local_issue_bcard; - - /* For proxies, `server_config` is meaningless and `server_config_business_card` is - empty. */ - server_config_versioned_t server_config; - optional server_config_business_card; - - cluster_directory_peer_type_t peer_type; -}; - -RDB_DECLARE_SERIALIZABLE_FOR_CLUSTER(cluster_directory_metadata_t); - -template -bool search_metadata_by_uuid( - std::map > *map, - const uuid_u &uuid, - typename std::map >::iterator *it_out) { - auto it = map->find(uuid); - if (it != map->end() && !it->second.is_deleted()) { - *it_out = it; - return true; - } else { - return false; - } -} - -template -bool search_const_metadata_by_uuid( - const std::map > *map, - const uuid_u &uuid, - typename std::map >::const_iterator *it_out) { - auto it = map->find(uuid); - if (it != map->end() && !it->second.is_deleted()) { - *it_out = it; - return true; - } else { - return false; - } -} - -bool search_db_metadata_by_name( - const databases_semilattice_metadata_t &metadata, - const name_string_t &name, - database_id_t *id_out, - admin_err_t *error_out); - -#endif // CLUSTERING_ADMINISTRATION_METADATA_HPP_ diff --git a/src/clustering/administration/namespace_interface_repository.cc b/src/clustering/administration/namespace_interface_repository.cc deleted file mode 100644 index adc967e4e69..00000000000 --- a/src/clustering/administration/namespace_interface_repository.cc +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "clustering/administration/namespace_interface_repository.hpp" - -#include "arch/timing.hpp" -#include "clustering/query_routing/table_query_client.hpp" -#include "concurrency/cross_thread_signal.hpp" -#include "concurrency/cross_thread_watchable.hpp" - -#define NAMESPACE_INTERFACE_EXPIRATION_MS (60 * 1000) - -struct namespace_repo_t::namespace_cache_t { -public: - std::map > entries; - auto_drainer_t drainer; -}; - -struct namespace_repo_t::namespace_cache_entry_t : - public namespace_interface_access_t::ref_tracker_t -{ -public: - void add_ref() { - ref_count++; - if (ref_count == 1) { - if (pulse_when_ref_count_becomes_nonzero) { - pulse_when_ref_count_becomes_nonzero-> - pulse_if_not_already_pulsed(); - } - } - } - void release() { - ref_count--; - if (ref_count == 0) { - if (pulse_when_ref_count_becomes_zero) { - pulse_when_ref_count_becomes_zero-> - pulse_if_not_already_pulsed(); - } - } - } - - promise_t namespace_interface; - int ref_count; - cond_t *pulse_when_ref_count_becomes_zero; - cond_t *pulse_when_ref_count_becomes_nonzero; -}; - -namespace_repo_t::namespace_repo_t( - mailbox_manager_t *_mailbox_manager, - watchable_map_t *_directory, - multi_table_manager_t *_multi_table_manager, - rdb_context_t *_ctx, - table_meta_client_t *table_meta_client) - : mailbox_manager(_mailbox_manager), - directory(_directory), - multi_table_manager(_multi_table_manager), - ctx(_ctx), - m_table_meta_client(table_meta_client) - { } - -namespace_repo_t::~namespace_repo_t() { } - -void copy_region_maps_to_thread( - const std::map > &from, - one_per_thread_t > > *to, - int thread, UNUSED auto_drainer_t::lock_t keepalive) { - on_thread_t th((threadnum_t(thread))); - *to->get() = from; -} - -void namespace_repo_t::create_and_destroy_namespace_interface( - namespace_cache_t *cache, - const namespace_id_t &table_id, - auto_drainer_t::lock_t keepalive) - THROWS_NOTHING { - keepalive.assert_is_holding(&cache->drainer); - threadnum_t thread = get_thread_id(); - - namespace_cache_entry_t *cache_entry = - cache->entries.find(table_id)->second.get(); - guarantee(!cache_entry->namespace_interface.get_ready_signal()->is_pulsed()); - - /* We want to extract the entries in the directory that are for this table. This is - the job of `table_directory_converter_t`. Since `directory` is thread-local, we have - to construct it on the home thread. So we switch to the home thread, construct it and - a `cross_thread_watchable_map_var_t`, then switch back. When the destructors are - called, we do the same in reverse, making good use of RAII. */ - on_thread_t switch_to_home_thread(home_thread()); - class table_directory_converter_t : - public watchable_map_transform_t< - directory_key_t, table_query_bcard_t, - std::pair, table_query_bcard_t> { - public: - table_directory_converter_t( - watchable_map_t *_directory, - const namespace_id_t &_table_id) : - watchable_map_transform_t(_directory), - table_id(_table_id) - { } - bool key_1_to_2( - const directory_key_t &key1, - std::pair *key2_out) { - if (key1.second.first == table_id) { - key2_out->first = key1.first; - key2_out->second = key1.second.second; - return true; - } else { - return false; - } - } - void value_1_to_2( - const table_query_bcard_t *value1, - const table_query_bcard_t **value2_out) { - *value2_out = value1; - } - bool key_2_to_1( - const std::pair &key2, - directory_key_t *key1_out) { - key1_out->first = key2.first; - key1_out->second.first = table_id; - key1_out->second.second = key2.second; - return true; - } - private: - namespace_id_t table_id; - } directory_converter_on_home_thread(directory, table_id); - cross_thread_watchable_map_var_t, table_query_bcard_t> - directory_converter_on_this_thread(&directory_converter_on_home_thread, thread); - on_thread_t switch_back(thread); - - table_query_client_t query_client( - table_id, - mailbox_manager, - directory_converter_on_this_thread.get_watchable(), - multi_table_manager, - ctx, - m_table_meta_client); - - try { - /* Wait for the table to become available for use */ - wait_interruptible(query_client.get_initial_ready_signal(), - keepalive.get_drain_signal()); - - /* Give the outside world access to `query_client` */ - cache_entry->namespace_interface.pulse(&query_client); - - /* Wait until it's time to shut down */ - while (true) { - while (cache_entry->ref_count != 0) { - cond_t ref_count_is_zero; - assignment_sentry_t notify_if_ref_count_becomes_zero( - &cache_entry->pulse_when_ref_count_becomes_zero, - &ref_count_is_zero); - wait_interruptible(&ref_count_is_zero, keepalive.get_drain_signal()); - } - signal_timer_t expiration_timer; - expiration_timer.start(NAMESPACE_INTERFACE_EXPIRATION_MS); - cond_t ref_count_is_nonzero; - assignment_sentry_t notify_if_ref_count_becomes_nonzero( - &cache_entry->pulse_when_ref_count_becomes_nonzero, - &ref_count_is_nonzero); - wait_any_t waiter(&expiration_timer, &ref_count_is_nonzero); - wait_interruptible(&waiter, keepalive.get_drain_signal()); - if (!ref_count_is_nonzero.is_pulsed()) { - guarantee(cache_entry->ref_count == 0); - /* We waited a whole `NAMESPACE_INTERFACE_EXPIRATION_MS` and - nothing used us. So let's destroy ourselves. */ - break; - } - } - - } catch (const interrupted_exc_t &) { - /* We got here because we were interrupted in the startup process. That - means the `namespace_repo_t` destructor was called, which means there - mustn't exist any `access_t` objects. So ref_count must be 0. */ - guarantee(cache_entry->ref_count == 0); - } - - ASSERT_NO_CORO_WAITING; - cache->entries.erase(table_id); -} - -namespace_interface_access_t namespace_repo_t::get_namespace_interface( - const uuid_u &ns_id, signal_t *interruptor) { - /* Find or create a cache entry for the table. When we find or create the cache, we - need to wait until the `namespace_interface_t *` is actually ready before returning, - but we want to be sure to hold a reference to the cache entry in the meantime. So we - construct `temporary_holder`, which manages a reference to the `cache_entry`, but has - its namespace interface set to `NULL`. Then when the real table is ready, we - construct a real `namespace_interface_access_t` with a non-`NULL` namespace - interface, and then delete `temporary_holder`. */ - namespace_interface_access_t temporary_holder; - namespace_cache_entry_t *cache_entry; - { - ASSERT_FINITE_CORO_WAITING; - namespace_cache_t *cache = namespace_caches.get(); - if (cache->entries.find(ns_id) == cache->entries.end()) { - cache_entry = new namespace_cache_entry_t; - cache_entry->ref_count = 0; - cache_entry->pulse_when_ref_count_becomes_zero = nullptr; - cache_entry->pulse_when_ref_count_becomes_nonzero = nullptr; - - namespace_id_t id(ns_id); - cache->entries.insert(std::make_pair(id, - scoped_ptr_t(cache_entry))); - - coro_t::spawn_sometime(std::bind( - &namespace_repo_t::create_and_destroy_namespace_interface, this, - cache, ns_id, - auto_drainer_t::lock_t(&cache->drainer))); - } else { - cache_entry = cache->entries[ns_id].get(); - } - } - wait_interruptible(cache_entry->namespace_interface.get_ready_signal(), interruptor); - return namespace_interface_access_t( - cache_entry->namespace_interface.wait(), - cache_entry, - get_thread_id()); -} - diff --git a/src/clustering/administration/namespace_interface_repository.hpp b/src/clustering/administration/namespace_interface_repository.hpp deleted file mode 100644 index 7954ad61738..00000000000 --- a/src/clustering/administration/namespace_interface_repository.hpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_NAMESPACE_INTERFACE_REPOSITORY_HPP_ -#define CLUSTERING_ADMINISTRATION_NAMESPACE_INTERFACE_REPOSITORY_HPP_ - -#include - -#include "clustering/administration/metadata.hpp" -#include "concurrency/auto_drainer.hpp" -#include "concurrency/one_per_thread.hpp" -#include "concurrency/promise.hpp" -#include "containers/clone_ptr.hpp" -#include "containers/incremental_lenses.hpp" -#include "rdb_protocol/real_table.hpp" -#include "rpc/semilattice/view.hpp" - -/* `namespace_repo_t` is a helper class for `real_reql_cluster_interface_t`. It's -responsible for constructing and caching `cluster_namespace_interface_t` objects. */ - -class mailbox_manager_t; -class multi_table_manager_t; -class namespace_interface_t; -class namespaces_directory_metadata_t; -class peer_id_t; -class rdb_context_t; -class signal_t; -class table_meta_client_t; -class uuid_u; -template class watchable_t; - -class namespace_repo_t : public home_thread_mixin_t { -public: - /* Alias to reduce verbosity */ - typedef std::pair > - directory_key_t; - - namespace_repo_t( - mailbox_manager_t *mailbox_manager, - watchable_map_t *directory, - multi_table_manager_t *multi_table_manager, - rdb_context_t *ctx, - table_meta_client_t *table_meta_client); - ~namespace_repo_t(); - - namespace_interface_access_t get_namespace_interface(const namespace_id_t &ns_id, - signal_t *interruptor); - -private: - struct namespace_cache_t; - struct namespace_cache_entry_t; - - void create_and_destroy_namespace_interface( - namespace_cache_t *cache, - const uuid_u &namespace_id, - auto_drainer_t::lock_t keepalive) - THROWS_NOTHING; - - mailbox_manager_t * const mailbox_manager; - watchable_map_t * const directory; - multi_table_manager_t *const multi_table_manager; - rdb_context_t * const ctx; - table_meta_client_t *m_table_meta_client; - - one_per_thread_t namespace_caches; - - auto_drainer_t drainer; - - DISABLE_COPYING(namespace_repo_t); -}; - -#endif /* CLUSTERING_ADMINISTRATION_NAMESPACE_INTERFACE_REPOSITORY_HPP_ */ diff --git a/src/clustering/administration/perfmon_collection_repo.cc b/src/clustering/administration/perfmon_collection_repo.cc deleted file mode 100644 index e4c1f045fe0..00000000000 --- a/src/clustering/administration/perfmon_collection_repo.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "clustering/administration/perfmon_collection_repo.hpp" - -#include "stl_utils.hpp" - -perfmon_collection_repo_t::collections_t::collections_t(perfmon_collection_t *_parent, namespace_id_t id) - : namespace_collection(), serializers_collection(), - parent_membership(_parent, &namespace_collection, uuid_to_str(id)), - namespace_membership(&namespace_collection, &serializers_collection, "serializers") -{ -} - -perfmon_collection_repo_t::perfmon_collection_repo_t(perfmon_collection_t *_parent) - : parent(_parent) -{ } - -perfmon_collection_repo_t::collections_t *perfmon_collection_repo_t::get_perfmon_collections_for_namespace(namespace_id_t n_id) { - if (!std_contains(perfmon_collections, n_id)) { - perfmon_collections.insert( - std::make_pair(n_id, make_scoped(parent, n_id))); - } - return perfmon_collections.find(n_id)->second.get(); -} diff --git a/src/clustering/administration/perfmon_collection_repo.hpp b/src/clustering/administration/perfmon_collection_repo.hpp deleted file mode 100644 index 644a570121d..00000000000 --- a/src/clustering/administration/perfmon_collection_repo.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_PERFMON_COLLECTION_REPO_HPP_ -#define CLUSTERING_ADMINISTRATION_PERFMON_COLLECTION_REPO_HPP_ - -#include - -#include "containers/uuid.hpp" -#include "perfmon/perfmon.hpp" - -class perfmon_collection_repo_t { -public: - class collections_t { - public: - collections_t(perfmon_collection_t *parent, namespace_id_t id); - - perfmon_collection_t namespace_collection; - perfmon_collection_t serializers_collection; - private: - perfmon_membership_t parent_membership; - perfmon_membership_t namespace_membership; - }; - explicit perfmon_collection_repo_t(perfmon_collection_t *); - collections_t *get_perfmon_collections_for_namespace(namespace_id_t); - -private: - perfmon_collection_t *parent; - std::map > perfmon_collections; - - DISABLE_COPYING(perfmon_collection_repo_t); -}; - -#endif /* CLUSTERING_ADMINISTRATION_PERFMON_COLLECTION_REPO_HPP_ */ diff --git a/src/clustering/administration/persist/branch_history_manager.cc b/src/clustering/administration/persist/branch_history_manager.cc deleted file mode 100644 index f51525b0cc2..00000000000 --- a/src/clustering/administration/persist/branch_history_manager.cc +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/persist/branch_history_manager.hpp" - -#include "clustering/administration/persist/file_keys.hpp" - -void real_branch_history_manager_t::erase( - metadata_file_t::write_txn_t *write_txn, - const namespace_id_t &table_id) { - cond_t non_interruptor; - std::set branch_ids; - write_txn->read_many( - mdprefix_branch_birth_certificate().suffix(uuid_to_str(table_id) + "/"), - [&](const std::string &branch_id_str, const branch_birth_certificate_t &) { - branch_id_t branch_id = str_to_uuid(branch_id_str); - branch_ids.insert(branch_id); - }, - &non_interruptor); - for (const branch_id_t &b : branch_ids) { - write_txn->erase( - mdprefix_branch_birth_certificate().suffix( - uuid_to_str(table_id) + "/" + uuid_to_str(b)), - &non_interruptor); - } -} - -real_branch_history_manager_t::real_branch_history_manager_t( - const namespace_id_t &_table_id, - metadata_file_t *_metadata_file, - metadata_file_t::read_txn_t *metadata_read_txn, - signal_t *interruptor): - table_id(_table_id), metadata_file(_metadata_file) -{ - metadata_read_txn->read_many( - mdprefix_branch_birth_certificate().suffix(uuid_to_str(table_id) + "/"), - [&](const std::string &branch_id_str, const branch_birth_certificate_t &bc) { - branch_id_t branch_id = str_to_uuid(branch_id_str); - cache.branches.insert(std::make_pair(branch_id, bc)); - }, - interruptor); -} - -branch_birth_certificate_t -real_branch_history_manager_t::get_branch(const branch_id_t &branch) - const THROWS_ONLY(missing_branch_exc_t) { - assert_thread(); - return cache.get_branch(branch); -} - -bool real_branch_history_manager_t::is_branch_known(const branch_id_t &branch) - const THROWS_NOTHING { - assert_thread(); - return cache.is_branch_known(branch); -} - -void real_branch_history_manager_t::create_branch( - branch_id_t branch_id, - const branch_birth_certificate_t &bc) - THROWS_NOTHING { - assert_thread(); - { - cond_t non_interruptor; - metadata_file_t::write_txn_t write_txn(metadata_file, &non_interruptor); - write_txn.write( - mdprefix_branch_birth_certificate().suffix( - uuid_to_str(table_id) + "/" + uuid_to_str(branch_id)), - bc, - &non_interruptor); - write_txn.commit(); - } - cache.branches.insert(std::make_pair(branch_id, bc)); -} - -void real_branch_history_manager_t::import_branch_history( - const branch_history_t &new_records) - THROWS_NOTHING { - assert_thread(); - { - cond_t non_interruptor; - metadata_file_t::write_txn_t write_txn(metadata_file, &non_interruptor); - for (const auto &pair : new_records.branches) { - if (!is_branch_known(pair.first)) { - write_txn.write( - mdprefix_branch_birth_certificate().suffix( - uuid_to_str(table_id) + "/" + uuid_to_str(pair.first)), - pair.second, - &non_interruptor); - cache.branches.insert(pair); - } - } - write_txn.commit(); - } -} - -void real_branch_history_manager_t::prepare_gc( - std::set *branches_out) - THROWS_NOTHING { - branches_out->clear(); - for (const auto &pair : cache.branches) { - branches_out->insert(pair.first); - } -} - -void real_branch_history_manager_t::perform_gc( - const std::set &remove_branches) - THROWS_NOTHING { - cond_t non_interruptor; - metadata_file_t::write_txn_t write_txn(metadata_file, &non_interruptor); - for (const branch_id_t &bid : remove_branches) { - if (is_branch_known(bid)) { - write_txn.erase( - mdprefix_branch_birth_certificate().suffix( - uuid_to_str(table_id) + "/" + uuid_to_str(bid)), - &non_interruptor); - cache.branches.erase(bid); - } - } - write_txn.commit(); -} - diff --git a/src/clustering/administration/persist/branch_history_manager.hpp b/src/clustering/administration/persist/branch_history_manager.hpp deleted file mode 100644 index e78baa27812..00000000000 --- a/src/clustering/administration/persist/branch_history_manager.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_PERSIST_BRANCH_HISTORY_MANAGER_HPP_ -#define CLUSTERING_ADMINISTRATION_PERSIST_BRANCH_HISTORY_MANAGER_HPP_ - -#include "clustering/administration/persist/file.hpp" -#include "clustering/immediate_consistency/history.hpp" - -class real_branch_history_manager_t : public branch_history_manager_t { -public: - static void erase( - metadata_file_t::write_txn_t *write_txn, - const namespace_id_t &_table_id); - - real_branch_history_manager_t( - const namespace_id_t &_table_id, - metadata_file_t *_metadata_file, - metadata_file_t::read_txn_t *metadata_read_txn, - signal_t *interruptor); - - branch_birth_certificate_t get_branch(const branch_id_t &branch) - const THROWS_ONLY(missing_branch_exc_t); - bool is_branch_known(const branch_id_t &branch) const THROWS_NOTHING; - void create_branch( - branch_id_t branch_id, - const branch_birth_certificate_t &bc) - THROWS_NOTHING; - void import_branch_history( - const branch_history_t &new_records) - THROWS_NOTHING; - void prepare_gc( - std::set *branches_out) - THROWS_NOTHING; - void perform_gc( - const std::set &remove_branches) - THROWS_NOTHING; - -private: - namespace_id_t const table_id; - metadata_file_t * const metadata_file; - branch_history_t cache; -}; - -#endif /* CLUSTERING_ADMINISTRATION_PERSIST_BRANCH_HISTORY_MANAGER_HPP_ */ - diff --git a/src/clustering/administration/persist/file.cc b/src/clustering/administration/persist/file.cc deleted file mode 100644 index b7cbaf19e3e..00000000000 --- a/src/clustering/administration/persist/file.cc +++ /dev/null @@ -1,444 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/persist/file.hpp" - -#include "btree/depth_first_traversal.hpp" -#include "btree/operations.hpp" -#include "btree/types.hpp" -#include "buffer_cache/blob.hpp" -#include "buffer_cache/cache_balancer.hpp" -#include "buffer_cache/serialize_onto_blob.hpp" -#include "clustering/administration/persist/migrate/migrate_v1_16.hpp" -#include "clustering/administration/persist/migrate/migrate_v2_1.hpp" -#include "clustering/administration/persist/migrate/migrate_v2_3.hpp" -#include "clustering/administration/persist/migrate/rewrite.hpp" -#include "config/args.hpp" -#include "logger.hpp" -#include "paths.hpp" -#include "serializer/log/log_serializer.hpp" -#include "serializer/merger.hpp" - -const uint64_t METADATA_CACHE_SIZE = 32 * MEGABYTE; - -ATTR_PACKED(struct metadata_disk_superblock_t { - block_magic_t magic; - - block_id_t root_block; - block_id_t stat_block; -}); - -// Etymology: In version 1.13, the magic was 'RDmd', for "(R)ethink(D)B (m)eta(d)ata". -// Every subsequent version, the last character has been incremented. -static const block_magic_t metadata_sb_magic = { { 'R', 'D', 'm', 'l' } }; - -void init_metadata_superblock(void *sb_void, size_t block_size) { - memset(sb_void, 0, block_size); - metadata_disk_superblock_t *sb = static_cast(sb_void); - sb->magic = metadata_sb_magic; - sb->root_block = NULL_BLOCK_ID; - sb->stat_block = NULL_BLOCK_ID; -} - -void update_metadata_superblock_version(void *sb_void) { - metadata_disk_superblock_t *sb = static_cast(sb_void); - sb->magic = metadata_sb_magic; -} - -cluster_version_t magic_to_version(block_magic_t magic) { - guarantee(magic.bytes[0] == metadata_sb_magic.bytes[0]); - guarantee(magic.bytes[1] == metadata_sb_magic.bytes[1]); - guarantee(magic.bytes[2] == metadata_sb_magic.bytes[2]); - switch (magic.bytes[3]) { - case 'd': // obsolete version - v1.13 - fail_due_to_user_error("This version of RethinkDB cannot migrate in-place " - "from databases created by versions older than RethinkDB 1.14."); - case 'e': return cluster_version_t::v1_14; - case 'f': return cluster_version_t::v1_15; - case 'g': return cluster_version_t::v1_16; - case 'h': return cluster_version_t::v2_0; - case 'i': return cluster_version_t::v2_1; - case 'j': return cluster_version_t::v2_2; - case 'k': return cluster_version_t::v2_3; - case 'l': return cluster_version_t::v2_4_is_latest_disk; - default: - fail_due_to_user_error("You're trying to use an earlier version of RethinkDB " - "to open a database created by a later version of RethinkDB."); - } - // This is here so you don't forget to add new versions above. - // Please also update the value of metadata_sb_magic at the top of this file! - static_assert(cluster_version_t::LATEST_DISK == cluster_version_t::v2_4, - "Please add new version to magic_to_version."); -} - -class metadata_superblock_t : public superblock_t { -public: - explicit metadata_superblock_t(buf_lock_t &&sb_buf) : sb_buf_(std::move(sb_buf)) { } - void release() { - sb_buf_.reset_buf_lock(); - } - block_id_t get_root_block_id() { - buf_read_t read(&sb_buf_); - auto ptr = static_cast(read.get_data_read()); - return ptr->root_block; - } - void set_root_block_id(const block_id_t new_root_block) { - buf_write_t write(&sb_buf_); - auto ptr = static_cast(write.get_data_write()); - ptr->root_block = new_root_block; - } - block_id_t get_stat_block_id() { - buf_read_t read(&sb_buf_); - auto ptr = static_cast(read.get_data_read()); - return ptr->stat_block; - } - void set_stat_block_id(const block_id_t new_stat_block) { - buf_write_t write(&sb_buf_); - auto ptr = static_cast(write.get_data_write()); - ptr->stat_block = new_stat_block; - } - buf_parent_t expose_buf() { - return buf_parent_t(&sb_buf_); - } -private: - buf_lock_t sb_buf_; -}; - -class metadata_value_sizer_t : public value_sizer_t { -public: - explicit metadata_value_sizer_t(max_block_size_t _bs) : bs(_bs) { } - int size(const void *value) const { - return blob::ref_size( - bs, - static_cast(value), - blob::btree_maxreflen); - } - bool fits(const void *value, int length_available) const { - return blob::ref_fits( - bs, - length_available, - static_cast(value), - blob::btree_maxreflen); - } - int max_possible_size() const { - return blob::btree_maxreflen; - } - block_magic_t btree_leaf_magic() const { - return block_magic_t { { 'R', 'D', 'l', 'n' } }; - } - max_block_size_t block_size() const { - return bs; - } -private: - max_block_size_t bs; -}; - -class metadata_value_deleter_t : public value_deleter_t { -public: - void delete_value(buf_parent_t parent, const void *value) const { - // To not destroy constness, we operate on a copy of the value - metadata_value_sizer_t sizer(parent.cache()->max_block_size()); - scoped_malloc_t value_copy(sizer.max_possible_size()); - memcpy(value_copy.get(), value, sizer.size(value)); - blob_t blob( - parent.cache()->max_block_size(), - static_cast(value_copy.get()), - blob::btree_maxreflen); - blob.clear(parent); - } -}; - -class metadata_value_detacher_t : public value_deleter_t { -public: - void delete_value(buf_parent_t parent, const void *value) const { - /* This `const_cast` is needed because `blob_t` expects a non-const pointer. But - it will not actually modify the contents if the only method we ever call is - `detach_subtrees`. */ - blob_t blob(parent.cache()->max_block_size(), - static_cast(const_cast(value)), - blob::btree_maxreflen); - blob.detach_subtrees(parent); - } -}; - -metadata_file_t::read_txn_t::read_txn_t( - metadata_file_t *f, - signal_t *interruptor) : - file(f), - txn(file->cache_conn.get(), read_access_t::read), - rwlock_acq(&file->rwlock, access_t::read, interruptor) - { } - -metadata_file_t::read_txn_t::read_txn_t( - metadata_file_t *f, - write_access_t, - signal_t *interruptor) : - file(f), - txn(file->cache_conn.get(), write_durability_t::HARD, 1), - rwlock_acq(&file->rwlock, access_t::write, interruptor) - { } - -void metadata_file_t::read_txn_t::blob_to_stream( - buf_parent_t parent, - const void *ref, - const std::function &callback) { - blob_t blob( - file->cache->max_block_size(), - /* `blob_t` requires a non-const pointer because it has functions that mutate the - blob. But we're not using those functions. That's why there's a `const_cast` - here. */ - static_cast(const_cast(ref)), - blob::btree_maxreflen); - blob_acq_t acq_group; - buffer_group_t buf_group; - blob.expose_all(parent, access_t::read, &buf_group, &acq_group); - buffer_group_read_stream_t read_stream(const_view(&buf_group)); - callback(&read_stream); -} - -void metadata_file_t::read_txn_t::read_bin( - const store_key_t &key, - const std::function &callback, - signal_t *interruptor) { - metadata_value_sizer_t sizer(file->cache->max_block_size()); - buf_lock_t sb_lock(buf_parent_t(&txn), SUPERBLOCK_ID, access_t::read); - wait_interruptible(sb_lock.read_acq_signal(), interruptor); - metadata_superblock_t superblock(std::move(sb_lock)); - keyvalue_location_t kvloc; - find_keyvalue_location_for_read( - &sizer, - &superblock, - key.btree_key(), - &kvloc, - &file->btree_stats, - nullptr); - if (kvloc.there_originally_was_value) { - blob_to_stream(buf_parent_t(&kvloc.buf), kvloc.value.get(), callback); - } -} - -void metadata_file_t::read_txn_t::read_many_bin( - const store_key_t &key_prefix, - const std::function &cb, - signal_t *interruptor) { - buf_lock_t sb_lock(buf_parent_t(&txn), SUPERBLOCK_ID, access_t::read); - wait_interruptible(sb_lock.read_acq_signal(), interruptor); - metadata_superblock_t superblock(std::move(sb_lock)); - class : public depth_first_traversal_callback_t { - public: - continue_bool_t handle_pair(scoped_key_value_t &&kv, signal_t *) { - guarantee(kv.key()->size >= key_prefix.size()); - guarantee(memcmp( - kv.key()->contents, key_prefix.contents(), key_prefix.size()) == 0); - std::string suffix( - reinterpret_cast(kv.key()->contents + key_prefix.size()), - kv.key()->size - key_prefix.size()); - txn->blob_to_stream( - kv.expose_buf(), - kv.value(), - [&](read_stream_t *s) { (*cb)(suffix, s); }); - return continue_bool_t::CONTINUE; - } - read_txn_t *txn; - store_key_t key_prefix; - const std::function *cb; - } dftcb; - dftcb.txn = this; - dftcb.key_prefix = key_prefix; - dftcb.cb = &cb; - btree_depth_first_traversal( - &superblock, - key_range_t::with_prefix(key_prefix), - &dftcb, - access_t::read, - FORWARD, - release_superblock_t::RELEASE, - interruptor); -} - -metadata_file_t::write_txn_t::write_txn_t( - metadata_file_t *_file, - signal_t *interruptor) : - read_txn_t(_file, write_access_t::write, interruptor) - { } - -void metadata_file_t::write_txn_t::write_bin( - const store_key_t &key, - const write_message_t *msg, - signal_t *interruptor) { - metadata_value_sizer_t sizer(file->cache->max_block_size()); - metadata_value_detacher_t detacher; - metadata_value_deleter_t deleter; - buf_lock_t sb_lock(buf_parent_t(&txn), SUPERBLOCK_ID, access_t::write); - wait_interruptible(sb_lock.write_acq_signal(), interruptor); - metadata_superblock_t superblock(std::move(sb_lock)); - keyvalue_location_t kvloc; - find_keyvalue_location_for_write( - &sizer, - &superblock, - key.btree_key(), - repli_timestamp_t::distant_past, - &detacher, - &kvloc, - nullptr /* trace */); - if (kvloc.there_originally_was_value) { - deleter.delete_value(buf_parent_t(&kvloc.buf), kvloc.value.get()); - kvloc.value.reset(); - } - if (msg != nullptr) { - kvloc.value = scoped_malloc_t(blob::btree_maxreflen); - memset(kvloc.value.get(), 0, blob::btree_maxreflen); - blob_t blob(file->cache->max_block_size(), - static_cast(kvloc.value.get()), - blob::btree_maxreflen); - write_onto_blob(buf_parent_t(&kvloc.buf), &blob, *msg); - } - apply_keyvalue_change(&sizer, &kvloc, key.btree_key(), repli_timestamp_t::invalid, - &detacher, delete_mode_t::ERASE); -} - -metadata_file_t::metadata_file_t( - io_backender_t *io_backender, - const base_path_t &base_path, - perfmon_collection_t *perfmon_parent, - signal_t *interruptor) : - btree_stats(perfmon_parent, "metadata") -{ - filepath_file_opener_t file_opener(get_filename(base_path), io_backender); - init_serializer(&file_opener, perfmon_parent); - balancer.init(new dummy_cache_balancer_t(METADATA_CACHE_SIZE)); - cache.init(new cache_t(serializer.get(), balancer.get(), perfmon_parent)); - cache_conn.init(new cache_conn_t(cache.get())); - - /* Migrate data if necessary */ - if (interruptor->is_pulsed()) { - throw interrupted_exc_t(); - } - cond_t non_interruptor; - write_txn_t write_txn(this, &non_interruptor); - { - object_buffer_t sb_lock; - sb_lock.create( - buf_parent_t(&write_txn.txn), SUPERBLOCK_ID, access_t::write); - object_buffer_t sb_write; - sb_write.create(sb_lock.get()); - void *sb_data = sb_write->get_data_write(); - - cluster_version_t metadata_version = - magic_to_version(*static_cast(sb_data)); - switch (metadata_version) { - case cluster_version_t::v1_14: // fallthrough intentional - case cluster_version_t::v1_15: // fallthrough intentional - case cluster_version_t::v1_16: // fallthrough intentional - case cluster_version_t::v2_0: { - scoped_malloc_t sb_copy(cache->max_block_size().value()); - memcpy(sb_copy.get(), sb_data, cache->max_block_size().value()); - init_metadata_superblock(sb_data, cache->max_block_size().value()); - sb_write.reset(); - sb_lock.reset(); - - logNTC("Migrating cluster metadata to v2.1"); - migrate_cluster_metadata_to_v2_1( - io_backender, base_path, - buf_parent_t(&write_txn.txn), sb_copy.get(), &write_txn, - &non_interruptor); - - // The metadata is now serialized using the latest serialization version - metadata_version = cluster_version_t::LATEST_DISK; - } // fallthrough intentional - case cluster_version_t::v2_1: // fallthrough intentional - case cluster_version_t::v2_2: { - if (sb_lock.has()) { - update_metadata_superblock_version(sb_data); - sb_write.reset(); - sb_lock.reset(); - } - - logNTC("Migrating cluster metadata to v2.3"); - migrate_metadata_v2_1_to_v2_3( - metadata_version, &write_txn, &non_interruptor); - - // The metadata is now serialized using the latest serialization version - metadata_version = cluster_version_t::LATEST_DISK; - } // fallthrough intentional - case cluster_version_t::v2_3: { - if (sb_lock.has()) { - update_metadata_superblock_version(sb_data); - sb_write.reset(); - sb_lock.reset(); - } - - logNTC("Migrating cluster metadata to v2.4"); - migrate_metadata_v2_3_to_v2_4( - metadata_version, &write_txn, &non_interruptor); - - // The metadata is now serialized using the latest serialization version - metadata_version = cluster_version_t::LATEST_DISK; - } // fallthrough intentional - case cluster_version_t::v2_4_is_latest: - break; // Up-to-date, do nothing - default: unreachable(); - } - } - write_txn.commit(); -} - -metadata_file_t::metadata_file_t( - io_backender_t *io_backender, - const base_path_t &base_path, - perfmon_collection_t *perfmon_parent, - const std::function &initializer, - signal_t *interruptor) : - btree_stats(perfmon_parent, "metadata") -{ - filepath_file_opener_t file_opener(get_filename(base_path), io_backender); - log_serializer_t::create( - &file_opener, - log_serializer_t::static_config_t()); - init_serializer(&file_opener, perfmon_parent); - balancer.init(new dummy_cache_balancer_t(METADATA_CACHE_SIZE)); - cache.init(new cache_t(serializer.get(), balancer.get(), perfmon_parent)); - cache_conn.init(new cache_conn_t(cache.get())); - - { - if (interruptor->is_pulsed()) { - throw interrupted_exc_t(); - } - cond_t non_interruptor; - write_txn_t write_txn(this, &non_interruptor); - { - buf_lock_t sb_lock(&write_txn.txn, SUPERBLOCK_ID, alt_create_t::create); - buf_write_t sb_write(&sb_lock); - void *sb_data = sb_write.get_data_write(); - init_metadata_superblock(sb_data, cache->max_block_size().value()); - } - initializer(&write_txn, &non_interruptor); - write_txn.commit(); - } - - file_opener.move_serializer_file_to_permanent_location(); -} - -void metadata_file_t::init_serializer( - filepath_file_opener_t *file_opener, - perfmon_collection_t *perfmon_parent) { - scoped_ptr_t standard_ser( - new log_serializer_t( - log_serializer_t::dynamic_config_t(), - file_opener, - perfmon_parent)); - if (!standard_ser->coop_lock_and_check()) { - throw file_in_use_exc_t(); - } - serializer.init(new merger_serializer_t( - std::move(standard_ser), - MERGER_SERIALIZER_MAX_ACTIVE_WRITES)); -} - -serializer_filepath_t metadata_file_t::get_filename(const base_path_t &path) { - return serializer_filepath_t(path, "metadata"); -} - -metadata_file_t::~metadata_file_t() { - /* This is defined in the `.cc` file so the `.hpp` file doesn't need to see the - definitions of `log_serializer_t` and `cache_balancer_t`. */ -} - diff --git a/src/clustering/administration/persist/file.hpp b/src/clustering/administration/persist/file.hpp deleted file mode 100644 index 6eab99c58ee..00000000000 --- a/src/clustering/administration/persist/file.hpp +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_PERSIST_FILE_HPP_ -#define CLUSTERING_ADMINISTRATION_PERSIST_FILE_HPP_ - -#include "btree/keys.hpp" -#include "btree/stats.hpp" -#include "buffer_cache/alt.hpp" -#include "buffer_cache/types.hpp" -#include "concurrency/rwlock.hpp" -#include "serializer/types.hpp" -#include "utils.hpp" - -class base_path_t; -class io_backender_t; -class merger_serializer_t; -class filepath_file_opener_t; -class serializer_filepath_t; - -class file_in_use_exc_t : public std::exception { -public: - const char *what() const throw () { - return "metadata file is being used by another rethinkdb process"; - } -}; - -namespace metadata { -class read_txn_t; -class write_txn_t; - -template -class key_t { -public: - explicit key_t(const std::string &s) : key(s) { } - key_t suffix(const std::string &s) const { - key_t copy = *this; - guarantee(key.size() + s.size() <= MAX_KEY_SIZE); - copy.key.set_size(key.size() + s.size()); - memcpy(copy.key.contents() + key.size(), s.c_str(), s.size()); - return copy; - } -private: - friend class metadata_file_t; - friend class read_txn_t; - friend class write_txn_t; - store_key_t key; -}; -} // namespace metadata - -class metadata_file_t { -public: - template - using key_t = metadata::key_t; - using read_txn_t = metadata::read_txn_t; - using write_txn_t = metadata::write_txn_t; - - // Used to open an existing metadata file - metadata_file_t( - io_backender_t *io_backender, - const base_path_t &base_path, - perfmon_collection_t *perfmon_parent, - signal_t *interruptor); - - // Used top create a new metadata file - metadata_file_t( - io_backender_t *io_backender, - const base_path_t &base_path, - perfmon_collection_t *perfmon_parent, - const std::function &initializer, - signal_t *interruptor); - ~metadata_file_t(); - -private: - friend class metadata::read_txn_t; - friend class metadata::write_txn_t; - - void init_serializer( - filepath_file_opener_t *file_opener, - perfmon_collection_t *perfmon_parent); - - static serializer_filepath_t get_filename(const base_path_t &path); - - scoped_ptr_t serializer; - scoped_ptr_t balancer; - scoped_ptr_t cache; - scoped_ptr_t cache_conn; - btree_stats_t btree_stats; - rwlock_t rwlock; -}; - - -namespace metadata { -class read_txn_t { -public: - read_txn_t(metadata_file_t *file, signal_t *interruptor); - - template - T read(const key_t &key, signal_t *interruptor) { - T value; - bool found = read_maybe(key, &value, interruptor); - guarantee(found, "failed to find expected metadata key"); - return value; - } - - template - bool read_maybe( - const key_t &key, - T *value_out, - signal_t *interruptor) { - bool found = false; - read_bin( - key.key, - [&](read_stream_t *bin_value) { - archive_result_t res = deserialize(bin_value, value_out); - guarantee_deserialization(res, "metadata_file_t::read_txn_t::read"); - found = true; - }, - interruptor); - return found; - } - - template - void read_many( - const key_t &key_prefix, - const std::function &cb, - signal_t *interruptor) { - read_many_bin( - key_prefix.key, - [&](const std::string &key_suffix, read_stream_t *bin_value) { - T value; - archive_result_t res = deserialize(bin_value, &value); - guarantee_deserialization(res, - "metadata_file_t::read_txn_t::read_many"); - cb(key_suffix, value); - }, - interruptor); - } - - friend class ::metadata_file_t; - friend class write_txn_t; - -private: - /* This constructor is used by `write_txn_t` */ - read_txn_t(metadata_file_t *file, write_access_t, signal_t *interruptor); - - void blob_to_stream( - buf_parent_t parent, - const void *ref, - const std::function &callback); - - void read_bin( - const store_key_t &key, - const std::function &callback, - signal_t *interruptor); - - void read_many_bin( - const store_key_t &key_prefix, - const std::function &cb, - signal_t *interruptor); - - metadata_file_t *file; - txn_t txn; - rwlock_acq_t rwlock_acq; -}; - -class write_txn_t : public read_txn_t { -public: - write_txn_t(metadata_file_t *file, signal_t *interruptor); - - template - void write( - const key_t &key, - const T &value, - signal_t *interruptor) { - write_message_t wm; - serialize(&wm, value); - write_bin(key.key, &wm, interruptor); - } - - template - void erase(const key_t &key, signal_t *interruptor) { - write_bin(key.key, nullptr, interruptor); - } - - // Must be called before the `write_txn_t` is destructed. - // This acts as a safety check to make sure a transaction - // is not interrupted in the middle, which could leave the - // metadata in an inconsistent state. - void commit() { - txn.commit(); - } - -private: - friend class ::metadata_file_t; - - void write_bin( - const store_key_t &key, - const write_message_t *msg, - signal_t *interruptor); -}; -} // namespace metadata - -#endif /* CLUSTERING_ADMINISTRATION_PERSIST_FILE_HPP_ */ - diff --git a/src/clustering/administration/persist/file_keys.cc b/src/clustering/administration/persist/file_keys.cc deleted file mode 100644 index 4d4097a2471..00000000000 --- a/src/clustering/administration/persist/file_keys.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/persist/file_keys.hpp" - -#include "rpc/connectivity/server_id.hpp" - -metadata_file_t::key_t - mdkey_cluster_semilattices() { - return metadata_file_t::key_t("cluster_semilattice"); -} - -metadata_file_t::key_t - mdkey_auth_semilattices() { - return metadata_file_t::key_t("auth_semilattice"); -} - -metadata_file_t::key_t - mdkey_heartbeat_semilattices() { - return metadata_file_t::key_t( - "heartbeat_semilattice"); -} - -metadata_file_t::key_t - mdkey_server_id() { - return metadata_file_t::key_t("server_id"); -} - -metadata_file_t::key_t - mdkey_server_config() { - return metadata_file_t::key_t("server_config"); -} - -metadata_file_t::key_t - mdprefix_table_active() { - return metadata_file_t::key_t("table.active/"); -} - -metadata_file_t::key_t - mdprefix_table_inactive() { - return metadata_file_t::key_t("table.inactive/"); -} - -metadata_file_t::key_t - mdprefix_table_raft_header() { - return metadata_file_t::key_t("table.header/"); -} - -metadata_file_t::key_t - mdprefix_table_raft_snapshot() { - return metadata_file_t::key_t("table.snapshot/"); -} - -metadata_file_t::key_t > - mdprefix_table_raft_log() { - return metadata_file_t::key_t >("table.log/"); -} - -metadata_file_t::key_t - mdprefix_branch_birth_certificate() { - return metadata_file_t::key_t("branch/"); -} diff --git a/src/clustering/administration/persist/file_keys.hpp b/src/clustering/administration/persist/file_keys.hpp deleted file mode 100644 index 3343127eb8d..00000000000 --- a/src/clustering/administration/persist/file_keys.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_PERSIST_FILE_KEYS_HPP_ -#define CLUSTERING_ADMINISTRATION_PERSIST_FILE_KEYS_HPP_ - -#include "clustering/administration/persist/file.hpp" -#include "containers/uuid.hpp" - -class auth_semilattice_metadata_t; -class branch_birth_certificate_t; -class cluster_semilattice_metadata_t; -class heartbeat_semilattice_metadata_t; -template class raft_log_entry_t; -class server_config_versioned_t; -class server_id_t; -class table_active_persistent_state_t; -class table_inactive_persistent_state_t; -class table_raft_state_t; -class table_raft_stored_header_t; -class table_raft_stored_snapshot_t; - -/* This file defines the keys that are used to index the `metadata_file_t`. Changing -these keys will break the on-disk format. */ - -metadata_file_t::key_t - mdkey_cluster_semilattices(); -metadata_file_t::key_t - mdkey_auth_semilattices(); -metadata_file_t::key_t - mdkey_heartbeat_semilattices(); -metadata_file_t::key_t - mdkey_server_id(); -metadata_file_t::key_t - mdkey_server_config(); - -/* These prefixes should be followed by the table's UUID as formatted by `uuid_to_str()`. -*/ -metadata_file_t::key_t - mdprefix_table_active(); -metadata_file_t::key_t - mdprefix_table_inactive(); -metadata_file_t::key_t - mdprefix_table_raft_header(); -metadata_file_t::key_t - mdprefix_table_raft_snapshot(); - -/* This prefix should be followed by a string of the form `TABLE/LOG_INDEX`, where -`TABLE` is a UUID as before, and `LOG_INDEX` is a 16-digit hexadecimal. */ -metadata_file_t::key_t > - mdprefix_table_raft_log(); - -/* This prefix should be followed by a string of the form `TABLE/BRANCH_ID`, where -`TABLE` and `BRANCH_ID` are UUIDs as formatted by `uuid_to_str()`. */ -metadata_file_t::key_t - mdprefix_branch_birth_certificate(); - -#endif /* CLUSTERING_ADMINISTRATION_PERSIST_FILE_KEYS_HPP_ */ - diff --git a/src/clustering/administration/persist/migrate/metadata_v1_14.cc b/src/clustering/administration/persist/migrate/metadata_v1_14.cc deleted file mode 100644 index 5cb2b965881..00000000000 --- a/src/clustering/administration/persist/migrate/metadata_v1_14.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/persist/migrate/metadata_v1_14.hpp" - -namespace metadata_v1_14 { - -RDB_IMPL_SERIALIZABLE_1(persistable_blueprint_t, machines_roles); -INSTANTIATE_DESERIALIZE_SINCE_v1_13(persistable_blueprint_t); -RDB_IMPL_SERIALIZABLE_1(database_semilattice_metadata_t, name); -INSTANTIATE_DESERIALIZE_SINCE_v1_13(database_semilattice_metadata_t); -RDB_IMPL_SERIALIZABLE_1(databases_semilattice_metadata_t, databases); -INSTANTIATE_DESERIALIZE_SINCE_v1_13(databases_semilattice_metadata_t); -RDB_IMPL_SERIALIZABLE_1(datacenter_semilattice_metadata_t, name); -INSTANTIATE_DESERIALIZE_SINCE_v1_13(datacenter_semilattice_metadata_t); -RDB_IMPL_SERIALIZABLE_1(datacenters_semilattice_metadata_t, datacenters); -INSTANTIATE_DESERIALIZE_SINCE_v1_13(datacenters_semilattice_metadata_t); -RDB_IMPL_SERIALIZABLE_2(machine_semilattice_metadata_t, datacenter, name); -INSTANTIATE_DESERIALIZE_SINCE_v1_13(machine_semilattice_metadata_t); -RDB_IMPL_SERIALIZABLE_1(machines_semilattice_metadata_t, machines); -INSTANTIATE_DESERIALIZE_SINCE_v1_13(machines_semilattice_metadata_t); -RDB_IMPL_SERIALIZABLE_2(ack_expectation_t, expectation_, hard_durability_); -INSTANTIATE_DESERIALIZE_SINCE_v1_13(ack_expectation_t); -RDB_IMPL_SERIALIZABLE_10(namespace_semilattice_metadata_t, - blueprint, primary_datacenter, replica_affinities, - ack_expectations, shards, name, primary_pinnings, - secondary_pinnings, primary_key, database); -INSTANTIATE_DESERIALIZE_SINCE_v1_13(namespace_semilattice_metadata_t); -RDB_IMPL_SERIALIZABLE_1(namespaces_semilattice_metadata_t, namespaces); -INSTANTIATE_DESERIALIZE_SINCE_v1_13(namespaces_semilattice_metadata_t); -RDB_IMPL_SERIALIZABLE_4(cluster_semilattice_metadata_t, - rdb_namespaces, machines, datacenters, databases); -INSTANTIATE_DESERIALIZE_SINCE_v1_13(cluster_semilattice_metadata_t); -RDB_IMPL_SERIALIZABLE_1(auth_key_t, key); -INSTANTIATE_SERIALIZABLE_SINCE_v1_13(auth_key_t); -RDB_IMPL_SERIALIZABLE_1(auth_semilattice_metadata_t, auth_key); -INSTANTIATE_DESERIALIZE_SINCE_v1_13(auth_semilattice_metadata_t); - -} // namespace metadata_v1_14 diff --git a/src/clustering/administration/persist/migrate/metadata_v1_14.hpp b/src/clustering/administration/persist/migrate/metadata_v1_14.hpp deleted file mode 100644 index 3460e1464e2..00000000000 --- a/src/clustering/administration/persist/migrate/metadata_v1_14.hpp +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_METADATA_V1_14 -#define CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_METADATA_V1_14 - -#include -#include -#include - -#include "clustering/generic/nonoverlapping_regions.hpp" -#include "containers/archive/versioned.hpp" -#include "containers/name_string.hpp" -#include "containers/uuid.hpp" -#include "region/region.hpp" -#include "region/region_map.hpp" -#include "rpc/connectivity/server_id.hpp" -#include "rpc/semilattice/joins/deletable.hpp" -#include "rpc/serialize_macros.hpp" - -/* The transition from v1.15 to v1.16 changed the metadata format in some major ways. -This file preserves the metadata structure used in version 1.14 and 1.15 for migration -purposes. */ - -namespace metadata_v1_14 { - -typedef uuid_u datacenter_id_t; - -typedef std::map version_map_t; - -template -struct vclock_t { - typedef std::pair stamped_value_t; - - typedef std::map value_map_t; - value_map_t values; - - RDB_MAKE_ME_SERIALIZABLE_1(vclock_t, values); -}; - -enum blueprint_role_t { blueprint_role_primary, blueprint_role_secondary, blueprint_role_nothing }; -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE(blueprint_role_t, int8_t, blueprint_role_primary, blueprint_role_nothing); - -struct persistable_blueprint_t { - typedef std::map region_to_role_map_t; - typedef std::map role_map_t; - - role_map_t machines_roles; -}; -RDB_DECLARE_SERIALIZABLE(persistable_blueprint_t); - -struct database_semilattice_metadata_t { - vclock_t name; -}; -RDB_DECLARE_SERIALIZABLE(database_semilattice_metadata_t); - -struct databases_semilattice_metadata_t { - typedef std::map > database_map_t; - database_map_t databases; -}; -RDB_DECLARE_SERIALIZABLE(databases_semilattice_metadata_t); - -struct datacenter_semilattice_metadata_t { - vclock_t name; -}; -RDB_DECLARE_SERIALIZABLE(datacenter_semilattice_metadata_t); - -struct datacenters_semilattice_metadata_t { - typedef std::map > datacenter_map_t; - datacenter_map_t datacenters; -}; -RDB_DECLARE_SERIALIZABLE(datacenters_semilattice_metadata_t); - -struct machine_semilattice_metadata_t { - vclock_t datacenter; - vclock_t name; -}; -RDB_DECLARE_SERIALIZABLE(machine_semilattice_metadata_t); - -struct machines_semilattice_metadata_t { - typedef std::map > machine_map_t; - machine_map_t machines; -}; -RDB_DECLARE_SERIALIZABLE(machines_semilattice_metadata_t); - -struct ack_expectation_t { - uint32_t expectation_; - bool hard_durability_; - - RDB_DECLARE_ME_SERIALIZABLE(ack_expectation_t); -}; - -struct namespace_semilattice_metadata_t { - vclock_t blueprint; - vclock_t primary_datacenter; - vclock_t > replica_affinities; - vclock_t > - ack_expectations; - vclock_t shards; - vclock_t name; - vclock_t > primary_pinnings; - vclock_t > > secondary_pinnings; - vclock_t primary_key; - vclock_t database; -}; -RDB_DECLARE_SERIALIZABLE(namespace_semilattice_metadata_t); - -struct namespaces_semilattice_metadata_t { - typedef std::map > namespace_map_t; - namespace_map_t namespaces; -}; -RDB_DECLARE_SERIALIZABLE(namespaces_semilattice_metadata_t); - -struct cluster_semilattice_metadata_t { - namespaces_semilattice_metadata_t rdb_namespaces; - machines_semilattice_metadata_t machines; - datacenters_semilattice_metadata_t datacenters; - databases_semilattice_metadata_t databases; -}; -RDB_DECLARE_SERIALIZABLE(cluster_semilattice_metadata_t); - -struct auth_key_t { - std::string key; -}; -RDB_DECLARE_SERIALIZABLE(auth_key_t); - -struct auth_semilattice_metadata_t { - vclock_t auth_key; -}; -RDB_DECLARE_SERIALIZABLE(auth_semilattice_metadata_t); - -} // namespace metadata_v1_14 - -#endif /* CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_MIGRATE_METADATA_V1_14 */ diff --git a/src/clustering/administration/persist/migrate/metadata_v1_16.cc b/src/clustering/administration/persist/migrate/metadata_v1_16.cc deleted file mode 100644 index 26dabd5b9d7..00000000000 --- a/src/clustering/administration/persist/migrate/metadata_v1_16.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/persist/migrate/metadata_v1_16.hpp" - -namespace metadata_v1_16 { - -size_t table_shard_scheme_t::num_shards() const { - return split_points.size() + 1; -} - -key_range_t table_shard_scheme_t::get_shard_range(size_t i) const { - guarantee(i < num_shards()); - store_key_t left = (i == 0) ? store_key_t::min() : split_points[i-1]; - if (i != num_shards() - 1) { - return key_range_t( - key_range_t::closed, left, - key_range_t::open, split_points[i]); - } else { - return key_range_t( - key_range_t::closed, left, - key_range_t::none, store_key_t()); - } -} - -RDB_IMPL_SERIALIZABLE_2(version_t, branch, timestamp); -RDB_IMPL_EQUALITY_COMPARABLE_2(version_t, branch, timestamp); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(version_t); - -RDB_IMPL_SERIALIZABLE_2(version_range_t, earliest, latest); -RDB_IMPL_EQUALITY_COMPARABLE_2(version_range_t, earliest, latest); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(version_range_t); - -RDB_IMPL_SERIALIZABLE_3(branch_birth_certificate_t, region, initial_timestamp, origin); -RDB_IMPL_EQUALITY_COMPARABLE_3(branch_birth_certificate_t, region, initial_timestamp, origin); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(branch_birth_certificate_t); - -RDB_IMPL_SERIALIZABLE_1(branch_history_t, branches); -INSTANTIATE_DESERIALIZE_SINCE_v1_13(branch_history_t); - -RDB_IMPL_SERIALIZABLE_3(server_semilattice_metadata_t, name, tags, cache_size_bytes); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(server_semilattice_metadata_t); - -RDB_IMPL_SERIALIZABLE_1(servers_semilattice_metadata_t, servers); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(servers_semilattice_metadata_t); - -RDB_IMPL_SERIALIZABLE_1(database_semilattice_metadata_t, name); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(database_semilattice_metadata_t); - -RDB_IMPL_SERIALIZABLE_1(databases_semilattice_metadata_t, databases); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(databases_semilattice_metadata_t); - -RDB_IMPL_SERIALIZABLE_2(write_ack_config_t::req_t, replicas, mode); -RDB_IMPL_EQUALITY_COMPARABLE_2(write_ack_config_t::req_t, replicas, mode); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(write_ack_config_t::req_t); - -RDB_IMPL_SERIALIZABLE_2(write_ack_config_t, mode, complex_reqs); -RDB_IMPL_EQUALITY_COMPARABLE_2(write_ack_config_t, mode, complex_reqs); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(write_ack_config_t); - -RDB_IMPL_SERIALIZABLE_2(table_config_t::shard_t, replicas, primary_replica); -RDB_IMPL_EQUALITY_COMPARABLE_2(table_config_t::shard_t, replicas, primary_replica); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(table_config_t::shard_t); - -RDB_IMPL_SERIALIZABLE_3(table_config_t, shards, write_ack_config, durability); -RDB_IMPL_EQUALITY_COMPARABLE_3(table_config_t, shards, write_ack_config, durability); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(table_config_t); - -RDB_IMPL_SERIALIZABLE_1(table_shard_scheme_t, split_points); -RDB_IMPL_EQUALITY_COMPARABLE_1(table_shard_scheme_t, split_points); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(table_shard_scheme_t); - -RDB_IMPL_SERIALIZABLE_2(table_replication_info_t, config, shard_scheme); -RDB_IMPL_EQUALITY_COMPARABLE_2(table_replication_info_t, config, shard_scheme); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(table_replication_info_t); - -RDB_IMPL_SERIALIZABLE_4(namespace_semilattice_metadata_t, name, database, primary_key, replication_info); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(namespace_semilattice_metadata_t); - -RDB_IMPL_SERIALIZABLE_1(namespaces_semilattice_metadata_t, namespaces); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(namespaces_semilattice_metadata_t); - -RDB_IMPL_SERIALIZABLE_3(cluster_semilattice_metadata_t, rdb_namespaces, servers, databases); -INSTANTIATE_DESERIALIZE_SINCE_v1_16(cluster_semilattice_metadata_t); - -RDB_IMPL_SERIALIZABLE_1(auth_semilattice_metadata_t, auth_key); -INSTANTIATE_SERIALIZABLE_SINCE_v1_16(auth_semilattice_metadata_t); - -} // namespace metadata_v1_16 diff --git a/src/clustering/administration/persist/migrate/metadata_v1_16.hpp b/src/clustering/administration/persist/migrate/metadata_v1_16.hpp deleted file mode 100644 index a552d10aa1c..00000000000 --- a/src/clustering/administration/persist/migrate/metadata_v1_16.hpp +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_METADATA_V1_16_HPP_ -#define CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_METADATA_V1_16_HPP_ - -#include -#include -#include -#include - -#include "btree/keys.hpp" -#include "buffer_cache/types.hpp" -#include "clustering/administration/persist/migrate/metadata_v1_14.hpp" -#include "containers/name_string.hpp" -#include "containers/optional.hpp" -#include "containers/uuid.hpp" -#include "region/region_map.hpp" -#include "rpc/connectivity/server_id.hpp" -#include "rpc/semilattice/joins/deletable.hpp" -#include "rpc/semilattice/joins/versioned.hpp" -#include "rpc/semilattice/joins/macros.hpp" -#include "timestamps.hpp" - -namespace metadata_v1_16 { - -struct version_t { - branch_id_t branch; - state_timestamp_t timestamp; -}; - -RDB_DECLARE_SERIALIZABLE(version_t); -RDB_DECLARE_EQUALITY_COMPARABLE(version_t); - -/* `version_range_t` is a pair of `version_t`s. The meta-info, which is stored - in the superblock of the B-tree, records a `version_range_t` for each range of - keys. Each key's value is the value it had at some `version_t` in the recorded - `version_range_t`. - - The reason we store `version_range_t` instead of `version_t` is that if a - backfill were interrupted, we wouldn't know which keys were up-to-date and - which were not. All that we would know is that each key's state lay at some - point between the version the B-tree was at before the backfill started and the - version that the backfiller is at. */ - -struct version_range_t { - bool is_coherent() const { - return earliest == latest; - } - version_t earliest; - version_t latest; -}; - -RDB_DECLARE_SERIALIZABLE(version_range_t); -RDB_DECLARE_EQUALITY_COMPARABLE(version_range_t); - -struct branch_birth_certificate_t { - region_t region; - state_timestamp_t initial_timestamp; - region_map_t origin; -}; - -RDB_DECLARE_SERIALIZABLE(branch_birth_certificate_t); -RDB_DECLARE_EQUALITY_COMPARABLE(branch_birth_certificate_t); - -struct branch_history_t { - std::map branches; -}; - -RDB_DECLARE_SERIALIZABLE(branch_history_t); - -struct server_semilattice_metadata_t { - versioned_t name; - versioned_t > tags; - versioned_t > cache_size_bytes; -}; - -RDB_DECLARE_SERIALIZABLE(server_semilattice_metadata_t); - -struct servers_semilattice_metadata_t { - std::map > servers; -}; - -RDB_DECLARE_SERIALIZABLE(servers_semilattice_metadata_t); - -struct database_semilattice_metadata_t { - versioned_t name; -}; - -RDB_DECLARE_SERIALIZABLE(database_semilattice_metadata_t); - -struct databases_semilattice_metadata_t { - std::map > databases; -}; - -RDB_DECLARE_SERIALIZABLE(databases_semilattice_metadata_t); - -struct write_ack_config_t { - enum class mode_t { single, majority, complex }; - struct req_t { - std::set replicas; - mode_t mode; /* must not be `complex` */ - }; - mode_t mode; - std::vector complex_reqs; /* `complex_reqs` must be empty unless `mode` is `complex`. */ -}; - -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE(write_ack_config_t::mode_t, int8_t, - write_ack_config_t::mode_t::single, - write_ack_config_t::mode_t::complex); -RDB_DECLARE_SERIALIZABLE(write_ack_config_t::req_t); -RDB_DECLARE_EQUALITY_COMPARABLE(write_ack_config_t::req_t); -RDB_DECLARE_SERIALIZABLE(write_ack_config_t); -RDB_DECLARE_EQUALITY_COMPARABLE(write_ack_config_t); - -struct table_config_t { - struct shard_t { - std::set replicas; - server_id_t primary_replica; - }; - std::vector shards; - write_ack_config_t write_ack_config; - ::write_durability_t durability; -}; - -RDB_DECLARE_SERIALIZABLE(table_config_t::shard_t); -RDB_DECLARE_EQUALITY_COMPARABLE(table_config_t::shard_t); -RDB_DECLARE_SERIALIZABLE(table_config_t); -RDB_DECLARE_EQUALITY_COMPARABLE(table_config_t); - -struct table_shard_scheme_t { - size_t num_shards() const; - key_range_t get_shard_range(size_t i) const; - - std::vector split_points; -}; - -RDB_DECLARE_SERIALIZABLE(table_shard_scheme_t); -RDB_DECLARE_EQUALITY_COMPARABLE(table_shard_scheme_t); - -struct table_replication_info_t { - table_config_t config; - table_shard_scheme_t shard_scheme; -}; - -RDB_DECLARE_SERIALIZABLE(table_replication_info_t); -RDB_DECLARE_EQUALITY_COMPARABLE(table_replication_info_t); - -struct namespace_semilattice_metadata_t { - versioned_t name; - versioned_t database; - versioned_t primary_key; - versioned_t replication_info; -}; - -RDB_DECLARE_SERIALIZABLE(namespace_semilattice_metadata_t); - -struct namespaces_semilattice_metadata_t { - std::map > namespaces; -}; - -RDB_DECLARE_SERIALIZABLE(namespaces_semilattice_metadata_t); - -struct cluster_semilattice_metadata_t { - namespaces_semilattice_metadata_t rdb_namespaces; - servers_semilattice_metadata_t servers; - databases_semilattice_metadata_t databases; -}; - -RDB_DECLARE_SERIALIZABLE(cluster_semilattice_metadata_t); - -struct auth_semilattice_metadata_t { - versioned_t auth_key; -}; - -RDB_DECLARE_SERIALIZABLE(auth_semilattice_metadata_t); - -} // namespace metadata_v1_16 - -#endif /* CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_METADATA_V1_16_HPP_ */ diff --git a/src/clustering/administration/persist/migrate/metadata_v2_1.cc b/src/clustering/administration/persist/migrate/metadata_v2_1.cc deleted file mode 100644 index 6c566144bb6..00000000000 --- a/src/clustering/administration/persist/migrate/metadata_v2_1.cc +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#include "clustering/administration/persist/migrate/metadata_v2_1.hpp" - -namespace metadata_v2_1 { - -metadata_file_t::key_t -mdkey_auth_semilattices() { - return metadata_file_t::key_t - ("auth_semilattice"); -} - -} // namespace metadata_v2_1 - diff --git a/src/clustering/administration/persist/migrate/metadata_v2_1.hpp b/src/clustering/administration/persist/migrate/metadata_v2_1.hpp deleted file mode 100644 index 6aa833d8dee..00000000000 --- a/src/clustering/administration/persist/migrate/metadata_v2_1.hpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_METADATA_V2_1_HPP_ -#define CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_METADATA_V2_1_HPP_ - -#include - -#include "clustering/administration/persist/file.hpp" -#include "clustering/administration/persist/migrate/metadata_v1_16.hpp" -#include "rpc/semilattice/joins/versioned.hpp" -#include "rpc/semilattice/joins/macros.hpp" - -namespace metadata_v2_1 { - -metadata_file_t::key_t mdkey_auth_semilattices(); - -} // namespace metadata_v2_1 - -#endif /* CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_METADATA_V2_1_HPP_ */ diff --git a/src/clustering/administration/persist/migrate/migrate_v1_14.cc b/src/clustering/administration/persist/migrate/migrate_v1_14.cc deleted file mode 100644 index 597210e2ea5..00000000000 --- a/src/clustering/administration/persist/migrate/migrate_v1_14.cc +++ /dev/null @@ -1,475 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/persist/migrate/migrate_v1_14.hpp" - -#include "logger.hpp" - -template -T get_vclock_best( - const metadata_v1_14::vclock_t &vclock, - int *best_total_out) { - guarantee(!vclock.values.empty()); - /* If there are multiple versions in the vector clock, choose the one with the - greatest sum of the vector clock versions. This is a heuristic that will tend to - choose the most recently modified version. */ - int best_total = -1; - T best_value; - for (const typename metadata_v1_14::vclock_t::stamped_value_t &value : vclock.values) { - int total = 0; - for (const auto &pair : value.first) { - total += pair.second; - } - guarantee(total >= 0); - if (total > best_total) { - best_value = value.second; - best_total = total; - } - } - if (best_total_out != nullptr) { - *best_total_out = best_total; - } - return best_value; -} - -template -versioned_t migrate_vclock_transform( - const metadata_v1_14::vclock_t &vclock, - const std::function &converter) { - int best_total; - old_t best_value = get_vclock_best(vclock, &best_total); - /* Calculate the timestamp so that it will appear before any "real" timestamp, but - still be ordered according to the sum of the vector clock versions. This means that - if the user migrates two files, and one has a vector clock update that the other one - lacks, the one with the vector clock update will end up with a larger `versioned_t` - timestamp, so the right thing will happen. */ - time_t timestamp = std::numeric_limits::min() + 1 + best_total; - return versioned_t::make_with_manual_timestamp( - timestamp, converter(best_value)); -} - -template -versioned_t migrate_vclock( - const metadata_v1_14::vclock_t &vclock) { - return migrate_vclock_transform(vclock, [](const T &v) { return v; }); -} - -/* The unit test needs to be able to test `migrate_vclock()`. Since nothing except for -the unit test and this file uses `migrate_vclock()`, we don't put it in the header; -instead, the unit test declares it and we explicitly instantiate it here. */ -template -versioned_t migrate_vclock( - const metadata_v1_14::vclock_t &); - -template -std::map > migrate_map( - const std::map > &old_map, - const std::function &converter) { - std::map > new_map; - for (const auto &pair : old_map) { - if (pair.second.is_deleted()) { - new_map[pair.first].mark_deleted(); - } else { - new_map.insert(std::make_pair( - pair.first, - deletable_t(converter(pair.second.get_ref())))); - } - } - return new_map; -} - -metadata_v1_16::database_semilattice_metadata_t migrate_database( - const metadata_v1_14::database_semilattice_metadata_t &old_md) { - metadata_v1_16::database_semilattice_metadata_t new_md; - new_md.name = migrate_vclock_transform( - old_md.name, - [](const name_string_t &old_name) -> name_string_t { - if (old_name == name_string_t::guarantee_valid("rethinkdb")) { - logWRN("Found an existing database named `rethinkdb` when migrating " - "metadata. Since `rethinkdb` is a reserved database name as of " - "RethinkDB 1.16, the existing database has been renamed to " - "`rethinkdb_`."); - return name_string_t::guarantee_valid("rethinkdb_"); - } - return old_name; - }); - return new_md; -} - -metadata_v1_16::databases_semilattice_metadata_t migrate_databases( - const metadata_v1_14::databases_semilattice_metadata_t &old_md) { - metadata_v1_16::databases_semilattice_metadata_t new_md; - new_md.databases = migrate_map< - database_id_t, - metadata_v1_14::database_semilattice_metadata_t, - metadata_v1_16::database_semilattice_metadata_t>( - old_md.databases, - [&](const metadata_v1_14::database_semilattice_metadata_t &old_database) { - return migrate_database(old_database); - }); - return new_md; -} - -metadata_v1_16::server_semilattice_metadata_t migrate_server( - const metadata_v1_14::machine_semilattice_metadata_t &old_md, - const metadata_v1_14::datacenters_semilattice_metadata_t &datacenters) { - metadata_v1_16::server_semilattice_metadata_t new_md; - new_md.name = migrate_vclock(old_md.name); - new_md.tags = migrate_vclock_transform< - metadata_v1_14::datacenter_id_t, std::set >( - old_md.datacenter, - [&](const metadata_v1_14::datacenter_id_t &dc) -> std::set { - std::set tags; - tags.insert(name_string_t::guarantee_valid("default")); - auto it = datacenters.datacenters.find(dc); - if (it != datacenters.datacenters.end() && !it->second.is_deleted()) { - tags.insert(get_vclock_best(it->second.get_ref().name, nullptr)); - } - return tags; - }); - return new_md; -} - -metadata_v1_16::servers_semilattice_metadata_t migrate_servers( - const metadata_v1_14::machines_semilattice_metadata_t &old_md, - const metadata_v1_14::datacenters_semilattice_metadata_t &datacenters) { - metadata_v1_16::servers_semilattice_metadata_t new_md; - new_md.servers = migrate_map< - server_id_t, - metadata_v1_14::machine_semilattice_metadata_t, - metadata_v1_16::server_semilattice_metadata_t>( - old_md.machines, - [&](const metadata_v1_14::machine_semilattice_metadata_t &old_machine) { - return migrate_server(old_machine, datacenters); - }); - return new_md; -} - -metadata_v1_16::write_ack_config_t::req_t migrate_ack_req( - int num_acks, - std::set replicas_for_acks, - const std::vector &config_shards, - name_string_t table_name, - name_string_t db_name, - const std::string &where) { - guarantee(num_acks != 0); - metadata_v1_16::write_ack_config_t::req_t req; - req.replicas = replicas_for_acks; - if (num_acks == 1) { - req.mode = metadata_v1_16::write_ack_config_t::mode_t::single; - } else { - int largest = 0; - for (const metadata_v1_16::table_config_t::shard_t &shard : config_shards) { - int overlap = 0; - for (const server_id_t &server : shard.replicas) { - overlap += replicas_for_acks.count(server); - } - largest = std::max(largest, overlap); - } - int majority_equivalent = (largest + 2) / 2; - if (num_acks != majority_equivalent) { - logWRN("For table `%s.%s`, you requested %d write acks %s; however, as of " - "RethinkDB version 1.16, RethinkDB does not allow you to specify an " - "arbitrary number of acks. Your ack setting has been translated to the " - "closest equivalent setting, which is `majority`; this is equivalent to " - "%d acks.", - db_name.c_str(), table_name.c_str(), num_acks, where.c_str(), - majority_equivalent); - } - req.mode = metadata_v1_16::write_ack_config_t::mode_t::majority; - } - return req; -} - -std::set get_servers_in_dc( - const metadata_v1_14::machines_semilattice_metadata_t &machines, - const metadata_v1_14::datacenter_id_t &dc) { - std::set servers; - for (const auto &pair : machines.machines) { - if (pair.second.is_deleted()) { - continue; - } - metadata_v1_14::datacenter_id_t server_dc = - get_vclock_best(pair.second.get_ref().datacenter, nullptr); - if (server_dc == dc) { - servers.insert(pair.first); - } - } - return servers; -} - -name_string_t get_name_of_dc( - const metadata_v1_14::datacenters_semilattice_metadata_t &datacenters, - const metadata_v1_14::datacenter_id_t &dc) { - auto it = datacenters.datacenters.find(dc); - if (it == datacenters.datacenters.end() || it->second.is_deleted()) { - return name_string_t::guarantee_valid("__deleted_datacenter__"); - } else { - return get_vclock_best(it->second.get_ref().name, nullptr); - } -} - -metadata_v1_16::namespace_semilattice_metadata_t migrate_table( - const metadata_v1_14::namespace_semilattice_metadata_t &old_md, - /* We need the databases, datacenters, and machines as context */ - const metadata_v1_14::databases_semilattice_metadata_t &databases, - const metadata_v1_14::datacenters_semilattice_metadata_t &datacenters, - const metadata_v1_14::machines_semilattice_metadata_t &machines) { - metadata_v1_16::namespace_semilattice_metadata_t new_md; - - /* Migrate the easy fields */ - new_md.name = migrate_vclock(old_md.name); - new_md.database = migrate_vclock(old_md.database); - new_md.primary_key = migrate_vclock(old_md.primary_key); - - /* Extract the table and database name for error message purposes */ - name_string_t table_name = new_md.name.get_ref(); - name_string_t db_name; - auto db_it = databases.databases.find(new_md.database.get_ref()); - if (db_it != databases.databases.end() && !db_it->second.is_deleted()) { - db_name = get_vclock_best(db_it->second.get_ref().name, nullptr); - } else { - db_name = name_string_t::guarantee_valid("__deleted_database__"); - } - - metadata_v1_16::table_replication_info_t repli_info; - - /* Extract the data we'll need for the more complicated translations. We store the - vclock totals individually and we'll add them together at the end. */ - int blueprint_vclock_total; - metadata_v1_14::persistable_blueprint_t blueprint = - get_vclock_best(old_md.blueprint, &blueprint_vclock_total); - int acks_vclock_total; - std::map acks = - get_vclock_best(old_md.ack_expectations, &acks_vclock_total); - - /* Use the same split points as before */ - std::set split_points; - for (const auto &pair : blueprint.machines_roles.begin()->second) { - region_t region = pair.first; - store_key_t key = region.inner.left; - if (key != store_key_t::min()) { - split_points.insert(key); - } - } - repli_info.shard_scheme.split_points.assign( - split_points.begin(), split_points.end()); - - /* Translate the old blueprint directly into the new table config */ - repli_info.config.shards.resize(repli_info.shard_scheme.num_shards()); - std::set all_relevant_servers; - for (const auto &server_roles : blueprint.machines_roles) { - guarantee(server_roles.second.size() == repli_info.shard_scheme.num_shards()); - for (size_t i = 0; i < repli_info.shard_scheme.num_shards(); ++i) { - metadata_v1_16::table_config_t::shard_t *s = &repli_info.config.shards[i]; - region_t r = hash_region_t( - repli_info.shard_scheme.get_shard_range(i)); - auto it = server_roles.second.find(r); - metadata_v1_14::blueprint_role_t role; - if (it == server_roles.second.end()) { - /* The reason we handle this instead of crashing is that this - hypothetically might happen if the user wrote directly to `/ajax` */ - logERR("Metadata corruption detected when migrating to RethinkDB 1.16 " - "format: table `%s.%s` has different shard boundaries for different " - "servers.", - db_name.c_str(), table_name.c_str()); - /* This is a hack; if the server doesn't have any role for this exact - shard, we'll just set it as a secondary replica. This will result in the - user's data being replicated too many times but at least they won't lose - any data. */ - role = metadata_v1_14::blueprint_role_secondary; - } else { - role = it->second; - } - if (role == metadata_v1_14::blueprint_role_primary) { - if (!s->primary_replica.get_uuid().is_unset()) { - logERR("Metadata corruption detected when migrating to RethinkDB " - "1.16 format: table `%s.%s` has two different servers listed as " - "primary replica for a single shard.", - db_name.c_str(), table_name.c_str()); - /* Choose one primary arbitrarily. It's not the end of the world. */ - } - s->primary_replica = server_roles.first; - s->replicas.insert(server_roles.first); - } else if (role == metadata_v1_14::blueprint_role_secondary) { - s->replicas.insert(server_roles.first); - } - if (role != metadata_v1_14::blueprint_role_nothing) { - all_relevant_servers.insert(server_roles.first); - } - } - } - for (size_t i = 0; i < repli_info.shard_scheme.num_shards(); ++i) { - metadata_v1_16::table_config_t::shard_t *s = &repli_info.config.shards[i]; - if (s->primary_replica.get_uuid().is_unset()) { - /* This is probably impossible unless the user was mucking around with - `/ajax`, but fortunately there's a pretty simple translation that won't break - anything. */ - s->primary_replica = server_id_t::from_server_uuid(nil_uuid()); - } - } - - /* Translating the write acks is the hardest part. We often can't translate directly; - in this case we issue a warning. */ - int num_general_acks = 0, num_specific_acks = 0; - for (const auto &pair : acks) { - if (pair.second.expectation_ == 0) { - continue; - } - if (pair.first.is_nil()) { - /* A nil entry means that we're requiring "general acks" from any datacenter, - in addition to the "specific acks" from particular datacenters. */ - num_general_acks += pair.second.expectation_; - continue; - } - std::set ack_servers = get_servers_in_dc(machines, pair.first); - if (std::includes(ack_servers.begin(), ack_servers.end(), - all_relevant_servers.begin(), all_relevant_servers.end())) { - /* Special-case the situation where all the replicas are in the same - datacenter, by bundling the specific ack requirement for that datacenter in - with the general ack requirement. The reason for special-casing this is that - if there are no specific acks, we can use the simplified syntax for write - acks, which is more user-friendly. */ - num_general_acks += pair.second.expectation_; - continue; - } - if (pair.second.expectation_ == 1) { - bool all_primaries_in_datacenter = true; - for (const metadata_v1_16::table_config_t::shard_t &shard : repli_info.config.shards) { - if (ack_servers.count(shard.primary_replica) != 1) { - all_primaries_in_datacenter = false; - break; - } - } - if (all_primaries_in_datacenter) { - /* If this ack expectation is asking for a single ack from the primary - datacenter, we can safely treat it as a "general ack" because it's - guaranteed to be satisfied anyway. This is convenient because it might - let us use the simplified syntax. */ - num_general_acks += 1; - continue; - } - } - name_string_t dc_name = get_name_of_dc(datacenters, pair.first); - metadata_v1_16::write_ack_config_t::req_t req = migrate_ack_req( - pair.second.expectation_, - ack_servers, - repli_info.config.shards, - table_name, - db_name, - strprintf("in datacenter `%s`", dc_name.c_str())); - repli_info.config.write_ack_config.complex_reqs.push_back(req); - num_specific_acks += pair.second.expectation_; - } - if (num_specific_acks == 0) { - /* There are no specific acks, so we can use the abbreviated syntax */ - if (num_general_acks == 0) { - /* This is possible if the user reduced the ack requirement to zero for some - reason. It's equivalent to a single general ack. */ - repli_info.config.write_ack_config.mode = - metadata_v1_16::write_ack_config_t::mode_t::single; - } else { - repli_info.config.write_ack_config.mode = migrate_ack_req( - num_general_acks, - all_relevant_servers, - repli_info.config.shards, - table_name, - db_name, - "overall").mode; - } - } else { - repli_info.config.write_ack_config.mode = - metadata_v1_16::write_ack_config_t::mode_t::complex; - if (num_general_acks > 0) { - metadata_v1_16::write_ack_config_t::req_t req = migrate_ack_req( - /* Pre-v1.16 ack requirements were non-overlapping, so N general acks - means N additional acks after all the specific acks are satisfied. v1.16 - ack requirements are overlapping, so to be equivalent we have to add the - number of general acks to the total number of specific acks. */ - num_specific_acks + num_general_acks, - all_relevant_servers, - repli_info.config.shards, - table_name, - db_name, - "overall"); - repli_info.config.write_ack_config.complex_reqs.insert( - /* Put the general requiremenet at the beginning because it's more - similar to how it was displayed in the web UI */ - repli_info.config.write_ack_config.complex_reqs.begin(), - req); - } - } - - /* Translate the hard/soft write durability setting. Usually we can translate it - directly; other times we have to default to hard durability. */ - int num_soft = 0, num_hard = 0; - for (const auto &pair : acks) { - if (pair.second.hard_durability_) { - ++num_hard; - } else { - ++num_soft; - } - } - if (num_hard > 0 && num_soft > 0) { - /* This is only possible if the user manually tweaked something through the CLI - or by messing with the HTTP admin interface */ - logWRN("For table `%s.%s`, your existing settings specified hard write " - "durability when writing to some servers and soft write durability for " - "others. As of version 1.16, RethinkDB no longer allows mixed durability " - "settings. Your table will now use hard durability everywhere. You can " - "change this setting by writing to the `rethinkdb.table_config` table.", - db_name.c_str(), table_name.c_str()); - repli_info.config.durability = write_durability_t::HARD; - } else if (num_soft > 0) { - repli_info.config.durability = write_durability_t::SOFT; - } else { - repli_info.config.durability = write_durability_t::HARD; - } - - /* Write `repli_info` back to `new_md`, wrapped in a `versioned_t` */ - new_md.replication_info = - versioned_t::make_with_manual_timestamp( - /* Choose the timestamp by adding together the vclock totals from the fields - on the pre-v1.16 metadata we consulted. This ensures that if we apply this - procedure to two servers' metadata, and one has a more up-to-date vector - clock, then the one with the more up-to-date vector clock will produce a - `versioned_t` with a later timestamp. */ - std::numeric_limits::min() + 1 + - blueprint_vclock_total + acks_vclock_total, - repli_info); - - return new_md; -} - -metadata_v1_16::namespaces_semilattice_metadata_t migrate_tables( - const metadata_v1_14::namespaces_semilattice_metadata_t &old_md, - /* We need the databases, datacenters, and machines as context */ - const metadata_v1_14::databases_semilattice_metadata_t &databases, - const metadata_v1_14::datacenters_semilattice_metadata_t &datacenters, - const metadata_v1_14::machines_semilattice_metadata_t &machines) { - metadata_v1_16::namespaces_semilattice_metadata_t new_md; - new_md.namespaces = migrate_map< - namespace_id_t, - metadata_v1_14::namespace_semilattice_metadata_t, - metadata_v1_16::namespace_semilattice_metadata_t>( - old_md.namespaces, - [&](const metadata_v1_14::namespace_semilattice_metadata_t &old_table) { - return migrate_table(old_table, databases, datacenters, machines); - }); - return new_md; -} - -metadata_v1_16::cluster_semilattice_metadata_t migrate_cluster_metadata_v1_14_to_v1_16( - const metadata_v1_14::cluster_semilattice_metadata_t &old_md) { - metadata_v1_16::cluster_semilattice_metadata_t new_md; - new_md.rdb_namespaces = migrate_tables(old_md.rdb_namespaces, old_md.databases, - old_md.datacenters, old_md.machines); - new_md.servers = migrate_servers(old_md.machines, old_md.datacenters); - new_md.databases = migrate_databases(old_md.databases); - return new_md; -} - -metadata_v1_16::auth_semilattice_metadata_t migrate_auth_metadata_v1_14_to_v1_16( - const metadata_v1_14::auth_semilattice_metadata_t &old_md) { - metadata_v1_16::auth_semilattice_metadata_t new_md; - new_md.auth_key = migrate_vclock(old_md.auth_key); - return new_md; -} diff --git a/src/clustering/administration/persist/migrate/migrate_v1_14.hpp b/src/clustering/administration/persist/migrate/migrate_v1_14.hpp deleted file mode 100644 index dbd56c0df99..00000000000 --- a/src/clustering/administration/persist/migrate/migrate_v1_14.hpp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_MIGRATE_V1_14_HPP_ -#define CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_MIGRATE_V1_14_HPP_ - -#include "clustering/administration/persist/migrate/metadata_v1_14.hpp" -#include "clustering/administration/persist/migrate/metadata_v1_16.hpp" - -// These functions are used to migrate metadata from v1.14 and v1.15 to the v1.16 format - -metadata_v1_16::cluster_semilattice_metadata_t migrate_cluster_metadata_v1_14_to_v1_16( - const metadata_v1_14::cluster_semilattice_metadata_t &old_metadata); -metadata_v1_16::auth_semilattice_metadata_t migrate_auth_metadata_v1_14_to_v1_16( - const metadata_v1_14::auth_semilattice_metadata_t &old_metadata); - -#endif /* CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_MIGRATE_V1_14_HPP_ */ diff --git a/src/clustering/administration/persist/migrate/migrate_v1_16.cc b/src/clustering/administration/persist/migrate/migrate_v1_16.cc deleted file mode 100644 index 228e3bc16b3..00000000000 --- a/src/clustering/administration/persist/migrate/migrate_v1_16.cc +++ /dev/null @@ -1,690 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/persist/migrate/migrate_v1_16.hpp" - -#include "buffer_cache/alt.hpp" -#include "buffer_cache/blob.hpp" -#include "buffer_cache/cache_balancer.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/persist/migrate/migrate_v1_14.hpp" -#include "clustering/administration/persist/migrate/metadata_v1_16.hpp" -#include "clustering/administration/persist/migrate/metadata_v2_1.hpp" -#include "clustering/administration/persist/raft_storage_interface.hpp" -#include "clustering/administration/persist/file_keys.hpp" -#include "clustering/immediate_consistency/history.hpp" -#include "containers/binary_blob.hpp" -#include "rdb_protocol/store.hpp" -#include "serializer/log/log_serializer.hpp" -#include "serializer/merger.hpp" -#include "serializer/translator.hpp" - -struct cluster_metadata_superblock_t { - block_magic_t magic; - - uuid_u server_id; - - static const int METADATA_BLOB_MAXREFLEN = 1500; - char metadata_blob[METADATA_BLOB_MAXREFLEN]; - - static const int BRANCH_HISTORY_BLOB_MAXREFLEN = 500; - char rdb_branch_history_blob[BRANCH_HISTORY_BLOB_MAXREFLEN]; -}; - -struct auth_metadata_superblock_t { - block_magic_t magic; - - static const int METADATA_BLOB_MAXREFLEN = 1500; - char metadata_blob[METADATA_BLOB_MAXREFLEN]; -}; - -template -struct cluster_metadata_magic_t { - static const block_magic_t value; -}; - -template <> -const block_magic_t - cluster_metadata_magic_t::value - = { { 'R', 'D', 'm', 'e' } }; -template <> -const block_magic_t - cluster_metadata_magic_t::value - = { { 'R', 'D', 'm', 'f' } }; -template <> -const block_magic_t - cluster_metadata_magic_t::value - = { { 'R', 'D', 'm', 'g' } }; -template <> -const block_magic_t - cluster_metadata_magic_t::value - = { { 'R', 'D', 'm', 'h' } }; - -cluster_version_t cluster_superblock_version(const cluster_metadata_superblock_t *sb) { - if (sb->magic == cluster_metadata_magic_t::value) { - return cluster_version_t::v1_14; - } else if (sb->magic == cluster_metadata_magic_t::value) { - return cluster_version_t::v1_15; - } else if (sb->magic == cluster_metadata_magic_t::value) { - return cluster_version_t::v1_16; - } else if (sb->magic == cluster_metadata_magic_t::value) { - return cluster_version_t::v2_0; - } else { - fail_due_to_user_error("Migration of cluster metadata could not be performed, it " - "is only supported for metadata from v1.14.x and later."); - } -} - -template -struct auth_metadata_magic_t { - static const block_magic_t value; -}; - -template <> -const block_magic_t auth_metadata_magic_t::value = - { { 'R', 'D', 'm', 'e' } }; - -template <> -const block_magic_t auth_metadata_magic_t::value = - { { 'R', 'D', 'm', 'f' } }; - -template <> -const block_magic_t auth_metadata_magic_t::value = - { { 'R', 'D', 'm', 'g' } }; - -template <> -const block_magic_t auth_metadata_magic_t::value = - { { 'R', 'D', 'm', 'h' } }; - -cluster_version_t auth_superblock_version(const auth_metadata_superblock_t *sb) { - if (sb->magic == auth_metadata_magic_t::value) { - return cluster_version_t::v1_14; - } else if (sb->magic == auth_metadata_magic_t::value) { - return cluster_version_t::v1_15; - } else if (sb->magic == auth_metadata_magic_t::value) { - return cluster_version_t::v1_16; - } else if (sb->magic == auth_metadata_magic_t::value) { - return cluster_version_t::v2_0; - } else { - fail_due_to_user_error("Migration of auth metadata could not be performed, it " - "is only supported for metadata from v1.14.x and later."); - } -} - -struct regioned_version_t { - regioned_version_t(const region_t &r, const ::version_t &v) : - region(r), version(v) { } - region_t region; - ::version_t version; -}; - -bool operator <(const regioned_version_t &a, const regioned_version_t &b) { - if (a.version.branch == b.version.branch) { - if (a.version.timestamp == b.version.timestamp) { - return a.region < b.region; - } - return a.version.timestamp < b.version.timestamp; - } - return a.version.branch < b.version.branch; -} - -static void read_blob(buf_parent_t parent, const char *ref, int maxreflen, - const std::function &reader) { - blob_t blob(parent.cache()->max_block_size(), - const_cast(ref), maxreflen); - blob_acq_t acq_group; - buffer_group_t group; - blob.expose_all(parent, access_t::read, &group, &acq_group); - buffer_group_read_stream_t ss(const_view(&group)); - archive_result_t res = reader(&ss); - guarantee_deserialization(res, "T (template code)"); -} - -void migrate_heartbeat(metadata_file_t::write_txn_t *out, - signal_t *interruptor) { - out->write(mdkey_heartbeat_semilattices(), - heartbeat_semilattice_metadata_t(), interruptor); -} - -void migrate_server(const server_id_t &server_id, - const metadata_v1_16::cluster_semilattice_metadata_t &metadata, - metadata_file_t::write_txn_t *out, - signal_t *interruptor) { - auto self_it = metadata.servers.servers.find(server_id); - guarantee(self_it != metadata.servers.servers.end(), - "Migration of cluster metadata failed, could not find own server config."); - if (self_it->second.is_deleted()) { - fail_due_to_user_error("Migration of cluster metadata failed because " - "this server was already deleted from the cluster."); - } - - server_config_versioned_t new_config; - new_config.version = 1; - new_config.config.name = self_it->second.get_ref().name.get_ref(); - new_config.config.tags = self_it->second.get_ref().tags.get_ref(); - new_config.config.cache_size_bytes = - self_it->second.get_ref().cache_size_bytes.get_ref(); - - out->write(mdkey_server_config(), new_config, interruptor); - out->write(mdkey_server_id(), server_id, interruptor); -} - -void migrate_databases(const metadata_v1_16::cluster_semilattice_metadata_t &metadata, - metadata_file_t::write_txn_t *out, - signal_t *interruptor) { - ::cluster_semilattice_metadata_t new_metadata; - for (auto const &pair : metadata.databases.databases) { - if (!pair.second.is_deleted()) { - ::database_semilattice_metadata_t db; - db.name = pair.second.get_ref().name; - auto res = new_metadata.databases.databases.insert( - std::make_pair(pair.first, make_deletable(db))); - guarantee(res.second); - } else { - auto res = new_metadata.databases.databases.insert( - std::make_pair(pair.first, - deletable_t())); - guarantee(res.second); - res.first->second.mark_deleted(); - } - } - out->write(mdkey_cluster_semilattices(), new_metadata, interruptor); -} - -::branch_history_t migrate_branch_ids( - const namespace_id_t &table_id, - const std::vector &table_versions, - const metadata_v1_16::branch_history_t &branch_history, - metadata_file_t::write_txn_t *out, - signal_t *interruptor) { - branch_history_t result; - std::set seen_branches{ nil_uuid() }; - std::deque branches_to_save; - - for (auto const &rv : table_versions) { - auto const branch_it = branch_history.branches.find(rv.version.branch); - - // If we don't find the branch, we assume that it is referencing a dummy branch - // that we created to replace an incoherent version_range_t. In this case, we - // want the dummy branch to inherit directly from version_t::zero(). This will - // leave the data as-is until such time that a raft cluster is established for - // the table and a more-recent coherent replica is found. At that point the - // incoherent data shall be erased and backfilled from the coherent replica. - if (branch_it == branch_history.branches.end()) { - branch_birth_certificate_t new_birth_certificate; - new_birth_certificate.initial_timestamp = - version_t::zero().timestamp; - new_birth_certificate.origin = - region_map_t(rv.region, version_t::zero()); - - result.branches.insert(std::make_pair(rv.version.branch, - new_birth_certificate)); - - out->write(mdprefix_branch_birth_certificate().suffix( - uuid_to_str(table_id) + "/" + - uuid_to_str(rv.version.branch)), - new_birth_certificate, interruptor); - } else if (seen_branches.count(rv.version.branch) == 0) { - seen_branches.insert(rv.version.branch); - branches_to_save.push_back(rv.version.branch); - } - } - - while (!branches_to_save.empty()) { - auto const branch_it = branch_history.branches.find(branches_to_save.front()); - guarantee(branch_it != branch_history.branches.end()); - branches_to_save.pop_front(); - - branch_birth_certificate_t new_birth_certificate; - - new_birth_certificate.initial_timestamp = branch_it->second.initial_timestamp; - new_birth_certificate.origin = - branch_it->second.origin.map(branch_it->second.region, - [&](const metadata_v1_16::version_range_t &v) -> ::version_t { - ::version_t res(v.earliest.branch, v.earliest.timestamp); - guarantee(v.is_coherent()); - if (seen_branches.count(res.branch) == 0) { - seen_branches.insert(res.branch); - branches_to_save.push_back(res.branch); - } - return res; - }); - - result.branches.insert(std::make_pair(branch_it->first, new_birth_certificate)); - - out->write(mdprefix_branch_birth_certificate().suffix( - uuid_to_str(table_id) + "/" + uuid_to_str(branch_it->first)), - new_birth_certificate, interruptor); - } - - return result; -} - -// This function will use a new uuid rather than the timestamp's tiebreaker because -// we are combining multiple timestamps into one - we could potentially lose changes across -// the cluster. Rather than have conflicting data in the committed raft log under the same -// epoch (on two different servers), we may instead lose a configuration change. This should -// only realistically happen if configuration changes were made while the cluster was in the -// process of shutting down before the upgrade. -template -multi_table_manager_timestamp_t max_versioned_timestamp(const versioned_t &...args) { - std::vector times = { args.get_timestamp()... }; - time_t ts = *std::max_element(times.begin(), times.end()); - - multi_table_manager_timestamp_t res; - res.epoch = multi_table_manager_timestamp_t::epoch_t::migrate(ts); - res.log_index = 0; - return res; -} - -bool is_server_deleted(const server_id_t &server_id, - const metadata_v1_16::servers_semilattice_metadata_t &servers_metadata) { - auto serv_it = servers_metadata.servers.find(server_id); - return (serv_it == servers_metadata.servers.end()) || - serv_it->second.is_deleted(); -} - -void migrate_table(const server_id_t &this_server_id, - const namespace_id_t &table_id, - const metadata_v1_16::namespace_semilattice_metadata_t &table_metadata, - const metadata_v1_16::servers_semilattice_metadata_t &servers_metadata, - const std::map > &sindexes, - const ::branch_history_t &branch_history, - metadata_file_t::write_txn_t *out, - signal_t *interruptor) { - metadata_v1_16::table_replication_info_t old_config = table_metadata.replication_info.get_ref(); - - table_config_and_shards_t config; - config.config.basic.name = table_metadata.name.get_ref(); - config.config.basic.database = table_metadata.database.get_ref(); - config.config.basic.primary_key = table_metadata.primary_key.get_ref(); - config.config.write_ack_config = - old_config.config.write_ack_config.mode == - metadata_v1_16::write_ack_config_t::mode_t::single ? - ::write_ack_config_t::SINGLE : ::write_ack_config_t::MAJORITY; - config.config.durability = old_config.config.durability; - config.shard_scheme.split_points = old_config.shard_scheme.split_points; - - // Scan the servers in the old shard config - need to remove deleted and nil servers - for (size_t i = 0; i < old_config.config.shards.size(); ++i) { - metadata_v1_16::table_config_t::shard_t &s = old_config.config.shards[i]; - std::set replicas_copy = s.replicas; - for (auto const &serv : replicas_copy) { - if (is_server_deleted(serv, servers_metadata)) { - s.replicas.erase(serv); - } - } - if (is_server_deleted(s.primary_replica, servers_metadata)) { - if (s.replicas.empty()) { - logWRN("Table %s has no replicas for the region %s\n", - uuid_to_str(table_id).c_str(), - old_config.shard_scheme.get_shard_range(i).print().c_str()); - s.replicas.insert(s.primary_replica); - } else { - s.primary_replica = *s.replicas.begin(); - } - } - } - - std::set used_servers; - for (auto const &s : old_config.config.shards) { - ::table_config_t::shard_t new_shard; - new_shard.all_replicas = s.replicas; - new_shard.primary_replica = s.primary_replica; - config.config.shards.push_back(std::move(new_shard)); - used_servers.insert(s.replicas.begin(), s.replicas.end()); - } - - for (auto const &pair : sindexes) { - config.config.sindexes.insert(std::make_pair(pair.first, pair.second.first)); - } - - for (auto const &serv_id : used_servers) { - auto serv_it = servers_metadata.servers.find(serv_id); - if (serv_it != servers_metadata.servers.end() && !serv_it->second.is_deleted()) { - config.server_names.names.insert(std::make_pair( - serv_id, std::make_pair(1, serv_it->second.get_ref().name.get_ref()))); - } else { - config.server_names.names.insert(std::make_pair( - serv_id, - std::make_pair(1, name_string_t::guarantee_valid("__deleted_server__")))); - } - } - - table_raft_state_t raft_state = make_new_table_raft_state(config); - raft_state.branch_history = branch_history; - auto own_membership = raft_state.member_ids.find(this_server_id); - - /* Set the `after_emergency_repair` flag for each contract in the config. - This is important to maintain certain invariants about the branch history. */ - for (auto &contract : raft_state.contracts) { - contract.second.second.after_emergency_repair = true; - } - - if (own_membership == raft_state.member_ids.end()) { - // This server is not involved in the table - write an inactive state - table_inactive_persistent_state_t state; - state.timestamp = max_versioned_timestamp(table_metadata.name, - table_metadata.database, - table_metadata.primary_key); - state.second_hand_config.name = table_metadata.name.get_ref(); - state.second_hand_config.database = table_metadata.database.get_ref(); - state.second_hand_config.primary_key = table_metadata.primary_key.get_ref(); - - out->write(mdprefix_table_inactive().suffix(uuid_to_str(table_id)), state, interruptor); - } else { - // This server is involved in the table - write an active state - raft_config_t raft_config; - for (auto const &pair : raft_state.member_ids) { - raft_config.voting_members.insert(pair.second); - } - - raft_persistent_state_t persistent_state = - raft_persistent_state_t::make_initial(raft_state, raft_config); - - table_active_persistent_state_t active_state; - active_state.epoch = max_versioned_timestamp(table_metadata.name, - table_metadata.database, - table_metadata.primary_key, - table_metadata.replication_info).epoch; - guarantee(own_membership != raft_state.member_ids.end()); - active_state.raft_member_id = own_membership->second; - out->write(mdprefix_table_active().suffix(uuid_to_str(table_id)), active_state, interruptor); - - // The `table_raft_storage_interface_t` constructor will persist the header, snapshot, and logs - table_raft_storage_interface_t storage_interface( - nullptr, out, table_id, persistent_state); - } -} - -void check_for_obsolete_sindexes(io_backender_t *io_backender, - const base_path_t &base_path, - const metadata_v1_16::cluster_semilattice_metadata_t &metadata, - signal_t *interruptor) { - dummy_cache_balancer_t balancer(GIGABYTE); - auto &tables = metadata.rdb_namespaces.namespaces; - pmap(tables.begin(), tables.end(), - [&](std::pair > &info) { - if (!info.second.is_deleted()) { - perfmon_collection_t dummy_stats; - serializer_filepath_t table_path(base_path, uuid_to_str(info.first)); - filepath_file_opener_t file_opener(table_path, io_backender); - scoped_ptr_t inner_serializer( - new log_serializer_t(log_serializer_t::dynamic_config_t(), - &file_opener, &dummy_stats)); - merger_serializer_t merger_serializer(std::move(inner_serializer), - MERGER_SERIALIZER_MAX_ACTIVE_WRITES); - std::vector underlying({ &merger_serializer }); - serializer_multiplexer_t multiplexer(underlying); - - pmap(CPU_SHARDING_FACTOR, [&](int index) { - perfmon_collection_t inner_dummy_stats; - store_t store(cpu_sharding_subspace(index), - multiplexer.proxies[index], - &balancer, - "table_migration", - false, - &inner_dummy_stats, - nullptr, - io_backender, - base_path, - info.first, - /* Important: Don't update indexes yet. */ - update_sindexes_t::LEAVE_ALONE); - - store.sindex_list(interruptor); - }); - } - }); -} - -void migrate_tables(io_backender_t *io_backender, - const base_path_t &base_path, - const server_id_t &this_server_id, - const metadata_v1_16::cluster_semilattice_metadata_t &metadata, - const metadata_v1_16::branch_history_t &old_branch_history, - metadata_file_t::write_txn_t *out, - signal_t *interruptor) { - dummy_cache_balancer_t balancer(GIGABYTE); - auto &tables = metadata.rdb_namespaces.namespaces; - pmap(tables.begin(), tables.end(), - [&](std::pair > &info) { - // We don't need to store anything for deleted tables - if (!info.second.is_deleted()) { - perfmon_collection_t dummy_stats; - serializer_filepath_t table_path(base_path, uuid_to_str(info.first)); - filepath_file_opener_t file_opener(table_path, io_backender); - scoped_ptr_t inner_serializer( - new log_serializer_t(log_serializer_t::dynamic_config_t(), - &file_opener, &dummy_stats)); - merger_serializer_t merger_serializer(std::move(inner_serializer), - MERGER_SERIALIZER_MAX_ACTIVE_WRITES); - std::vector underlying({ &merger_serializer }); - serializer_multiplexer_t multiplexer(underlying); - - std::vector table_versions; - std::map > sindex_list; - - pmap(CPU_SHARDING_FACTOR, [&](int index) { - perfmon_collection_t inner_dummy_stats; - store_t store(cpu_sharding_subspace(index), - multiplexer.proxies[index], - &balancer, - "table_migration", - false, - &inner_dummy_stats, - nullptr, - io_backender, - base_path, - info.first, - update_sindexes_t::UPDATE); - - if (index == 0) { - sindex_list = store.sindex_list(interruptor); - } - - read_token_t read_token; - if (store.metainfo_version(&read_token, - interruptor) == cluster_version_t::v2_0) { - write_token_t write_token; - store.new_write_token(&write_token); - store.migrate_metainfo( - order_token_t::ignore, &write_token, - cluster_version_t::v2_0, cluster_version_t::v2_1, - [&](const region_t &r, const binary_blob_t &blob) -> binary_blob_t { - auto const &v = - binary_blob_t::get(blob); - ::version_t res(v.earliest.branch, v.earliest.timestamp); - if (!v.is_coherent()) { - // Create a dummy branch that will be assigned a birth - // certificate off of version_t::zero - res = ::version_t(generate_uuid(), - state_timestamp_t::zero()); - } - if (!res.branch.is_nil()) { - table_versions.push_back(regioned_version_t(r, res)); - } - return binary_blob_t::make(res); - }, interruptor); - } - - guarantee(store.metainfo_version(&read_token, - interruptor) == cluster_version_t::v2_1); - }); - - branch_history_t new_branch_history = - migrate_branch_ids(info.first, - table_versions, - old_branch_history, - out, - interruptor); - - migrate_table(this_server_id, - info.first, - info.second.get_ref(), - metadata.servers, - sindex_list, - new_branch_history, - out, - interruptor); - } - }); - -} - -void migrate_cluster_metadata_to_v2_1(io_backender_t *io_backender, - const base_path_t &base_path, - buf_parent_t buf_parent, - const void *old_superblock, - metadata_file_t::write_txn_t *out, - signal_t *interruptor) { - const cluster_metadata_superblock_t *sb = - static_cast(old_superblock); - cluster_version_t v = cluster_superblock_version(sb); - metadata_v1_16::cluster_semilattice_metadata_t metadata; - - if (v == cluster_version_t::v1_14 || v == cluster_version_t::v1_15) { - metadata_v1_14::cluster_semilattice_metadata_t old_metadata; - read_blob(buf_parent, sb->metadata_blob, - cluster_metadata_superblock_t::METADATA_BLOB_MAXREFLEN, - [&](read_stream_t *s) -> archive_result_t { - switch(v) { - case cluster_version_t::v1_14: - return deserialize(s, &old_metadata); - case cluster_version_t::v1_15: - return deserialize(s, &old_metadata); - case cluster_version_t::v1_16: - case cluster_version_t::v2_0: - case cluster_version_t::v2_1: - case cluster_version_t::v2_2: - case cluster_version_t::v2_3: - case cluster_version_t::v2_4_is_latest: - default: - unreachable(); - } - }); - metadata = migrate_cluster_metadata_v1_14_to_v1_16(old_metadata); - } else if (v == cluster_version_t::v1_16 || v == cluster_version_t::v2_0) { - read_blob(buf_parent, sb->metadata_blob, - cluster_metadata_superblock_t::METADATA_BLOB_MAXREFLEN, - [&](read_stream_t *s) -> archive_result_t { - switch(v) { - case cluster_version_t::v1_16: - return deserialize(s, &metadata); - case cluster_version_t::v2_0: - return deserialize(s, &metadata); - case cluster_version_t::v1_14: - case cluster_version_t::v1_15: - case cluster_version_t::v2_1: - case cluster_version_t::v2_2: - case cluster_version_t::v2_3: - case cluster_version_t::v2_4_is_latest: - default: - unreachable(); - } - }); - } else { - unreachable(); - } - - // Check for obsolete sindexes before anything else because we don't want to - // migrate half the tables and leave the user with an unusable directory. - check_for_obsolete_sindexes(io_backender, base_path, metadata, interruptor); - - metadata_v1_16::branch_history_t branch_history; - read_blob(buf_parent, sb->rdb_branch_history_blob, - cluster_metadata_superblock_t::BRANCH_HISTORY_BLOB_MAXREFLEN, - [&](read_stream_t *s) -> archive_result_t { - return deserialize_for_version(v, s, &branch_history); - }); - - migrate_heartbeat(out, interruptor); - migrate_server( - server_id_t::from_server_uuid(sb->server_id), metadata, out, interruptor); - migrate_databases(metadata, out, interruptor); - migrate_tables( - io_backender, - base_path, - server_id_t::from_server_uuid(sb->server_id), - metadata, - branch_history, - out, - interruptor); -} - -void migrate_auth_metadata_to_v2_1(io_backender_t *io_backender, - const serializer_filepath_t &path, - metadata_file_t::write_txn_t *out, - signal_t *interruptor) { - perfmon_collection_t dummy_stats; - filepath_file_opener_t file_opener(path, io_backender); - log_serializer_t serializer(log_serializer_t::dynamic_config_t(), &file_opener, &dummy_stats); - - if (!serializer.coop_lock_and_check()) { - throw file_in_use_exc_t(); - } - - dummy_cache_balancer_t balancer(MEGABYTE); - cache_t cache(&serializer, &balancer, &dummy_stats); - cache_conn_t cache_conn(&cache); - - txn_t read_txn(&cache_conn, read_access_t::read); - buf_lock_t superblock(buf_parent_t(&read_txn), SUPERBLOCK_ID, access_t::read); - buf_read_t sb_read(&superblock); - - const auth_metadata_superblock_t *sb = - static_cast(sb_read.get_data_read()); - cluster_version_t v = auth_superblock_version(sb); - metadata_v1_16::auth_semilattice_metadata_t metadata; - - if (v == cluster_version_t::v1_14 || v == cluster_version_t::v1_15) { - metadata_v1_14::auth_semilattice_metadata_t old_metadata; - read_blob(buf_parent_t(&superblock), - sb->metadata_blob, - auth_metadata_superblock_t::METADATA_BLOB_MAXREFLEN, - [&](read_stream_t *s) -> archive_result_t { - switch (v) { - case cluster_version_t::v1_14: - return deserialize(s, &old_metadata); - case cluster_version_t::v1_15: - return deserialize(s, &old_metadata); - case cluster_version_t::v1_16: - case cluster_version_t::v2_0: - case cluster_version_t::v2_1: - case cluster_version_t::v2_2: - case cluster_version_t::v2_3: - case cluster_version_t::v2_4_is_latest: - default: - unreachable(); - } - }); - metadata = migrate_auth_metadata_v1_14_to_v1_16(old_metadata); - } else if (v == cluster_version_t::v1_16 || v == cluster_version_t::v2_0) { - read_blob(buf_parent_t(&superblock), - sb->metadata_blob, - auth_metadata_superblock_t::METADATA_BLOB_MAXREFLEN, - [&](read_stream_t *s) -> archive_result_t { - switch (v) { - case cluster_version_t::v1_16: - return deserialize(s, &metadata); - case cluster_version_t::v2_0: - return deserialize(s, &metadata); - case cluster_version_t::v1_14: - case cluster_version_t::v1_15: - case cluster_version_t::v2_1: - case cluster_version_t::v2_2: - case cluster_version_t::v2_3: - case cluster_version_t::v2_4_is_latest: - default: - unreachable(); - } - }); - } else { - unreachable(); - } - - metadata_v1_16::auth_semilattice_metadata_t new_metadata; - new_metadata.auth_key = metadata.auth_key; - - out->write(metadata_v2_1::mdkey_auth_semilattices(), new_metadata, interruptor); -} diff --git a/src/clustering/administration/persist/migrate/migrate_v1_16.hpp b/src/clustering/administration/persist/migrate/migrate_v1_16.hpp deleted file mode 100644 index 864cfae8613..00000000000 --- a/src/clustering/administration/persist/migrate/migrate_v1_16.hpp +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_MIGRATE_V1_16_HPP_ -#define CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_MIGRATE_V1_16_HPP_ - -#include "clustering/administration/persist/file.hpp" -#include "serializer/types.hpp" - -// These functions are used to migrate metadata from v1.14 through v2.0 to the v2.1 format - -void migrate_cluster_metadata_to_v2_1(io_backender_t *io_backender, - const base_path_t &base_path, - buf_parent_t buf_parent, - const void *old_superblock, - metadata_file_t::write_txn_t *out, - signal_t *interruptor); - -void migrate_auth_metadata_to_v2_1(io_backender_t *io_backender, - const serializer_filepath_t &path, - metadata_file_t::write_txn_t *out, - signal_t *interruptor); - -#endif /* CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_MIGRATE_V1_16_HPP_ */ diff --git a/src/clustering/administration/persist/migrate/migrate_v2_1.cc b/src/clustering/administration/persist/migrate/migrate_v2_1.cc deleted file mode 100644 index 63145b040b2..00000000000 --- a/src/clustering/administration/persist/migrate/migrate_v2_1.cc +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#include "clustering/administration/persist/migrate/migrate_v2_1.hpp" - -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/persist/file_keys.hpp" -#include "clustering/administration/persist/migrate/metadata_v2_1.hpp" -#include "clustering/administration/persist/migrate/rewrite.hpp" -#include "clustering/administration/persist/raft_storage_interface.hpp" -#include "clustering/table_manager/table_metadata.hpp" - -// This will migrate all metadata from v2_1 to v2_3 (including auth metadata). -template -void migrate_metadata_v2_1_to_v2_3(metadata_file_t::write_txn_t *txn, - signal_t *interruptor) { - // Rewrite all metadata so it's serialized under the latest version - rewrite_metadata_values(mdkey_cluster_semilattices(), txn, interruptor); - rewrite_metadata_values(mdkey_heartbeat_semilattices(), txn, interruptor); - rewrite_metadata_values(mdkey_server_id(), txn, interruptor); - rewrite_metadata_values(mdkey_server_config(), txn, interruptor); - - rewrite_metadata_values(mdprefix_table_active(), txn, interruptor); - rewrite_metadata_values(mdprefix_table_inactive(), txn, interruptor); - rewrite_metadata_values(mdprefix_table_raft_header(), txn, interruptor); - rewrite_metadata_values(mdprefix_table_raft_snapshot(), txn, interruptor); - rewrite_metadata_values(mdprefix_table_raft_log(), txn, interruptor); - rewrite_metadata_values(mdprefix_branch_birth_certificate(), txn, interruptor); - - rewrite_metadata_values(metadata_v2_1::mdkey_auth_semilattices(), - txn, interruptor); - - migrate_auth_metadata_v2_1_to_v2_3(txn, interruptor); -} - -// This is only exposed because auth metadata used to be in a separate file, -// and when migrating from that file, we need this function. -void migrate_auth_metadata_v2_1_to_v2_3(metadata_file_t::write_txn_t *txn, - signal_t *interruptor) { - // Read out the old auth metadata - metadata_v1_16::auth_semilattice_metadata_t old_metadata; - std::string old_auth_key; - time_t auth_key_ts = std::numeric_limits::min(); - - // This can fail if we're migrating very old metadata, and it hasn't brought - // in the auth_metadata file yet. That will be done later and overwrite the - // default value we write here. - if (txn->read_maybe(metadata_v2_1::mdkey_auth_semilattices(), - &old_metadata, - interruptor)) { - old_auth_key = old_metadata.auth_key.get_ref().key; - auth_key_ts = old_metadata.auth_key.get_timestamp(); - } - - // Write out the new auth metadata - auth_semilattice_metadata_t new_metadata; - auth::password_t admin_pw( - old_auth_key, - 1 /* 1 iteration, since auth_key was already insecure. */); - auto admin_pair = std::make_pair( - auth::username_t("admin"), - versioned_t>::make_with_manual_timestamp( - auth_key_ts, - make_optional(auth::user_t(std::move(admin_pw))))); - new_metadata.m_users.insert(std::move(admin_pair)); - txn->write(mdkey_auth_semilattices(), new_metadata, interruptor); -} - -// This will migrate all metadata from v2_1 to v2_3 (including auth metadata). -void migrate_metadata_v2_1_to_v2_3(cluster_version_t serialization_version, - metadata_file_t::write_txn_t *txn, - signal_t *interruptor) { - switch (serialization_version) { - case cluster_version_t::v2_1: - migrate_metadata_v2_1_to_v2_3(txn, interruptor); - break; - case cluster_version_t::v2_2: - migrate_metadata_v2_1_to_v2_3(txn, interruptor); - break; - case cluster_version_t::v2_3: - migrate_metadata_v2_1_to_v2_3(txn, interruptor); - break; - case cluster_version_t::v2_4_is_latest: - migrate_metadata_v2_1_to_v2_3(txn, interruptor); - break; - case cluster_version_t::v1_14: - case cluster_version_t::v1_15: - case cluster_version_t::v1_16: - case cluster_version_t::v2_0: - default: - unreachable(); - } -} diff --git a/src/clustering/administration/persist/migrate/migrate_v2_1.hpp b/src/clustering/administration/persist/migrate/migrate_v2_1.hpp deleted file mode 100644 index 750a6376493..00000000000 --- a/src/clustering/administration/persist/migrate/migrate_v2_1.hpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_MIGRATE_V2_1_HPP_ -#define CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_MIGRATE_V2_1_HPP_ - -#include "clustering/administration/persist/file.hpp" -#include "serializer/types.hpp" - -// These functions are used to migrate metadata from v2.1 through v2.2 to the v2.3 format - -// This will migrate all metadata from v2_1 to v2_3 (including auth metadata). -void migrate_metadata_v2_1_to_v2_3(cluster_version_t serialization_version, - metadata_file_t::write_txn_t *txn, - signal_t *interruptor); - -// This is only exposed because auth metadata used to be in a separate file, -// and when migrating from that file, we need this function. This function -// assumes that the metadata is serialized using the latest version. -void migrate_auth_metadata_v2_1_to_v2_3(metadata_file_t::write_txn_t *txn, - signal_t *interruptor); - -#endif /* CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_MIGRATE_V2_1_HPP_ */ diff --git a/src/clustering/administration/persist/migrate/migrate_v2_3.cc b/src/clustering/administration/persist/migrate/migrate_v2_3.cc deleted file mode 100644 index f3447f9aa1e..00000000000 --- a/src/clustering/administration/persist/migrate/migrate_v2_3.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2010-2017 RethinkDB, all rights reserved. -#include "clustering/administration/persist/migrate/migrate_v2_3.hpp" - -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/persist/file_keys.hpp" -#include "clustering/administration/persist/migrate/rewrite.hpp" -#include "clustering/administration/persist/raft_storage_interface.hpp" -#include "clustering/table_manager/table_metadata.hpp" - -// This will migrate all metadata from v2_3 to v2_4 -template -void migrate_metadata_v2_3_to_v2_4(metadata_file_t::write_txn_t *txn, - signal_t *interruptor) { - // We simply need to rewrite the table metadata to update to the latest - // format. - rewrite_metadata_values(mdprefix_table_active(), txn, interruptor); - rewrite_metadata_values(mdprefix_table_inactive(), txn, interruptor); - rewrite_metadata_values(mdprefix_table_raft_header(), txn, interruptor); - rewrite_metadata_values(mdprefix_table_raft_snapshot(), txn, interruptor); - rewrite_metadata_values(mdprefix_table_raft_log(), txn, interruptor); -} - - - -// This will migrate all metadata from v2_3 to v2_4 -void migrate_metadata_v2_3_to_v2_4(cluster_version_t serialization_version, - metadata_file_t::write_txn_t *txn, - signal_t *interruptor) { - switch (serialization_version) { - case cluster_version_t::v2_3: - migrate_metadata_v2_3_to_v2_4(txn, interruptor); - break; - case cluster_version_t::v2_4_is_latest: - break; - case cluster_version_t::v1_14: - case cluster_version_t::v1_15: - case cluster_version_t::v1_16: - case cluster_version_t::v2_0: - case cluster_version_t::v2_1: - case cluster_version_t::v2_2: - default: - unreachable(); - } -} diff --git a/src/clustering/administration/persist/migrate/migrate_v2_3.hpp b/src/clustering/administration/persist/migrate/migrate_v2_3.hpp deleted file mode 100644 index dd55e9b6953..00000000000 --- a/src/clustering/administration/persist/migrate/migrate_v2_3.hpp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_MIGRATE_V2_3_HPP_ -#define CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_MIGRATE_V2_3_HPP_ - -#include "clustering/administration/persist/file.hpp" -#include "serializer/types.hpp" - -// These functions are used to migrate metadata from v2.3 to the v2.4 format - -// This will migrate all metadata from v2_3 to v2_4 -void migrate_metadata_v2_3_to_v2_4(cluster_version_t serialization_version, - metadata_file_t::write_txn_t *txn, - signal_t *interruptor); - -#endif /* CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_MIGRATE_V2_3_HPP_ */ diff --git a/src/clustering/administration/persist/migrate/rewrite.hpp b/src/clustering/administration/persist/migrate/rewrite.hpp deleted file mode 100644 index acabef5832a..00000000000 --- a/src/clustering/administration/persist/migrate/rewrite.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_REWRITE_HPP_ -#define CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_REWRITE_HPP_ - -#include -#include - -#include "clustering/administration/persist/file.hpp" - -template -void rewrite_metadata_values(const metadata_file_t::key_t &prefix, - metadata_file_t::write_txn_t *txn, - signal_t *interruptor) { - std::map values; - txn->read_many(prefix, - [&](const std::string &key_suffix, const T &value) { - values.insert(std::make_pair(key_suffix, value)); - }, interruptor); - - // Perform the writes separately to avoid recursive locking - for (const auto &pair : values) { - txn->write(prefix.suffix(pair.first), pair.second, interruptor); - } -} - -#endif /* CLUSTERING_ADMINISTRATION_PERSIST_MIGRATE_REWRITE_HPP_ */ diff --git a/src/clustering/administration/persist/raft_storage_interface.cc b/src/clustering/administration/persist/raft_storage_interface.cc deleted file mode 100644 index 382b9193dfb..00000000000 --- a/src/clustering/administration/persist/raft_storage_interface.cc +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/persist/raft_storage_interface.hpp" - -#include "clustering/administration/persist/file_keys.hpp" - -RDB_IMPL_SERIALIZABLE_3_SINCE_v2_1(table_raft_stored_header_t, - current_term, voted_for, commit_index); -RDB_IMPL_SERIALIZABLE_4_SINCE_v2_1(table_raft_stored_snapshot_t, - snapshot_state, snapshot_config, log_prev_index, log_prev_term); - -raft_log_index_t str_to_log_index(const std::string &str) { - guarantee(str.size() == 16); - raft_log_index_t index = 0; - for (size_t i = 0; i < 16; ++i) { - int val; - if (str[i] >= '0' && str[i] <= '9') { - val = str[i] - '0'; - } else if (str[i] >= 'a' && str[i] <= 'f') { - val = 10 + (str[i] - 'a'); - } else { - crash("bad character in str_to_log_index()"); - } - index += val << ((15 - i) * 4); - } - return index; -} - -std::string log_index_to_str(raft_log_index_t log_index) { - std::string str; - str.reserve(16); - for (size_t i = 0; i < 16; ++i) { - int val = (log_index >> ((15 - i) * 4)) & 0x0f; - rassert(val >= 0 && val < 16); - str.push_back("0123456789abcdef"[val]); - } - return str; -} - -table_raft_storage_interface_t::table_raft_storage_interface_t( - metadata_file_t *_file, - metadata_file_t::read_txn_t *txn, - const namespace_id_t &_table_id, - signal_t *interruptor) : - file(_file), table_id(_table_id) { - table_raft_stored_header_t header = txn->read( - mdprefix_table_raft_header().suffix(uuid_to_str(table_id)), interruptor); - state.current_term = header.current_term; - state.voted_for = header.voted_for; - state.commit_index = header.commit_index; - table_raft_stored_snapshot_t snapshot = txn->read( - mdprefix_table_raft_snapshot().suffix(uuid_to_str(table_id)), interruptor); - state.snapshot_state = std::move(snapshot.snapshot_state); - state.snapshot_config = std::move(snapshot.snapshot_config); - state.log.prev_index = snapshot.log_prev_index; - state.log.prev_term = snapshot.log_prev_term; - txn->read_many >( - mdprefix_table_raft_log().suffix(uuid_to_str(table_id) + "/"), - [&](const std::string &index_str, - const raft_log_entry_t &entry) { - guarantee(str_to_log_index(index_str) == state.log.get_latest_index() + 1, - "%" PRIu64 " ('%s') == %" PRIu64, - str_to_log_index(index_str), - index_str.c_str(), - state.log.get_latest_index() + 1); - state.log.append(entry); - }, - interruptor); -} - -table_raft_storage_interface_t::table_raft_storage_interface_t( - metadata_file_t *_file, - metadata_file_t::write_txn_t *txn, - const namespace_id_t &_table_id, - const raft_persistent_state_t &_state) : - file(_file), table_id(_table_id), state(_state) { - cond_t non_interruptor; - - txn->write( - mdprefix_table_raft_header().suffix(uuid_to_str(table_id)), - table_raft_stored_header_t::from_state(state), - &non_interruptor); - - /* To avoid expensive copies of `state`, we move `state` into the snapshot and then - back out after we're done */ - table_raft_stored_snapshot_t snapshot; - snapshot.snapshot_state = std::move(state.snapshot_state); - snapshot.snapshot_config = std::move(state.snapshot_config); - snapshot.log_prev_index = state.log.prev_index; - snapshot.log_prev_term = state.log.prev_term; - txn->write( - mdprefix_table_raft_snapshot().suffix(uuid_to_str(table_id)), - snapshot, - &non_interruptor); - state.snapshot_state = std::move(snapshot.snapshot_state); - state.snapshot_config = std::move(snapshot.snapshot_config); - - for (raft_log_index_t i = state.log.prev_index + 1; - i <= state.log.get_latest_index(); ++i) { - txn->write( - mdprefix_table_raft_log().suffix( - uuid_to_str(table_id) + "/" + log_index_to_str(i)), - state.log.get_entry_ref(i), - &non_interruptor); - } -} - -void table_raft_storage_interface_t::erase( - metadata_file_t::write_txn_t *txn, - const namespace_id_t &table_id) { - cond_t non_interruptor; - txn->erase( - mdprefix_table_raft_header().suffix(uuid_to_str(table_id)), - &non_interruptor); - txn->erase( - mdprefix_table_raft_snapshot().suffix(uuid_to_str(table_id)), - &non_interruptor); - std::vector log_keys; - txn->read_many >( - mdprefix_table_raft_log().suffix(uuid_to_str(table_id) + "/"), - [&](const std::string &index_str, const raft_log_entry_t &) { - log_keys.push_back(index_str); - }, - &non_interruptor); - for (const std::string &key : log_keys) { - txn->erase( - mdprefix_table_raft_log().suffix(uuid_to_str(table_id) + "/" + key), - &non_interruptor); - } -} - -const raft_persistent_state_t * -table_raft_storage_interface_t::get() { - return &state; -} - -void table_raft_storage_interface_t::write_current_term_and_voted_for( - raft_term_t current_term, - raft_member_id_t voted_for) { - cond_t non_interruptor; - metadata_file_t::write_txn_t txn(file, &non_interruptor); - state.current_term = current_term; - state.voted_for = voted_for; - txn.write( - mdprefix_table_raft_header().suffix(uuid_to_str(table_id)), - table_raft_stored_header_t::from_state(state), - &non_interruptor); - txn.commit(); -} - -void table_raft_storage_interface_t::write_commit_index( - raft_log_index_t commit_index) { - cond_t non_interruptor; - metadata_file_t::write_txn_t txn(file, &non_interruptor); - state.commit_index = commit_index; - txn.write( - mdprefix_table_raft_header().suffix(uuid_to_str(table_id)), - table_raft_stored_header_t::from_state(state), - &non_interruptor); - txn.commit(); -} - -void table_raft_storage_interface_t::write_log_replace_tail( - const raft_log_t &source, - raft_log_index_t first_replaced) { - cond_t non_interruptor; - metadata_file_t::write_txn_t txn(file, &non_interruptor); - guarantee(first_replaced > state.log.prev_index); - guarantee(first_replaced <= state.log.get_latest_index() + 1); - for (raft_log_index_t i = first_replaced; - i <= std::max(state.log.get_latest_index(), source.get_latest_index()); ++i) { - metadata_file_t::key_t > key = - mdprefix_table_raft_log().suffix( - uuid_to_str(table_id) + "/" + log_index_to_str(i)); - if (i <= source.get_latest_index()) { - txn.write(key, source.get_entry_ref(i), &non_interruptor); - } else { - txn.erase(key, &non_interruptor); - } - } - if (first_replaced != state.log.get_latest_index() + 1) { - state.log.delete_entries_from(first_replaced); - } - for (raft_log_index_t i = first_replaced; i <= source.get_latest_index(); ++i) { - state.log.append(source.get_entry_ref(i)); - } - txn.commit(); -} - -void table_raft_storage_interface_t::write_log_append_one( - const raft_log_entry_t &entry) { - cond_t non_interruptor; - metadata_file_t::write_txn_t txn(file, &non_interruptor); - raft_log_index_t index = state.log.get_latest_index() + 1; - txn.write( - mdprefix_table_raft_log().suffix( - uuid_to_str(table_id) + "/" + log_index_to_str(index)), - entry, - &non_interruptor); - state.log.append(entry); - txn.commit(); -} - -void table_raft_storage_interface_t::write_snapshot( - const table_raft_state_t &snapshot_state, - const raft_complex_config_t &snapshot_config, - bool clear_log, - raft_log_index_t log_prev_index, - raft_term_t log_prev_term, - raft_log_index_t commit_index) { - cond_t non_interruptor; - metadata_file_t::write_txn_t txn(file, &non_interruptor); - state.commit_index = commit_index; - txn.write( - mdprefix_table_raft_header().suffix(uuid_to_str(table_id)), - table_raft_stored_header_t::from_state(state), - &non_interruptor); - table_raft_stored_snapshot_t snapshot; - snapshot.snapshot_state = snapshot_state; - snapshot.snapshot_config = snapshot_config; - snapshot.log_prev_index = log_prev_index; - snapshot.log_prev_term = log_prev_term; - txn.write( - mdprefix_table_raft_snapshot().suffix(uuid_to_str(table_id)), - snapshot, - &non_interruptor); - for (raft_log_index_t i = state.log.prev_index + 1; - i <= (clear_log ? state.log.get_latest_index() : log_prev_index); ++i) { - txn.erase( - mdprefix_table_raft_log().suffix( - uuid_to_str(table_id) + "/" + log_index_to_str(i)), - &non_interruptor); - } - state.snapshot_state = std::move(snapshot.snapshot_state); - state.snapshot_config = std::move(snapshot.snapshot_config); - if (clear_log) { - state.log.entries.clear(); - state.log.prev_index = log_prev_index; - state.log.prev_term = log_prev_term; - } else { - state.log.delete_entries_to(log_prev_index, log_prev_term); - } - txn.commit(); -} - diff --git a/src/clustering/administration/persist/raft_storage_interface.hpp b/src/clustering/administration/persist/raft_storage_interface.hpp deleted file mode 100644 index 91310ad183e..00000000000 --- a/src/clustering/administration/persist/raft_storage_interface.hpp +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_PERSIST_RAFT_STORAGE_INTERFACE_HPP_ -#define CLUSTERING_ADMINISTRATION_PERSIST_RAFT_STORAGE_INTERFACE_HPP_ - -#include "clustering/administration/persist/file.hpp" -#include "clustering/generic/raft_core.hpp" -#include "clustering/table_contract/contract_metadata.hpp" - -class metadata_file_t; - -/* The raft state is stored as follows: -- There is a single `table_raft_stored_header_t` which stores `current_term`, - `voted_for`, and `commit_index`. -- There is a single `table_raft_stored_snapshot_t` which stores `snapshot_state`, - `snapshot_config`, `log.prev_index`, and `log.prev_term`. -- There are zero or more `raft_log_entry_t`s, which use the log index as part of the - B-tree key. */ - -class table_raft_stored_header_t { -public: - static table_raft_stored_header_t from_state( - const raft_persistent_state_t &state) { - return table_raft_stored_header_t { - state.current_term, state.voted_for, state.commit_index }; - } - raft_term_t current_term; - raft_member_id_t voted_for; - raft_log_index_t commit_index; -}; -RDB_DECLARE_SERIALIZABLE(table_raft_stored_header_t); - -class table_raft_stored_snapshot_t { -public: - table_raft_state_t snapshot_state; - raft_complex_config_t snapshot_config; - raft_log_index_t log_prev_index; - raft_term_t log_prev_term; -}; -RDB_DECLARE_SERIALIZABLE(table_raft_stored_snapshot_t); - -class table_raft_storage_interface_t : - public raft_storage_interface_t { -public: - /* This constructor loads an existing Raft state from the metadata file. */ - table_raft_storage_interface_t( - metadata_file_t *file, - metadata_file_t::read_txn_t *txn, - const namespace_id_t &table_id, - signal_t *interruptor); - - /* This constructor writes a new Raft state to the metadata file. */ - table_raft_storage_interface_t( - metadata_file_t *file, - metadata_file_t::write_txn_t *txn, - const namespace_id_t &table_id, - const raft_persistent_state_t &state); - - /* This method erases an existing Raft state in the metadata file. */ - static void erase( - metadata_file_t::write_txn_t *txn, - const namespace_id_t &table_id); - - /* `raft_storage_interface_t` methods */ - const raft_persistent_state_t *get(); - void write_current_term_and_voted_for( - raft_term_t current_term, - raft_member_id_t voted_for); - void write_commit_index( - raft_log_index_t commit_index); - void write_log_replace_tail( - const raft_log_t &source, - raft_log_index_t first_replaced); - void write_log_append_one( - const raft_log_entry_t &entry); - void write_snapshot( - const table_raft_state_t &snapshot_state, - const raft_complex_config_t &snapshot_config, - bool clear_log, - raft_log_index_t log_prev_index, - raft_term_t log_prev_term, - raft_log_index_t commit_index); - -private: - metadata_file_t *const file; - namespace_id_t const table_id; - raft_persistent_state_t state; -}; - -#endif /* CLUSTERING_ADMINISTRATION_PERSIST_RAFT_STORAGE_INTERFACE_HPP_ */ - diff --git a/src/clustering/administration/persist/semilattice.cc b/src/clustering/administration/persist/semilattice.cc deleted file mode 100644 index 8efc4c8883f..00000000000 --- a/src/clustering/administration/persist/semilattice.cc +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/persist/semilattice.hpp" - -#include "clustering/administration/metadata.hpp" -#include "concurrency/wait_any.hpp" - -template -semilattice_persister_t::semilattice_persister_t( - metadata_file_t *f, - const metadata_file_t::key_t &k, - std::shared_ptr > v) : - file(f), key(k), view(v), - persist_pumper(std::bind(&semilattice_persister_t::persist, this, ph::_1)), - subs(std::bind(&pump_coro_t::notify, &persist_pumper), v) -{ - persist_pumper.notify(); -} - -template -void semilattice_persister_t::persist(signal_t *interruptor) { - if (interruptor->is_pulsed()) { - throw interrupted_exc_t(); - } - cond_t non_interruptor; - metadata_file_t::write_txn_t txn(file, &non_interruptor); - txn.write(key, view->get(), &non_interruptor); - txn.commit(); -} - -template class semilattice_persister_t; -template class semilattice_persister_t; -template class semilattice_persister_t; diff --git a/src/clustering/administration/persist/semilattice.hpp b/src/clustering/administration/persist/semilattice.hpp deleted file mode 100644 index 303ad708061..00000000000 --- a/src/clustering/administration/persist/semilattice.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_PERSIST_SEMILATTICE_HPP_ -#define CLUSTERING_ADMINISTRATION_PERSIST_SEMILATTICE_HPP_ - -#include "clustering/administration/persist/file.hpp" -#include "concurrency/pump_coro.hpp" -#include "rpc/semilattice/view.hpp" - -template -class semilattice_persister_t { -public: - semilattice_persister_t( - metadata_file_t *file, - const metadata_file_t::key_t &key, - std::shared_ptr > view); - - /* `stop_and_flush()` finishes flushing the current value to disk but stops - responding to future changes. It's usually called right before the - destructor. */ - void stop_and_flush(signal_t *interruptor) THROWS_NOTHING { - subs.reset(); - persist_pumper.flush(interruptor); - } - -private: - void persist(signal_t *interruptor); - - metadata_file_t * const file; - metadata_file_t::key_t const key; - std::shared_ptr > const view; - - pump_coro_t persist_pumper; - - typename semilattice_read_view_t::subscription_t subs; - - DISABLE_COPYING(semilattice_persister_t); -}; - -#endif /* CLUSTERING_ADMINISTRATION_PERSIST_SEMILATTICE_HPP_ */ - diff --git a/src/clustering/administration/persist/table_interface.cc b/src/clustering/administration/persist/table_interface.cc deleted file mode 100644 index b2cf6fcdd30..00000000000 --- a/src/clustering/administration/persist/table_interface.cc +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/persist/table_interface.hpp" - -#include -#include - -#include "clustering/administration/persist/branch_history_manager.hpp" -#include "clustering/administration/persist/file_keys.hpp" -#include "clustering/administration/persist/raft_storage_interface.hpp" -#include "clustering/administration/perfmon_collection_repo.hpp" -#include "logger.hpp" -#include "rdb_protocol/store.hpp" -#include "serializer/log/log_serializer.hpp" -#include "serializer/merger.hpp" -#include "serializer/translator.hpp" - -class real_multistore_ptr_t : - public multistore_ptr_t { -public: - real_multistore_ptr_t( - const namespace_id_t &table_id, - const serializer_filepath_t &path, - scoped_ptr_t &&bhm, - const base_path_t &base_path, - io_backender_t *io_backender, - cache_balancer_t *cache_balancer, - rdb_context_t *rdb_context, - perfmon_collection_t *perfmon_collection_serializers, - scoped_ptr_t &&serializer_thread, - std::vector > &&store_threads, - std::map< - namespace_id_t, std::pair - > *real_multistores) : - branch_history_manager(std::move(bhm)), - serializer_thread_allocation(std::move(serializer_thread)), - store_thread_allocations(std::move(store_threads)), - map_insertion_sentry( - real_multistores, table_id, std::make_pair(this, drainer.lock())) - { - // TODO: If the server gets killed when starting up, we can - // get a database in an invalid startup state. - - // TODO: This is quite suspicious in that we check if the file - // exists and then assume it exists or does not exist when - // loading or creating it. - - // TODO: We should use N slices on M serializers, not N slices - // on 1 serializer. - - int res = access(path.permanent_path().c_str(), R_OK | W_OK); - bool create = (res != 0); - - on_thread_t thread_switcher(serializer_thread_allocation->get_thread()); - filepath_file_opener_t file_opener(path, io_backender); - - if (create) { - log_serializer_t::create( - &file_opener, - log_serializer_t::static_config_t()); - } - - // TODO: Could we handle failure when loading the serializer? Right - // now, we don't. - - scoped_ptr_t inner_serializer(new log_serializer_t( - log_serializer_t::dynamic_config_t(), - &file_opener, - perfmon_collection_serializers)); - serializer.init(new merger_serializer_t( - std::move(inner_serializer), - MERGER_SERIALIZER_MAX_ACTIVE_WRITES)); - - std::vector ptrs; - ptrs.push_back(serializer.get()); - if (create) { - serializer_multiplexer_t::create(ptrs, CPU_SHARDING_FACTOR); - } - multiplexer.init(new serializer_multiplexer_t(ptrs)); - - pmap(CPU_SHARDING_FACTOR, [&](int ix) { - // TODO: Exceptions? If exceptions are being thrown in here, nothing is - // handling them. - - on_thread_t thread_switcher_2(store_thread_allocations[ix]->get_thread()); - - stores[ix].init(new store_t( - cpu_sharding_subspace(ix), - multiplexer->proxies[ix], - cache_balancer, - strprintf("shard_%d", ix), - create, - perfmon_collection_serializers, - rdb_context, - io_backender, - base_path, - table_id, - update_sindexes_t::UPDATE)); - - /* Initialize the metainfo if necessary */ - if (create) { - order_source_t order_source; - cond_t non_interruptor; - write_token_t write_token; - stores[ix]->new_write_token(&write_token); - stores[ix]->set_metainfo( - region_map_t( - region_t::universe(), - binary_blob_t(version_t::zero())), - order_source.check_in("real_multistore_ptr_t"), - &write_token, - write_durability_t::HARD, - &non_interruptor); - } - }); - - if (create) { - file_opener.move_serializer_file_to_permanent_location(); - } - } - - ~real_multistore_ptr_t() { - serializer_thread_allocation.reset(); - store_thread_allocations.clear(); - map_insertion_sentry.reset(); - drainer.drain(); - pmap(CPU_SHARDING_FACTOR, [this](int ix) { - if (stores[ix].has()) { - on_thread_t thread_switcher(stores[ix]->home_thread()); - stores[ix].reset(); - } - }); - if (serializer.has()) { - on_thread_t thread_switcher(serializer->home_thread()); - if (multiplexer.has()) { - multiplexer.reset(); - } - serializer.reset(); - } - } - - branch_history_manager_t *get_branch_history_manager() { - return branch_history_manager.get(); - } - - serializer_t *get_serializer() { - return serializer.get_or_null(); - } - - store_view_t *get_cpu_sharded_store(size_t i) { - return stores[i].get(); - } - - store_t *get_underlying_store(size_t i) { - return stores[i].get(); - } - - bool is_gc_active() { - rassert(!drainer.is_draining()); - if (serializer.has()) { - return serializer->is_gc_active(); - } else { - return false; - } - } - -private: - scoped_ptr_t branch_history_manager; - scoped_ptr_t serializer; - scoped_ptr_t multiplexer; - scoped_ptr_t stores[CPU_SHARDING_FACTOR]; - - scoped_ptr_t serializer_thread_allocation; - std::vector > store_thread_allocations; - - auto_drainer_t drainer; - map_insertion_sentry_t< - namespace_id_t, std::pair - > map_insertion_sentry; - - DISABLE_COPYING(real_multistore_ptr_t); -}; - -void real_table_persistence_interface_t::read_all_metadata( - const std::function *raft_storage, - metadata_file_t::read_txn_t *metadata_read_txn)> &active_cb, - const std::function &inactive_cb, - signal_t *interruptor) { - metadata_file_t::read_txn_t read_txn(metadata_file, interruptor); - - std::map active_tables; - read_txn.read_many( - mdprefix_table_active(), - [&](const std::string &uuid_str, const table_active_persistent_state_t &state) { - active_tables[str_to_uuid(uuid_str)] = state; - }, - interruptor); - storage_interfaces.clear(); - for (const auto &pair : active_tables) { - storage_interfaces[pair.first].init(new table_raft_storage_interface_t( - metadata_file, &read_txn, pair.first, interruptor)); - active_cb( - pair.first, pair.second, storage_interfaces[pair.first].get(), &read_txn); - } - - read_txn.read_many( - mdprefix_table_inactive(), - [&](const std::string &uuid_str, const table_inactive_persistent_state_t &s) { - inactive_cb(str_to_uuid(uuid_str), s, &read_txn); - }, - interruptor); -} - -void real_table_persistence_interface_t::write_metadata_active( - const namespace_id_t &table_id, - const table_active_persistent_state_t &state, - const raft_persistent_state_t &raft_state, - raft_storage_interface_t **raft_storage_out) { - cond_t non_interruptor; - storage_interfaces.erase(table_id); - metadata_file_t::write_txn_t write_txn(metadata_file, &non_interruptor); - write_txn.erase( - mdprefix_table_inactive().suffix(uuid_to_str(table_id)), - &non_interruptor); - table_raft_storage_interface_t::erase(&write_txn, table_id); - write_txn.write( - mdprefix_table_active().suffix(uuid_to_str(table_id)), - state, - &non_interruptor); - storage_interfaces[table_id].init(new table_raft_storage_interface_t( - metadata_file, &write_txn, table_id, raft_state)); - *raft_storage_out = storage_interfaces[table_id].get(); - write_txn.commit(); -} - -void real_table_persistence_interface_t::write_metadata_inactive( - const namespace_id_t &table_id, - const table_inactive_persistent_state_t &state) { - cond_t non_interruptor; - storage_interfaces.erase(table_id); - metadata_file_t::write_txn_t write_txn(metadata_file, &non_interruptor); - write_txn.erase( - mdprefix_table_active().suffix(uuid_to_str(table_id)), - &non_interruptor); - write_txn.write( - mdprefix_table_inactive().suffix(uuid_to_str(table_id)), - state, - &non_interruptor); - table_raft_storage_interface_t::erase(&write_txn, table_id); - real_branch_history_manager_t::erase(&write_txn, table_id); - write_txn.commit(); -} - -void real_table_persistence_interface_t::delete_metadata( - const namespace_id_t &table_id) { - cond_t non_interruptor; - storage_interfaces.erase(table_id); - metadata_file_t::write_txn_t write_txn(metadata_file, &non_interruptor); - write_txn.erase( - mdprefix_table_active().suffix(uuid_to_str(table_id)), - &non_interruptor); - write_txn.erase( - mdprefix_table_inactive().suffix(uuid_to_str(table_id)), - &non_interruptor); - table_raft_storage_interface_t::erase(&write_txn, table_id); - real_branch_history_manager_t::erase(&write_txn, table_id); - write_txn.commit(); -} - -void real_table_persistence_interface_t::load_multistore( - const namespace_id_t &table_id, - metadata_file_t::read_txn_t *metadata_read_txn, - scoped_ptr_t *multistore_ptr_out, - signal_t *interruptor, - perfmon_collection_t *perfmon_collection_serializers) { - scoped_ptr_t bhm( - new real_branch_history_manager_t( - table_id, metadata_file, metadata_read_txn, interruptor)); - - scoped_ptr_t serializer_thread( - new thread_allocation_t(&thread_allocator)); - std::vector > store_threads; - for (size_t i = 0; i < CPU_SHARDING_FACTOR; ++i) { - store_threads.emplace_back(new thread_allocation_t(&thread_allocator)); - } - - multistore_ptr_out->init(new real_multistore_ptr_t( - table_id, - file_name_for(table_id), - std::move(bhm), - base_path, - io_backender, - cache_balancer, - rdb_context, - perfmon_collection_serializers, - std::move(serializer_thread), - std::move(store_threads), - &real_multistores)); -} - -void real_table_persistence_interface_t::create_multistore( - const namespace_id_t &table_id, - scoped_ptr_t *multistore_ptr_out, - signal_t *interruptor, - perfmon_collection_t *perfmon_collection_serializers) { - metadata_file_t::read_txn_t read_txn(metadata_file, interruptor); - load_multistore( - table_id, &read_txn, multistore_ptr_out, interruptor, - perfmon_collection_serializers); -} - -void real_table_persistence_interface_t::destroy_multistore( - const namespace_id_t &table_id, - scoped_ptr_t *multistore_ptr_in) { - guarantee(multistore_ptr_in->has()); - multistore_ptr_in->reset(); - - std::string filepath = file_name_for(table_id).permanent_path(); - logNTC("Removing file %s\n", filepath.c_str()); - const int res = ::unlink(filepath.c_str()); - guarantee_err(res == 0 || get_errno() == ENOENT, - "unlink failed for file %s", filepath.c_str()); -} - -serializer_filepath_t real_table_persistence_interface_t::file_name_for( - const namespace_id_t &table_id) { - return serializer_filepath_t(base_path, uuid_to_str(table_id)); -} - -bool real_table_persistence_interface_t::is_gc_active() const { - for (int thread = 0; thread < get_num_db_threads(); ++thread) { - std::map serializers_copy; - - // Note the copy in the loop below is intentional, one of the members is a - // `auto_drainer_t::lock_t` that we want to hold. - for (auto real_multistore : real_multistores) { - serializer_t *serializer = - real_multistore.second.first->get_serializer(); - if (serializer == nullptr || - serializer->home_thread() != threadnum_t(thread)) { - continue; - } - serializers_copy.insert( - std::make_pair(serializer, real_multistore.second.second)); - } - - { - on_thread_t on_thread((threadnum_t(thread))); - for (auto const &serializer : serializers_copy) { - if (serializer.first->is_gc_active()) { - return true; - } - } - } - } - - return false; -} diff --git a/src/clustering/administration/persist/table_interface.hpp b/src/clustering/administration/persist/table_interface.hpp deleted file mode 100644 index f794567a1c7..00000000000 --- a/src/clustering/administration/persist/table_interface.hpp +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_PERSIST_TABLE_INTERFACE_HPP_ -#define CLUSTERING_ADMINISTRATION_PERSIST_TABLE_INTERFACE_HPP_ - -#include "clustering/administration/perfmon_collection_repo.hpp" -#include "clustering/administration/persist/raft_storage_interface.hpp" -#include "clustering/table_manager/table_metadata.hpp" - -class cache_balancer_t; -class metadata_file_t; -class real_multistore_ptr_t; -class table_raft_storage_interface_t; - -class real_table_persistence_interface_t : - public table_persistence_interface_t { -public: - real_table_persistence_interface_t( - io_backender_t *_io_backender, - cache_balancer_t *_cache_balancer, - const base_path_t &_base_path, - rdb_context_t *_rdb_context, - metadata_file_t *_metadata_file) : - io_backender(_io_backender), - cache_balancer(_cache_balancer), - base_path(_base_path), - rdb_context(_rdb_context), - metadata_file(_metadata_file), - /* We assign threads from the lowest thread number upwards. This is to reduce - the potential for conflicting with cluster connection threads, which are - assigned from the highest thread number downwards. */ - thread_allocator([](threadnum_t a, threadnum_t b) { - return a.threadnum < b.threadnum; - }) - { } - - void read_all_metadata( - const std::function *raft_storage, - metadata_file_t::read_txn_t *metadata_read_txn)> &active_cb, - const std::function &inactive_cb, - signal_t *interruptor); - void write_metadata_active( - const namespace_id_t &table_id, - const table_active_persistent_state_t &state, - const raft_persistent_state_t &raft_state, - raft_storage_interface_t **raft_storage_out); - void write_metadata_inactive( - const namespace_id_t &table_id, - const table_inactive_persistent_state_t &state); - void delete_metadata( - const namespace_id_t &table_id); - - void load_multistore( - const namespace_id_t &table_id, - metadata_file_t::read_txn_t *metadata_read_txn, - scoped_ptr_t *multistore_ptr_out, - signal_t *interruptor, - perfmon_collection_t *perfmon_collection_serializers); - void create_multistore( - const namespace_id_t &table_id, - scoped_ptr_t *multistore_ptr_out, - signal_t *interruptor, - perfmon_collection_t *perfmon_collection_serializers); - void destroy_multistore( - const namespace_id_t &table_id, - scoped_ptr_t *multistore_ptr_in); - - bool is_gc_active() const; - -private: - serializer_filepath_t file_name_for(const namespace_id_t &table_id); - threadnum_t pick_thread(); - - io_backender_t * const io_backender; - cache_balancer_t * const cache_balancer; - base_path_t const base_path; - rdb_context_t * const rdb_context; - metadata_file_t * const metadata_file; - - std::map< - namespace_id_t, std::pair - > real_multistores; - - std::map > - storage_interfaces; - - /* Used to distribute objects evenly over threads */ - thread_allocator_t thread_allocator; -}; - -#endif /* CLUSTERING_ADMINISTRATION_PERSIST_TABLE_INTERFACE_HPP_ */ diff --git a/src/clustering/administration/real_reql_cluster_interface.cc b/src/clustering/administration/real_reql_cluster_interface.cc deleted file mode 100644 index 71406f8cbfc..00000000000 --- a/src/clustering/administration/real_reql_cluster_interface.cc +++ /dev/null @@ -1,1490 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/real_reql_cluster_interface.hpp" - -#include "clustering/administration/artificial_reql_cluster_interface.hpp" -#include "clustering/administration/auth/grant.hpp" -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/main/watchable_fields.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "clustering/administration/tables/calculate_status.hpp" -#include "clustering/administration/tables/generate_config.hpp" -#include "clustering/administration/tables/split_points.hpp" -#include "clustering/administration/tables/table_config.hpp" -#include "clustering/table_manager/table_meta_client.hpp" -#include "concurrency/cross_thread_signal.hpp" -#include "containers/archive/string_stream.hpp" -#include "rdb_protocol/artificial_table/artificial_table.hpp" -#include "rdb_protocol/env.hpp" -#include "rdb_protocol/table_common.hpp" -#include "rdb_protocol/terms/write_hook.hpp" -#include "rdb_protocol/val.hpp" -#include "rpc/semilattice/watchable.hpp" -#include "rpc/semilattice/view/field.hpp" - -#define NAMESPACE_INTERFACE_EXPIRATION_MS (60 * 1000) - -real_reql_cluster_interface_t::real_reql_cluster_interface_t( - mailbox_manager_t *mailbox_manager, - std::shared_ptr > auth_semilattice_view, - std::shared_ptr > cluster_semilattice_view, - rdb_context_t *rdb_context, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - multi_table_manager_t *multi_table_manager, - watchable_map_t< - std::pair >, - table_query_bcard_t> *table_query_directory, - lifetime_t name_resolver) : - m_mailbox_manager(mailbox_manager), - m_auth_semilattice_view(auth_semilattice_view), - m_cluster_semilattice_view(cluster_semilattice_view), - m_table_meta_client(table_meta_client), - m_cross_thread_database_watchables(get_num_threads()), - m_rdb_context(rdb_context), - m_namespace_repo( - m_mailbox_manager, - table_query_directory, - multi_table_manager, - m_rdb_context, - m_table_meta_client), - m_changefeed_client( - m_mailbox_manager, - [this](const namespace_id_t &id, signal_t *interruptor) { - return this->m_namespace_repo.get_namespace_interface(id, interruptor); - }, - name_resolver), - m_server_config_client(server_config_client) -{ - guarantee(m_auth_semilattice_view->home_thread() == home_thread()); - guarantee(m_cluster_semilattice_view->home_thread() == home_thread()); - guarantee(m_table_meta_client->home_thread() == home_thread()); - guarantee(m_server_config_client->home_thread() == home_thread()); - for (int thr = 0; thr < get_num_threads(); ++thr) { - m_cross_thread_database_watchables[thr].init( - new cross_thread_watchable_variable_t( - clone_ptr_t > - (new semilattice_watchable_t( - metadata_field(&cluster_semilattice_metadata_t::databases, m_cluster_semilattice_view))), threadnum_t(thr))); - } -} - -bool real_reql_cluster_interface_t::db_create( - auth::user_context_t const &user_context, - const name_string_t &name, - signal_t *interruptor_on_caller, - ql::datum_t *result_out, - admin_err_t *error_out) { - guarantee(name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - - user_context.require_config_permission(m_rdb_context); - - cluster_semilattice_metadata_t metadata; - ql::datum_t new_config; - { - on_thread_t thread_switcher(home_thread()); - metadata = m_cluster_semilattice_view->get(); - - /* Make sure there isn't an existing database with the same name. */ - for (const auto &pair : metadata.databases.databases) { - if (!pair.second.is_deleted() && - pair.second.get_ref().name.get_ref() == name) { - *error_out = admin_err_t{ - strprintf("Database `%s` already exists.", name.c_str()), - query_state_t::FAILED}; - return false; - } - } - - database_id_t db_id = generate_uuid(); - database_semilattice_metadata_t db; - db.name = versioned_t(name); - metadata.databases.databases.insert(std::make_pair(db_id, make_deletable(db))); - - m_cluster_semilattice_view->join(metadata); - metadata = m_cluster_semilattice_view->get(); - - new_config = convert_db_config_and_name_to_datum(name, db_id); - } - wait_for_cluster_metadata_to_propagate(metadata, interruptor_on_caller); - - ql::datum_object_builder_t result_builder; - result_builder.overwrite("dbs_created", ql::datum_t(1.0)); - result_builder.overwrite("config_changes", - make_replacement_pair(ql::datum_t::null(), new_config)); - *result_out = std::move(result_builder).to_datum(); - - return true; -} - -bool real_reql_cluster_interface_t::db_drop_uuid( - auth::user_context_t const &user_context, - database_id_t database_id, - const name_string_t &name, - signal_t *interruptor_on_home, - ql::datum_t *result_out, - admin_err_t *error_out) { - assert_thread(); - guarantee(name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - - std::map table_names; - m_table_meta_client->list_names(&table_names); - std::set table_ids; - for (auto const &table_name : table_names) { - if (table_name.second.database == database_id) { - table_ids.insert(table_name.first); - } - } - - user_context.require_config_permission(m_rdb_context, database_id, table_ids); - - // Here we actually delete the tables - size_t tables_dropped = 0; - for (const auto &table_id : table_ids) { - try { - m_table_meta_client->drop(table_id, interruptor_on_home); - ++tables_dropped; - } catch (const no_such_table_exc_t &) { - /* The table was dropped by something else between the time when we called - `list_names()` and when we went to actually delete it. This is OK. */ - } CATCH_OP_ERRORS(name, table_names.at(table_id).name, error_out, - "The database was not dropped, but some of the tables in it may or may not " - "have been dropped.", - "The database was not dropped, but some of the tables in it may or may not " - "have been dropped.") - } - - cluster_semilattice_metadata_t metadata = m_cluster_semilattice_view->get(); - auto iter = metadata.databases.databases.find(database_id); - if (iter != metadata.databases.databases.end() && !iter->second.is_deleted()) { - iter->second.mark_deleted(); - m_cluster_semilattice_view->join(metadata); - metadata = m_cluster_semilattice_view->get(); - } else { - *error_out = admin_err_t{ - "The database was already deleted.", query_state_t::FAILED}; - return false; - } - wait_for_cluster_metadata_to_propagate(metadata, interruptor_on_home); - - if (result_out != nullptr) { - ql::datum_t old_config = - convert_db_config_and_name_to_datum(name, database_id); - - ql::datum_object_builder_t result_builder; - result_builder.overwrite("dbs_dropped", ql::datum_t(1.0)); - result_builder.overwrite( - "tables_dropped", ql::datum_t(static_cast(tables_dropped))); - result_builder.overwrite( - "config_changes", - make_replacement_pair(std::move(old_config), ql::datum_t::null())); - *result_out = std::move(result_builder).to_datum(); - } - - return true; -} - -bool real_reql_cluster_interface_t::db_drop( - auth::user_context_t const &user_context, - const name_string_t &name, - signal_t *interruptor_on_caller, - ql::datum_t *result_out, - admin_err_t *error_out) { - guarantee(name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - - { - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - - cluster_semilattice_metadata_t metadata = m_cluster_semilattice_view->get(); - database_id_t database_id; - if (!search_db_metadata_by_name( - metadata.databases, - name, - &database_id, - error_out)) { - return false; - } - - if (!db_drop_uuid( - user_context, - database_id, - name, - &interruptor_on_home, - result_out, - error_out)) { - return false; - } - } - - return true; -} - -bool real_reql_cluster_interface_t::db_list( - UNUSED signal_t *interruptor_on_caller, - std::set *names_out, - UNUSED admin_err_t *error_out) { - databases_semilattice_metadata_t db_metadata; - get_databases_metadata(&db_metadata); - for (const auto &pair : db_metadata.databases) { - if (!pair.second.is_deleted()) { - names_out->insert(pair.second.get_ref().name.get_ref()); - } - } - return true; -} - -bool real_reql_cluster_interface_t::db_find( - const name_string_t &name, - UNUSED signal_t *interruptor_on_caller, - counted_t *db_out, - admin_err_t *error_out) { - guarantee(name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - /* Find the specified database */ - databases_semilattice_metadata_t db_metadata; - get_databases_metadata(&db_metadata); - database_id_t db_id; - if (!search_db_metadata_by_name(db_metadata, name, &db_id, error_out)) { - return false; - } - *db_out = make_counted(db_id, name); - return true; -} - -bool real_reql_cluster_interface_t::db_config( - auth::user_context_t const &user_context, - const counted_t &db, - ql::backtrace_id_t backtrace_id, - ql::env_t *env, - scoped_ptr_t *selection_out, - admin_err_t *error_out) { - try { - user_context.require_config_permission(m_rdb_context, db->id); - - make_single_selection( - user_context, - name_string_t::guarantee_valid("db_config"), - db->id, - backtrace_id, - env, - selection_out); - return true; - } catch (const no_such_table_exc_t &) { - *error_out = admin_err_t{ - strprintf("Database `%s` does not exist.", db->name.c_str()), - query_state_t::FAILED}; - return false; - } catch (const admin_op_exc_t &admin_op_exc) { - *error_out = admin_op_exc.to_admin_err(); - return false; - } -} - -bool real_reql_cluster_interface_t::table_create( - auth::user_context_t const &user_context, - const name_string_t &name, - counted_t db, - const table_generate_config_params_t &config_params, - const std::string &primary_key, - write_durability_t durability, - signal_t *interruptor_on_caller, - ql::datum_t *result_out, - admin_err_t *error_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - - user_context.require_config_permission(m_rdb_context, db->id); - - namespace_id_t table_id; - cluster_semilattice_metadata_t metadata; - ql::datum_t new_config; - try { - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - - /* Make sure there isn't an existing table with the same name */ - if (m_table_meta_client->exists(db->id, name)) { - *error_out = admin_err_t{ - strprintf("Table `%s.%s` already exists.", - db->name.c_str(), name.c_str()), - query_state_t::FAILED}; - return false; - } - - table_config_and_shards_t config; - - config.config.basic.name = name; - config.config.basic.database = db->id; - config.config.basic.primary_key = primary_key; - - /* We don't have any data to generate split points based on, so assume UUIDs */ - calculate_split_points_for_uuids( - config_params.num_shards, &config.shard_scheme); - - /* Pick which servers to host the data */ - table_generate_config( - m_server_config_client, nil_uuid(), m_table_meta_client, - config_params, config.shard_scheme, &interruptor_on_home, - &config.config.shards, &config.server_names); - - config.config.write_ack_config = write_ack_config_t::MAJORITY; - config.config.durability = durability; - - table_id = generate_uuid(); - m_table_meta_client->create(table_id, config, &interruptor_on_home); - - new_config = convert_table_config_to_datum(table_id, - convert_name_to_datum(db->name), config.config, - admin_identifier_format_t::name, config.server_names); - - } catch (const admin_op_exc_t &admin_op_exc) { - *error_out = admin_op_exc.to_admin_err(); - return false; - } CATCH_NAME_ERRORS(db->name, name, error_out) - CATCH_OP_ERRORS(db->name, name, error_out, - "The table was not created.", - "The table may or may not have been created.") - - /* Wait for the table to set itself up. Various things could interfere with this - process, causing it to wait indefinitely; for example, the table might be deleted, or - a server might go down. So we set a 10-second timeout. */ - signal_timer_t timer; - timer.start(10000); - wait_any_t combined_interruptor(&timer, interruptor_on_caller); - try { - wait_for_table_readiness( - table_id, - table_readiness_t::finished, - &m_namespace_repo, - m_table_meta_client, - &combined_interruptor); - } catch (const no_such_table_exc_t &) { - /* The table was deleted after being created. Just return normally. */ - } catch (const interrupted_exc_t &) { - if (interruptor_on_caller->is_pulsed()) { - throw interrupted_exc_t(); - } - /* Ten seconds elapsed. Just return normally. */ - } - - ql::datum_object_builder_t result_builder; - result_builder.overwrite("tables_created", ql::datum_t(1.0)); - result_builder.overwrite("config_changes", - make_replacement_pair(ql::datum_t::null(), new_config)); - *result_out = std::move(result_builder).to_datum(); - - return true; -} - -bool real_reql_cluster_interface_t::table_drop( - auth::user_context_t const &user_context, - const name_string_t &name, - counted_t db, - signal_t *interruptor_on_caller, - ql::datum_t *result_out, - admin_err_t *error_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - - cluster_semilattice_metadata_t metadata; - try { - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - metadata = m_cluster_semilattice_view->get(); - - namespace_id_t table_id; - m_table_meta_client->find(db->id, name, &table_id); - - user_context.require_config_permission(m_rdb_context, db->id, table_id); - - /* Fetch the old config via the `table_config_backend` rather than via - `table_meta_client_t` because this will return an error document instead of - crashing if the table is not reachable. */ - ql::datum_t old_config; - artificial_table_backend_t *config_backend = - artificial_reql_cluster_interface->get_table_backend( - name_string_t::guarantee_valid("table_config"), - admin_identifier_format_t::name); - guarantee(config_backend != nullptr); - if (!config_backend->read_row( - user_context, - convert_uuid_to_datum(table_id), - &interruptor_on_home, - &old_config, - error_out)) { - return false; - } else if (!old_config.has()) { - throw no_such_table_exc_t(); - } - - m_table_meta_client->drop(table_id, &interruptor_on_home); - - ql::datum_object_builder_t result_builder; - result_builder.overwrite("tables_dropped", ql::datum_t(1.0)); - result_builder.overwrite("config_changes", - make_replacement_pair(old_config, ql::datum_t::null())); - *result_out = std::move(result_builder).to_datum(); - - return true; - - } CATCH_NAME_ERRORS(db->name, name, error_out) -} - -bool real_reql_cluster_interface_t::table_list( - counted_t db, - UNUSED signal_t *interruptor_on_caller, - std::set *names_out, - UNUSED admin_err_t *error_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - std::map tables; - m_table_meta_client->list_names(&tables); - for (const auto &pair : tables) { - if (pair.second.database == db->id) { - names_out->insert(pair.second.name); - } - } - return true; -} - -bool real_reql_cluster_interface_t::table_find( - const name_string_t &name, - counted_t db, - UNUSED optional identifier_format, - signal_t *interruptor_on_caller, - counted_t *table_out, - admin_err_t *error_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - namespace_id_t table_id; - std::string primary_key; - try { - m_table_meta_client->find(db->id, name, &table_id, &primary_key); - - /* Note that we completely ignore `identifier_format`. `identifier_format` is - meaningless for real tables, so it might seem like we should produce an error. - The reason we don't is that the user might write a query that access both a - system table and a real table, and they might specify `identifier_format` as a - global optarg. So then they would get a spurious error for the real table. This - behavior is also consistent with that of system tables that aren't affected by - `identifier_format`. */ - table_out->reset(new real_table_t( - table_id, - m_namespace_repo.get_namespace_interface(table_id, interruptor_on_caller), - primary_key, - &m_changefeed_client, - m_table_meta_client)); - - return true; - } CATCH_NAME_ERRORS(db->name, name, error_out) -} - -bool real_reql_cluster_interface_t::table_estimate_doc_counts( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - ql::env_t *env, - std::vector *doc_counts_out, - admin_err_t *error_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - - cross_thread_signal_t interruptor_on_home(env->interruptor, home_thread()); - try { - on_thread_t thread_switcher(home_thread()); - - namespace_id_t table_id; - m_table_meta_client->find(db->id, name, &table_id); - - user_context.require_read_permission(m_rdb_context, db->id, table_id); - - table_config_and_shards_t config; - m_table_meta_client->get_config(table_id, &interruptor_on_home, &config); - - /* Perform a distribution query against the database */ - std::map counts; - fetch_distribution(table_id, this, &interruptor_on_home, &counts); - - /* Match the results of the distribution query against the table's shard - boundaries */ - *doc_counts_out = std::vector(config.shard_scheme.num_shards(), 0); - for (auto it = counts.begin(); it != counts.end(); ++it) { - /* Calculate the range of shards that this key-range overlaps with */ - size_t left_shard = config.shard_scheme.find_shard_for_key(it->first); - auto jt = it; - ++jt; - size_t right_shard; - if (jt == counts.end()) { - right_shard = config.shard_scheme.num_shards() - 1; - } else { - store_key_t right_key = jt->first; - bool ok = right_key.decrement(); - guarantee(ok, "jt->first cannot be the leftmost key"); - right_shard = config.shard_scheme.find_shard_for_key(right_key); - } - /* We assume that every shard that this key-range overlaps with has an equal - share of the keys in the key-range. This is shitty but oh well. */ - for (size_t shard = left_shard; shard <= right_shard; ++shard) { - doc_counts_out->at(shard) += it->second / (right_shard - left_shard + 1); - } - } - return true; - } CATCH_NAME_ERRORS(db->name, name, error_out) - CATCH_OP_ERRORS(db->name, name, error_out, "", "") -} - -bool real_reql_cluster_interface_t::table_config( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - ql::backtrace_id_t bt, - ql::env_t *env, - scoped_ptr_t *selection_out, - admin_err_t *error_out) { - try { - namespace_id_t table_id; - m_table_meta_client->find(db->id, name, &table_id); - - user_context.require_config_permission(m_rdb_context, db->id, table_id); - - make_single_selection( - user_context, - name_string_t::guarantee_valid("table_config"), - table_id, - bt, - env, - selection_out); - return true; - } catch (const admin_op_exc_t &admin_op_exc) { - *error_out = admin_op_exc.to_admin_err(); - return false; - } CATCH_NAME_ERRORS(db->name, name, error_out) -} - -bool real_reql_cluster_interface_t::table_status( - counted_t db, - const name_string_t &name, - ql::backtrace_id_t bt, - ql::env_t *env, - scoped_ptr_t *selection_out, - admin_err_t *error_out) { - try { - namespace_id_t table_id; - m_table_meta_client->find(db->id, name, &table_id); - make_single_selection( - env->get_user_context(), - name_string_t::guarantee_valid("table_status"), - table_id, - bt, - env, - selection_out); - return true; - } catch (const admin_op_exc_t &admin_op_exc) { - *error_out = admin_op_exc.to_admin_err(); - return false; - } CATCH_NAME_ERRORS(db->name, name, error_out) -} - -bool real_reql_cluster_interface_t::table_wait( - counted_t db, - const name_string_t &name, - table_readiness_t readiness, - signal_t *interruptor_on_caller, - ql::datum_t *result_out, - admin_err_t *error_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - try { - namespace_id_t table_id; - m_table_meta_client->find(db->id, name, &table_id); - wait_for_table_readiness(table_id, readiness, &m_namespace_repo, - m_table_meta_client, interruptor_on_caller); - ql::datum_object_builder_t builder; - builder.overwrite("ready", ql::datum_t(1.0)); - *result_out = std::move(builder).to_datum(); - return true; - } catch (const admin_op_exc_t &admin_op_exc) { - *error_out = admin_op_exc.to_admin_err(); - return false; - } CATCH_NAME_ERRORS(db->name, name, error_out) -} - -bool real_reql_cluster_interface_t::db_wait( - counted_t db, - table_readiness_t readiness, - signal_t *interruptor_on_caller, - ql::datum_t *result_out, - admin_err_t *error_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - - std::set table_ids; - std::map tables; - m_table_meta_client->list_names(&tables); - for (const auto &table : tables) { - if (table.second.database == db->id) { - table_ids.insert(table.first); - } - } - - try { - size_t count = wait_for_many_tables_readiness(table_ids, readiness, - &m_namespace_repo, m_table_meta_client, interruptor_on_caller); - ql::datum_object_builder_t builder; - builder.overwrite("ready", ql::datum_t(static_cast(count))); - *result_out = std::move(builder).to_datum(); - return true; - } catch (const admin_op_exc_t &admin_op_exc) { - *error_out = admin_op_exc.to_admin_err(); - return false; - } -} - -void real_reql_cluster_interface_t::reconfigure_internal( - auth::user_context_t const &user_context, - const counted_t &db, - const namespace_id_t &table_id, - const table_generate_config_params_t ¶ms, - bool dry_run, - signal_t *interruptor_on_home, - ql::datum_t *result_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, admin_op_exc_t, - failed_table_op_exc_t, maybe_failed_table_op_exc_t) { - assert_thread(); - - /* Fetch the table's current configuration */ - table_config_and_shards_t old_config; - m_table_meta_client->get_config(table_id, interruptor_on_home, &old_config); - - // Store the old value of the config and status - ql::datum_t old_config_datum = convert_table_config_to_datum( - table_id, convert_name_to_datum(db->name), old_config.config, - admin_identifier_format_t::name, old_config.server_names); - - artificial_table_backend_t *status_backend = - artificial_reql_cluster_interface->get_table_backend( - name_string_t::guarantee_valid("table_status"), - admin_identifier_format_t::name); - guarantee(status_backend != nullptr); - ql::datum_t old_status; - admin_err_t error; - if (!status_backend->read_row( - user_context, - convert_uuid_to_datum(table_id), - interruptor_on_home, - &old_status, - &error)) { - throw admin_op_exc_t(error); - } else if (!old_status.has()) { - throw no_such_table_exc_t(); - } - - table_config_and_shards_t new_config; - new_config.config.basic = old_config.config.basic; - new_config.config.sindexes = old_config.config.sindexes; - new_config.config.write_ack_config = old_config.config.write_ack_config; - new_config.config.durability = old_config.config.durability; - - calculate_split_points_intelligently( - table_id, - this, - params.num_shards, - old_config.shard_scheme, - interruptor_on_home, - &new_config.shard_scheme); - - /* `table_generate_config()` just generates the config; it doesn't apply it */ - table_generate_config( - m_server_config_client, table_id, m_table_meta_client, - params, new_config.shard_scheme, interruptor_on_home, &new_config.config.shards, - &new_config.server_names); - - if (!dry_run) { - table_config_and_shards_change_t table_config_and_shards_change( - table_config_and_shards_change_t::set_table_config_and_shards_t{ new_config }); - m_table_meta_client->set_config( - table_id, table_config_and_shards_change, interruptor_on_home); - } - - // Compute the new value of the config and status - ql::datum_t new_config_datum = convert_table_config_to_datum( - table_id, convert_name_to_datum(db->name), new_config.config, - admin_identifier_format_t::name, new_config.server_names); - ql::datum_t new_status; - if (!status_backend->read_row( - user_context, - convert_uuid_to_datum(table_id), - interruptor_on_home, - &new_status, - &error)) { - throw admin_op_exc_t(error); - } else if (!new_status.has()) { - throw no_such_table_exc_t(); - } - - ql::datum_object_builder_t result_builder; - if (!dry_run) { - result_builder.overwrite("reconfigured", ql::datum_t(1.0)); - result_builder.overwrite("config_changes", - make_replacement_pair(old_config_datum, new_config_datum)); - result_builder.overwrite("status_changes", - make_replacement_pair(old_status, new_status)); - } else { - result_builder.overwrite("reconfigured", ql::datum_t(0.0)); - result_builder.overwrite("config_changes", - make_replacement_pair(old_config_datum, new_config_datum)); - } - *result_out = std::move(result_builder).to_datum(); -} - -bool real_reql_cluster_interface_t::table_reconfigure( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - const table_generate_config_params_t ¶ms, - bool dry_run, - signal_t *interruptor_on_caller, - ql::datum_t *result_out, - admin_err_t *error_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - try { - on_thread_t thread_switcher(home_thread()); - namespace_id_t table_id; - m_table_meta_client->find(db->id, name, &table_id); - - user_context.require_config_permission(m_rdb_context, db->id, table_id); - - reconfigure_internal( - user_context, - db, - table_id, - params, - dry_run, - &interruptor_on_home, - result_out); - return true; - } catch (const admin_op_exc_t &admin_op_exc) { - *error_out = admin_op_exc.to_admin_err(); - return false; - } CATCH_NAME_ERRORS(db->name, name, error_out) - CATCH_OP_ERRORS(db->name, name, error_out, - "The table was not reconfigured.", - "The table may or may not have been reconfigured.") -} - -bool real_reql_cluster_interface_t::db_reconfigure( - auth::user_context_t const &user_context, - counted_t db, - const table_generate_config_params_t ¶ms, - bool dry_run, - signal_t *interruptor_on_caller, - ql::datum_t *result_out, - admin_err_t *error_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - - std::map table_names; - m_table_meta_client->list_names(&table_names); - std::set table_ids; - for (auto const &table_name : table_names) { - if (table_name.second.database == db->id) { - table_ids.insert(table_name.first); - } - } - - user_context.require_config_permission(m_rdb_context, db->id, table_ids); - - ql::datum_t combined_stats = ql::datum_t::empty_object(); - for (const auto &table_id : table_ids) { - ql::datum_t stats; - try { - reconfigure_internal( - user_context, - db, - table_id, - params, - dry_run, - &interruptor_on_home, - &stats); - } catch (const no_such_table_exc_t &) { - /* The table got deleted during the reconfiguration. It would be weird if - `r.db('foo').reconfigure()` produced an error complaining that some table - `foo.bar` did not exist. So we just skip the table, as though it were - deleted before the operation even began. */ - continue; - } catch (const admin_op_exc_t &admin_op_exc) { - *error_out = admin_op_exc.to_admin_err(); - return false; - } CATCH_OP_ERRORS(db->name, table_names.at(table_id).name, error_out, - "The tables may or may not have been reconfigured.", - "The tables may or may not have been reconfigured.") - std::set dummy_conditions; - combined_stats = combined_stats.merge(stats, &ql::stats_merge, - ql::configured_limits_t::unlimited, &dummy_conditions); - guarantee(dummy_conditions.empty()); - } - *result_out = combined_stats; - return true; -} - -void real_reql_cluster_interface_t::emergency_repair_internal( - auth::user_context_t const &user_context, - const counted_t &db, - const namespace_id_t &table_id, - emergency_repair_mode_t mode, - bool dry_run, - signal_t *interruptor_on_home, - ql::datum_t *result_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, - failed_table_op_exc_t, maybe_failed_table_op_exc_t, admin_op_exc_t) { - assert_thread(); - - /* Fetch the table's current configuration */ - table_config_and_shards_t old_config; - m_table_meta_client->get_config(table_id, interruptor_on_home, &old_config); - - // Store the old value of the config and status - ql::datum_t old_config_datum = convert_table_config_to_datum( - table_id, convert_name_to_datum(db->name), old_config.config, - admin_identifier_format_t::name, old_config.server_names); - - artificial_table_backend_t *status_backend = - artificial_reql_cluster_interface->get_table_backend( - name_string_t::guarantee_valid("table_status"), - admin_identifier_format_t::name); - guarantee(status_backend != nullptr); - ql::datum_t old_status; - admin_err_t error; - if (!status_backend->read_row( - user_context, - convert_uuid_to_datum(table_id), - interruptor_on_home, - &old_status, - &error)) { - throw admin_op_exc_t(error); - } else if (!old_status.has()) { - throw no_such_table_exc_t(); - } - - table_config_and_shards_t new_config; - bool rollback_found; - bool erase_found; - m_table_meta_client->emergency_repair( - table_id, - mode, - dry_run, - interruptor_on_home, - &new_config, - &rollback_found, - &erase_found); - - if (!rollback_found && mode != emergency_repair_mode_t::DEBUG_RECOMMIT) { - if (!erase_found) { - throw admin_op_exc_t("This table doesn't need to be repaired.", - query_state_t::FAILED); - } else if (erase_found && - mode != emergency_repair_mode_t::UNSAFE_ROLLBACK_OR_ERASE) { - throw admin_op_exc_t( - "One or more shards of this table have no available " - "replicas. Since there are no available copies of the data that was " - "stored in those shards, those shards cannot be repaired. If you run " - "the command again with `emergency_repair` set to " - "`unsafe_rollback_or_erase`, those shards will be reset to an empty, " - "but writeable state; but if the missing replicas later reconnect, the " - "original data that was stored on them will be lost permanently.", - query_state_t::FAILED); - } - } - - // Compute the new value of the config and status - ql::datum_t new_config_datum = convert_table_config_to_datum( - table_id, convert_name_to_datum(db->name), new_config.config, - admin_identifier_format_t::name, new_config.server_names); - ql::datum_t new_status; - if (!status_backend->read_row( - user_context, - convert_uuid_to_datum(table_id), - interruptor_on_home, - &new_status, - &error)) { - throw admin_op_exc_t(error); - } else if (!new_status.has()) { - throw no_such_table_exc_t(); - } - - ql::datum_object_builder_t result_builder; - if (!dry_run) { - result_builder.overwrite("repaired", ql::datum_t(1.0)); - result_builder.overwrite("config_changes", - make_replacement_pair(old_config_datum, new_config_datum)); - result_builder.overwrite("status_changes", - make_replacement_pair(old_status, new_status)); - } else { - result_builder.overwrite("repaired", ql::datum_t(0.0)); - result_builder.overwrite("config_changes", - make_replacement_pair(old_config_datum, new_config_datum)); - } - *result_out = std::move(result_builder).to_datum(); -} - -bool real_reql_cluster_interface_t::table_emergency_repair( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - emergency_repair_mode_t mode, - bool dry_run, - signal_t *interruptor_on_caller, - ql::datum_t *result_out, - admin_err_t *error_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - try { - on_thread_t thread_switcher(home_thread()); - namespace_id_t table_id; - m_table_meta_client->find(db->id, name, &table_id); - - user_context.require_config_permission(m_rdb_context, db->id, table_id); - - emergency_repair_internal( - user_context, - db, - table_id, - mode, - dry_run, - &interruptor_on_home, - result_out); - return true; - } catch (const admin_op_exc_t &admin_op_exc) { - *error_out = admin_op_exc.to_admin_err(); - return false; - } CATCH_NAME_ERRORS(db->name, name, error_out) - CATCH_OP_ERRORS(db->name, name, error_out, - /* Note the non-standard error message here, to clarify if the user tries to - repair a table that's completely inaccessible. */ - "The table was not repaired. At least one of a table's replicas must be " - "accessible in order to repair it.", - "The table may or may not have been repaired.") -} - -void real_reql_cluster_interface_t::rebalance_internal( - auth::user_context_t const &user_context, - const namespace_id_t &table_id, - signal_t *interruptor_on_home, - ql::datum_t *results_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, - failed_table_op_exc_t, maybe_failed_table_op_exc_t, admin_op_exc_t) { - assert_thread(); - - /* Fetch the table's current configuration */ - table_config_and_shards_t config; - m_table_meta_client->get_config(table_id, interruptor_on_home, &config); - - artificial_table_backend_t *status_backend = - artificial_reql_cluster_interface->get_table_backend( - name_string_t::guarantee_valid("table_status"), - admin_identifier_format_t::name); - guarantee(status_backend != nullptr); - ql::datum_t old_status; - admin_err_t error; - if (!status_backend->read_row( - user_context, - convert_uuid_to_datum(table_id), - interruptor_on_home, - &old_status, - &error)) { - throw admin_op_exc_t(error); - } else if (!old_status.has()) { - throw no_such_table_exc_t(); - } - - std::map counts; - fetch_distribution(table_id, this, interruptor_on_home, &counts); - - /* If there's not enough data to rebalance, return `rebalanced: 0` but don't report - an error */ - bool actually_rebalanced = calculate_split_points_with_distribution( - counts, config.config.shards.size(), &config.shard_scheme); - if (actually_rebalanced) { - table_config_and_shards_change_t table_config_and_shards_change( - table_config_and_shards_change_t::set_table_config_and_shards_t{ config }); - m_table_meta_client->set_config( - table_id, table_config_and_shards_change, interruptor_on_home); - } - - ql::datum_t new_status; - if (!status_backend->read_row( - user_context, - convert_uuid_to_datum(table_id), - interruptor_on_home, - &new_status, &error)) { - throw admin_op_exc_t(error); - } else if (!new_status.has()) { - throw no_such_table_exc_t(); - } - - ql::datum_object_builder_t builder; - builder.overwrite("rebalanced", ql::datum_t(actually_rebalanced ? 1.0 : 0.0)); - builder.overwrite("status_changes", make_replacement_pair(old_status, new_status)); - *results_out = std::move(builder).to_datum(); -} - -bool real_reql_cluster_interface_t::table_rebalance( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - signal_t *interruptor_on_caller, - ql::datum_t *result_out, - admin_err_t *error_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - try { - on_thread_t thread_switcher(home_thread()); - namespace_id_t table_id; - m_table_meta_client->find(db->id, name, &table_id); - - user_context.require_config_permission(m_rdb_context, db->id, table_id); - - rebalance_internal(user_context, table_id, &interruptor_on_home, result_out); - return true; - } catch (const admin_op_exc_t &admin_op_exc) { - *error_out = admin_op_exc.to_admin_err(); - return false; - } CATCH_NAME_ERRORS(db->name, name, error_out) - CATCH_OP_ERRORS(db->name, name, error_out, - "The table was not rebalanced.", - "The table may or may not have been rebalanced.") -} - -bool real_reql_cluster_interface_t::db_rebalance( - auth::user_context_t const &user_context, - counted_t db, - signal_t *interruptor_on_caller, - ql::datum_t *result_out, - admin_err_t *error_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - - std::map table_names; - m_table_meta_client->list_names(&table_names); - std::set table_ids; - for (auto const &table_name : table_names) { - if (table_name.second.database == db->id) { - table_ids.insert(table_name.first); - } - } - - user_context.require_config_permission(m_rdb_context, db->id, table_ids); - - ql::datum_t combined_stats = ql::datum_t::empty_object(); - for (const auto &table_id : table_ids) { - ql::datum_t stats; - try { - rebalance_internal(user_context, table_id, &interruptor_on_home, &stats); - } catch (const no_such_table_exc_t &) { - /* This table was deleted while we were iterating over the tables list. So - just ignore it to avoid making a confusing error message. */ - continue; - } catch (const admin_op_exc_t &admin_op_exc) { - *error_out = admin_op_exc.to_admin_err(); - return false; - } CATCH_OP_ERRORS(db->name, table_names.at(table_id).name, error_out, - "The tables may or may not have been rebalanced.", - "The tables may or may not have been rebalanced.") - std::set dummy_conditions; - combined_stats = combined_stats.merge(stats, &ql::stats_merge, - ql::configured_limits_t::unlimited, &dummy_conditions); - guarantee(dummy_conditions.empty()); - } - *result_out = combined_stats; - return true; -} - -bool real_reql_cluster_interface_t::grant_global( - auth::user_context_t const &user_context, - auth::username_t username, - ql::datum_t permissions, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out) { - cross_thread_signal_t interruptor_on_home(interruptor, home_thread()); - on_thread_t on_thread(home_thread()); - - return auth::grant( - m_auth_semilattice_view, - m_rdb_context, - user_context, - std::move(username), - std::move(permissions), - &interruptor_on_home, - [](auth::user_t &user) -> auth::permissions_t & { - return user.get_global_permissions(); - }, - result_out, - error_out); -} - -bool real_reql_cluster_interface_t::grant_database( - auth::user_context_t const &user_context, - database_id_t const &database_id, - auth::username_t username, - ql::datum_t permissions, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out) { - cross_thread_signal_t interruptor_on_home(interruptor, home_thread()); - on_thread_t on_thread(home_thread()); - - return auth::grant( - m_auth_semilattice_view, - m_rdb_context, - user_context, - std::move(username), - std::move(permissions), - &interruptor_on_home, - [&](auth::user_t &user) -> auth::permissions_t & { - return user.get_database_permissions(database_id); - }, - result_out, - error_out); -} - -bool real_reql_cluster_interface_t::grant_table( - auth::user_context_t const &user_context, - UNUSED database_id_t const &database_id, - namespace_id_t const &table_id, - auth::username_t username, - ql::datum_t permissions, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out) { - cross_thread_signal_t interruptor_on_home(interruptor, home_thread()); - on_thread_t on_thread(home_thread()); - - return auth::grant( - m_auth_semilattice_view, - m_rdb_context, - user_context, - std::move(username), - std::move(permissions), - &interruptor_on_home, - [&](auth::user_t &user) -> auth::permissions_t & { - return user.get_table_permissions(table_id); - }, - result_out, - error_out); -} - -bool real_reql_cluster_interface_t::set_write_hook( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const optional &config, - signal_t *interruptor_on_caller, - admin_err_t *) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - - namespace_id_t table_id; - m_table_meta_client->find(db->id, table, &table_id); - - user_context.require_config_permission(m_rdb_context, db->id, table_id); - - if (config.has_value()) { - table_config_and_shards_change_t table_config_and_shards_change( - table_config_and_shards_change_t::write_hook_create_t{config.get()}); - - m_table_meta_client->set_config( - table_id, table_config_and_shards_change, &interruptor_on_home); - } else { - table_config_and_shards_change_t table_config_and_shards_change( - table_config_and_shards_change_t::write_hook_drop_t{}); - m_table_meta_client->set_config( - table_id, table_config_and_shards_change, &interruptor_on_home); - } - return true; -} - -bool real_reql_cluster_interface_t::get_write_hook( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - signal_t *interruptor_on_caller, - ql::datum_t *write_hook_datum_out, - admin_err_t *) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - - namespace_id_t table_id; - m_table_meta_client->find(db->id, table, &table_id); - - user_context.require_config_permission(m_rdb_context, db->id, table_id); - - table_config_and_shards_t existing_config; - - m_table_meta_client->get_config( - table_id, - &interruptor_on_home, - &existing_config); - - *write_hook_datum_out = ql::datum_t::null(); - if (existing_config.config.write_hook) { - *write_hook_datum_out = convert_write_hook_to_datum(existing_config.config.write_hook); - } - return true; -} - -bool real_reql_cluster_interface_t::sindex_create( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const std::string &name, - const sindex_config_t &config, - signal_t *interruptor_on_caller, - admin_err_t *error_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - - try { - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - - namespace_id_t table_id; - m_table_meta_client->find(db->id, table, &table_id); - - user_context.require_config_permission(m_rdb_context, db->id, table_id); - - table_config_and_shards_change_t table_config_and_shards_change( - table_config_and_shards_change_t::sindex_create_t{name, config}); - m_table_meta_client->set_config( - table_id, table_config_and_shards_change, &interruptor_on_home); - - return true; - } catch (const config_change_exc_t &) { - *error_out = admin_err_t{ - strprintf("Index `%s` already exists on table `%s.%s`.", - name.c_str(), db->name.c_str(), table.c_str()), - query_state_t::FAILED}; - return false; - } CATCH_NAME_ERRORS(db->name, name, error_out) - CATCH_OP_ERRORS(db->name, name, error_out, - "The secondary index was not created.", - "The secondary index may or may not have been created.") -} - -bool real_reql_cluster_interface_t::sindex_drop( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const std::string &name, - signal_t *interruptor_on_caller, - admin_err_t *error_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - - try { - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - - namespace_id_t table_id; - m_table_meta_client->find(db->id, table, &table_id); - - user_context.require_config_permission(m_rdb_context, db->id, table_id); - - table_config_and_shards_change_t table_config_and_shards_change( - table_config_and_shards_change_t::sindex_drop_t{name}); - m_table_meta_client->set_config( - table_id, table_config_and_shards_change, &interruptor_on_home); - - return true; - } catch (const config_change_exc_t &) { - *error_out = admin_err_t{ - strprintf("Index `%s` does not exist on table `%s.%s`.", - name.c_str(), db->name.c_str(), table.c_str()), - query_state_t::FAILED}; - return false; - } CATCH_NAME_ERRORS(db->name, name, error_out) - CATCH_OP_ERRORS(db->name, name, error_out, - "The secondary index was not dropped.", - "The secondary index may or may not have been dropped.") -} - -bool real_reql_cluster_interface_t::sindex_rename( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const std::string &name, - const std::string &new_name, - bool overwrite, - signal_t *interruptor_on_caller, - admin_err_t *error_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - - try { - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - - namespace_id_t table_id; - m_table_meta_client->find(db->id, table, &table_id); - - user_context.require_config_permission(m_rdb_context, db->id, table_id); - - table_config_and_shards_change_t table_config_and_shards_change( - table_config_and_shards_change_t::sindex_rename_t{ - name, new_name, overwrite}); - m_table_meta_client->set_config( - table_id, table_config_and_shards_change, &interruptor_on_home); - - return true; - } catch (const config_change_exc_t &) { - if (overwrite) { - *error_out = admin_err_t{ - strprintf("Index `%s` does not exist on table `%s.%s`.", - name.c_str(), db->name.c_str(), table.c_str()), - query_state_t::FAILED}; - } else { - *error_out = admin_err_t{ - strprintf( - "Index `%s` does not exist or index `%s` already exists " - "on table `%s.%s`.", - name.c_str(), new_name.c_str(), db->name.c_str(), table.c_str()), - query_state_t::FAILED}; - } - return false; - } CATCH_NAME_ERRORS(db->name, name, error_out) - CATCH_OP_ERRORS(db->name, name, error_out, - "The secondary index was not renamed.", - "The secondary index may or may not have been renamed.") -} - -bool real_reql_cluster_interface_t::sindex_list( - counted_t db, - const name_string_t &table_name, - signal_t *interruptor_on_caller, - admin_err_t *error_out, - std::map > - *configs_and_statuses_out) { - guarantee(db->name != name_string_t::guarantee_valid("rethinkdb"), - "real_reql_cluster_interface_t should never get queries for system tables"); - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - try { - on_thread_t thread_switcher(home_thread()); - namespace_id_t table_id; - m_table_meta_client->find(db->id, table_name, &table_id); - m_table_meta_client->get_sindex_status( - table_id, &interruptor_on_home, configs_and_statuses_out); - return true; - } CATCH_NAME_ERRORS(db->name, table_name, error_out) - CATCH_OP_ERRORS(db->name, table_name, error_out, - "Failed to retrieve all secondary indexes.", - "Failed to retrieve all secondary indexes.") -} - -/* Checks that divisor is indeed a divisor of multiple. */ -template -bool is_joined(const T &multiple, const T &divisor) { - T cpy = multiple; - - semilattice_join(&cpy, divisor); - return cpy == multiple; -} - -void real_reql_cluster_interface_t::wait_for_cluster_metadata_to_propagate( - const cluster_semilattice_metadata_t &metadata, - signal_t *interruptor_on_caller) { - int threadnum = get_thread_id().threadnum; - - guarantee(m_cross_thread_database_watchables[threadnum].has()); - m_cross_thread_database_watchables[threadnum]->get_watchable()->run_until_satisfied( - [&](const databases_semilattice_metadata_t &md) -> bool { - return is_joined(md, metadata.databases); - }, - interruptor_on_caller); -} - -template -void copy_value(const T *in, T *out) { - *out = *in; -} - -void real_reql_cluster_interface_t::get_databases_metadata( - databases_semilattice_metadata_t *out) { - int threadnum = get_thread_id().threadnum; - r_sanity_check(m_cross_thread_database_watchables[threadnum].has()); - m_cross_thread_database_watchables[threadnum]->apply_read( - std::bind(©_value, - ph::_1, out)); -} - -void real_reql_cluster_interface_t::make_single_selection( - auth::user_context_t const &user_context, - const name_string_t &table_name, - const uuid_u &primary_key, - ql::backtrace_id_t bt, - ql::env_t *env, - scoped_ptr_t *selection_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, admin_op_exc_t) { - artificial_table_backend_t *table_backend = - artificial_reql_cluster_interface->get_table_backend( - table_name, - admin_identifier_format_t::name); - guarantee(table_backend != nullptr); - - admin_err_t error; - - ql::datum_t row; - if (!table_backend->read_row( - user_context, - convert_uuid_to_datum(primary_key), - env->interruptor, - &row, - &error)) { - throw admin_op_exc_t(error); - } else if (!row.has()) { - /* This is unlikely, but it can happen if the object is deleted between when we - look up its name and when we call `read_row()` */ - throw no_such_table_exc_t(); - } - - counted_t db; - if (!artificial_reql_cluster_interface->db_find( - name_string_t::guarantee_valid("rethinkdb"), - env->interruptor, - &db, - &error)) { - throw admin_op_exc_t(error); - } - - counted_t table = make_counted( - counted_t( - new artificial_table_t(m_rdb_context, db->id, table_backend)), - db, - table_name.str(), - read_mode_t::SINGLE, - bt); - - *selection_out = make_scoped( - ql::single_selection_t::from_row(env, bt, table, row), - bt); -} diff --git a/src/clustering/administration/real_reql_cluster_interface.hpp b/src/clustering/administration/real_reql_cluster_interface.hpp deleted file mode 100644 index 41614589053..00000000000 --- a/src/clustering/administration/real_reql_cluster_interface.hpp +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_REAL_REQL_CLUSTER_INTERFACE_HPP_ -#define CLUSTERING_ADMINISTRATION_REAL_REQL_CLUSTER_INTERFACE_HPP_ - -#include -#include - -#include "clustering/administration/admin_op_exc.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/namespace_interface_repository.hpp" -#include "clustering/administration/tables/generate_config.hpp" -#include "concurrency/cross_thread_watchable.hpp" -#include "concurrency/watchable.hpp" -#include "rdb_protocol/context.hpp" -#include "rpc/semilattice/view.hpp" - -class artificial_reql_cluster_interface_t; -class artificial_table_backend_t; -class name_resolver_t; -class server_config_client_t; - -/* `real_reql_cluster_interface_t` is a concrete subclass of `reql_cluster_interface_t` -that translates the user's `table_create()`, `table_drop()`, etc. requests into specific -actions on the semilattices. By performing these actions through the abstract -`reql_cluster_interface_t`, we can keep the ReQL code separate from the semilattice code. -*/ - -class real_reql_cluster_interface_t : - public reql_cluster_interface_t, - public home_thread_mixin_t { -public: - real_reql_cluster_interface_t( - mailbox_manager_t *mailbox_manager, - std::shared_ptr > auth_semilattice_view, - std::shared_ptr > cluster_semilattice_view, - rdb_context_t *rdb_context, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - multi_table_manager_t *multi_table_manager, - watchable_map_t< - std::pair >, - table_query_bcard_t> *table_query_directory, - lifetime_t name_resolver); - - bool db_create( - auth::user_context_t const &user_context, - const name_string_t &name, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool db_drop_uuid( - auth::user_context_t const &user_context, - database_id_t database_id, - const name_string_t &name, - signal_t *interruptor_on_home, - ql::datum_t *result_out, - admin_err_t *error_out); - bool db_drop( - auth::user_context_t const &user_context, - const name_string_t &name, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool db_list( - signal_t *interruptor, - std::set *names_out, - admin_err_t *error_out); - bool db_find(const name_string_t &name, - signal_t *interruptor, - counted_t *db_out, - admin_err_t *error_out); - bool db_config( - auth::user_context_t const &user_context, - const counted_t &db, - ql::backtrace_id_t bt, - ql::env_t *env, - scoped_ptr_t *selection_out, - admin_err_t *error_out); - - bool table_create( - auth::user_context_t const &user_context, - const name_string_t &name, - counted_t db, - const table_generate_config_params_t &config_params, - const std::string &primary_key, - write_durability_t durability, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool table_drop( - auth::user_context_t const &user_context, - const name_string_t &name, - counted_t db, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool table_list( - counted_t db, - signal_t *interruptor, - std::set *names_out, - admin_err_t *error_out); - bool table_find( - const name_string_t &name, - counted_t db, - optional identifier_format, - signal_t *interruptor, counted_t *table_out, - admin_err_t *error_out); - bool table_estimate_doc_counts( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - ql::env_t *env, - std::vector *doc_counts_out, - admin_err_t *error_out); - bool table_config( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - ql::backtrace_id_t bt, - ql::env_t *env, - scoped_ptr_t *selection_out, - admin_err_t *error_out); - bool table_status( - counted_t db, - const name_string_t &name, - ql::backtrace_id_t bt, - ql::env_t *env, - scoped_ptr_t *selection_out, - admin_err_t *error_out); - - bool table_wait( - counted_t db, - const name_string_t &name, - table_readiness_t readiness, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool db_wait( - counted_t db, - table_readiness_t readiness, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - - bool table_reconfigure( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - const table_generate_config_params_t ¶ms, - bool dry_run, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool db_reconfigure( - auth::user_context_t const &user_context, - counted_t db, - const table_generate_config_params_t ¶ms, - bool dry_run, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - - bool table_emergency_repair( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - emergency_repair_mode_t, - bool dry_run, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - - bool table_rebalance( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &name, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool db_rebalance( - auth::user_context_t const &user_context, - counted_t db, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - - bool grant_global( - auth::user_context_t const &user_context, - auth::username_t username, - ql::datum_t permissions, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool grant_database( - auth::user_context_t const &user_context, - database_id_t const &database_id, - auth::username_t username, - ql::datum_t permissions, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - bool grant_table( - auth::user_context_t const &user_context, - database_id_t const &database_id, - namespace_id_t const &table_id, - auth::username_t username, - ql::datum_t permissions, - signal_t *interruptor, - ql::datum_t *result_out, - admin_err_t *error_out); - - bool set_write_hook( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const optional &config, - signal_t *interruptor, - admin_err_t *error_out); - - bool get_write_hook( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - signal_t *interruptor, - ql::datum_t *write_hook_datum_out, - admin_err_t *error_out); - - bool sindex_create( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const std::string &name, - const sindex_config_t &config, - signal_t *interruptor, - admin_err_t *error_out); - bool sindex_drop( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const std::string &name, - signal_t *interruptor, - admin_err_t *error_out); - bool sindex_rename( - auth::user_context_t const &user_context, - counted_t db, - const name_string_t &table, - const std::string &name, - const std::string &new_name, - bool overwrite, - signal_t *interruptor, - admin_err_t *error_out); - bool sindex_list( - counted_t db, - const name_string_t &table, - signal_t *interruptor, - admin_err_t *error_out, - std::map > - *configs_and_statuses_out); - - /* `calculate_split_points_with_distribution` needs access to the underlying - `namespace_interface_t` and `table_meta_client_t`. */ - table_meta_client_t *get_table_meta_client() { - return m_table_meta_client; - } - namespace_repo_t *get_namespace_repo() { - return &m_namespace_repo; - } - - /* This is public because it needs to be set after we're created to solve a certain - chicken-and-egg problem */ - artificial_reql_cluster_interface_t *artificial_reql_cluster_interface; - -private: - mailbox_manager_t *m_mailbox_manager; - std::shared_ptr > m_auth_semilattice_view; - std::shared_ptr > m_cluster_semilattice_view; - table_meta_client_t *m_table_meta_client; - scoped_array_t< scoped_ptr_t< cross_thread_watchable_variable_t< - databases_semilattice_metadata_t > > > m_cross_thread_database_watchables; - rdb_context_t *m_rdb_context; - - namespace_repo_t m_namespace_repo; - ql::changefeed::client_t m_changefeed_client; - server_config_client_t *m_server_config_client; - - void wait_for_cluster_metadata_to_propagate( - const cluster_semilattice_metadata_t &metadata, - signal_t *interruptor); - - // This could soooo be optimized if you don't want to copy the whole thing. - void get_databases_metadata(databases_semilattice_metadata_t *out); - - void make_single_selection( - auth::user_context_t const &user_context, - const name_string_t &table_name, - const uuid_u &primary_key, - ql::backtrace_id_t bt, - ql::env_t *env, - scoped_ptr_t *selection_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, admin_op_exc_t); - - void wait_internal( - std::set tables, - table_readiness_t readiness, - signal_t *interruptor, - ql::datum_t *result_out, - int *count_out) - THROWS_ONLY(interrupted_exc_t, admin_op_exc_t); - - void reconfigure_internal( - auth::user_context_t const &user_context, - const counted_t &db, - const namespace_id_t &table_id, - const table_generate_config_params_t ¶ms, - bool dry_run, - signal_t *interruptor, - ql::datum_t *result_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, - failed_table_op_exc_t, maybe_failed_table_op_exc_t, admin_op_exc_t); - - void emergency_repair_internal( - auth::user_context_t const &user_context, - const counted_t &db, - const namespace_id_t &table_id, - emergency_repair_mode_t mode, - bool dry_run, - signal_t *interruptor, - ql::datum_t *result_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, - failed_table_op_exc_t, maybe_failed_table_op_exc_t, admin_op_exc_t); - - void rebalance_internal( - auth::user_context_t const &user_context, - const namespace_id_t &table_id, - signal_t *interruptor, - ql::datum_t *results_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, - failed_table_op_exc_t, maybe_failed_table_op_exc_t, admin_op_exc_t); - - DISABLE_COPYING(real_reql_cluster_interface_t); -}; - -#endif /* CLUSTERING_ADMINISTRATION_REAL_REQL_CLUSTER_INTERFACE_HPP_ */ - diff --git a/src/clustering/administration/servers/auto_reconnect.cc b/src/clustering/administration/servers/auto_reconnect.cc deleted file mode 100644 index 68b287ca958..00000000000 --- a/src/clustering/administration/servers/auto_reconnect.cc +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "clustering/administration/servers/auto_reconnect.hpp" - -#include "arch/timing.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "concurrency/exponential_backoff.hpp" -#include "concurrency/wait_any.hpp" -#include "logger.hpp" - -auto_reconnector_t::auto_reconnector_t( - connectivity_cluster_t *connectivity_cluster_, - connectivity_cluster_t::run_t *connectivity_cluster_run_, - server_config_client_t *server_config_client_, - const int join_delay_secs_, - const int give_up_ms_) : - connectivity_cluster(connectivity_cluster_), - connectivity_cluster_run(connectivity_cluster_run_), - server_config_client(server_config_client_), - join_delay_secs(join_delay_secs_), - give_up_ms(give_up_ms_), - server_id_subs( - server_config_client->get_peer_to_server_map(), - std::bind(&auto_reconnector_t::on_connect_or_disconnect, this, ph::_1), - initial_call_t::NO), - connection_subs( - connectivity_cluster->get_connections(), - std::bind(&auto_reconnector_t::on_connect_or_disconnect, this, ph::_1), - initial_call_t::YES) - { } - -void auto_reconnector_t::on_connect_or_disconnect(const peer_id_t &peer_id) { - optional server_id = - server_config_client->get_peer_to_server_map()->get_key(peer_id); - optional conn = - connectivity_cluster->get_connections()->get_key(peer_id); - // The criterion for when we consider a peer connected must be consistent with - // what we define in `initial_joiner_t::on_connection_change`, or else we might - // never start the `try_reconnect` routine for an initial join peer. - if (conn.has_value()) { - // We never reconnect *to* proxies. If we are a full server, the proxy is going - // to connect to us instead. - if (!conn->first->get_server_id().is_proxy()) { - addresses[conn->first->get_server_id()] = conn->first->get_peer_address(); - server_ids[peer_id] = conn->first->get_server_id(); - } - } else if (!server_id.has_value() && !conn.has_value()) { - auto it = server_ids.find(peer_id); - if (it != server_ids.end()) { - coro_t::spawn_sometime(std::bind(&auto_reconnector_t::try_reconnect, this, - it->second, drainer.lock())); - server_ids.erase(it); - } - } -} - -void auto_reconnector_t::try_reconnect(const server_id_t &server, - auto_drainer_t::lock_t keepalive) { - peer_address_t last_known_address; - auto it = addresses.find(server); - guarantee(it != addresses.end()); - last_known_address = it->second; - - signal_timer_t give_up_timer; - give_up_timer.start(give_up_ms); - - cond_t reconnected; - watchable_map_t::key_subs_t subs( - server_config_client->get_server_to_peer_map(), - server, - [&](const peer_id_t *pid) { - if (pid != nullptr) { - reconnected.pulse_if_not_already_pulsed(); - // Add the server back into `server_ids` so that `try_reconnect` is going - // to be started again in `on_connect_or_disconnect` when the connection - // gets dropped, even if we never get an entry in the - // `connectivity_cluster`'s connections map that - // `on_connect_or_disconnect` is listening on. - server_ids[*pid] = server; - } - }, - initial_call_t::YES); - - cond_t join_failed; - wait_any_t interruptor( - &reconnected, &give_up_timer, keepalive.get_drain_signal(), &join_failed); - exponential_backoff_t backoff(50, 15 * 1000); - try { - // These can be safely passed into the coroutine below. - // They will be reset to `nullptr` by the assignment_sentry_ts when this function - // call ends. - std::shared_ptr join_failed_out(new cond_t *(nullptr)); - std::shared_ptr last_known_address_out( - new peer_address_t *(nullptr)); - assignment_sentry_t join_failed_out_assignment( - join_failed_out.get(), &join_failed); - assignment_sentry_t last_known_address_out_assignment( - last_known_address_out.get(), &last_known_address); - - while (!interruptor.is_pulsed()) { - // This coroutine can keep running even after this function has returned - // or the auto_reconnector_t was destructed. So we must pass everything - // we need by value. - - coro_t::spawn_now_dangerously( - [this, last_known_address, server, join_failed_out, last_known_address_out]() { - auto connectivity_cluster_run_local = connectivity_cluster_run; - auto join_delay_secs_local = join_delay_secs; - - join_results_t results = - connectivity_cluster_run_local->join_blocking( - last_known_address, r_nullopt, make_optional(server), - join_delay_secs_local, - auto_drainer_t::lock_t(&connectivity_cluster_run_local->drainer)); - - // Check if the `try_reconnect` function that spawned us is still alive - if (*join_failed_out != nullptr && *last_known_address_out != nullptr) { - ASSERT_NO_CORO_WAITING; - - for (auto r = results.begin(); r != results.end(); ++r) { - if (r->second != join_result_t::PERMANENT_ERROR) continue; - (*last_known_address_out)->erase_ip(r->first); - if (last_known_address.ips().empty()) { - logNTC("Unrecoverable connection error to remote server: %s", - server.print().c_str()); - (*join_failed_out)->pulse_if_not_already_pulsed(); - } - } - } - }); - - backoff.failure(&interruptor); - } - } catch (const interrupted_exc_t &) { - /* ignore; this is how we escape the loop */ - } -} - diff --git a/src/clustering/administration/servers/auto_reconnect.hpp b/src/clustering/administration/servers/auto_reconnect.hpp deleted file mode 100644 index 0283fe95fc1..00000000000 --- a/src/clustering/administration/servers/auto_reconnect.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_SERVERS_AUTO_RECONNECT_HPP_ -#define CLUSTERING_ADMINISTRATION_SERVERS_AUTO_RECONNECT_HPP_ - -#include -#include - -#include "clustering/administration/servers/server_metadata.hpp" -#include "concurrency/watchable.hpp" -#include "containers/incremental_lenses.hpp" -#include "rpc/connectivity/cluster.hpp" -#include "rpc/semilattice/view.hpp" - -class server_config_client_t; - -class auto_reconnector_t { -public: - auto_reconnector_t( - connectivity_cluster_t *connectivity_cluster, - connectivity_cluster_t::run_t *connectivity_cluster_run, - server_config_client_t *server_config_client, - const int join_delay_secs, - const int give_up_ms); - -private: - void on_connect_or_disconnect(const peer_id_t &peer_id); - - /* `try_reconnect()` runs in a coroutine. It automatically terminates - itself if the server ever reconnects. */ - void try_reconnect(const server_id_t &server, auto_drainer_t::lock_t drainer); - - connectivity_cluster_t *const connectivity_cluster; - connectivity_cluster_t::run_t *const connectivity_cluster_run; - server_config_client_t *const server_config_client; - - /* `addresses` contains the last known address of every server we've ever seen */ - std::map addresses; - - /* `server_ids` contains the server IDs of servers that are currently connected. We - detect connection and disconnection events by comparing this to the list of currently - connected servers we get from the `connectivity_cluster_t`. */ - std::map server_ids; - - int join_delay_secs; - int give_up_ms; - - auto_drainer_t drainer; - - watchable_map_t::all_subs_t - server_id_subs; - watchable_map_t::all_subs_t - connection_subs; - - DISABLE_COPYING(auto_reconnector_t); -}; - -#endif /* CLUSTERING_ADMINISTRATION_SERVERS_AUTO_RECONNECT_HPP_ */ diff --git a/src/clustering/administration/servers/config_client.cc b/src/clustering/administration/servers/config_client.cc deleted file mode 100644 index bebefcb98c5..00000000000 --- a/src/clustering/administration/servers/config_client.cc +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/servers/config_client.hpp" - -#include "clustering/administration/admin_op_exc.hpp" -#include "clustering/administration/metadata.hpp" -#include "rpc/mailbox/disconnect_watcher.hpp" - -server_config_client_t::server_config_client_t( - mailbox_manager_t *_mailbox_manager, - watchable_map_t *_directory_view, - watchable_map_t, empty_value_t> - *_peer_connections_map) : - mailbox_manager(_mailbox_manager), - directory_view(_directory_view), - peer_connections_map(_peer_connections_map), - directory_subs( - directory_view, - std::bind(&server_config_client_t::on_directory_change, this, ph::_1, ph::_2), - initial_call_t::YES), - peer_connections_map_subs( - peer_connections_map, - std::bind(&server_config_client_t::on_peer_connections_map_change, - this, ph::_1, ph::_2), - initial_call_t::YES), - server_connectivity_subscription( - &connections_map, - std::bind(&server_config_client_t::update_server_connectivity, - this, - ph::_1, - ph::_2), - initial_call_t::YES) - { } - -void server_config_client_t::update_server_connectivity( - const std::pair &key, - const empty_value_t *val) { - if (val == nullptr) { - // Entry removed - if (--server_connectivity.all_servers[key.first] == 0) { - server_connectivity.all_servers.erase(key.first); - } - if (--server_connectivity.all_servers[key.second] == 0) { - server_connectivity.all_servers.erase(key.second); - } - server_connectivity.connected_to[key.first].erase(key.second); - if (server_connectivity.connected_to[key.first].empty()) { - server_connectivity.connected_to.erase(key.first); - } - } else { - // Entry added - ++server_connectivity.all_servers[key.first]; - ++server_connectivity.all_servers[key.second]; - server_connectivity.connected_to[key.first].insert(key.second); - } -} - -bool server_config_client_t::set_config( - const server_id_t &server_id, - const name_string_t &old_server_name, - const server_config_t &new_config, - signal_t *interruptor, - admin_err_t *error_out) { - bool name_collision = false, is_noop = false; - server_config_map.read_all( - [&](const server_id_t &sid, const server_config_versioned_t *conf) { - if (sid != server_id && conf->config.name == new_config.name) { - name_collision = true; - } else if (sid == server_id && conf->config == new_config) { - is_noop = true; - } - }); - if (name_collision) { - *error_out = admin_err_t{ - strprintf("Cannot rename server `%s` to `%s` because server `%s` " - "already exists.", - old_server_name.c_str(), new_config.name.c_str(), - new_config.name.c_str()), - query_state_t::FAILED}; - return false; - } - if (is_noop) { - return true; - } - - optional peer = server_to_peer_map.get_key(server_id); - if (!peer.has_value()) { - std::string s = strprintf( - "Could not contact server `%s` while trying to change the server " - "configuration. The configuration was not changed.", - old_server_name.c_str()); - *error_out = admin_err_t{s, query_state_t::FAILED}; - return false; - } - server_config_business_card_t bcard; - directory_view->read_key(*peer, [&](const cluster_directory_metadata_t *md) { - guarantee(md != nullptr); - bcard = md->server_config_business_card.get(); - }); - - server_config_version_t version; - { - promise_t > reply; - mailbox_t ack_mailbox( - mailbox_manager, - [&](signal_t *, server_config_version_t v, const std::string &m) { - reply.pulse(std::make_pair(v, m)); - }); - disconnect_watcher_t disconnect_watcher(mailbox_manager, *peer); - send(mailbox_manager, bcard.set_config_addr, - new_config, ack_mailbox.get_address()); - wait_any_t waiter(reply.get_ready_signal(), &disconnect_watcher); - wait_interruptible(&waiter, interruptor); - if (!reply.is_pulsed()) { - - std::string disconnect_msg = strprintf( - "Lost contact with server `%s` while trying " - "to change the server configuration. The configuration may or may not " - "have been changed.", old_server_name.c_str()); - *error_out = admin_err_t{disconnect_msg, query_state_t::INDETERMINATE}; - return false; - } - if (!reply.assert_get_value().second.empty()) { - guarantee(reply.assert_get_value().first == 0); - *error_out = admin_err_t{ - strprintf("Error when trying to change the configuration of " - "server `%s`: %s The configuration was not changed.", - old_server_name.c_str(), - reply.assert_get_value().second.c_str()), - query_state_t::FAILED}; - return false; - } - version = reply.assert_get_value().first; - } - - /* Wait up to 10 seconds for the change to appear in the directory. */ - try { - signal_timer_t timeout; - timeout.start(10000); - wait_any_t waiter(interruptor, &timeout); - server_config_map.run_key_until_satisfied( - server_id, - [&](const server_config_versioned_t *conf) { - return conf == nullptr || conf->version >= version; - }, - &waiter); - } catch (const interrupted_exc_t &) { - if (interruptor->is_pulsed()) { - throw; - } - } - - return true; -} - -void server_config_client_t::install_server_metadata( - const peer_id_t &peer_id, - const cluster_directory_metadata_t &metadata) { - const server_id_t &server_id = metadata.server_id; - server_to_peer_map.set_key(server_id, peer_id); - peer_connections_map->read_all( - [&](const std::pair &pair, const empty_value_t *) { - if (pair.first == peer_id) { - connections_map.set_key( - std::make_pair(server_id, pair.second), empty_value_t()); - } - }); - server_config_map.set_key(server_id, metadata.server_config); -} - -void server_config_client_t::on_directory_change( - const peer_id_t &peer_id, - const cluster_directory_metadata_t *metadata) { - if (metadata != nullptr) { - // This is so we don't track servers inaccurately - // if metadata updates after connections. - peer_connections_map->read_all( - [&] (const std::pair &connection, - const empty_value_t *value) { - if (connection.first == peer_id) { - // This connection matches the directory view change - if (value != nullptr) { - connections_map.set_key( - std::make_pair(metadata->server_id, connection.second), - empty_value_t()); - } else { - connections_map.delete_key( - std::make_pair(metadata->server_id, connection.second)); - } - } - }); - - if (metadata->peer_type != SERVER_PEER) { - return; - } - const server_id_t &server_id = metadata->server_id; - if (!peer_to_server_map.get_key(peer_id).has_value()) { - all_server_to_peer_map.insert(std::make_pair(server_id, peer_id)); - peer_to_server_map.set_key(peer_id, server_id); - install_server_metadata(peer_id, *metadata); - } else { - server_config_map.set_key(server_id, metadata->server_config); - } - - } else { - optional server_id = peer_to_server_map.get_key(peer_id); - if (!server_id.has_value()) { - return; - } - for (auto it = all_server_to_peer_map.lower_bound(*server_id); ; ++it) { - guarantee(it != all_server_to_peer_map.end()); - if (it->second == peer_id) { - all_server_to_peer_map.erase(it); - break; - } - } - peer_to_server_map.delete_key(peer_id); - server_to_peer_map.delete_key(*server_id); - std::vector > connection_pairs_to_delete; - connections_map.read_all( - [&](const std::pair &pair, const empty_value_t *) { - if (pair.first == *server_id) { - connection_pairs_to_delete.push_back(pair); - } - }); - for (const auto &pair : connection_pairs_to_delete) { - connections_map.delete_key(pair); - } - server_config_map.delete_key(*server_id); - - /* If there is another connected peer with the same server ID, reinstall its - values. */ - auto jt = all_server_to_peer_map.find(*server_id); - if (jt != all_server_to_peer_map.end()) { - directory_view->read_key(jt->second, - [&](const cluster_directory_metadata_t *other_metadata) { - guarantee(other_metadata != nullptr); - guarantee(other_metadata->server_id == *server_id); - install_server_metadata(jt->second, *other_metadata); - }); - } - } -} - -void server_config_client_t::on_peer_connections_map_change( - const std::pair &key, - const empty_value_t *value) { - directory_view->read_key(key.first, - [&](const cluster_directory_metadata_t *metadata) { - if (metadata != nullptr) { - if (value != nullptr) { - connections_map.set_key( - std::make_pair(metadata->server_id, key.second), empty_value_t()); - } else { - connections_map.delete_key( - std::make_pair(metadata->server_id, key.second)); - } - } - }); -} - - diff --git a/src/clustering/administration/servers/config_client.hpp b/src/clustering/administration/servers/config_client.hpp deleted file mode 100644 index e8561143200..00000000000 --- a/src/clustering/administration/servers/config_client.hpp +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_SERVERS_CONFIG_CLIENT_HPP_ -#define CLUSTERING_ADMINISTRATION_SERVERS_CONFIG_CLIENT_HPP_ - -#include -#include -#include - -#include "clustering/administration/servers/server_metadata.hpp" -#include "concurrency/watchable_map.hpp" -#include "containers/incremental_lenses.hpp" -#include "rpc/semilattice/view.hpp" - -struct admin_err_t; -class cluster_directory_metadata_t; -class empty_value_t; -class mailbox_manager_t; - -struct server_connectivity_t { - std::map > connected_to; - std::map all_servers; -}; - -class server_config_client_t : public home_thread_mixin_t { -public: - server_config_client_t( - mailbox_manager_t *_mailbox_manager, - watchable_map_t - *_directory_view, - watchable_map_t, empty_value_t> - *_peer_connections_map); - - /* `get_server_config_map()` returns the server IDs and current configurations of - every connected server. */ - watchable_map_t *get_server_config_map() { - return &server_config_map; - } - - /* `get_peer_to_server_map()` and `get_server_to_peer_map()` allow conversion back - and forth between server IDs and peer IDs. */ - watchable_map_t *get_peer_to_server_map() { - return &peer_to_server_map; - } - watchable_map_t *get_server_to_peer_map() { - return &server_to_peer_map; - } - - /* This map contains the pair (X, Y) if we can see server X and server X can see - server Y. */ - watchable_map_t, empty_value_t> - *get_connections_map() { - return &connections_map; - } - - const server_connectivity_t& get_server_connectivity() const { - return server_connectivity; - } - /* `set_config()` changes the config of the server with the given server ID. */ - bool set_config( - const server_id_t &server_id, - const name_string_t &old_server_name, /* for error messages */ - const server_config_t &new_server_config, - signal_t *interruptor, - admin_err_t *error_out); - - void update_server_connectivity( - const std::pair &key, - const empty_value_t *val); - -private: - void install_server_metadata( - const peer_id_t &peer_id, - const cluster_directory_metadata_t &metadata); - void on_directory_change( - const peer_id_t &peer_id, - const cluster_directory_metadata_t *metadata); - void on_peer_connections_map_change( - const std::pair &key, - const empty_value_t *value); - - mailbox_manager_t *const mailbox_manager; - watchable_map_t * const directory_view; - watchable_map_t< std::pair, empty_value_t> - * const peer_connections_map; - - watchable_map_var_t server_config_map; - watchable_map_var_t peer_to_server_map; - watchable_map_var_t server_to_peer_map; - watchable_map_var_t, empty_value_t> - connections_map; - - /* We use this to produce reasonable results when multiple peers have the same server - ID. In general multiple peers cannot have the same server ID, but a server might - conceivably shut down and then reconnect with a new peer ID before we had dropped the - original connection. */ - std::multimap all_server_to_peer_map; - - watchable_map_t::all_subs_t directory_subs; - watchable_map_t, empty_value_t>::all_subs_t - peer_connections_map_subs; - - server_connectivity_t server_connectivity; - typename watchable_map_t, empty_value_t> - ::all_subs_t server_connectivity_subscription; -}; - -#endif /* CLUSTERING_ADMINISTRATION_SERVERS_CONFIG_CLIENT_HPP_ */ - diff --git a/src/clustering/administration/servers/config_server.cc b/src/clustering/administration/servers/config_server.cc deleted file mode 100644 index edfd3ea66e5..00000000000 --- a/src/clustering/administration/servers/config_server.cc +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/servers/config_server.hpp" - -#include "clustering/administration/main/cache_size.hpp" -#include "clustering/administration/persist/file.hpp" -#include "clustering/administration/persist/file_keys.hpp" - -server_config_server_t::server_config_server_t( - mailbox_manager_t *_mailbox_manager, - metadata_file_t *_file) : - mailbox_manager(_mailbox_manager), - file(_file), - my_config(server_config_versioned_t()), - actual_cache_size_bytes(0), - set_config_mailbox(mailbox_manager, - std::bind(&server_config_server_t::on_set_config, this, - ph::_1, ph::_2, ph::_3)) -{ - cond_t non_interruptor; - metadata_file_t::read_txn_t read_txn(file, &non_interruptor); - my_server_id = read_txn.read(mdkey_server_id(), &non_interruptor); - my_config.set_value(read_txn.read(mdkey_server_config(), &non_interruptor)); - update_actual_cache_size(my_config.get_ref().config.cache_size_bytes); -} - -server_config_business_card_t server_config_server_t::get_business_card() { - server_config_business_card_t bcard; - bcard.set_config_addr = set_config_mailbox.get_address(); - return bcard; -} - -void server_config_server_t::on_set_config( - signal_t *interruptor, - const server_config_t &new_config, - const mailbox_t::address_t &ack_addr) { - if (interruptor->is_pulsed()) { - throw interrupted_exc_t(); - } - - if (static_cast(new_config.cache_size_bytes) && - *new_config.cache_size_bytes > get_max_total_cache_size()) { - send(mailbox_manager, ack_addr, static_cast(0), - strprintf("The proposed cache size of %" PRIu64 " MB is larger than the " - "maximum legal value for this platform (%" PRIu64 " MB).", - *new_config.cache_size_bytes, get_max_total_cache_size())); - return; - } - server_config_t old_config = my_config.get_ref().config; - my_config.apply_atomic_op([&](server_config_versioned_t *value) -> bool { - value->config = new_config; - ++value->version; - return true; - }); - { - cond_t non_interruptor; - metadata_file_t::write_txn_t write_txn(file, &non_interruptor); - write_txn.write(mdkey_server_config(), my_config.get_ref(), &non_interruptor); - write_txn.commit(); - } - if (old_config.name != new_config.name) { - logINF("Changed server's name from `%s` to `%s`.", - old_config.name.c_str(), new_config.name.c_str()); - } - if (old_config.cache_size_bytes != new_config.cache_size_bytes) { - update_actual_cache_size(new_config.cache_size_bytes); - } - send(mailbox_manager, ack_addr, my_config.get_ref().version, std::string()); -} - -void server_config_server_t::update_actual_cache_size( - const optional &setting) { - uint64_t actual_size; - if (!static_cast(setting)) { - actual_size = get_default_total_cache_size(); - logINF("Automatically using cache size of %" PRIu64 " MB", - actual_size / static_cast(MEGABYTE)); - } else { - if (*setting > get_max_total_cache_size()) { - /* Usually this won't happen, because something else will reject the value - before it gets to here. However, this can happen if a large cache size is - recorded on disk and then the same data files are opened on a platform with a - lower limit. I'm not sure if it's currently possible to open 64-bit metadata - files on 32-bit platform, but better safe than sorry. */ - logWRN("The cache size is set to %" PRIu64 " MB, which is larger than the " - "maximum legal value for this platform (%" PRIu64 " MB). The maximum " - "legal value will be used as the actual cache size.", - *setting, get_max_total_cache_size()); - actual_size = get_max_total_cache_size(); - } else { - actual_size = *setting; - logINF("Cache size is set to %" PRIu64 " MB", - actual_size / static_cast(MEGABYTE)); - } - } - log_warnings_for_cache_size(actual_size); - actual_cache_size_bytes.set_value(actual_size); -} - diff --git a/src/clustering/administration/servers/config_server.hpp b/src/clustering/administration/servers/config_server.hpp deleted file mode 100644 index 77c9cf21c8f..00000000000 --- a/src/clustering/administration/servers/config_server.hpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_SERVERS_CONFIG_SERVER_HPP_ -#define CLUSTERING_ADMINISTRATION_SERVERS_CONFIG_SERVER_HPP_ - -#include - -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/servers/server_metadata.hpp" - -class metadata_file_t; - -class server_config_server_t : public home_thread_mixin_t { -public: - server_config_server_t( - mailbox_manager_t *_mailbox_manager, - metadata_file_t *_file); - - server_config_business_card_t get_business_card(); - - clone_ptr_t > get_config() { - return my_config.get_watchable(); - } - - /* Returns the actual cache size, not the cache size setting. If the cache size - setting is "auto", the actual cache size will be some reasonable automatically - selected value; otherwise, the actual cache size will be the cache size setting. */ - clone_ptr_t > get_actual_cache_size_bytes() { - return actual_cache_size_bytes.get_watchable(); - } - -private: - /* `on_set_config()` is a mailbox callback */ - void on_set_config( - signal_t *interruptor, - const server_config_t &new_config, - const mailbox_t::address_t &ack_addr); - - void update_actual_cache_size(const optional &setting); - - mailbox_manager_t *const mailbox_manager; - metadata_file_t *const file; - server_id_t my_server_id; - watchable_variable_t my_config; - watchable_variable_t actual_cache_size_bytes; - - server_config_business_card_t::set_config_mailbox_t set_config_mailbox; -}; - -#endif /* CLUSTERING_ADMINISTRATION_SERVERS_CONFIG_SERVER_HPP_ */ - diff --git a/src/clustering/administration/servers/network_logger.cc b/src/clustering/administration/servers/network_logger.cc deleted file mode 100644 index 61270a7d6a7..00000000000 --- a/src/clustering/administration/servers/network_logger.cc +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "clustering/administration/servers/network_logger.hpp" - -network_logger_t::network_logger_t( - peer_id_t our_peer_id, - watchable_map_t *dv) : - us(our_peer_id), - directory_view(dv), - directory_subscription( - directory_view, - std::bind(&network_logger_t::on_change, this, ph::_1, ph::_2), - initial_call_t::YES) - { } - -void network_logger_t::on_change( - const peer_id_t &peer_id, const cluster_directory_metadata_t *value) { - if (value != nullptr) { - const server_id_t &server_id = value->server_id; - local_connections.set_key(server_id, empty_value_t()); - switch (value->peer_type) { - case SERVER_PEER: { - const name_string_t &name = value->server_config.config.name; - if (peer_id != us && connected_servers.count(peer_id) == 0) { - logNTC("Connected to server \"%s\" %s", - name.c_str(), server_id.print().c_str()); - } - connected_servers[peer_id] = std::make_pair(server_id, name); - break; - } - case PROXY_PEER: { - if (peer_id != us && connected_proxies.count(peer_id) == 0) { - logNTC("Connected to proxy %s", server_id.print().c_str()); - } - connected_proxies[peer_id] = server_id; - break; - } - default: unreachable(); - } - } else { - auto it = connected_servers.find(peer_id); - if (it != connected_servers.end()) { - if (peer_id != us) { - logNTC("Disconnected from server \"%s\" %s", - it->second.second.c_str(), - it->second.first.print().c_str()); - } - local_connections.delete_key(it->second.first); - connected_servers.erase(it); - } else { - auto jt = connected_proxies.find(peer_id); - if (jt != connected_proxies.end()) { - if (peer_id != us) { - logNTC("Disconnected from proxy %s", - jt->second.print().c_str()); - } - local_connections.delete_key(jt->second); - connected_proxies.erase(jt); - } - } - } -} - diff --git a/src/clustering/administration/servers/network_logger.hpp b/src/clustering/administration/servers/network_logger.hpp deleted file mode 100644 index 2a2f9881c38..00000000000 --- a/src/clustering/administration/servers/network_logger.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_SERVERS_NETWORK_LOGGER_HPP_ -#define CLUSTERING_ADMINISTRATION_SERVERS_NETWORK_LOGGER_HPP_ - -#include -#include - -#include "clustering/administration/servers/server_metadata.hpp" -#include "clustering/administration/metadata.hpp" -#include "rpc/semilattice/view.hpp" - -/* This class is responsible for writing log messages when a peer connects or -disconnects from us */ - -class network_logger_t { -public: - network_logger_t( - peer_id_t us, - watchable_map_t *directory_view); - - /* This contains an entry for a given server ID if we can see a server with that ID. - This will be piped over the network to other servers to form the full connections - map. */ - watchable_map_t *get_local_connections_map() { - return &local_connections; - } - -private: - void on_change(const peer_id_t &peer_id, const cluster_directory_metadata_t *value); - - peer_id_t us; - watchable_map_t *directory_view; - - /* Whenever the directory changes, we compare the directory to - `connected_servers` and `connected_proxies` to see what servers have - connected or disconnected. */ - std::map connected_proxies; - std::map > connected_servers; - - watchable_map_var_t local_connections; - - watchable_map_t::all_subs_t directory_subscription; -}; - -#endif /* CLUSTERING_ADMINISTRATION_SERVERS_NETWORK_LOGGER_HPP_ */ diff --git a/src/clustering/administration/servers/server_common.cc b/src/clustering/administration/servers/server_common.cc deleted file mode 100644 index eda87d91c04..00000000000 --- a/src/clustering/administration/servers/server_common.cc +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/servers/server_common.hpp" - -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "concurrency/cross_thread_signal.hpp" - -common_server_artificial_table_backend_t::common_server_artificial_table_backend_t( - name_string_t const &table_name, - rdb_context_t *rdb_context, - lifetime_t name_resolver, - server_config_client_t *_server_config_client, - watchable_map_t *_directory) - : caching_cfeed_artificial_table_backend_t(table_name, rdb_context, name_resolver), - directory(_directory), - server_config_client(_server_config_client), - directory_subs(directory, - [this](const peer_id_t &, const cluster_directory_metadata_t *md) { - if (md == nullptr) { - notify_all(); - } else { - /* This is one of the rare cases where we can tell exactly which row - needs to be recomputed */ - notify_row(convert_server_id_to_datum(md->server_id)); - } - }, initial_call_t::NO) { - directory->assert_thread(); - server_config_client->assert_thread(); -} - -std::string common_server_artificial_table_backend_t::get_primary_key_name() { - return "id"; -} - -bool common_server_artificial_table_backend_t::read_all_rows_as_vector( - auth::user_context_t const &user_context, - signal_t *interruptor_on_caller, - std::vector *rows_out, - admin_err_t *error_out) { - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - rows_out->clear(); - std::map metadata = directory->get_all(); - for (const auto &pair : metadata) { - if (pair.second.peer_type != SERVER_PEER) { - continue; - } - ql::datum_t row; - if (!format_row( - user_context, - pair.second.server_id, - pair.first, - pair.second, - &interruptor_on_home, - &row, - error_out)) { - return false; - } - rows_out->push_back(row); - } - return true; -} - -bool common_server_artificial_table_backend_t::read_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor_on_caller, - ql::datum_t *row_out, - admin_err_t *error_out) { - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - server_id_t server_id; - peer_id_t peer_id; - cluster_directory_metadata_t metadata; - if (!lookup(primary_key, &server_id, &peer_id, &metadata)) { - *row_out = ql::datum_t(); - return true; - } else { - return format_row( - user_context, - server_id, - peer_id, - metadata, - &interruptor_on_home, - row_out, - error_out); - } -} - -bool common_server_artificial_table_backend_t::lookup( - const ql::datum_t &primary_key, - server_id_t *server_id_out, - peer_id_t *peer_id_out, - cluster_directory_metadata_t *metadata_out) { - assert_thread(); - admin_err_t dummy_error; - if (!convert_server_id_from_datum(primary_key, server_id_out, &dummy_error)) { - return false; - } - optional peer_id = - server_config_client->get_server_to_peer_map()->get_key(*server_id_out); - if (!peer_id.has_value()) { - return false; - } - *peer_id_out = *peer_id; - bool ok; - directory->read_key(*peer_id, [&](const cluster_directory_metadata_t *metadata) { - if (metadata == nullptr) { - ok = false; - } else { - guarantee(metadata->peer_type == SERVER_PEER); - *metadata_out = *metadata; - ok = true; - } - }); - return ok; -} - diff --git a/src/clustering/administration/servers/server_common.hpp b/src/clustering/administration/servers/server_common.hpp deleted file mode 100644 index d24728d6703..00000000000 --- a/src/clustering/administration/servers/server_common.hpp +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_SERVERS_SERVER_COMMON_HPP_ -#define CLUSTERING_ADMINISTRATION_SERVERS_SERVER_COMMON_HPP_ - -#include -#include - -#include "clustering/administration/tables/database_metadata.hpp" -#include "clustering/administration/tables/table_metadata.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "rdb_protocol/artificial_table/caching_cfeed_backend.hpp" -#include "rdb_protocol/datum.hpp" -#include "rpc/semilattice/view.hpp" - -/* This is a base class for the `rethinkdb.server_config` and `rethinkdb.server_status` -pseudo-tables. Subclasses should implement `format_row()` and `write_row()`, in terms of -`lookup()`. */ - -class common_server_artificial_table_backend_t : - public caching_cfeed_artificial_table_backend_t { -public: - common_server_artificial_table_backend_t( - name_string_t const &table_name, - rdb_context_t *rdb_context, - lifetime_t name_resolver, - server_config_client_t *_server_config_client, - watchable_map_t *_directory); - - std::string get_primary_key_name(); - - bool read_all_rows_as_vector( - auth::user_context_t const &user_context, - signal_t *interruptor_on_caller, - std::vector *rows_out, - admin_err_t *error_out); - - bool read_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor_on_caller, - ql::datum_t *row_out, - admin_err_t *error_out); - -protected: - virtual bool format_row( - auth::user_context_t const &user_context, - server_id_t const & server_id, - peer_id_t const & peer_id, - cluster_directory_metadata_t const & directory_entry, - signal_t *interruptor_on_home, - ql::datum_t *row_out, - admin_err_t *error_out) = 0; - - /* `lookup()` returns `true` if it finds a row corresponding to the given - `primary_key` and `false` if it does not find a row. It never produces an error. It - should only be called on the home thread. */ - bool lookup( - const ql::datum_t &primary_key, - server_id_t *server_id_out, - peer_id_t *peer_id_out, - cluster_directory_metadata_t *metadata_out); - - watchable_map_t *directory; - server_config_client_t *server_config_client; - - /* We use `directory_subs` to notify the `caching_cfeed_..._backend_t` that a row has - changed. */ - watchable_map_t::all_subs_t directory_subs; -}; - -#endif /* CLUSTERING_ADMINISTRATION_SERVERS_SERVER_COMMON_HPP_ */ - diff --git a/src/clustering/administration/servers/server_config.cc b/src/clustering/administration/servers/server_config.cc deleted file mode 100644 index 6b4e905b45c..00000000000 --- a/src/clustering/administration/servers/server_config.cc +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/servers/server_config.hpp" - -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "concurrency/cross_thread_signal.hpp" - -bool convert_server_config_from_datum( - ql::datum_t datum, - server_id_t *server_id_out, - server_config_t *server_config_out, - admin_err_t *error_out) { - converter_from_datum_object_t converter; - if (!converter.init(datum, error_out)) { - return false; - } - - ql::datum_t name_datum; - if (!converter.get("name", &name_datum, error_out)) { - return false; - } - if (!convert_name_from_datum(name_datum, "server name", - &server_config_out->name, error_out)) { - error_out->msg = "In `name`: " + error_out->msg; - return false; - } - - ql::datum_t id_datum; - if (!converter.get("id", &id_datum, error_out)) { - return false; - } - if (!convert_server_id_from_datum(id_datum, server_id_out, error_out)) { - error_out->msg = "In `id`: " + error_out->msg; - return false; - } - - ql::datum_t tags_datum; - if (!converter.get("tags", &tags_datum, error_out)) { - return false; - } - if (!convert_set_from_datum( - [](ql::datum_t datum2, name_string_t *val2_out, admin_err_t *error2_out) { - return convert_name_from_datum( - datum2, "server tag", val2_out, error2_out); - }, - true, /* don't complain if a tag appears twice */ - tags_datum, &server_config_out->tags, error_out)) { - error_out->msg = "In `tags`: " + error_out->msg; - return false; - } - - ql::datum_t cache_size_datum; - if (!converter.get("cache_size_mb", &cache_size_datum, error_out)) { - return false; - } - if (cache_size_datum == ql::datum_t("auto")) { - server_config_out->cache_size_bytes = optional(); - } else if (cache_size_datum.get_type() == ql::datum_t::R_NUM) { - double cache_size_mb = cache_size_datum.as_num(); - if (cache_size_mb * MEGABYTE > - static_cast(std::numeric_limits::max())) { - *error_out = admin_err_t{ - "In `cache_size_mb`: Value is too big.", - query_state_t::FAILED}; - return false; - } - if (cache_size_mb < 0) { - *error_out = admin_err_t{ - "In `cache_size_mb`: Cache size cannot be negative.", - query_state_t::FAILED}; - return false; - } - server_config_out->cache_size_bytes = - optional(cache_size_mb * MEGABYTE); - } else { - *error_out = admin_err_t{ - "In `cache_size_mb`: Expected a number or 'auto', got " - + cache_size_datum.print(), - query_state_t::FAILED}; - return false; - } - - if (!converter.check_no_extra_keys(error_out)) { - return false; - } - - return true; -} - -server_config_artificial_table_backend_t::server_config_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - watchable_map_t *_directory, - server_config_client_t *_server_config_client) - : common_server_artificial_table_backend_t( - name_string_t::guarantee_valid("server_config"), - rdb_context, - name_resolver, - _server_config_client, - _directory) { -} - -server_config_artificial_table_backend_t::~server_config_artificial_table_backend_t() { - begin_changefeed_destruction(); -} - -bool server_config_artificial_table_backend_t::format_row( - UNUSED auth::user_context_t const &user_context, - server_id_t const & server_id, - UNUSED peer_id_t const & peer_id, - cluster_directory_metadata_t const & metadata, - UNUSED signal_t *interruptor_on_home, - ql::datum_t *row_out, - UNUSED admin_err_t *error_out) { - ql::datum_object_builder_t builder; - - builder.overwrite("name", - convert_name_to_datum(metadata.server_config.config.name)); - builder.overwrite("id", convert_uuid_to_datum(server_id.get_uuid())); - builder.overwrite("tags", convert_set_to_datum( - &convert_name_to_datum, metadata.server_config.config.tags)); - - optional cache_size_bytes = - metadata.server_config.config.cache_size_bytes; - if (static_cast(cache_size_bytes)) { - builder.overwrite("cache_size_mb", - ql::datum_t(static_cast(*cache_size_bytes) / MEGABYTE)); - } else { - builder.overwrite("cache_size_mb", ql::datum_t("auto")); - } - - *row_out = std::move(builder).to_datum(); - - return true; -} - -bool server_config_artificial_table_backend_t::write_row( - UNUSED auth::user_context_t const &user_context, - ql::datum_t primary_key, - UNUSED bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor_on_caller, - admin_err_t *error_out) { - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - new_mutex_in_line_t write_mutex_in_line(&write_mutex); - wait_interruptible(write_mutex_in_line.acq_signal(), &interruptor_on_home); - server_id_t server_id; - peer_id_t peer_id; - cluster_directory_metadata_t metadata; - if (!lookup(primary_key, &server_id, &peer_id, &metadata)) { - if (new_value_inout->has()) { - *error_out = admin_err_t{"It's illegal to insert new rows into the " - "`rethinkdb.server_config` system table.", - query_state_t::FAILED}; - return false; - } else { - /* The user is re-deleting an already-absent row. OK. */ - return true; - } - } - if (!new_value_inout->has()) { - *error_out = admin_err_t{ - "It's illegal to delete rows from the `rethinkdb.server_config` " - "system table.", - query_state_t::FAILED}; - return false; - } - server_id_t new_server_id; - server_config_t new_server_config; - if (!convert_server_config_from_datum(*new_value_inout, &new_server_id, - &new_server_config, error_out)) { - error_out->msg = "The row you're trying to put into `rethinkdb.server_config` " - "has the wrong format. " + error_out->msg; - return false; - } - guarantee(server_id == new_server_id, "artificial_table_t should ensure that " - "primary key is unchanged."); - if (!server_config_client->set_config(server_id, metadata.server_config.config.name, - new_server_config, &interruptor_on_home, error_out)) { - return false; - } - return true; -} - diff --git a/src/clustering/administration/servers/server_config.hpp b/src/clustering/administration/servers/server_config.hpp deleted file mode 100644 index 4008fa717d5..00000000000 --- a/src/clustering/administration/servers/server_config.hpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_SERVERS_SERVER_CONFIG_HPP_ -#define CLUSTERING_ADMINISTRATION_SERVERS_SERVER_CONFIG_HPP_ - -#include -#include - -#include "clustering/administration/servers/server_common.hpp" -#include "clustering/administration/servers/server_metadata.hpp" -#include "rdb_protocol/artificial_table/backend.hpp" -#include "rpc/semilattice/view.hpp" - -class server_config_client_t; - -class server_config_artificial_table_backend_t : - public common_server_artificial_table_backend_t -{ -public: - server_config_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - watchable_map_t *_directory, - server_config_client_t *_server_config_client); - ~server_config_artificial_table_backend_t(); - - bool write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor_on_caller, - admin_err_t *error_out); - -private: - bool format_row( - auth::user_context_t const &user_context, - server_id_t const & server_id, - peer_id_t const & peer_id, - cluster_directory_metadata_t const & metadata, - signal_t *interruptor_on_home, - ql::datum_t *row_out, - admin_err_t *error_out); - - /* All writes to this pseudo-table must acquire this mutex. This makes it impossible - for multiple concurrent changes via this one server to cause a name conflict. Name - conflicts are still possible if changes are made via multiple servers. */ - new_mutex_t write_mutex; -}; - -#endif /* CLUSTERING_ADMINISTRATION_SERVERS_SERVER_CONFIG_HPP_ */ - diff --git a/src/clustering/administration/servers/server_metadata.cc b/src/clustering/administration/servers/server_metadata.cc deleted file mode 100644 index 7778754c619..00000000000 --- a/src/clustering/administration/servers/server_metadata.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/servers/server_metadata.hpp" - -#include "containers/archive/stl_types.hpp" -#include "logger.hpp" - -RDB_IMPL_SERIALIZABLE_3_SINCE_v2_1(server_config_t, name, tags, cache_size_bytes); -RDB_IMPL_EQUALITY_COMPARABLE_3(server_config_t, name, tags, cache_size_bytes); - -RDB_IMPL_SERIALIZABLE_2_SINCE_v2_1(server_config_versioned_t, config, version); -RDB_IMPL_EQUALITY_COMPARABLE_2(server_config_versioned_t, config, version); - -RDB_IMPL_SERIALIZABLE_1_SINCE_v2_1(server_name_map_t, names); -RDB_IMPL_EQUALITY_COMPARABLE_1(server_name_map_t, names); - -RDB_IMPL_SERIALIZABLE_1(server_config_business_card_t, set_config_addr); -INSTANTIATE_SERIALIZABLE_FOR_CLUSTER(server_config_business_card_t); - -name_string_t server_name_map_t::get(const server_id_t &sid) const { - auto it = names.find(sid); - if (it == names.end()) { - /* This is a crash that's relatively likely to come up in production, simply - because the code paths for copying around the server names are easy to get - wrong. So in release mode we degrade gracefully. */ - std::string found; - for (const auto &pair : names) { - if (!found.empty()) { - found += ", "; - } - found += uuid_to_str(pair.first.get_uuid()) + "=" + - std::string(pair.second.second.c_str()); - } - if (found.empty()) { - found = "(none)"; - } -#ifndef NDEBUG - crash("server_name_map doesn't contain the expected server. expected: %s found: " - "%s", sid.print().c_str(), found.c_str()); -#else - logERR("Internal error: Couldn't find server name for server ID. Expected: %s " - "Found: %s Please file a bug report.", sid.print().c_str(), - found.c_str()); - return name_string_t::guarantee_valid("__unknown_server__"); -#endif /* NDEBUG */ - } else { - return it->second.second; - } -} - diff --git a/src/clustering/administration/servers/server_metadata.hpp b/src/clustering/administration/servers/server_metadata.hpp deleted file mode 100644 index ce57bcaf459..00000000000 --- a/src/clustering/administration/servers/server_metadata.hpp +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_SERVERS_SERVER_METADATA_HPP_ -#define CLUSTERING_ADMINISTRATION_SERVERS_SERVER_METADATA_HPP_ - -#include -#include -#include - -#include "containers/name_string.hpp" -#include "rpc/connectivity/server_id.hpp" -#include "rpc/mailbox/typed.hpp" -#include "rpc/semilattice/joins/deletable.hpp" -#include "rpc/semilattice/joins/macros.hpp" -#include "rpc/semilattice/joins/map.hpp" -#include "rpc/semilattice/joins/versioned.hpp" -#include "rpc/serialize_macros.hpp" -#include "utils.hpp" - -class server_config_t { -public: - name_string_t name; - std::set tags; - optional cache_size_bytes; -}; - -RDB_DECLARE_SERIALIZABLE(server_config_t); -RDB_DECLARE_EQUALITY_COMPARABLE(server_config_t); - -typedef uint64_t server_config_version_t; - -class server_config_versioned_t { -public: - server_config_t config; - server_config_version_t version; -}; - -RDB_DECLARE_SERIALIZABLE(server_config_versioned_t); -RDB_DECLARE_EQUALITY_COMPARABLE(server_config_versioned_t); - -class server_name_map_t { -public: - name_string_t get(const server_id_t &sid) const; - std::map > names; -}; - -RDB_DECLARE_SERIALIZABLE(server_name_map_t); -RDB_DECLARE_EQUALITY_COMPARABLE(server_name_map_t); - -class server_config_business_card_t { -public: - /* On success, the reply will be an empty string and the version of the new change. - On failure, the reply will be zero and an error string. */ - typedef mailbox_t< - server_config_t, - mailbox_addr_t - > set_config_mailbox_t; - set_config_mailbox_t::address_t set_config_addr; -}; - -RDB_DECLARE_SERIALIZABLE(server_config_business_card_t); - -#endif /* CLUSTERING_ADMINISTRATION_SERVERS_SERVER_METADATA_HPP_ */ diff --git a/src/clustering/administration/servers/server_status.cc b/src/clustering/administration/servers/server_status.cc deleted file mode 100644 index 6d5398dc3ad..00000000000 --- a/src/clustering/administration/servers/server_status.cc +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/servers/server_status.hpp" - -#include "clustering/administration/admin_op_exc.hpp" -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/main/watchable_fields.hpp" -#include "clustering/administration/servers/config_client.hpp" - -ql::datum_t convert_ip_to_datum(const ip_address_t &ip) { - return ql::datum_t(datum_string_t(ip.to_string())); -} - -ql::datum_t convert_host_and_port_to_datum(const host_and_port_t &x) { - ql::datum_object_builder_t builder; - builder.overwrite("host", ql::datum_t(datum_string_t(x.host()))); - builder.overwrite("port", convert_port_to_datum(x.port().value())); - return std::move(builder).to_datum(); -} - -server_status_artificial_table_backend_t::server_status_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - watchable_map_t *_directory, - server_config_client_t *_server_config_client, - admin_identifier_format_t _admin_format) - : common_server_artificial_table_backend_t( - name_string_t::guarantee_valid("server_status"), - rdb_context, - name_resolver, - _server_config_client, - _directory), - server_config_client(_server_config_client), - admin_format(_admin_format), - directory_subs(_directory, - [&](const peer_id_t &peer, const cluster_directory_metadata_t *metadata) { - if (metadata == nullptr) { - connect_times.erase(peer); - } else { - if (connect_times.count(peer) == 0) { - connect_times.insert(std::make_pair(peer, current_microtime())); - } - } - }, initial_call_t::YES) { -} - -server_status_artificial_table_backend_t::~server_status_artificial_table_backend_t() { - begin_changefeed_destruction(); -} - -bool server_status_artificial_table_backend_t::format_row( - UNUSED auth::user_context_t const &user_context, - server_id_t const & server_id, - peer_id_t const & peer_id, - cluster_directory_metadata_t const & metadata, - UNUSED signal_t *interruptor_on_home, - ql::datum_t *row_out, - UNUSED admin_err_t *error_out) { - ql::datum_object_builder_t builder; - builder.overwrite("name", - convert_name_to_datum(metadata.server_config.config.name)); - builder.overwrite("id", convert_server_id_to_datum(server_id)); - - ql::datum_object_builder_t proc_builder; - proc_builder.overwrite("time_started", - convert_microtime_to_datum(metadata.proc.time_started)); - proc_builder.overwrite("version", - ql::datum_t(datum_string_t(metadata.proc.version))); - proc_builder.overwrite("pid", ql::datum_t(static_cast(metadata.proc.pid))); - proc_builder.overwrite("argv", - convert_vector_to_datum( - &convert_string_to_datum, - metadata.proc.argv)); - proc_builder.overwrite("cache_size_mb", ql::datum_t( - static_cast(metadata.actual_cache_size_bytes) / MEGABYTE)); - builder.overwrite("process", std::move(proc_builder).to_datum()); - - ASSERT_NO_CORO_WAITING; - server_config_client->assert_thread(); - const server_connectivity_t& connect = - server_config_client->get_server_connectivity(); - ql::datum_object_builder_t net_builder; - ql::datum_object_builder_t server_connect_builder; - - for (auto pair : connect.all_servers) { - ql::datum_t server_name_or_uuid; - if (!convert_connected_server_id_to_datum( - pair.first, - admin_format, - server_config_client, - &server_name_or_uuid, - nullptr)) { - // If we can't resolve the name, use uuid - server_name_or_uuid = ql::datum_t( - datum_string_t(pair.first.print())); - } - if (server_id != pair.first) { - bool is_connected = false; - const auto server_it = connect.connected_to.find(server_id); - if (server_it != connect.connected_to.end() && - server_it->second.count(pair.first) > 0) { - is_connected = true; - } - bool conflict = - server_connect_builder.add( - server_name_or_uuid.as_str(), - ql::datum_t::boolean(is_connected)); - if (admin_format == admin_identifier_format_t::uuid) { - guarantee(!conflict); - } else { - if (conflict) { - std::string duplicate_name = server_name_or_uuid.as_str().to_std() - + "__conflict__" - + pair.first.print(); - std::replace(duplicate_name.begin(), duplicate_name.end(), '-', '_'); - bool retry_conflict = - server_connect_builder.add( - datum_string_t(duplicate_name), - ql::datum_t::boolean(is_connected)); - rcheck_toplevel(!retry_conflict, - ql::base_exc_t::INTERNAL, - "A server already has the duplicate name " - + duplicate_name); - } - } - } - } - net_builder.overwrite("connected_to", - std::move(server_connect_builder).to_datum()); - net_builder.overwrite("hostname", - ql::datum_t(datum_string_t(metadata.proc.hostname))); - net_builder.overwrite("cluster_port", - convert_port_to_datum(metadata.proc.cluster_port)); - net_builder.overwrite("reql_port", - convert_port_to_datum(metadata.proc.reql_port)); - net_builder.overwrite("http_admin_port", - static_cast(metadata.proc.http_admin_port) - ? convert_port_to_datum(*metadata.proc.http_admin_port) - : ql::datum_t("")); - net_builder.overwrite("canonical_addresses", - convert_set_to_datum( - &convert_host_and_port_to_datum, - metadata.proc.canonical_addresses)); - { - auto conn_time_it = connect_times.find(peer_id); - if (conn_time_it != connect_times.end()) { - net_builder.overwrite("time_connected", - convert_microtime_to_datum(conn_time_it->second)); - } else { - net_builder.overwrite("time_connected", - convert_microtime_to_datum(current_microtime())); - } - } - builder.overwrite("network", std::move(net_builder).to_datum()); - - *row_out = std::move(builder).to_datum(); - return true; -} - -bool server_status_artificial_table_backend_t::write_row( - UNUSED auth::user_context_t const &user_context, - UNUSED ql::datum_t primary_key, - UNUSED bool pkey_was_autogenerated, - UNUSED ql::datum_t *new_value_inout, - UNUSED signal_t *interruptor_on_caller, - admin_err_t *error_out) { - *error_out = admin_err_t{ - "It's illegal to write to the `rethinkdb.server_status` table.", - query_state_t::FAILED}; - return false; -} - diff --git a/src/clustering/administration/servers/server_status.hpp b/src/clustering/administration/servers/server_status.hpp deleted file mode 100644 index d08f4c45baa..00000000000 --- a/src/clustering/administration/servers/server_status.hpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_SERVERS_SERVER_STATUS_HPP_ -#define CLUSTERING_ADMINISTRATION_SERVERS_SERVER_STATUS_HPP_ - -#include -#include - -#include "clustering/administration/servers/server_common.hpp" -#include "clustering/administration/servers/server_metadata.hpp" -#include "rdb_protocol/artificial_table/backend.hpp" -#include "rpc/semilattice/view.hpp" - -class server_config_client_t; - -class server_status_artificial_table_backend_t : - public common_server_artificial_table_backend_t -{ -public: - server_status_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - watchable_map_t *_directory, - server_config_client_t *_server_config_client, - admin_identifier_format_t _admin_format); - ~server_status_artificial_table_backend_t(); - - bool write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor_on_caller, - admin_err_t *error_out); - -private: - bool format_row( - auth::user_context_t const &user_context, - server_id_t const & server_id, - peer_id_t const & peer_id, - cluster_directory_metadata_t const & metadata, - signal_t *interruptor_on_home, - ql::datum_t *row_out, - admin_err_t *error_out); - - std::map connect_times; - - server_config_client_t *server_config_client; - admin_identifier_format_t admin_format; - /* We use `directory_subs` to note when a server first connects. */ - watchable_map_t::all_subs_t directory_subs; -}; - -#endif /* CLUSTERING_ADMINISTRATION_SERVERS_SERVER_STATUS_HPP_ */ - diff --git a/src/clustering/administration/stats/debug_stats_backend.cc b/src/clustering/administration/stats/debug_stats_backend.cc deleted file mode 100644 index d428075cd04..00000000000 --- a/src/clustering/administration/stats/debug_stats_backend.cc +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/stats/debug_stats_backend.hpp" - -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "clustering/administration/main/watchable_fields.hpp" - -debug_stats_artificial_table_backend_t::debug_stats_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - watchable_map_t *_directory_view, - server_config_client_t *_server_config_client, - mailbox_manager_t *_mailbox_manager) - : common_server_artificial_table_backend_t( - name_string_t::guarantee_valid("_debug_stats"), - rdb_context, - name_resolver, - _server_config_client, - _directory_view), - directory_view(_directory_view), - mailbox_manager(_mailbox_manager) { -} - -debug_stats_artificial_table_backend_t::~debug_stats_artificial_table_backend_t() { - begin_changefeed_destruction(); -} - -bool debug_stats_artificial_table_backend_t::write_row( - auth::user_context_t const &user_context, - UNUSED ql::datum_t primary_key, - UNUSED bool pkey_was_autogenerated, - UNUSED ql::datum_t *new_value_inout, - UNUSED signal_t *interruptor_on_caller, - admin_err_t *error_out) { - user_context.require_admin_user(); - - *error_out = admin_err_t{ - "It's illegal to write to the `rethinkdb._debug_stats` table.", - query_state_t::FAILED}; - return false; -} - -bool debug_stats_artificial_table_backend_t::format_row( - auth::user_context_t const &user_context, - server_id_t const & server_id, - UNUSED peer_id_t const & peer_id, - cluster_directory_metadata_t const & metadata, - signal_t *interruptor_on_home, - ql::datum_t *row_out, - UNUSED admin_err_t *error_out) { - user_context.require_admin_user(); - - ql::datum_object_builder_t builder; - builder.overwrite("name", convert_name_to_datum( - metadata.server_config.config.name)); - builder.overwrite("id", convert_uuid_to_datum(server_id.get_uuid())); - - ql::datum_t stats; - admin_err_t stats_error; - if (stats_for_server(server_id, interruptor_on_home, &stats, &stats_error)) { - builder.overwrite("stats", stats); - } else { - builder.overwrite("error", ql::datum_t(datum_string_t(stats_error.msg))); - } - - *row_out = std::move(builder).to_datum(); - return true; -} - -bool debug_stats_artificial_table_backend_t::stats_for_server( - const server_id_t &server_id, - signal_t *interruptor_on_home, - ql::datum_t *stats_out, - admin_err_t *error_out) { - optional peer_id = - server_config_client->get_server_to_peer_map()->get_key(server_id); - if (!peer_id.has_value()) { - *error_out = admin_err_t{"Server is not connected.", query_state_t::FAILED}; - return false; - } - - get_stats_mailbox_address_t request_addr; - directory_view->read_key(*peer_id, [&](const cluster_directory_metadata_t *md) { - if (md != nullptr) { - request_addr = md->get_stats_mailbox_address; - } - }); - if (request_addr.is_nil()) { - *error_out = admin_err_t{"Server is not connected.", query_state_t::FAILED}; - return false; - } - - /* Make a filter that includes everything */ - std::set > filter; - filter.insert(std::vector()); - - return fetch_stats_from_server( - mailbox_manager, - request_addr, - filter, - interruptor_on_home, - stats_out, - error_out); -} - diff --git a/src/clustering/administration/stats/debug_stats_backend.hpp b/src/clustering/administration/stats/debug_stats_backend.hpp deleted file mode 100644 index 23360afd7b3..00000000000 --- a/src/clustering/administration/stats/debug_stats_backend.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_STATS_DEBUG_STATS_BACKEND_HPP_ -#define CLUSTERING_ADMINISTRATION_STATS_DEBUG_STATS_BACKEND_HPP_ - -#include -#include - -#include "clustering/administration/servers/server_common.hpp" -#include "clustering/administration/servers/server_metadata.hpp" -#include "clustering/administration/stats/stat_manager.hpp" -#include "rdb_protocol/artificial_table/backend.hpp" -#include "rpc/semilattice/view.hpp" - -class server_config_client_t; - -class debug_stats_artificial_table_backend_t : - public common_server_artificial_table_backend_t -{ -public: - debug_stats_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - watchable_map_t *_directory, - server_config_client_t *_server_config_client, - mailbox_manager_t *_mailbox_manager); - ~debug_stats_artificial_table_backend_t(); - - bool write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor_on_caller, - admin_err_t *error_out); - -private: - bool format_row( - auth::user_context_t const &user_context, - server_id_t const & server_id, - peer_id_t const & peer_id, - cluster_directory_metadata_t const & metadata, - signal_t *interruptor_on_home, - ql::datum_t *row_out, - admin_err_t *error_out); - - bool stats_for_server( - const server_id_t &server_id, - signal_t *interruptor_on_home, - ql::datum_t *stats_out, - admin_err_t *error_out); - - watchable_map_t *directory_view; - mailbox_manager_t *mailbox_manager; -}; - -#endif /* CLUSTERING_ADMINISTRATION_SERVERS_DEBUG_STATS_BACKEND_HPP_ */ - diff --git a/src/clustering/administration/stats/request.cc b/src/clustering/administration/stats/request.cc deleted file mode 100644 index 6e1c95f0d4c..00000000000 --- a/src/clustering/administration/stats/request.cc +++ /dev/null @@ -1,578 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/stats/request.hpp" - -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "clustering/table_manager/table_meta_client.hpp" - -const char *cluster_stats_request_t::cluster_request_type = "cluster"; -const char *server_stats_request_t::server_request_type = "server"; -const char *table_stats_request_t::table_request_type = "table"; -const char *table_server_stats_request_t::table_server_request_type = "table_server"; - -// Macros to make converting stats easier and more consistent -// The name of the field in the stats struct will be the same in the datum result -#define ADD_STAT(BUILDER, SUB_STATS, NAME) \ - (BUILDER).overwrite(#NAME, ql::datum_t((SUB_STATS).NAME)) - -#define ADD_CLUSTER_SERVER_STAT(BUILDER, STATS, NAME) \ - (BUILDER).overwrite(#NAME, ql::datum_t( \ - (STATS).accumulate(&parsed_stats_t::server_stats_t::NAME))); - -#define ADD_CLUSTER_TABLE_STAT(BUILDER, STATS, NAME) \ - (BUILDER).overwrite(#NAME, ql::datum_t( \ - (STATS).accumulate(&parsed_stats_t::table_stats_t::NAME))); - -#define ADD_TABLE_STAT(BUILDER, STATS, TABLE, NAME) \ - (BUILDER).overwrite(#NAME, ql::datum_t( \ - (STATS).accumulate_table(TABLE, &parsed_stats_t::table_stats_t::NAME))); - -#define ADD_SERVER_STAT(BUILDER, STATS, SERVER, NAME) \ - (BUILDER).overwrite(#NAME, ql::datum_t( \ - (STATS).accumulate_server(SERVER, &parsed_stats_t::table_stats_t::NAME))); - -parsed_stats_t::server_stats_t::server_stats_t() : - responsive(false), - queries_per_sec(0), queries_total(0), - client_connections(0), clients_active(0) { } - -parsed_stats_t::table_stats_t::table_stats_t() : - read_docs_per_sec(0), read_docs_total(0), - written_docs_per_sec(0), written_docs_total(0), - in_use_bytes(0), metadata_bytes(0), data_bytes(0), - garbage_bytes(0), preallocated_bytes(0), - read_bytes_per_sec(0), read_bytes_total(0), - written_bytes_per_sec(0), written_bytes_total(0) { } - -parsed_stats_t::parsed_stats_t(const std::vector &stats) { - for (auto const &s : stats) { - if (!s.has()) { - continue; - } - - ql::datum_t server_id_datum = s.get_field("server_id", - ql::throw_bool_t::NOTHROW); - server_id_t server_id; - admin_err_t error; - bool res = convert_server_id_from_datum(server_id_datum, &server_id, &error); - guarantee(res, "Stats contained an invalid server id. %s", error.msg.c_str()); - - server_stats_t &serv_stats = servers[server_id]; - - serv_stats.responsive = true; - r_sanity_check(s.get_type() == ql::datum_t::R_OBJECT); - for (size_t i = 0; i < s.obj_size(); ++i) { - std::pair perf_pair = s.get_pair(i); - if (perf_pair.first == "query_engine") { - store_query_engine_stats(perf_pair.second, &serv_stats); - } else { - namespace_id_t table_id; - res = str_to_uuid(perf_pair.first.to_std(), &table_id); - if (res) { - store_table_stats(table_id, perf_pair.second, &serv_stats); - } - } - } - } -} - -void parsed_stats_t::store_perfmon_value(const ql::datum_t &perf, - const std::string &key, - double *value_out) { - rassert(*value_out == 0); - ql::datum_t v = perf.get_field(key.c_str(), ql::throw_bool_t::NOTHROW); - // If the value is missing, it should mean that the given stat was not specified - // in the request, and therefore isn't needed - so we ignore any missing values. - if (v.has()) { - r_sanity_check(v.get_type() == ql::datum_t::R_NUM); - *value_out = v.as_num(); - } -} - -void parsed_stats_t::add_perfmon_value(const ql::datum_t &perf, - const std::string &key, - double *value_out) { - ql::datum_t v = perf.get_field(key.c_str(), ql::throw_bool_t::NOTHROW); - // If the value is missing, it should mean that the given stat was not specified - // in the request, and therefore isn't needed - so we ignore any missing values. - if (v.has()) { - r_sanity_check(v.get_type() == ql::datum_t::R_NUM); - *value_out += v.as_num(); - } -} - -void parsed_stats_t::store_shard_values(const ql::datum_t &shard_perf, - table_stats_t *stats_out) { - r_sanity_check(shard_perf.get_type() == ql::datum_t::R_OBJECT); - for (size_t i = 0; i < shard_perf.obj_size(); ++i) { - std::pair pair = shard_perf.get_pair(i); - if (pair.first.to_std().find("shard_") == 0) { - r_sanity_check(pair.second.get_type() == ql::datum_t::R_OBJECT); - for (size_t j = 0; j < pair.second.obj_size(); ++j) { - std::pair sub_pair = pair.second.get_pair(j); - std::string key = sub_pair.first.to_std(); - - if (key.find("btree-") == 0) { - r_sanity_check(sub_pair.second.get_type() == ql::datum_t::R_OBJECT); - add_perfmon_value(sub_pair.second, "keys_read", - &stats_out->read_docs_per_sec); - add_perfmon_value(sub_pair.second, "keys_set", - &stats_out->written_docs_per_sec); - add_perfmon_value(sub_pair.second, "total_keys_read", - &stats_out->read_docs_total); - add_perfmon_value(sub_pair.second, "total_keys_set", - &stats_out->written_docs_total); - } else if (key == "cache") { - add_perfmon_value(sub_pair.second, "in_use_bytes", - &stats_out->in_use_bytes); - } - } - } - } -} - -void parsed_stats_t::store_serializer_values(const ql::datum_t &ser_perf, - table_stats_t *stats_out) { - r_sanity_check(ser_perf.get_type() == ql::datum_t::R_OBJECT); - store_perfmon_value(ser_perf, "serializer_read_bytes_per_sec", - &stats_out->read_bytes_per_sec); - store_perfmon_value(ser_perf, "serializer_read_bytes_total", - &stats_out->read_bytes_total); - store_perfmon_value(ser_perf, "serializer_written_bytes_per_sec", - &stats_out->written_bytes_per_sec); - store_perfmon_value(ser_perf, "serializer_written_bytes_total", - &stats_out->written_bytes_total); - - store_perfmon_value(ser_perf, "serializer_data_extents", - &stats_out->data_bytes); - store_perfmon_value(ser_perf, "serializer_lba_extents", - &stats_out->metadata_bytes); - store_perfmon_value(ser_perf, "serializer_old_garbage_block_bytes", - &stats_out->garbage_bytes); - store_perfmon_value(ser_perf, "serializer_file_size_bytes", - &stats_out->preallocated_bytes); - stats_out->data_bytes = stats_out->data_bytes * DEFAULT_EXTENT_SIZE - stats_out->garbage_bytes; - stats_out->metadata_bytes *= DEFAULT_EXTENT_SIZE; - stats_out->preallocated_bytes -= stats_out->data_bytes + - stats_out->garbage_bytes + stats_out->metadata_bytes; - // The file size might temporarily lack behind if allocated extents haven't - // been written to disk yet. - stats_out->preallocated_bytes = std::max(0.0, stats_out->preallocated_bytes); -} - -void parsed_stats_t::store_query_engine_stats(const ql::datum_t &qe_perf, - server_stats_t *stats_out) { - r_sanity_check(qe_perf.get_type() == ql::datum_t::R_OBJECT); - store_perfmon_value(qe_perf, "queries_per_sec", &stats_out->queries_per_sec); - store_perfmon_value(qe_perf, "queries_total", &stats_out->queries_total); - store_perfmon_value(qe_perf, "client_connections", &stats_out->client_connections); - store_perfmon_value(qe_perf, "clients_active", &stats_out->clients_active); -} - -void parsed_stats_t::store_table_stats(const namespace_id_t &table_id, - const ql::datum_t &table_perf, - server_stats_t *stats_out) { - r_sanity_check(table_perf.get_type() == ql::datum_t::R_OBJECT); - ql::datum_t sers_perf = table_perf.get_field("serializers", - ql::throw_bool_t::NOTHROW); - if (sers_perf.has()) { - r_sanity_check(sers_perf.get_type() == ql::datum_t::R_OBJECT); - table_stats_t &table_stats_out = stats_out->tables[table_id]; - - store_shard_values(sers_perf, &table_stats_out); - - ql::datum_t sub_sers_perf = sers_perf.get_field("serializer", - ql::throw_bool_t::NOTHROW); - if (sub_sers_perf.has()) { - store_serializer_values(sub_sers_perf, &table_stats_out); - } - } -} - -double parsed_stats_t::accumulate(double server_stats_t::*field) const { - double res = 0; - for (auto const &pair : servers) { - res += pair.second.*field; - } - return res; -} - -double parsed_stats_t::accumulate(double table_stats_t::*field) const { - double res = 0; - for (auto const &server_pair : servers) { - for (auto const &table_pair : server_pair.second.tables) { - res += table_pair.second.*field; - } - } - return res; -} - -double parsed_stats_t::accumulate_table(const namespace_id_t &table_id, - double table_stats_t::*field) const { - double res = 0; - for (auto const &server_pair : servers) { - auto const &table_it = server_pair.second.tables.find(table_id); - if (table_it != server_pair.second.tables.end()) { - res += table_it->second.*field; - } - } - return res; -} - -double parsed_stats_t::accumulate_server(const server_id_t &server_id, - double table_stats_t::*field) const { - double res = 0; - auto const server_it = servers.find(server_id); - r_sanity_check(server_it != servers.end()); - for (auto const &table_pair : server_it->second.tables) { - res += table_pair.second.*field; - } - return res; -} - -bool add_table_fields(const namespace_id_t &table_id, - const cluster_semilattice_metadata_t &metadata, - table_meta_client_t *table_meta_client, - admin_identifier_format_t admin_format, - ql::datum_object_builder_t *builder) { - ql::datum_t db_identifier; - ql::datum_t table_identifier; - if (!convert_table_id_to_datums(table_id, admin_format, metadata, table_meta_client, - &table_identifier, nullptr, &db_identifier, nullptr)) { - return false; // The table was deleted or does not exist - } - builder->overwrite("db", db_identifier); - builder->overwrite("table", table_identifier); - return true; -} - -// ------------------------------------ -// stats_request_t -// ------------------------------------ - -std::set > stats_request_t::global_stats_filter() { - return std::set >( - { {"query_engine"}, - {"[0-9A-Fa-f-]+", "serializers" } }); -} - -std::vector stats_request_t::all_peers( - const std::map &directory) { - std::vector res; - res.reserve(directory.size()); - for (auto const &pair : directory) { - res.push_back(pair.first); - } - return res; -} - -// ------------------------------------ -// cluster_stats_request_t -// ------------------------------------ - -bool cluster_stats_request_t::parse(const ql::datum_t &info, - scoped_ptr_t *request_out) { - rassert(info.get(0).as_str() == get_name()); - r_sanity_check(info.get_type() == ql::datum_t::R_ARRAY); - if (info.arr_size() != 1) { - return false; - } - request_out->init(new cluster_stats_request_t()); - return true; -} - -cluster_stats_request_t::cluster_stats_request_t() { } - -std::set > cluster_stats_request_t::get_filter() const { - return std::set >( - { {"query_engine" }, - {".*", "serializers", "shard_[0-9]+", "btree-.*", "keys_.*" } }); -} - -std::vector cluster_stats_request_t::get_peers( - const std::map &directory, - server_config_client_t *) const { - return all_peers(directory); -} - -bool cluster_stats_request_t::to_datum(const parsed_stats_t &stats, - const metadata_t &, - server_config_client_t *, - table_meta_client_t *, - admin_identifier_format_t, - ql::datum_t *result_out) const { - ql::datum_object_builder_t row_builder; - ql::datum_array_builder_t id_builder(ql::configured_limits_t::unlimited); - id_builder.add(ql::datum_t(get_name())); - row_builder.overwrite("id", std::move(id_builder).to_datum()); - - ql::datum_object_builder_t qe_builder; - ADD_CLUSTER_SERVER_STAT(qe_builder, stats, queries_per_sec); - ADD_CLUSTER_SERVER_STAT(qe_builder, stats, client_connections); - ADD_CLUSTER_SERVER_STAT(qe_builder, stats, clients_active); - ADD_CLUSTER_TABLE_STAT(qe_builder, stats, read_docs_per_sec); - ADD_CLUSTER_TABLE_STAT(qe_builder, stats, written_docs_per_sec); - row_builder.overwrite("query_engine", std::move(qe_builder).to_datum()); - - *result_out = std::move(row_builder).to_datum(); - return true; -} - -// ------------------------------------ -// table_stats_request_t -// ------------------------------------ - -bool table_stats_request_t::parse(const ql::datum_t &info, - scoped_ptr_t *request_out) { - rassert(info.get(0).as_str() == get_name()); - r_sanity_check(info.get_type() == ql::datum_t::R_ARRAY); - if (info.arr_size() != 2) { - return false; - } - - admin_err_t dummy_error; - namespace_id_t t; - if (!convert_uuid_from_datum(info.get(1), &t, &dummy_error)) { - return false; - } - request_out->init(new table_stats_request_t(t)); - return true; -} - -table_stats_request_t::table_stats_request_t(const namespace_id_t &_table_id) : - table_id(_table_id) { } - -std::set > table_stats_request_t::get_filter() const { - return std::set >({ - { uuid_to_str(table_id), "serializers", "shard_[0-9]+", "btree-.*", "keys_.*" } - }); -} - -std::vector table_stats_request_t::get_peers( - const std::map &directory, - server_config_client_t *) const { - return all_peers(directory); -} - -bool table_stats_request_t::to_datum(const parsed_stats_t &stats, - const metadata_t &metadata, - server_config_client_t *, - table_meta_client_t *table_meta_client, - admin_identifier_format_t admin_format, - ql::datum_t *result_out) const { - ql::datum_object_builder_t row_builder; - ql::datum_array_builder_t id_builder(ql::configured_limits_t::unlimited); - id_builder.add(ql::datum_t(get_name())); - id_builder.add(convert_uuid_to_datum(table_id)); - row_builder.overwrite("id", std::move(id_builder).to_datum()); - - if (!add_table_fields(table_id, metadata, table_meta_client, admin_format, - &row_builder)) { - return false; - } - - ql::datum_object_builder_t qe_builder; - ADD_TABLE_STAT(qe_builder, stats, table_id, read_docs_per_sec); - ADD_TABLE_STAT(qe_builder, stats, table_id, written_docs_per_sec); - row_builder.overwrite("query_engine", std::move(qe_builder).to_datum()); - - *result_out = std::move(row_builder).to_datum(); - return true; -} - -// ------------------------------------ -// server_stats_request_t -// ------------------------------------ - -bool server_stats_request_t::parse(const ql::datum_t &info, - scoped_ptr_t *request_out) { - rassert(info.get(0).as_str() == get_name()); - r_sanity_check(info.get_type() == ql::datum_t::R_ARRAY); - if (info.arr_size() != 2) { - return false; - } - - server_id_t s; - admin_err_t dummy_error; - if (!convert_server_id_from_datum(info.get(1), &s, &dummy_error)) { - return false; - } - request_out->init(new server_stats_request_t(s)); - return true; -} - -server_stats_request_t::server_stats_request_t(const server_id_t &_server_id) : - server_id(_server_id) { } - -std::set > server_stats_request_t::get_filter() const { - return std::set >( - { {"query_engine"}, - {".*", "serializers", "shard_[0-9]+", "btree-.*" } }); -} - -std::vector server_stats_request_t::get_peers( - const std::map &, - server_config_client_t *server_config_client) const { - optional peer = - server_config_client->get_server_to_peer_map()->get_key(server_id); - if (!peer.has_value()) { - return std::vector(); - } - return std::vector(1, peer.get()); -} - -bool server_stats_request_t::to_datum(const parsed_stats_t &stats, - const metadata_t &, - server_config_client_t *server_config_client, - UNUSED table_meta_client_t *table_meta_client, - admin_identifier_format_t admin_format, - ql::datum_t *result_out) const { - ql::datum_object_builder_t row_builder; - ql::datum_array_builder_t id_builder(ql::configured_limits_t::unlimited); - id_builder.add(ql::datum_t(get_name())); - id_builder.add(convert_uuid_to_datum(server_id.get_uuid())); - row_builder.overwrite("id", std::move(id_builder).to_datum()); - - ql::datum_t server_identifier; - if (!convert_connected_server_id_to_datum(server_id, admin_format, - server_config_client, &server_identifier, nullptr)) { - return false; - } - row_builder.overwrite("server", server_identifier); - - auto const &server_it = stats.servers.find(server_id); - if (server_it == stats.servers.end() || - !server_it->second.responsive) { - row_builder.overwrite("error", - ql::datum_t("Timed out. Unable to retrieve stats.")); - } else { - const parsed_stats_t::server_stats_t &server_stats = server_it->second; - ql::datum_object_builder_t qe_builder; - ADD_STAT(qe_builder, server_stats, client_connections); - ADD_STAT(qe_builder, server_stats, clients_active); - ADD_STAT(qe_builder, server_stats, queries_per_sec); - ADD_STAT(qe_builder, server_stats, queries_total); - ADD_SERVER_STAT(qe_builder, stats, server_id, read_docs_per_sec); - ADD_SERVER_STAT(qe_builder, stats, server_id, read_docs_total); - ADD_SERVER_STAT(qe_builder, stats, server_id, written_docs_per_sec); - ADD_SERVER_STAT(qe_builder, stats, server_id, written_docs_total); - row_builder.overwrite("query_engine", std::move(qe_builder).to_datum()); - } - *result_out = std::move(row_builder).to_datum(); - return true; -} - -// ------------------------------------ -// table_server_stats_request_t -// ------------------------------------ - -bool table_server_stats_request_t::parse(const ql::datum_t &info, - scoped_ptr_t *request_out) { - rassert(info.get(0).as_str() == get_name()); - r_sanity_check(info.get_type() == ql::datum_t::R_ARRAY); - if (info.arr_size() != 3) { - return false; - } - - admin_err_t dummy_error; - namespace_id_t t; - server_id_t s; - if (!convert_uuid_from_datum(info.get(1), &t, &dummy_error)) { - return false; - } - if (!convert_server_id_from_datum(info.get(2), &s, &dummy_error)) { - return false; - } - request_out->init(new table_server_stats_request_t(t, s)); - return true; -} - -table_server_stats_request_t::table_server_stats_request_t( - const namespace_id_t &_table_id, - const server_id_t &_server_id) : - table_id(_table_id), server_id(_server_id) { } - -std::set > table_server_stats_request_t::get_filter() const { - return std::set >({ - { uuid_to_str(table_id), "serializers" } }); -} - -std::vector table_server_stats_request_t::get_peers( - const std::map &, - server_config_client_t *server_config_client) const { - optional peer = - server_config_client->get_server_to_peer_map()->get_key(server_id); - if (!peer.has_value()) { - return std::vector(); - } - return std::vector(1, peer.get()); -} - -bool table_server_stats_request_t::to_datum(const parsed_stats_t &stats, - const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t admin_format, - ql::datum_t *result_out) const { - ql::datum_object_builder_t row_builder; - ql::datum_array_builder_t id_builder(ql::configured_limits_t::unlimited); - id_builder.add(ql::datum_t(get_name())); - id_builder.add(convert_uuid_to_datum(table_id)); - id_builder.add(convert_uuid_to_datum(server_id.get_uuid())); - row_builder.overwrite("id", std::move(id_builder).to_datum()); - - ql::datum_t server_identifier; - if (!convert_connected_server_id_to_datum(server_id, admin_format, - server_config_client, &server_identifier, nullptr)) { - return false; - } - row_builder.overwrite("server", server_identifier); - - if (!add_table_fields(table_id, metadata, table_meta_client, admin_format, - &row_builder)) { - return false; - } - - auto const &server_it = stats.servers.find(server_id); - if (server_it == stats.servers.end() || - !server_it->second.responsive) { - row_builder.overwrite("error", ql::datum_t("Timed out. Unable to retrieve stats.")); - } else { - auto const table_it = server_it->second.tables.find(table_id); - parsed_stats_t::table_stats_t table_stats; - if (table_it != server_it->second.tables.end()) { - table_stats = table_it->second; - } - - ql::datum_object_builder_t qe_builder; - ADD_STAT(qe_builder, table_stats, read_docs_per_sec); - ADD_STAT(qe_builder, table_stats, read_docs_total); - ADD_STAT(qe_builder, table_stats, written_docs_per_sec); - ADD_STAT(qe_builder, table_stats, written_docs_total); - - ql::datum_object_builder_t se_cache_builder; - ADD_STAT(se_cache_builder, table_stats, in_use_bytes); - - ql::datum_object_builder_t se_disk_space_builder; - ADD_STAT(se_disk_space_builder, table_stats, metadata_bytes); - ADD_STAT(se_disk_space_builder, table_stats, data_bytes); - ADD_STAT(se_disk_space_builder, table_stats, garbage_bytes); - ADD_STAT(se_disk_space_builder, table_stats, preallocated_bytes); - - ql::datum_object_builder_t se_disk_builder; - ADD_STAT(se_disk_builder, table_stats, read_bytes_per_sec); - ADD_STAT(se_disk_builder, table_stats, read_bytes_total); - ADD_STAT(se_disk_builder, table_stats, written_bytes_per_sec); - ADD_STAT(se_disk_builder, table_stats, written_bytes_total); - se_disk_builder.overwrite("space_usage", std::move(se_disk_space_builder).to_datum()); - - ql::datum_object_builder_t se_builder; - se_builder.overwrite("cache", std::move(se_cache_builder).to_datum()); - se_builder.overwrite("disk", std::move(se_disk_builder).to_datum()); - - row_builder.overwrite("query_engine", std::move(qe_builder).to_datum()); - row_builder.overwrite("storage_engine", std::move(se_builder).to_datum()); - } - *result_out = std::move(row_builder).to_datum(); - return true; -} diff --git a/src/clustering/administration/stats/request.hpp b/src/clustering/administration/stats/request.hpp deleted file mode 100644 index 162df811dc5..00000000000 --- a/src/clustering/administration/stats/request.hpp +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_STATS_REQUEST_HPP_ -#define CLUSTERING_ADMINISTRATION_STATS_REQUEST_HPP_ - -#include -#include -#include -#include - -#include "clustering/administration/metadata.hpp" -#include "containers/uuid.hpp" -#include "rdb_protocol/datum.hpp" - -class server_config_client_t; -class table_meta_client_t; - -// A `parsed_stats_t` represents all the values that have been gathered from other -// servers in the cluster. At construction, it parses the given responses to create -// per-server/table stats structures. These stats can later be used to build multiple -// rows in the `stats` table, without performing more requests. -class parsed_stats_t { -public: - struct table_stats_t { - table_stats_t(); - - double read_docs_per_sec; - double read_docs_total; - double written_docs_per_sec; - double written_docs_total; - double in_use_bytes; - double metadata_bytes; - double data_bytes; - double garbage_bytes; - double preallocated_bytes; - double read_bytes_per_sec; - double read_bytes_total; - double written_bytes_per_sec; - double written_bytes_total; - }; - - struct server_stats_t { - server_stats_t(); - - bool responsive; - double queries_per_sec; - double queries_total; - double client_connections; - double clients_active; - - std::map tables; - }; - - explicit parsed_stats_t(const std::vector &stats); - - // Accumulate a field in all servers - double accumulate(double server_stats_t::*field) const; - - // Accumulate a field in all tables (across all servers) - double accumulate(double table_stats_t::*field) const; - - // Accumulate a field in a specific table (across all servers) - double accumulate_table(const namespace_id_t &table_id, - double table_stats_t::*field) const; - - // Accumulate a field in all tables (on a specific server) - double accumulate_server(const server_id_t &server_id, - double table_stats_t::*field) const; - - std::map servers; - -private: - // Adds a given stat value to an existing value, useful for summing stats - // that are spread out across multiple objects. - void add_perfmon_value(const ql::datum_t &perf, - const std::string &key, - double *value_out); - - // Stores a given stat value and asserts that the value is the default (0); - // use `add_perfmon_value` for summing stats from multple sources. - void store_perfmon_value(const ql::datum_t &perf, - const std::string &key, - double *value_out); - - void store_shard_values(const ql::datum_t &shard_perf, - table_stats_t *stats_out); - - void store_serializer_values(const ql::datum_t &ser_perf, - table_stats_t *); - - void store_query_engine_stats(const ql::datum_t &qe_perf, - server_stats_t *stats_out); - - void store_table_stats(const namespace_id_t &table_id, - const ql::datum_t &table_perf, - server_stats_t *stats_out); -}; - -/* A `stats_request_t` encapsulates all the behavior that differentiates between -different types of rows in the `stats` table. First, `get_filter` provides a set of -regular expressions for which stats are needed by the request. Then, `get_peers` will -tell us which peers should be contacted to get the relevant data. Finally, `to_datum` -will take the `parsed_stats_t` obtained from the cross-cluster stats and format it into -the appropriate row format. Each subclass of `stats_request_t` should correspond to a -category within the admin `stats` table. */ -class stats_request_t { -public: - typedef cluster_semilattice_metadata_t metadata_t; - - static std::set > global_stats_filter(); - static std::vector all_peers( - const std::map &directory); - - virtual ~stats_request_t() { } - - // Gets the regex filter to be applied to the perfmons on every server - virtual std::set > get_filter() const = 0; - - // Gets the list of servers/peers the request should be sent to - virtual std::vector get_peers( - const std::map &directory, - server_config_client_t *server_config_client) const = 0; - - // Converts stats from the response into the row format for the stats table - virtual bool to_datum(const parsed_stats_t &stats, - const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t admin_format, - ql::datum_t *result_out) const = 0; -}; - -class cluster_stats_request_t : public stats_request_t { - static const char *cluster_request_type; -public: - static const char *get_name() { return cluster_request_type; } - static bool parse(const ql::datum_t &info, - scoped_ptr_t *request_out); - - cluster_stats_request_t(); - - std::set > get_filter() const; - - std::vector get_peers( - const std::map &directory, - server_config_client_t *server_config_client) const; - - virtual bool to_datum(const parsed_stats_t &stats, - const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t admin_format, - ql::datum_t *result_out) const; -}; - -class table_stats_request_t : public stats_request_t { - static const char *table_request_type; - const namespace_id_t table_id; -public: - - static const char *get_name() { return table_request_type; } - static bool parse(const ql::datum_t &info, - scoped_ptr_t *request_out); - - explicit table_stats_request_t(const namespace_id_t &_table_id); - - std::set > get_filter() const; - - std::vector get_peers( - const std::map &directory, - server_config_client_t *server_config_client) const; - - virtual bool to_datum(const parsed_stats_t &stats, - const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t admin_format, - ql::datum_t *result_out) const; -}; - -class server_stats_request_t : public stats_request_t { - static const char *server_request_type; - const server_id_t server_id; -public: - static const char *get_name() { return server_request_type; } - static bool parse(const ql::datum_t &info, - scoped_ptr_t *request_out); - - explicit server_stats_request_t(const server_id_t &_server_id); - - std::set > get_filter() const; - - std::vector get_peers( - const std::map &directory, - server_config_client_t *server_config_client) const; - - virtual bool to_datum(const parsed_stats_t &stats, - const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t admin_format, - ql::datum_t *result_out) const; -}; - -class table_server_stats_request_t : public stats_request_t { - static const char *table_server_request_type; - const namespace_id_t table_id; - const server_id_t server_id; -public: - static const char *get_name() { return table_server_request_type; } - static bool parse(const ql::datum_t &info, - scoped_ptr_t *request_out); - - table_server_stats_request_t(const namespace_id_t &_table_id, - const server_id_t &_server_id); - - std::set > get_filter() const; - - std::vector get_peers( - const std::map &directory, - server_config_client_t *server_config_client) const; - - virtual bool to_datum(const parsed_stats_t &stats, - const metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t admin_format, - ql::datum_t *result_out) const; -}; - -#endif // CLUSTERING_ADMINISTRATION_STATS_REQUEST_HPP_ diff --git a/src/clustering/administration/stats/stat_manager.cc b/src/clustering/administration/stats/stat_manager.cc deleted file mode 100644 index cb067d87cf4..00000000000 --- a/src/clustering/administration/stats/stat_manager.cc +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/stats/stat_manager.hpp" - -#include - -#include "clustering/administration/datum_adapter.hpp" -#include "concurrency/watchable.hpp" -#include "perfmon/collect.hpp" -#include "perfmon/filter.hpp" -#include "rdb_protocol/serialize_datum.hpp" -#include "rpc/mailbox/disconnect_watcher.hpp" -#include "stl_utils.hpp" - -stat_manager_t::stat_manager_t(mailbox_manager_t* mm, - server_id_t _own_server_id) : - own_server_id(_own_server_id), - mailbox_manager(mm), - get_stats_mailbox(mailbox_manager, - std::bind(&stat_manager_t::on_stats_request, - this, ph::_1, ph::_2, ph::_3)) - { } - -get_stats_mailbox_address_t stat_manager_t::get_address() { - return get_stats_mailbox.get_address(); -} - -void stat_manager_t::on_stats_request( - UNUSED signal_t *interruptor, - const return_address_t& reply_address, - const std::set >& requested_stats) { - perfmon_filter_t request(requested_stats); - ql::datum_t perfmon_result(perfmon_get_stats()); - perfmon_result = request.filter(perfmon_result); - - // Add in our own server id so the other side does not need to perform lookups - ql::datum_object_builder_t stats(perfmon_result); - stats.overwrite("server_id", convert_uuid_to_datum(own_server_id.get_uuid())); - send(mailbox_manager, reply_address, std::move(stats).to_datum()); -} - -bool fetch_stats_from_server( - mailbox_manager_t *mailbox_manager, - const get_stats_mailbox_address_t &request_addr, - const std::set > &filter, - signal_t *interruptor, - ql::datum_t *stats_out, - admin_err_t *error_out) { - cond_t done; - mailbox_t return_mailbox(mailbox_manager, - [&](signal_t *, ql::datum_t s) { - *stats_out = s; - done.pulse(); - }); - - disconnect_watcher_t disconnect_watcher(mailbox_manager, request_addr.get_peer()); - - send(mailbox_manager, request_addr, return_mailbox.get_address(), filter); - - signal_timer_t timeout; - timeout.start(5000); - - wait_any_t waiter(&done, &disconnect_watcher, &timeout); - wait_interruptible(&waiter, interruptor); - - if (disconnect_watcher.is_pulsed()) { - *error_out = admin_err_t{"Server disconnected.", query_state_t::FAILED}; - return false; - } - - if (timeout.is_pulsed()) { - *error_out = admin_err_t{"Stats request timed out.", query_state_t::FAILED}; - return false; - } - - guarantee(done.is_pulsed()); - return true; -} - diff --git a/src/clustering/administration/stats/stat_manager.hpp b/src/clustering/administration/stats/stat_manager.hpp deleted file mode 100644 index e79c7024913..00000000000 --- a/src/clustering/administration/stats/stat_manager.hpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_STATS_STAT_MANAGER_HPP_ -#define CLUSTERING_ADMINISTRATION_STATS_STAT_MANAGER_HPP_ - -#include -#include -#include - -#include "perfmon/types.hpp" -#include "rpc/connectivity/server_id.hpp" -#include "rpc/mailbox/typed.hpp" - -struct admin_err_t; -namespace ql { -class datum_t; -} - -class stat_manager_t { -public: - typedef std::string stat_id_t; - typedef mailbox_addr_t return_address_t; - typedef mailbox_t >> get_stats_mailbox_t; - typedef get_stats_mailbox_t::address_t get_stats_mailbox_address_t; - - explicit stat_manager_t(mailbox_manager_t* mailbox_manager, - server_id_t _own_server_id); - - get_stats_mailbox_address_t get_address(); - -private: - void on_stats_request( - signal_t *interruptor, - const return_address_t& reply_address, - const std::set >& requested_stats); - - server_id_t own_server_id; - mailbox_manager_t *mailbox_manager; - get_stats_mailbox_t get_stats_mailbox; - - DISABLE_COPYING(stat_manager_t); -}; - -typedef stat_manager_t::get_stats_mailbox_t::address_t get_stats_mailbox_address_t; - -bool fetch_stats_from_server( - mailbox_manager_t *mailbox_manager, - const get_stats_mailbox_address_t &request_addr, - const std::set > &filter, - signal_t *interruptor, - ql::datum_t *stats_out, - admin_err_t *error_out); - -#endif /* CLUSTERING_ADMINISTRATION_STATS_STAT_MANAGER_HPP_ */ - diff --git a/src/clustering/administration/stats/stats_backend.cc b/src/clustering/administration/stats/stats_backend.cc deleted file mode 100644 index b42c0c454d9..00000000000 --- a/src/clustering/administration/stats/stats_backend.cc +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/stats/stats_backend.hpp" - -#include "clustering/administration/stats/request.hpp" -#include "clustering/table_manager/table_meta_client.hpp" -#include "concurrency/cross_thread_signal.hpp" - -stats_artificial_table_backend_t::stats_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - const clone_ptr_t > > - &_directory_view, - std::shared_ptr > - _cluster_sl_view, - server_config_client_t *_server_config_client, - table_meta_client_t *_table_meta_client, - mailbox_manager_t *_mailbox_manager, - admin_identifier_format_t _admin_format) : - timer_cfeed_artificial_table_backend_t( - name_string_t::guarantee_valid("stats"), rdb_context, name_resolver), - directory_view(_directory_view), - cluster_sl_view(_cluster_sl_view), - server_config_client(_server_config_client), - table_meta_client(_table_meta_client), - mailbox_manager(_mailbox_manager), - admin_format(_admin_format) { } - -stats_artificial_table_backend_t::~stats_artificial_table_backend_t() { - begin_changefeed_destruction(); -} - -std::string stats_artificial_table_backend_t::get_primary_key_name() { - return std::string("id"); -} - -// Since this is called through pmap, it will not throw an `interrupted_exc_t`. Instead, -// the behavior is undefined if `interruptor` is pulsed. -void stats_artificial_table_backend_t::get_peer_stats( - const peer_id_t &peer, - const std::set > &filter, - ql::datum_t *result_out, - signal_t *interruptor_on_home) { - // Loop up peer in directory - find get stats mailbox - get_stats_mailbox_address_t request_addr; - directory_view->apply_read( - [&](const change_tracking_map_t *dir) { - auto const peer_it = dir->get_inner().find(peer); - if (peer_it != dir->get_inner().end()) { - request_addr = peer_it->second.get_stats_mailbox_address; - } - }); - - if (!request_addr.is_nil()) { - try { - admin_err_t dummy_error; - if (!fetch_stats_from_server(mailbox_manager, request_addr, filter, - interruptor_on_home, result_out, &dummy_error)) { - /* Signal an error with an empty `datum_t` */ - *result_out = ql::datum_t(); - } - } catch (const interrupted_exc_t &) { - /* It doesn't matter what we return */ - } - } -} - -void stats_artificial_table_backend_t::perform_stats_request( - const std::vector &peers, - const std::set > &filter, - std::vector *results_out, - signal_t *interruptor_on_home) { - results_out->resize(peers.size()); - pmap(peers.size(), - [&](int64_t index) { - get_peer_stats(peers[index], filter, &results_out->at(index), - interruptor_on_home); - }); - if (interruptor_on_home->is_pulsed()) { - throw interrupted_exc_t(); - } -} - -// A row is excluded if it fails to convert to a datum - which should only -// happen if the entity was deleted from the metadata -void maybe_append_result(const stats_request_t &request, - const parsed_stats_t &parsed_stats, - const cluster_semilattice_metadata_t &metadata, - server_config_client_t *server_config_client, - table_meta_client_t *table_meta_client, - admin_identifier_format_t admin_format, - std::vector *rows_out) { - ql::datum_t row; - if (request.to_datum(parsed_stats, metadata, server_config_client, table_meta_client, - admin_format, &row)) { - rows_out->push_back(row); - } -} - -bool stats_artificial_table_backend_t::read_all_rows_as_vector( - UNUSED auth::user_context_t const &user_context, - signal_t *interruptor_on_caller, - std::vector *rows_out, - UNUSED admin_err_t *error_out) { - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t rethreader(home_thread()); - rows_out->clear(); - - cluster_semilattice_metadata_t metadata = cluster_sl_view->get(); - - std::set > filter = stats_request_t::global_stats_filter(); - std::vector peers = - stats_request_t::all_peers(directory_view->get().get_inner()); - - std::vector results; - perform_stats_request(peers, filter, &results, &interruptor_on_home); - parsed_stats_t parsed_stats(results); - - // Start building results - rows_out->clear(); - maybe_append_result(cluster_stats_request_t(), parsed_stats, metadata, - server_config_client, table_meta_client, admin_format, rows_out); - - server_config_client->get_server_to_peer_map()->read_all( - [&](const server_id_t &server_id, const peer_id_t *) { - maybe_append_result(server_stats_request_t(server_id), parsed_stats, - metadata, server_config_client, table_meta_client, admin_format, rows_out); - }); - - /* Note that we don't display any stats for tables that we can't reach any of the - replicas of. */ - - std::map configs; - std::map disconnected_configs; - table_meta_client->list_configs( - &interruptor_on_home, &configs, &disconnected_configs); - for (const auto &table_pair : configs) { - maybe_append_result(table_stats_request_t(table_pair.first), parsed_stats, - metadata, server_config_client, table_meta_client, admin_format, rows_out); - std::set servers; - for (const auto &shard : table_pair.second.config.shards) { - servers.insert(shard.all_replicas.begin(), shard.all_replicas.end()); - } - for (const auto &server : servers) { - maybe_append_result( - table_server_stats_request_t(table_pair.first, server), - parsed_stats, metadata, server_config_client, table_meta_client, - admin_format, rows_out); - } - } - - return true; -} - -template -bool parse_stats_request(const ql::datum_t &info, - scoped_ptr_t *request_out) { - if (info.get(0).as_str() == T::get_name()) { - return T::parse(info, request_out); - } - return false; -} - -bool stats_artificial_table_backend_t::read_row( - UNUSED auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor_on_caller, - ql::datum_t *row_out, - UNUSED admin_err_t *error_out) { - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t rethreader(home_thread()); - - // Check format - any incorrect format means the row doesn't exist - if (primary_key.get_type() != ql::datum_t::R_ARRAY || - primary_key.arr_size() == 0 || - primary_key.get(0).get_type() != ql::datum_t::R_STR) { - *row_out = ql::datum_t(); - return true; - } - - scoped_ptr_t request; - if (!parse_stats_request(primary_key, &request) && - !parse_stats_request(primary_key, &request) && - !parse_stats_request(primary_key, &request) && - !parse_stats_request(primary_key, &request)) { - *row_out = ql::datum_t(); - return true; - } - - std::vector peers = request->get_peers( - directory_view->get().get_inner(), server_config_client); - std::vector results_map; - perform_stats_request( - peers, request->get_filter(), &results_map, &interruptor_on_home); - parsed_stats_t parsed_stats(results_map); - if (!request->to_datum(parsed_stats, cluster_sl_view->get(), server_config_client, - table_meta_client, admin_format, row_out)) { - *row_out = ql::datum_t(); - } - - return true; -} - -bool stats_artificial_table_backend_t::write_row( - UNUSED auth::user_context_t const &user_context, - UNUSED ql::datum_t primary_key, - UNUSED bool pkey_was_autogenerated, - UNUSED ql::datum_t *new_value_inout, - UNUSED signal_t *interruptor_on_caller, - admin_err_t *error_out) { - *error_out = admin_err_t{ - "It's illegal to write to the `rethinkdb.stats` table.", - query_state_t::FAILED}; - return false; -} diff --git a/src/clustering/administration/stats/stats_backend.hpp b/src/clustering/administration/stats/stats_backend.hpp deleted file mode 100644 index b0d7d91da48..00000000000 --- a/src/clustering/administration/stats/stats_backend.hpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_STATS_STATS_BACKEND_HPP_ -#define CLUSTERING_ADMINISTRATION_STATS_STATS_BACKEND_HPP_ - -#include -#include -#include - -#include "rdb_protocol/artificial_table/caching_cfeed_backend.hpp" -#include "rdb_protocol/context.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "concurrency/watchable.hpp" - -class cluster_semilattice_metadata_t; -class table_meta_client_t; - -class stats_artificial_table_backend_t : - public timer_cfeed_artificial_table_backend_t -{ -public: - stats_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - const clone_ptr_t > > - &_directory_view, - std::shared_ptr > - _cluster_sl_view, - server_config_client_t *_server_config_client, - table_meta_client_t *_table_meta_client, - mailbox_manager_t *_mailbox_manager, - admin_identifier_format_t _admin_format); - ~stats_artificial_table_backend_t(); - - std::string get_primary_key_name(); - - bool read_all_rows_as_vector( - auth::user_context_t const &user_context, - signal_t *interruptor_on_caller, - std::vector *rows_out, - admin_err_t *error_out); - - bool read_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor_on_caller, - ql::datum_t *row_out, - admin_err_t *error_out); - - bool write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor_on_caller, - admin_err_t *error_out); - -private: - void get_peer_stats(const peer_id_t &peer, - const std::set > &filter, - ql::datum_t *result_out, - signal_t *interruptor_on_home); - - void perform_stats_request(const std::vector &peers, - const std::set > &filter, - std::vector *results_out, - signal_t *interruptor_on_home); - - clone_ptr_t > > directory_view; - std::shared_ptr > - cluster_sl_view; - server_config_client_t *server_config_client; - table_meta_client_t *table_meta_client; - mailbox_manager_t *mailbox_manager; - admin_identifier_format_t admin_format; -}; - -#endif /* CLUSTERING_ADMINISTRATION_STATS_STATS_BACKEND_HPP_ */ diff --git a/src/clustering/administration/tables/calculate_status.cc b/src/clustering/administration/tables/calculate_status.cc deleted file mode 100644 index 3e49d0eb675..00000000000 --- a/src/clustering/administration/tables/calculate_status.cc +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/administration/tables/calculate_status.hpp" - -#include "clustering/administration/namespace_interface_repository.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "concurrency/exponential_backoff.hpp" - -bool wait_for_table_readiness( - const namespace_id_t &table_id, - table_readiness_t readiness, - namespace_repo_t *namespace_repo, - table_meta_client_t *table_meta_client, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t) { - namespace_interface_access_t ns_if = - namespace_repo->get_namespace_interface(table_id, interruptor); - exponential_backoff_t backoff(100, 5000); - bool immediate = true; - while (true) { - if (!table_meta_client->exists(table_id)) { - throw no_such_table_exc_t(); - } - bool ok = ns_if.get()->check_readiness( - std::min(readiness, table_readiness_t::writes), - interruptor); - if (ok && readiness == table_readiness_t::finished) { - try { - table_meta_client->get_shard_status( - table_id, all_replicas_ready_mode_t::INCLUDE_RAFT_TEST, interruptor, - nullptr, &ok); - } catch (const failed_table_op_exc_t &) { - ok = false; - } - } - if (ok) { - return immediate; - } - immediate = false; - backoff.failure(interruptor); - } -} - -size_t wait_for_many_tables_readiness( - const std::set &original_tables, - table_readiness_t readiness, - namespace_repo_t *namespace_repo, - table_meta_client_t *table_meta_client, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t) { - std::set tables = original_tables; - while (true) { - bool all_immediate = true; - for (auto it = tables.begin(); it != tables.end();) { - try { - all_immediate &= wait_for_table_readiness( - *it, readiness, namespace_repo, table_meta_client, interruptor); - } catch (const no_such_table_exc_t &) { - tables.erase(it++); - continue; - } - ++it; - } - if (all_immediate) { - break; - } - } - return tables.size(); -} - -void get_table_status( - const namespace_id_t &table_id, - const table_config_and_shards_t &config, - namespace_repo_t *namespace_repo, - table_meta_client_t *table_meta_client, - server_config_client_t *server_config_client, - signal_t *interruptor, - table_status_t *status_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t) { - status_out->total_loss = false; - *status_out->config = config; - - /* Get the Raft leader for this table. */ - table_meta_client->get_raft_leader(table_id, interruptor, &status_out->raft_leader); - - /* Send the status query to every server for the table. */ - bool all_replicas_ready; - try { - table_meta_client->get_shard_status( - table_id, all_replicas_ready_mode_t::EXCLUDE_RAFT_TEST, interruptor, - &status_out->server_shards, &all_replicas_ready); - } catch (const failed_table_op_exc_t &) { - all_replicas_ready = false; - status_out->server_shards.clear(); - } - - /* We need to pay special attention to servers that appear in the config but not in - the status response. There are two possible reasons: they might be disconnected, or - they might not be hosting the server currently. In the former case we insert them - into `disconnected`, and in the latter case we set their state to `transitioning` in - `server_shards`. */ - for (const table_config_t::shard_t &shard : config.config.shards) { - for (const server_id_t &server : shard.all_replicas) { - if (status_out->server_shards.count(server) == 0 && - status_out->disconnected.count(server) == 0) { - if (server_config_client-> - get_server_to_peer_map()->get_key(server).has_value()) { - table_shard_status_t status; - status.transitioning = true; - status_out->server_shards.insert(std::make_pair(server, - range_map_t( - key_range_t::right_bound_t(store_key_t()), - key_range_t::right_bound_t::make_unbounded(), - std::move(status)))); - - } else { - status_out->disconnected.insert(server); - } - } - } - } - - /* Collect server names. We need the name of every server in the config, every - server in `server_shards`, and the Raft leader. */ - status_out->server_names = config.server_names; - - std::set server_ids; - for (const auto &server_shard : status_out->server_shards) { - server_ids.insert(server_shard.first); - } - if (static_cast(status_out->raft_leader)) { - server_ids.insert(status_out->raft_leader.get()); - } - - for (const auto &server_id : server_ids) { - if (status_out->server_names.names.count(server_id) == 0) { - server_config_client->get_server_config_map()->read_key( - server_id, - [&](const server_config_versioned_t *server_config) { - if (server_config != nullptr) { - status_out->server_names.names.insert(std::make_pair( - server_id, - std::make_pair( - server_config->version, server_config->config.name))); - } else { - /* If we can't find the name for one of the servers in the - response, then act as though it was disconnected. */ - status_out->server_names.names.insert(std::make_pair( - server_id, - std::make_pair( - 0, - name_string_t::guarantee_valid( - "__disconnected_server__")))); - status_out->disconnected.insert(server_id); - status_out->server_shards.erase(server_id); - } - }); - } - } - - /* Compute the overall level of table readiness by sending probe queries. */ - if (all_replicas_ready) { - status_out->readiness = table_readiness_t::finished; - } else { - namespace_interface_access_t ns_if = - namespace_repo->get_namespace_interface(table_id, interruptor); - if (ns_if.get()->check_readiness(table_readiness_t::writes, interruptor)) { - status_out->readiness = table_readiness_t::writes; - } else if (ns_if.get()->check_readiness(table_readiness_t::reads, interruptor)) { - status_out->readiness = table_readiness_t::reads; - } else if (ns_if.get()->check_readiness( - table_readiness_t::outdated_reads, interruptor)) { - status_out->readiness = table_readiness_t::outdated_reads; - } else { - status_out->readiness = table_readiness_t::unavailable; - } - } -} - diff --git a/src/clustering/administration/tables/calculate_status.hpp b/src/clustering/administration/tables/calculate_status.hpp deleted file mode 100644 index 06ab247ea65..00000000000 --- a/src/clustering/administration/tables/calculate_status.hpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_TABLES_CALCULATE_STATUS_HPP_ -#define CLUSTERING_ADMINISTRATION_TABLES_CALCULATE_STATUS_HPP_ - -#include "clustering/administration/servers/server_metadata.hpp" -#include "clustering/table_manager/table_meta_client.hpp" -#include "rpc/connectivity/server_id.hpp" -#include "protocol_api.hpp" - -class namespace_repo_t; -class signal_t; - -/* There's a slight difference between `wait_for_*_readiness()` and `get_table_status()` -in how they react when the table's leader reports that all replicas are ready. -`wait_for_*_readiness()` will go ahead and run the probe read/write query even if the -leader reports everything is fine, because this will ensure that the `namespace_repo_t` -is also ready. But `get_table_status()` will go ahead and report -`table_readiness_t::finished` without running any probe queries against the -`namespace_repo_t`. This is to save work and also to make the results of -`get_table_status()` more consistent between servers. */ - -/* Blocks until the given table is available at the given level of readiness. If the -table is deleted, throws `no_such_table_exc_t`. The return value is `true` if the table -was ready immediately, or `false` if it was necessary to wait.*/ -bool wait_for_table_readiness( - const namespace_id_t &table_id, - table_readiness_t readiness, - namespace_repo_t *namespace_repo, - table_meta_client_t *table_meta_client, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t); - -/* Blocks until all of the tables in the given set are either deleted or ready at the -given level of readiness. Returns the number of tables that were ready (as opposed to -deleted.) */ -size_t wait_for_many_tables_readiness( - const std::set &tables, - table_readiness_t readiness, - namespace_repo_t *namespace_repo, - table_meta_client_t *table_meta_client, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t); - -/* `table_status_t` describes the current status of a table, in a format that can be -converted to a `rethinkdb.table_status` entry by `convert_table_status_to_datum()`. */ -class table_status_t { -public: - table_readiness_t readiness; - - /* If `total_loss` is `true`, that indicates that we were unable to access even one - server for the table, and the fields below are meaningless. */ - bool total_loss; - - /* No server appears in both `server_shards` and `disconnected`. Every server in - `config` appears in either `server_shards` and `disconnected`, but some servers may - appear in `server_shards` that aren't in `config`. Every server in any of the three - appears in `server_names`. */ - - scoped_ptr_t config = make_scoped(); - std::map > server_shards; - optional raft_leader; - std::set disconnected; - server_name_map_t server_names; -}; - -/* Fetches the current `table_status_t` for a table that isn't a total loss. */ -void get_table_status( - const namespace_id_t &table_id, - const table_config_and_shards_t &config, - namespace_repo_t *namespace_repo, - table_meta_client_t *table_meta_client, - server_config_client_t *server_config_client, - signal_t *interruptor, - table_status_t *status_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t); - -#endif /* CLUSTERING_ADMINISTRATION_TABLES_CALCULATE_STATUS_HPP_ */ - diff --git a/src/clustering/administration/tables/database_metadata.hpp b/src/clustering/administration/tables/database_metadata.hpp deleted file mode 100644 index e90577b360f..00000000000 --- a/src/clustering/administration/tables/database_metadata.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_TABLES_DATABASE_METADATA_HPP_ -#define CLUSTERING_ADMINISTRATION_TABLES_DATABASE_METADATA_HPP_ - -#include -#include - -#include "containers/archive/stl_types.hpp" -#include "containers/name_string.hpp" -#include "containers/uuid.hpp" -#include "rpc/semilattice/joins/deletable.hpp" -#include "rpc/semilattice/joins/macros.hpp" -#include "rpc/semilattice/joins/map.hpp" -#include "rpc/semilattice/joins/versioned.hpp" -#include "rpc/serialize_macros.hpp" - - -class database_semilattice_metadata_t { -public: - versioned_t name; -}; - -RDB_DECLARE_SERIALIZABLE(database_semilattice_metadata_t); -RDB_DECLARE_SEMILATTICE_JOINABLE(database_semilattice_metadata_t); -RDB_DECLARE_EQUALITY_COMPARABLE(database_semilattice_metadata_t); - -void debug_print(printf_buffer_t *buf, const database_semilattice_metadata_t &x); - - -class databases_semilattice_metadata_t { -public: - typedef std::map > database_map_t; - database_map_t databases; -}; - -RDB_DECLARE_SERIALIZABLE(databases_semilattice_metadata_t); -RDB_DECLARE_SEMILATTICE_JOINABLE(databases_semilattice_metadata_t); -RDB_DECLARE_EQUALITY_COMPARABLE(databases_semilattice_metadata_t); - - -#endif /* CLUSTERING_ADMINISTRATION_TABLES_DATABASE_METADATA_HPP_ */ - diff --git a/src/clustering/administration/tables/db_config.cc b/src/clustering/administration/tables/db_config.cc deleted file mode 100644 index 801484592e3..00000000000 --- a/src/clustering/administration/tables/db_config.cc +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/tables/db_config.hpp" - -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/real_reql_cluster_interface.hpp" -#include "concurrency/cross_thread_signal.hpp" - -ql::datum_t convert_db_config_and_name_to_datum( - name_string_t db_name, - namespace_id_t id) { - ql::datum_object_builder_t builder; - builder.overwrite("name", convert_name_to_datum(db_name)); - builder.overwrite("id", convert_uuid_to_datum(id)); - return std::move(builder).to_datum(); -} - -bool convert_db_config_and_name_from_datum( - ql::datum_t datum, - name_string_t *db_name_out, - namespace_id_t *id_out, - admin_err_t *error_out) { - /* In practice, the input will always be an object and the `id` field will always - be valid, because `artificial_table_t` will check those thing before passing the - row to `db_config_artificial_table_backend_t`. But we check them anyway for - consistency. */ - converter_from_datum_object_t converter; - if (!converter.init(datum, error_out)) { - return false; - } - - ql::datum_t name_datum; - if (!converter.get("name", &name_datum, error_out)) { - return false; - } - if (!convert_name_from_datum(name_datum, "db name", db_name_out, error_out)) { - error_out->msg = "In `name`: " + error_out->msg; - return false; - } - - ql::datum_t id_datum; - if (!converter.get("id", &id_datum, error_out)) { - return false; - } - if (!convert_uuid_from_datum(id_datum, id_out, error_out)) { - error_out->msg = "In `id`: " + error_out->msg; - return false; - } - - if (!converter.check_no_extra_keys(error_out)) { - return false; - } - - return true; -} - -db_config_artificial_table_backend_t::db_config_artificial_table_backend_t( - rdb_context_t *_rdb_context, - lifetime_t name_resolver, - std::shared_ptr< semilattice_readwrite_view_t< - databases_semilattice_metadata_t> > _database_sl_view, - real_reql_cluster_interface_t *_reql_cluster_interface) : - caching_cfeed_artificial_table_backend_t( - name_string_t::guarantee_valid("db_config"), - _rdb_context, - name_resolver), - rdb_context(_rdb_context), - database_sl_view(_database_sl_view), - subs([this]() { notify_all(); }, database_sl_view), - reql_cluster_interface(_reql_cluster_interface) - { } - -db_config_artificial_table_backend_t::~db_config_artificial_table_backend_t() { - begin_changefeed_destruction(); -} - -std::string db_config_artificial_table_backend_t::get_primary_key_name() { - return "id"; -} - -bool db_config_artificial_table_backend_t::read_all_rows_as_vector( - UNUSED auth::user_context_t const &user_context, - UNUSED signal_t *interruptor_on_caller, - std::vector *rows_out, - UNUSED admin_err_t *error_out) { - on_thread_t thread_switcher(home_thread()); - rows_out->clear(); - databases_semilattice_metadata_t md = database_sl_view->get(); - for (auto it = md.databases.begin(); - it != md.databases.end(); - ++it) { - if (it->second.is_deleted()) { - continue; - } - - name_string_t db_name = it->second.get_ref().name.get_ref(); - rows_out->push_back(convert_db_config_and_name_to_datum(db_name, it->first)); - } - return true; -} - -bool db_config_artificial_table_backend_t::read_row( - UNUSED auth::user_context_t const &user_context, - ql::datum_t primary_key, - UNUSED signal_t *interruptor_on_caller, - ql::datum_t *row_out, - UNUSED admin_err_t *error_out) { - on_thread_t thread_switcher(home_thread()); - databases_semilattice_metadata_t md = database_sl_view->get(); - database_id_t database_id; - admin_err_t dummy_error; - if (!convert_uuid_from_datum(primary_key, &database_id, &dummy_error)) { - /* If the primary key was not a valid UUID, then it must refer to a nonexistent - row. */ - database_id = nil_uuid(); - } - std::map >::iterator it; - if (search_metadata_by_uuid(&md.databases, database_id, &it)) { - name_string_t db_name = it->second.get_ref().name.get_ref(); - *row_out = convert_db_config_and_name_to_datum(db_name, database_id); - } else { - *row_out = ql::datum_t(); - } - return true; -} - -bool db_config_artificial_table_backend_t::write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor_on_caller, - admin_err_t *error_out) { - cross_thread_signal_t interruptor_on_home_thread(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - databases_semilattice_metadata_t md = database_sl_view->get(); - - /* Look for an existing DB with the given UUID */ - namespace_id_t database_id; - admin_err_t dummy_error; - if (!convert_uuid_from_datum(primary_key, &database_id, &dummy_error)) { - /* If the primary key was not a valid UUID, then it must refer to a nonexistent - row. */ - database_id = nil_uuid(); - } - std::map >::iterator it; - bool existed_before = search_metadata_by_uuid(&md.databases, database_id, &it); - - if (existed_before) { - user_context.require_config_permission(rdb_context, database_id); - } else { - user_context.require_config_permission(rdb_context); - } - - if (new_value_inout->has()) { - /* We're updating an existing database (if `existed_before == true`) or creating - a new one (if `existed_before == false`) */ - - /* Parse the new value the user provided for the database */ - name_string_t new_db_name; - namespace_id_t new_database_id; - if (!convert_db_config_and_name_from_datum(*new_value_inout, - &new_db_name, &new_database_id, error_out)) { - error_out->msg = "The change you're trying to make to " - "`rethinkdb.db_config` has the wrong format. " + error_out->msg; - return false; - } - guarantee(new_database_id == database_id, "artificial_table_t should ensure " - "that the primary key doesn't change."); - - if (existed_before) { - guarantee(!pkey_was_autogenerated, "UUID collision happened"); - } else { - if (!pkey_was_autogenerated) { - *error_out = admin_err_t{ - "If you want to create a new table by inserting into " - "`rethinkdb.db_config`, you must use an auto-generated primary key.", - query_state_t::FAILED}; - return false; - } - /* Assert that we didn't randomly generate the UUID of a database that used - to exist but was deleted */ - guarantee(md.databases.count(database_id) == 0, "UUID collision happened"); - } - - name_string_t old_db_name; - if (existed_before) { - old_db_name = it->second.get_ref().name.get_ref(); - } - - if (!existed_before || new_db_name != old_db_name) { - /* Reserve the `rethinkdb` database name */ - if (new_db_name == name_string_t::guarantee_valid("rethinkdb")) { - if (!existed_before) { - *error_out = admin_err_t{ - "Database `rethinkdb` already exists.", - query_state_t::FAILED}; - } else { - *error_out = admin_err_t{ - strprintf("Cannot rename database `%s` to `rethinkdb` " - "because database `rethinkdb` already exists.", - old_db_name.c_str()), - query_state_t::FAILED}; - } - return false; - } - /* Prevent name collisions if possible */ - for (const auto &pair : md.databases) { - if (!pair.second.is_deleted() && - pair.second.get_ref().name.get_ref() == new_db_name) { - if (!existed_before) { - /* This message looks weird in the context of the variable named - `existed_before`, but it's correct. `existed_before` is true if a - database with the specified UUID already exists; but we're - showing the user an error if a database with the specified name - already exists. */ - *error_out = admin_err_t{ - strprintf("Database `%s` already exists.", - new_db_name.c_str()), - query_state_t::FAILED}; - } else { - *error_out = admin_err_t{ - strprintf("Cannot rename database `%s` to `%s` " - "because database `%s` already exists.", - old_db_name.c_str(), - new_db_name.c_str(), - new_db_name.c_str()), - query_state_t::FAILED}; - } - return false; - } - } - } - - /* Update `md`. The change will be committed to the semilattices at the end of - this function. */ - if (existed_before) { - it->second.get_mutable()->name.set(new_db_name); - } else { - database_semilattice_metadata_t db_md; - db_md.name = versioned_t(new_db_name); - md.databases[database_id] = - deletable_t(db_md); - } - } else { - /* We're deleting a database (or it was already deleted) */ - if (existed_before) { - guarantee(!pkey_was_autogenerated, "UUID collision happened"); - - // `db_drop_uuid` asserts we're on its home thread - // https://github.com/rethinkdb/rethinkdb/issues/5598 - reql_cluster_interface->db_drop_uuid( - user_context, - database_id, - it->second.get_ref().name.get_ref(), - &interruptor_on_home_thread, - nullptr, - error_out); - } - } - - database_sl_view->join(md); - - return true; -} - diff --git a/src/clustering/administration/tables/db_config.hpp b/src/clustering/administration/tables/db_config.hpp deleted file mode 100644 index 999128c50af..00000000000 --- a/src/clustering/administration/tables/db_config.hpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_TABLES_DB_CONFIG_HPP_ -#define CLUSTERING_ADMINISTRATION_TABLES_DB_CONFIG_HPP_ - -#include -#include -#include - -#include "clustering/administration/tables/database_metadata.hpp" -#include "containers/uuid.hpp" -#include "rdb_protocol/artificial_table/caching_cfeed_backend.hpp" -#include "rpc/semilattice/view.hpp" - -/* This is public because it's used by `real_reql_cluster_interface_t`. It's kind of a -trivial function right now, but it will become non-trivial if we ever add more -configuration to databases. */ -ql::datum_t convert_db_config_and_name_to_datum( - name_string_t db_name, - namespace_id_t id); - -class real_reql_cluster_interface_t; - -class db_config_artificial_table_backend_t : - public caching_cfeed_artificial_table_backend_t -{ -public: - db_config_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - std::shared_ptr< semilattice_readwrite_view_t< - databases_semilattice_metadata_t> > _database_sl_view, - real_reql_cluster_interface_t *_reql_cluster_interface); - ~db_config_artificial_table_backend_t(); - - std::string get_primary_key_name(); - - bool read_all_rows_as_vector( - auth::user_context_t const &user_context, - signal_t *interruptor_on_caller, - std::vector *rows_out, - admin_err_t *error_out); - - bool read_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor_on_caller, - ql::datum_t *row_out, - admin_err_t *error_out); - - bool write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_is_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor_on_caller, - admin_err_t *error_out); - -private: - rdb_context_t *rdb_context; - std::shared_ptr > database_sl_view; - semilattice_read_view_t::subscription_t subs; - real_reql_cluster_interface_t *reql_cluster_interface; -}; - -#endif /* CLUSTERING_ADMINISTRATION_TABLES_DB_CONFIG_HPP_ */ - diff --git a/src/clustering/administration/tables/debug_table_status.cc b/src/clustering/administration/tables/debug_table_status.cc deleted file mode 100644 index 358b5de2cea..00000000000 --- a/src/clustering/administration/tables/debug_table_status.cc +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/tables/debug_table_status.hpp" - -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "clustering/administration/tables/table_config.hpp" -#include "clustering/table_manager/table_meta_client.hpp" - -namespace ql { - -// src/rdb_protocol/terms/sindex.cc -ql::datum_t sindex_status_to_datum( - const std::string &, - const sindex_config_t &, - const sindex_status_t &); - -} // namespace ql - -debug_table_status_artificial_table_backend_t:: - debug_table_status_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - std::shared_ptr > _semilattice_view, - table_meta_client_t *_table_meta_client) - : common_table_artificial_table_backend_t( - name_string_t::guarantee_valid("_debug_table_status"), - rdb_context, - name_resolver, - _semilattice_view, - _table_meta_client, - admin_identifier_format_t::uuid) { -} - -debug_table_status_artificial_table_backend_t:: - ~debug_table_status_artificial_table_backend_t() { - begin_changefeed_destruction(); -} - -bool debug_table_status_artificial_table_backend_t::write_row( - auth::user_context_t const &user_context, - UNUSED ql::datum_t primary_key, - UNUSED bool pkey_was_autogenerated, - UNUSED ql::datum_t *new_value_inout, - UNUSED signal_t *interruptor_on_caller, - admin_err_t *error_out) { - user_context.require_admin_user(); - - *error_out = admin_err_t{ - "It's illegal to write to the `rethinkdb.table_status` table.", - query_state_t::FAILED}; - return false; -} - -ql::datum_t convert_debug_multi_table_manager_bcard_timestamp_to_datum( - const multi_table_manager_timestamp_t ×tamp) { - ql::datum_object_builder_t builder; - builder.overwrite( - "epoch", timestamp.epoch.to_datum()); - builder.overwrite( - "log_index", ql::datum_t(static_cast(timestamp.log_index))); - return std::move(builder).to_datum(); -} - -ql::datum_t convert_debug_store_key_to_datum(const store_key_t &store_key) { - return ql::datum_t::binary(datum_string_t( - store_key.size(), - reinterpret_cast(store_key.contents()))); -} - -ql::datum_t convert_debug_region_to_datum(const region_t ®ion) { - ql::datum_object_builder_t builder; - builder.overwrite("hash_min", ql::datum_t(datum_string_t( - strprintf("%016" PRIx64, region.beg)))); - builder.overwrite("hash_max", ql::datum_t(datum_string_t( - strprintf("%016" PRIx64, region.end)))); - builder.overwrite("key_min", - convert_debug_store_key_to_datum(region.inner.left)); - builder.overwrite("key_max", - region.inner.right.unbounded - ? ql::datum_t::null() - : convert_debug_store_key_to_datum(region.inner.right.key())); - return std::move(builder).to_datum(); -} - -ql::datum_t convert_debug_contract_primary_to_datum( - const contract_t::primary_t &primary) { - ql::datum_object_builder_t builder; - builder.overwrite("server", convert_uuid_to_datum(primary.server.get_uuid())); - builder.overwrite( - "hand_over", - static_cast(primary.hand_over) - ? convert_uuid_to_datum(primary.hand_over.get().get_uuid()) - : ql::datum_t::null()); - return std::move(builder).to_datum(); -} - -ql::datum_t convert_debug_contracts_to_datum( - const std::map > &contracts) { - ql::datum_array_builder_t builder(ql::configured_limits_t::unlimited); - for (const auto &contract : contracts) { - ql::datum_object_builder_t contract_builder; - contract_builder.overwrite( - "contract", convert_uuid_to_datum(contract.first)); - contract_builder.overwrite( - "region", convert_debug_region_to_datum(contract.second.first)); - contract_builder.overwrite( - "replicas", convert_set_to_datum( - [] (const server_id_t &sid) { - return convert_uuid_to_datum(sid.get_uuid()); - }, contract.second.second.replicas)); - contract_builder.overwrite( - "voters", convert_set_to_datum( - [] (const server_id_t &sid) { - return convert_uuid_to_datum(sid.get_uuid()); - }, contract.second.second.voters)); - contract_builder.overwrite( - "temp_voters", - static_cast(contract.second.second.temp_voters) - ? convert_set_to_datum( - [] (const server_id_t &sid) { - return convert_uuid_to_datum(sid.get_uuid()); - }, contract.second.second.temp_voters.get()) - : ql::datum_t::null()); - contract_builder.overwrite( - "primary", - static_cast(contract.second.second.primary) - ? convert_debug_contract_primary_to_datum( - contract.second.second.primary.get()) - : ql::datum_t::null()); - builder.add(std::move(contract_builder).to_datum()); - } - return std::move(builder).to_datum(); -} - -ql::datum_t convert_debug_table_shard_scheme_to_datum( - const table_shard_scheme_t &shard_scheme) { - ql::datum_object_builder_t builder; - builder.overwrite("split_points", convert_vector_to_datum( - &convert_debug_store_key_to_datum, shard_scheme.split_points)); - return std::move(builder).to_datum(); -} - -ql::datum_t convert_debug_contract_ack_state_to_datum( - const contract_ack_t::state_t &state) { - switch (state) { - case contract_ack_t::state_t::primary_need_branch: - return convert_string_to_datum("primary_need_branch"); - case contract_ack_t::state_t::primary_in_progress: - return convert_string_to_datum("primary_in_progress"); - case contract_ack_t::state_t::primary_ready: - return convert_string_to_datum("primary_ready"); - case contract_ack_t::state_t::secondary_need_primary: - return convert_string_to_datum("secondary_need_primary"); - case contract_ack_t::state_t::secondary_backfilling: - return convert_string_to_datum("secondary_backfilling"); - case contract_ack_t::state_t::secondary_streaming: - return convert_string_to_datum("secondary_streaming"); - default: - unreachable(); - } -} - -ql::datum_t convert_debug_version_to_datum(const version_t &version) { - ql::datum_object_builder_t builder; - builder.overwrite("branch", convert_uuid_to_datum(version.branch)); - builder.overwrite("timestamp", ql::datum_t(static_cast( - version.timestamp.to_repli_timestamp().longtime))); - return std::move(builder).to_datum(); -} - -ql::datum_t convert_debug_version_map_to_datum( - const region_map_t &map) { - ql::datum_array_builder_t builder(ql::configured_limits_t::unlimited); - map.visit( - map.get_domain(), - [&](const region_t ®ion, const version_t &version) { - ql::datum_object_builder_t pair_builder; - pair_builder.overwrite("region", - convert_debug_region_to_datum(region)); - pair_builder.overwrite("version", - convert_debug_version_to_datum(version)); - builder.add(std::move(pair_builder).to_datum()); - }); - return std::move(builder).to_datum(); -} - -ql::datum_t convert_debug_current_branches_to_datum( - const region_map_t &map) { - ql::datum_array_builder_t builder(ql::configured_limits_t::unlimited); - map.visit( - map.get_domain(), - [&](const region_t ®ion, const branch_id_t &branch) { - ql::datum_object_builder_t pair_builder; - pair_builder.overwrite("region", - convert_debug_region_to_datum(region)); - pair_builder.overwrite("branch", - convert_uuid_to_datum(branch)); - builder.add(std::move(pair_builder).to_datum()); - }); - return std::move(builder).to_datum(); -} - -ql::datum_t convert_debug_branch_birth_certificate_to_datum( - const branch_birth_certificate_t &birth_certificate) { - ql::datum_object_builder_t builder; - builder.overwrite("initial_timestamp", ql::datum_t(static_cast( - birth_certificate.initial_timestamp.to_repli_timestamp().longtime))); - builder.overwrite( - "origin", convert_debug_version_map_to_datum(birth_certificate.origin)); - return std::move(builder).to_datum(); -} - -ql::datum_t convert_debug_branch_history_to_datum( - const branch_history_t &branch_history) { - ql::datum_array_builder_t builder(ql::configured_limits_t::unlimited); - for (const auto &branch : branch_history.branches) { - ql::datum_object_builder_t branch_builder; - branch_builder.overwrite("branch", convert_uuid_to_datum(branch.first)); - branch_builder.overwrite( - "branch_birth_certificate", - convert_debug_branch_birth_certificate_to_datum(branch.second)); - builder.add(std::move(branch_builder).to_datum()); - } - return std::move(builder).to_datum(); -} - -ql::datum_t convert_debug_contract_acks_to_datum( - const std::map &contract_acks) { - ql::datum_array_builder_t builder(ql::configured_limits_t::unlimited); - for (const auto &contract_ack : contract_acks) { - ql::datum_object_builder_t contract_builder; - contract_builder.overwrite( - "contract", convert_uuid_to_datum(contract_ack.first)); - contract_builder.overwrite( - "state", - convert_debug_contract_ack_state_to_datum(contract_ack.second.state)); - contract_builder.overwrite( - "version", - static_cast(contract_ack.second.version) - ? convert_debug_version_map_to_datum( - contract_ack.second.version.get()) - : ql::datum_t::null()); - contract_builder.overwrite( - "branch_history", - convert_debug_branch_history_to_datum(contract_ack.second.branch_history)); - builder.add(std::move(contract_builder).to_datum()); - } - return std::move(builder).to_datum(); -} - -ql::datum_t convert_debug_statuses_to_datum( - const std::map &statuses) { - ql::datum_array_builder_t builder(ql::configured_limits_t::unlimited); - for (const auto &peer : statuses) { - ql::datum_object_builder_t peer_builder; - peer_builder.overwrite("server", convert_uuid_to_datum(peer.first.get_uuid())); - peer_builder.overwrite("timestamp", - convert_debug_multi_table_manager_bcard_timestamp_to_datum( - *peer.second.raft_state_timestamp)); - peer_builder.overwrite("contracts", - convert_debug_contracts_to_datum(peer.second.raft_state->contracts)); - peer_builder.overwrite("contract_acks", - convert_debug_contract_acks_to_datum(peer.second.contract_acks)); - peer_builder.overwrite("current_branches", - convert_debug_current_branches_to_datum( - peer.second.raft_state->current_branches)); - builder.add(std::move(peer_builder).to_datum()); - } - return std::move(builder).to_datum(); -} - -void debug_table_status_artificial_table_backend_t::format_row( - auth::user_context_t const &user_context, - const namespace_id_t &table_id, - const table_config_and_shards_t &config_and_shards, - const ql::datum_t &db_name_or_uuid, - signal_t *interruptor_on_home, - ql::datum_t *row_out) - THROWS_ONLY( - interrupted_exc_t, - no_such_table_exc_t, - failed_table_op_exc_t, - auth::permission_error_t) { - assert_thread(); - - user_context.require_admin_user(); - - std::map statuses; - table_meta_client->get_debug_status( - table_id, all_replicas_ready_mode_t::INCLUDE_RAFT_TEST, interruptor_on_home, - &statuses); - - ql::datum_object_builder_t builder; - builder.overwrite("id", convert_uuid_to_datum(table_id)); - builder.overwrite("name", - convert_name_to_datum(config_and_shards.config.basic.name)); - builder.overwrite("db", db_name_or_uuid); - builder.overwrite( - "config", - convert_table_config_to_datum( - table_id, - db_name_or_uuid, - config_and_shards.config, - admin_identifier_format_t::uuid, - config_and_shards.server_names)); - builder.overwrite( - "shard_scheme", - convert_debug_table_shard_scheme_to_datum(config_and_shards.shard_scheme)); - builder.overwrite( - "table_server_status", - convert_debug_statuses_to_datum(statuses)); - *row_out = std::move(builder).to_datum(); -} diff --git a/src/clustering/administration/tables/debug_table_status.hpp b/src/clustering/administration/tables/debug_table_status.hpp deleted file mode 100644 index 5607d97e0b9..00000000000 --- a/src/clustering/administration/tables/debug_table_status.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_TABLES_DEBUG_TABLE_STATUS_HPP_ -#define CLUSTERING_ADMINISTRATION_TABLES_DEBUG_TABLE_STATUS_HPP_ - -#include -#include - -#include "clustering/administration/tables/table_common.hpp" - -class server_config_client_t; - -class debug_table_status_artificial_table_backend_t : - public common_table_artificial_table_backend_t -{ -public: - debug_table_status_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - std::shared_ptr > _semilattice_view, - table_meta_client_t *_table_meta_client); - ~debug_table_status_artificial_table_backend_t(); - - bool write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor_on_caller, - admin_err_t *error_out); - -private: - void format_row( - auth::user_context_t const &user_context, - const namespace_id_t &table_id, - const table_config_and_shards_t &config, - const ql::datum_t &db_name_or_uuid, - signal_t *interruptor_on_home, - ql::datum_t *row_out) - THROWS_ONLY( - interrupted_exc_t, - no_such_table_exc_t, - failed_table_op_exc_t, - auth::permission_error_t); -}; - -#endif /* CLUSTERING_ADMINISTRATION_TABLES_DEBUG_TABLE_STATUS_HPP_ */ - diff --git a/src/clustering/administration/tables/generate_config.cc b/src/clustering/administration/tables/generate_config.cc deleted file mode 100644 index bfb70929ecd..00000000000 --- a/src/clustering/administration/tables/generate_config.cc +++ /dev/null @@ -1,447 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/tables/generate_config.hpp" - -#include "clustering/administration/servers/config_client.hpp" -#include "clustering/table_manager/table_meta_client.hpp" -#include "containers/counted.hpp" - -/* `long_calculation_yielder_t` is used in a long-running calculation to periodically -yield control of the CPU, thereby preventing locking up the server. Construct one at the -beginning of the calculation and call `maybe_yield()` regularly during the calculation. -`maybe_yield()` will sometimes call `coro_t::yield()`. The advantage over just calling -`coro_t::yield()` directly is that `long_calculation_yielder_t` won't yield unless this -coroutine has held the CPU for a long time, so it's reasonable to call it even in a tight -inner loop. It also checks the whether an interruptor signal has been pulsed. */ -class long_calculation_yielder_t { -public: - long_calculation_yielder_t() : t(get_ticks()) { } - void maybe_yield(signal_t *interruptor) { - ticks_t now = get_ticks(); - /* We yield every 10ms. */ - if (now > t + secs_to_ticks(1) / 100) { - coro_t::yield(); - t = now; - } - if (interruptor->is_pulsed()) { - throw interrupted_exc_t(); - } - } -private: - ticks_t t; -}; - -// Because being the primary replica for a shard usually comes with a higher cost than -// being a secondary replica, we want to consider that difference in the replica -// assignment. The concrete value of these doesn't matter, only their ratio -// (float)PRIMARY_USAGE_COST/(float)SECONDARY_USAGE_COST is important. -// As long as PRIMARY_USAGE_COST > SECONDARY_USAGE_COST, this is a solution to -// https://github.com/rethinkdb/rethinkdb/issues/344 (if the server roles are -// otherwise equal). -#define PRIMARY_USAGE_COST 10 -#define SECONDARY_USAGE_COST 8 -void calculate_server_usage( - const table_config_t &config, - std::map *usage) { - for (const table_config_t::shard_t &shard : config.shards) { - for (const server_id_t &server : shard.all_replicas) { - (*usage)[server] += SECONDARY_USAGE_COST; - } - (*usage)[shard.primary_replica] += (PRIMARY_USAGE_COST - SECONDARY_USAGE_COST); - } -} - -/* `validate_params()` checks if `params` are legal. */ -static void validate_params( - const table_generate_config_params_t ¶ms, - const std::map > &servers_with_tags, - const server_name_map_t &server_names) - THROWS_ONLY(admin_op_exc_t) { - if (params.num_shards <= 0) { - throw admin_op_exc_t("Every table must have at least one shard.", - query_state_t::FAILED); - } - size_t total_replicas = 0; - for (const auto &pair : params.num_replicas) { - total_replicas += pair.second; - } - if (total_replicas == 0) { - throw admin_op_exc_t( - "You must set `replicas` to at least one. `replicas` " - "includes the primary replica; if there are zero replicas, there is nowhere " - "to put the data.", - query_state_t::FAILED); - } - static const size_t max_shards = 64; - if (params.num_shards > max_shards) { - throw admin_op_exc_t( - strprintf("Maximum number of shards is %zu.", max_shards), - query_state_t::FAILED); - } - if (params.num_replicas.count(params.primary_replica_tag) == 0 || - params.num_replicas.at(params.primary_replica_tag) == 0) { - throw admin_op_exc_t( - strprintf("Can't use server tag `%s` for primary replicas " - "because you specified no replicas in server tag `%s`.", - params.primary_replica_tag.c_str(), - params.primary_replica_tag.c_str()), - query_state_t::FAILED); - } - for (const name_string_t &nonvoting_tag : params.nonvoting_replica_tags) { - if (params.num_replicas.count(nonvoting_tag) == 0) { - throw admin_op_exc_t( - strprintf("You specified that the replicas in server " - "tag `%s` should be non-voting, but you didn't specify a " - "number of replicas in server tag `%s`.", - nonvoting_tag.c_str(), nonvoting_tag.c_str()), - query_state_t::FAILED); - } - } - std::map servers_claimed; - for (auto it = params.num_replicas.begin(); it != params.num_replicas.end(); ++it) { - if (it->second == 0) { - continue; - } - if (servers_with_tags.count(it->first) == 0) { - throw admin_op_exc_t( - strprintf("Could not find any servers with server tag `%s`.", - it->first.c_str()), - query_state_t::FAILED); - } - for (const server_id_t &server : servers_with_tags.at(it->first)) { - if (servers_claimed.count(server) == 0) { - servers_claimed.insert(std::make_pair(server, it->first)); - } else { - throw admin_op_exc_t( - strprintf("Server tags `%s` and `%s` overlap; the server " - "`%s` has both tags. The server tags used for replication " - "settings for a given table must be non-overlapping.", - it->first.c_str(), servers_claimed.at(server).c_str(), - server_names.get(server).c_str()), - query_state_t::FAILED); - } - } - } -} - -/* `estimate_backfill_cost()` estimates the cost of backfilling the data in `range` from -the locations specified in `old_config` to the server `server`. The cost is measured in -arbitrary units that are only comparable to each other. */ -double estimate_backfill_cost( - const key_range_t &range, - const table_config_and_shards_t &old_config, - const server_id_t &server) { - /* If `range` aligns perfectly to an existing shard, the cost is 0.0 if `server` is - already primary for that shard; 1.0 if it's a voting non-primary replica; 2.0 if it's - a non-voting replica; and 3.0 otherwise. If `range` overlaps multiple existing - shards' ranges, we average over all of them. */ - guarantee(!range.is_empty()); - double numerator = 0; - int denominator = 0; - for (size_t i = 0; i < old_config.shard_scheme.num_shards(); ++i) { - if (old_config.shard_scheme.get_shard_range(i).overlaps(range)) { - ++denominator; - if (old_config.config.shards[i].primary_replica == server) { - numerator += 0.0; - } else if (old_config.config.shards[i].all_replicas.count(server)) { - if (old_config.config.shards[i].nonvoting_replicas.count(server) == 0) { - numerator += 1.0; - } else { - numerator += 2.0; - } - } else { - numerator += 3.0; - } - } - } - guarantee(denominator != 0); - return numerator / denominator; -} - -/* A `pairing_t` represents the possibility of using the given server as a replica for -the given shard. - - We sort pairings according to three variables: `self_usage_cost`, `backfill_cost`, and -`other_usage_cost`. `self_usage_cost` is the sum of `PRIMARY_USAGE_COST` and -`SECONDARY_USAGE_COST` for other shards in the same table on that server; -`other_usage_cost` is for shards of other tables on the server. `backfill_cost` is the -cost to copy data to the given server, as computed by -`estimate_cost_to_get_up_to_date()`. When comparing two pairings, we first prioritize -`self_usage_cost`, then `backfill_cost`, then `other_usage_cost`. - -Because we'll be regularly updating `self_usage_cost`, we want to make updating it -inexpensive. We solve this by storing `self_usage_cost` for an entire group of pairings -(a `server_pairings_t`) simultaneously. The `server_pairings_t`s are themselves sorted -first by `self_usage_cost` and then by the cost of the cheapest internal `pairing_t`. */ - -class pairing_t { -public: - double backfill_cost; - size_t shard; -}; - -class server_pairings_t { -public: - server_pairings_t() { } - server_pairings_t(const server_pairings_t &) = default; - server_pairings_t(server_pairings_t &&) = default; - server_pairings_t &operator=(server_pairings_t &&) = default; - - int self_usage_cost; - std::multiset pairings; - int other_usage_cost; - server_id_t server; -}; - -bool operator<(const pairing_t &x, const pairing_t &y) { - return x.backfill_cost < y.backfill_cost; -} - -bool operator<(const counted_t > &x, - const counted_t > &y) { - guarantee(!x->pairings.empty()); - guarantee(!y->pairings.empty()); - if (x->self_usage_cost < y->self_usage_cost) { - return true; - } else if (x->self_usage_cost > y->self_usage_cost) { - return false; - } else if (*x->pairings.begin() < *y->pairings.begin()) { - return true; - } else if (*y->pairings.begin() < *x->pairings.begin()) { - return false; - } else { - return (x->other_usage_cost < y->other_usage_cost); - } -} - -/* `pick_best_pairings()` chooses the `num_replicas` best pairings for each shard from -the given set of pairings. It reports its choices by calling `callback`. */ -void pick_best_pairings( - size_t num_shards, - size_t num_replicas, - std::multiset > > &&pairings, - int usage_cost, - long_calculation_yielder_t *yielder, - signal_t *interruptor, - const std::function &callback) { - std::vector shard_replicas(num_shards, 0); - size_t total_replicas = 0; - while (total_replicas < num_shards * num_replicas) { - counted_t > sp = *pairings.begin(); - pairings.erase(pairings.begin()); - auto it = sp->pairings.begin(); - if (shard_replicas[it->shard] < num_replicas) { - callback(it->shard, sp->server); - ++shard_replicas[it->shard]; - ++total_replicas; - sp->self_usage_cost += usage_cost; - } - sp->pairings.erase(it); - if (!sp->pairings.empty()) { - pairings.insert(sp); - } - yielder->maybe_yield(interruptor); - } -} - -void table_generate_config( - server_config_client_t *server_config_client, - namespace_id_t table_id, - table_meta_client_t *table_meta_client, - const table_generate_config_params_t ¶ms, - const table_shard_scheme_t &shard_scheme, - signal_t *interruptor, - std::vector *config_shards_out, - server_name_map_t *server_names_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t, - admin_op_exc_t) { - long_calculation_yielder_t yielder; - - /* First, make local copies of the server name map and the list of servers with each - tag. We have to make local copies because the values returned by - `server_config_client` could change at any time, but we need the values to be - consistent. */ - server_name_map_t server_names; - std::map > servers_with_tags; - server_config_client->get_server_config_map()->read_all( - [&](const server_id_t &sid, const server_config_versioned_t *config) { - bool any = false; - for (const name_string_t &tag : config->config.tags) { - if (params.num_replicas.count(tag) != 0) { - servers_with_tags[tag].insert(sid); - any = true; - } - } - if (any) { - server_names.names.insert(std::make_pair( - sid, std::make_pair(config->version, config->config.name))); - } - }); - - validate_params(params, servers_with_tags, server_names); - - yielder.maybe_yield(interruptor); - - /* Fetch the current configurations for all tables */ - std::map old_table_configs; - std::map disconnected_tables; - table_meta_client->list_configs( - interruptor, &old_table_configs, &disconnected_tables); - - /* Find the config for the specific table we're reconfiguring, if any */ - const table_config_and_shards_t *old_config = nullptr; - if (!table_id.is_nil()) { - auto it = old_table_configs.find(table_id); - if (it == old_table_configs.end()) { - if (disconnected_tables.count(table_id) == 1) { - throw failed_table_op_exc_t(); - } else { - throw no_such_table_exc_t(); - } - } - old_config = &it->second; - } - - /* Calculate the current load on each server */ - std::map server_usage; - for (const auto &pair : old_table_configs) { - if (pair.first == table_id) { - /* Don't count the table being reconfigured in the load calculation */ - continue; - } - calculate_server_usage(pair.second.config, &server_usage); - } - - config_shards_out->resize(params.num_shards); - - size_t total_replicas = 0; - for (auto it = params.num_replicas.begin(); it != params.num_replicas.end(); ++it) { - if (it->second == 0) { - /* Avoid unnecessary computation and possibly spurious error messages */ - continue; - } - - total_replicas += it->second; - - name_string_t server_tag = it->first; - size_t num_in_tag = servers_with_tags.at(server_tag).size(); - if (num_in_tag < it->second) { - throw admin_op_exc_t( - strprintf("Can't put %zu replicas on servers with the tag `%s` because " - "there are only %zu servers with the tag `%s`. It's impossible" - " to have more replicas of the data than there are servers.", - it->second, server_tag.c_str(), - num_in_tag, server_tag.c_str()), - query_state_t::FAILED); - } - - /* Compute the desirability of each shard/server pair */ - std::map pairings; - for (const server_id_t &server : servers_with_tags.at(server_tag)) { - server_pairings_t sp; - sp.server = server; - sp.self_usage_cost = 0; - auto u_it = server_usage.find(server); - sp.other_usage_cost = (u_it == server_usage.end()) ? 0 : u_it->second; - for (size_t shard = 0; shard < params.num_shards; ++shard) { - pairing_t p; - p.shard = shard; - if (!table_id.is_nil()) { - p.backfill_cost = estimate_backfill_cost( - shard_scheme.get_shard_range(shard), *old_config, server); - } else { - /* We're creating a new table, so we won't have to backfill no matter - which servers we choose. */ - p.backfill_cost = 0; - } - sp.pairings.insert(p); - } - pairings[server] = std::move(sp); - yielder.maybe_yield(interruptor); - } - - /* This algorithm has a flaw; it will sometimes distribute replicas unevenly. For - example, suppose that we have three servers, A, B, and C; three shards; and we - want to place a primary replica and a secondary replica for each shard. We assign - primary replicas as follows: - server: A B C - primary replica: 1 2 3 - Now, it's time to assign secondary replicas. We start assigning secondaries as - follows: - server: A B C - primary replica: 1 2 3 - secondary replica: 2 1 - When it comes time to place the replica for shard 3, we cannot place it on server - C because server C is already the primary replica for shard 3. So we have to - place it on server A or B. So we end up with a server with 3 replicas and a - server with only 1 replica, instead of having two replicas on each server. */ - - /* First, select the primary replicas if appropriate. We select primary - replicas separately before selecting secondary replicas because it's important - for all the primary replicas to end up on different servers if possible. */ - if (server_tag == params.primary_replica_tag) { - std::multiset > > s; - for (const auto &x : pairings) { - if (!x.second.pairings.empty()) { - s.insert(make_counted >( - x.second)); - } - } - pick_best_pairings( - params.num_shards, - 1, /* only one primary replica per shard */ - std::move(s), - PRIMARY_USAGE_COST, - &yielder, - interruptor, - [&](size_t shard, const server_id_t &server) { - guarantee(config_shards_out->at(shard).primary_replica.get_uuid().is_unset()); - config_shards_out->at(shard).all_replicas.insert(server); - config_shards_out->at(shard).primary_replica = server; - /* We have to update `pairings` as priamry replicas are selected so - that our second call to `pick_best_pairings()` will take into account - the choices made in this round. */ - pairings[server].self_usage_cost += PRIMARY_USAGE_COST; - for (auto p_it = pairings[server].pairings.begin(); - p_it != pairings[server].pairings.end(); - ++p_it) { - if (p_it->shard == shard) { - pairings[server].pairings.erase(p_it); - break; - } - } - }); - } - - /* Now select the remaining secondary replicas. */ - std::multiset > > s; - for (const auto &x : pairings) { - if (!x.second.pairings.empty()) { - s.insert(make_counted >( - std::move(x.second))); - } - } - pick_best_pairings( - params.num_shards, - it->second - (server_tag == params.primary_replica_tag ? 1 : 0), - std::move(s), - SECONDARY_USAGE_COST, - &yielder, - interruptor, - [&](size_t shard, const server_id_t &server) { - (*config_shards_out)[shard].all_replicas.insert(server); - if (params.nonvoting_replica_tags.count(it->first) == 1) { - (*config_shards_out)[shard].nonvoting_replicas.insert(server); - } - }); - } - - for (size_t shard_ix = 0; shard_ix < params.num_shards; ++shard_ix) { - const table_config_t::shard_t &shard = (*config_shards_out)[shard_ix]; - guarantee(!shard.primary_replica.get_uuid().is_unset()); - guarantee(shard.all_replicas.size() == total_replicas); - for (const server_id_t &replica : shard.all_replicas) { - server_names_out->names[replica] = server_names.names.at(replica); - } - } -} - diff --git a/src/clustering/administration/tables/generate_config.hpp b/src/clustering/administration/tables/generate_config.hpp deleted file mode 100644 index ba02dc887e6..00000000000 --- a/src/clustering/administration/tables/generate_config.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_TABLES_GENERATE_CONFIG_HPP_ -#define CLUSTERING_ADMINISTRATION_TABLES_GENERATE_CONFIG_HPP_ - -#include -#include - -#include "clustering/administration/admin_op_exc.hpp" -#include "clustering/administration/tables/table_metadata.hpp" -#include "rdb_protocol/context.hpp" - -class real_reql_cluster_interface_t; -class server_config_client_t; -class table_meta_client_t; - -/* Suggests a `table_config_t` for the table. This is the brains behind -`table.reconfigure()`. */ -void table_generate_config( - /* This is used to get the list of servers with each tag, and to look up the - names to fill `server_names_out`. */ - server_config_client_t *server_config_client, - /* The UUID of the table being reconfigured. This can be `nil_uuid()`. */ - namespace_id_t table_id, - /* This is used to determine where the table's data is currently stored and - prioritize keeping it there. It's also used to determine where the other tables' - data is currently stored, so we can put this table's data elsewhere if possible. - */ - table_meta_client_t *table_meta_client, - /* `table_generate_config()` will validate `params`, so there's no need for the - caller to do so. */ - const table_generate_config_params_t ¶ms, - /* What the new sharding scheme for the table will be. If `table_id` is - `nil_uuid()` this is unused. */ - const table_shard_scheme_t &shard_scheme, - - signal_t *interruptor, - - std::vector *config_shards_out, - server_name_map_t *server_names_out) - - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t, - admin_op_exc_t); - -#endif /* CLUSTERING_ADMINISTRATION_TABLES_GENERATE_CONFIG_HPP_ */ - diff --git a/src/clustering/administration/tables/name_resolver.cc b/src/clustering/administration/tables/name_resolver.cc deleted file mode 100644 index 809ac3a4aa4..00000000000 --- a/src/clustering/administration/tables/name_resolver.cc +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#include "clustering/administration/tables/name_resolver.hpp" - -#include "clustering/administration/artificial_reql_cluster_interface.hpp" - -name_resolver_t::name_resolver_t( - std::shared_ptr> - cluster_semilattice_view, - table_meta_client_t *table_meta_client, - lifetime_t - artificial_reql_cluster_interface) - : m_cluster_semilattice_view(cluster_semilattice_view), - m_table_meta_client(table_meta_client), - m_artificial_reql_cluster_interface(artificial_reql_cluster_interface) { -} - -cluster_semilattice_metadata_t name_resolver_t::get_cluster_metadata() const noexcept { - return m_cluster_semilattice_view->get(); -} - -optional name_resolver_t::database_id_to_name( - database_id_t const &database_id) const noexcept { - return database_id_to_name(database_id, get_cluster_metadata()); -} - -optional name_resolver_t::database_id_to_name( - database_id_t const &database_id, - cluster_semilattice_metadata_t const &cluster_metadata) const noexcept { - if (artificial_reql_cluster_interface_t::database_id == database_id) { - return make_optional(artificial_reql_cluster_interface_t::database_name); - } - - auto iterator = cluster_metadata.databases.databases.find(database_id); - if (iterator != cluster_metadata.databases.databases.end() && - !iterator->second.is_deleted()) { - return make_optional(iterator->second.get_ref().name.get_ref()); - } - - return r_nullopt; -} - -optional name_resolver_t::table_id_to_basic_config( - namespace_id_t const &table_id, - optional const &database_id) const noexcept { - if (!static_cast(database_id) || - artificial_reql_cluster_interface_t::database_id == database_id.get()) { - for (auto const &table_backend : - m_artificial_reql_cluster_interface.get_table_backends_map()) { - if (table_backend.second.first != nullptr && - table_backend.second.first->get_table_id() == table_id) { - return make_optional(table_basic_config_t{ - table_backend.first, - artificial_reql_cluster_interface_t::database_id, - table_backend.second.first->get_primary_key_name() - }); - } - } - } - - if (m_table_meta_client != nullptr) { - table_basic_config_t table_basic_config; - try { - m_table_meta_client->get_name(table_id, &table_basic_config); - } catch (no_such_table_exc_t const &) { - return r_nullopt; - } - - if (static_cast(database_id) && - table_basic_config.database != database_id.get()) { - return r_nullopt; - } - - return make_optional(table_basic_config); - } else { - return r_nullopt; - } -} - -resolved_id_optional_t name_resolver_t::database_name_to_id( - name_string_t const &database_name) const noexcept { - return database_name_to_id(database_name, get_cluster_metadata()); -} - -resolved_id_optional_t name_resolver_t::database_name_to_id( - name_string_t const &database_name, - cluster_semilattice_metadata_t const &cluster_metadata) const noexcept { - if (artificial_reql_cluster_interface_t::database_name == database_name) { - return resolved_id_optional_t( - artificial_reql_cluster_interface_t::database_id); - } - - database_id_t database_id; - size_t count = 0; - for (auto const &database : cluster_metadata.databases.databases) { - if (!database.second.is_deleted() && - database.second.get_ref().name.get_ref() == database_name) { - database_id = database.first; - ++count; - } - } - - switch (count) { - case 0: - return resolved_id_optional_t( - resolved_id_optional_t::no_such_name_t()); - case 1: - return resolved_id_optional_t(database_id); - default: - return resolved_id_optional_t( - resolved_id_optional_t::ambiguous_name_t()); - } -} - -resolved_id_optional_t name_resolver_t::table_name_to_id( - database_id_t const &database_id, - name_string_t const &table_name) const noexcept { - if (artificial_reql_cluster_interface_t::database_id == database_id) { - auto const &table_backends_map = - m_artificial_reql_cluster_interface.get_table_backends_map(); - auto iterator = table_backends_map.find(table_name); - if (iterator != table_backends_map.end() && iterator->second.first != nullptr) { - return resolved_id_optional_t( - iterator->second.first->get_table_id()); - } else { - return resolved_id_optional_t( - resolved_id_optional_t::no_such_name_t()); - } - } - - if (m_table_meta_client != nullptr) { - namespace_id_t table_id; - try { - m_table_meta_client->find(database_id, table_name, &table_id); - } catch (no_such_table_exc_t const &) { - return resolved_id_optional_t( - resolved_id_optional_t::no_such_name_t()); - } catch (ambiguous_table_exc_t const &) { - return resolved_id_optional_t( - resolved_id_optional_t::ambiguous_name_t()); - } - return resolved_id_optional_t(table_id); - } else { - return resolved_id_optional_t( - resolved_id_optional_t::no_such_name_t()); - } -} diff --git a/src/clustering/administration/tables/name_resolver.hpp b/src/clustering/administration/tables/name_resolver.hpp deleted file mode 100644 index 435adab53d4..00000000000 --- a/src/clustering/administration/tables/name_resolver.hpp +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_TABLES_NAME_RESOLVER_HPP_ -#define CLUSTERING_ADMINISTRATION_TABLES_NAME_RESOLVER_HPP_ - -#include "clustering/administration/artificial_reql_cluster_interface.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/table_manager/table_meta_client.hpp" -#include "containers/lifetime.hpp" -#include "containers/name_string.hpp" - -template -class resolved_id_optional_t { -public: - struct no_such_name_t { }; - struct ambiguous_name_t { }; - - explicit resolved_id_optional_t(T const &value) - : m_optional(value), - m_error(error_t::OK) { } - explicit resolved_id_optional_t(T &&value) - : m_optional(std::forward(value)), - m_error(error_t::OK) { } - explicit resolved_id_optional_t(no_such_name_t) - : m_optional(r_nullopt), - m_error(error_t::NO_SUCH_NAME) { } - explicit resolved_id_optional_t(ambiguous_name_t) - : m_optional(r_nullopt), - m_error(error_t::AMBIGUOUS_NAME) { } - - explicit operator bool() const noexcept { - return m_error == error_t::OK; - } - - T const &get() const { - return m_optional.get(); - } - - bool is_no_such() const noexcept { - return m_error == error_t::NO_SUCH_NAME; - } - - bool is_ambiguous() const noexcept { - return m_error == error_t::AMBIGUOUS_NAME; - } - -private: - optional m_optional; - enum class error_t { OK = 0, NO_SUCH_NAME, AMBIGUOUS_NAME }; - error_t m_error; -}; - -class name_resolver_t { -public: - name_resolver_t( - std::shared_ptr>, - table_meta_client_t *, - lifetime_t); - - cluster_semilattice_metadata_t get_cluster_metadata() const noexcept; - - // A number of these functions come in two flavours, one where it fetches the - // metadata itself and another where you can pass it in yourself. If you're doing - // multiple operations in a row it's more efficient to get a copy of the metadata - // yourself using the function above and reusing it. - - optional database_id_to_name( - database_id_t const &) const noexcept; - optional database_id_to_name( - database_id_t const &, - cluster_semilattice_metadata_t const &) const noexcept; - - optional table_id_to_basic_config( - namespace_id_t const &, - optional const & = r_nullopt) const noexcept; - - resolved_id_optional_t database_name_to_id( - name_string_t const &) const noexcept; - resolved_id_optional_t database_name_to_id( - name_string_t const &, - cluster_semilattice_metadata_t const &) const noexcept; - - resolved_id_optional_t table_name_to_id( - database_id_t const &, - name_string_t const &) const noexcept; - -private: - std::shared_ptr> - m_cluster_semilattice_view; - table_meta_client_t *m_table_meta_client; - artificial_reql_cluster_interface_t const &m_artificial_reql_cluster_interface; -}; - -#endif // CLUSTERING_ADMINISTRATION_TABLES_NAME_RESOLVER_HPP_ diff --git a/src/clustering/administration/tables/split_points.cc b/src/clustering/administration/tables/split_points.cc deleted file mode 100644 index 01b7150db0c..00000000000 --- a/src/clustering/administration/tables/split_points.cc +++ /dev/null @@ -1,243 +0,0 @@ -#include "clustering/administration/tables/split_points.hpp" - -#include "clustering/administration/real_reql_cluster_interface.hpp" -#include "math.hpp" /* for `clamp()` */ -#include "rdb_protocol/real_table.hpp" - -/* `interpolate_key()` produces a `store_key_t` that is interpolated between `in1` and -`in2`. For example, if `fraction` is 0.50, the return value will be halfway between `in1` -and `in2`; if it's 0.25, the return value will be closer to `in1`; and so on. This -function is not exact; the only absolute guarantee it provides is that the return value -will lie in the range `in1 <= out <= in2`. */ -static store_key_t interpolate_key(store_key_t in1, store_key_t in2, double fraction) { - rassert(in1 <= in2); - rassert(fraction >= 0 && fraction <= 1); - const uint8_t *in1_buf = in1.contents(), *in2_buf = in2.contents(); - uint8_t out_buf[MAX_KEY_SIZE]; - - /* Calculate the shared prefix of `in1` and `in2` */ - size_t i = 0; - while (i < static_cast(in1.size()) && - i < static_cast(in2.size()) && - in1_buf[i] == in2_buf[i]) { - out_buf[i] = in1_buf[i]; - ++i; - } - - /* Convert the first non-shared parts of `in1` and `in2` into integers so we can do - arithmetic on them. If `in1` or `in2` terminates early, pad it with zeroes. This - isn't perfect but the error doesn't matter for our purposes. */ - uint32_t in1_tail = 0, in2_tail = 0; - for (size_t j = i; j < i + sizeof(in1_tail); ++j) { - uint8_t c1 = j < static_cast(in1.size()) ? in1_buf[j] : 0; - uint8_t c2 = j < static_cast(in2.size()) ? in2_buf[j] : 0; - in1_tail = (in1_tail << 8) + c1; - in2_tail = (in2_tail << 8) + c2; - } - rassert(in1_tail <= in2_tail); - - /* Compute an integer representation of the interpolated value */ - uint32_t out_tail = in1_tail * (1 - fraction) + in2_tail * fraction; - - /* Append the interpolated value onto `out_buf`, discarding trailing null bytes */ - size_t num_interp = 0; - while (num_interp < sizeof(out_tail) && out_tail != 0 && i + num_interp < MAX_KEY_SIZE) { - out_buf[i + num_interp] = (out_tail >> 24) & 0xFF; - ++num_interp; - out_tail <<= 8; - } - - /* Construct the final result */ - store_key_t out(i + num_interp, out_buf); - - /* For various reasons (rounding errors, corner cases involving keys very close - together, etc.), it's possible that the above procedure will produce an `out` that is - not between `in1` and `in2`. Rather than trying to interpolate properly in these - complicated cases, we just clamp the result. */ - out = clamp(out, in1, in2); - - return out; -} - -/* `ensure_distinct()` ensures that all of the `store_key_t`s in the given vector are -distinct from eachother. Initially, they should be non-strictly monotonically increasing; -upon return, they will be strictly monotonically increasing. */ -static void ensure_distinct(std::vector *split_points) { - for (size_t i = 1; i < split_points->size(); ++i) { - /* Make sure the initial condition is met */ - guarantee(split_points->at(i) >= split_points->at(i-1)); - } - for (size_t i = 1; i < split_points->size(); ++i) { - /* Normally, we fix any overlaps by just pushing keys forward. */ - while (split_points->at(i) <= split_points->at(i-1)) { - bool ok = split_points->at(i).increment(); - if (!ok) { - /* Oops, we ran into the maximum possible key. This is incredibly - unlikely in practice, but we handle it anyway. */ - size_t j = i; - while (j > 1 && split_points->at(j) == split_points->at(j-1)) { - bool ok2 = split_points->at(j-1).decrement(); - guarantee(ok2); - --j; - } - } - } - } -} - -void fetch_distribution( - const namespace_id_t &table_id, - real_reql_cluster_interface_t *reql_cluster_interface, - signal_t *interruptor, - std::map *counts_out) - THROWS_ONLY(interrupted_exc_t, failed_table_op_exc_t, no_such_table_exc_t) { - namespace_interface_access_t ns_if_access = - reql_cluster_interface->get_namespace_repo()->get_namespace_interface( - table_id, interruptor); - static const int depth = 2; - static const int limit = 128; - distribution_read_t inner_read(depth, limit); - read_t read(inner_read, profile_bool_t::DONT_PROFILE, read_mode_t::OUTDATED); - read_response_t resp; - try { - ns_if_access.get()->read( - auth::user_context_t(auth::permissions_t(tribool::True, tribool::False, tribool::False, tribool::False)), - read, - &resp, - order_token_t::ignore, - interruptor); - } catch (const cannot_perform_query_exc_t &) { - /* If the table was deleted, this will throw `no_such_table_exc_t` */ - table_basic_config_t dummy; - reql_cluster_interface->get_table_meta_client()->get_name(table_id, &dummy); - /* If `get_name()` didn't throw, the table exists but is inaccessible */ - throw failed_table_op_exc_t(); - } - *counts_out = std::move( - boost::get(resp.response).key_counts); -} - -bool calculate_split_points_with_distribution( - const std::map &counts, - size_t num_shards, - table_shard_scheme_t *split_points_out) { - std::vector > pairs; - int64_t total_count = 0; - for (auto const &pair : counts) { - if (pair.second != 0) { - pairs.push_back(std::make_pair(total_count, pair.first)); - } - total_count += pair.second; - } - if (pairs.size() < static_cast(num_shards)) { - return false; - } - - split_points_out->split_points.clear(); - size_t left_pair = 0; - for (size_t split_index = 1; split_index < num_shards; ++split_index) { - int64_t split_count = (split_index * total_count) / num_shards; - rassert(pairs[left_pair].first <= split_count); - while (left_pair+1 < pairs.size() && - pairs[left_pair+1].first <= split_count) { - ++left_pair; - } - std::pair left = pairs[left_pair]; - std::pair right = - (left_pair == pairs.size() - 1) - ? std::make_pair(total_count, store_key_t::max()) - : pairs[left_pair+1]; - store_key_t split_key = interpolate_key(left.second, right.second, - (split_count - left.first) / static_cast(right.first - left.first)); - split_points_out->split_points.push_back(split_key); - } - ensure_distinct(&split_points_out->split_points); - - return true; -} - -store_key_t key_for_uuid(uint64_t first_8_bytes) { - uuid_u uuid; - memset(uuid.data(), 0, uuid_u::static_size()); - for (size_t i = 0; i < 8; i++) { - /* Copy one byte at a time to avoid endianness issues */ - uuid.data()[i] = (first_8_bytes >> (8 * (7 - i))) & 0xFF; - } - return store_key_t(ql::datum_t(datum_string_t(uuid_to_str(uuid))).print_primary()); -} - -/* `calculate_split_points_for_uuids` generates a set of split points that will divide -the range of UUIDs evenly. */ -void calculate_split_points_for_uuids( - size_t num_shards, - table_shard_scheme_t *split_points_out) { - split_points_out->split_points.clear(); - for (size_t i = 0; i < num_shards-1; ++i) { - split_points_out->split_points.push_back(key_for_uuid( - (std::numeric_limits::max() / num_shards) * (i+1))); - guarantee(i == 0 || - split_points_out->split_points[i] > split_points_out->split_points[i-1]); - } -} - -/* In practice this will only ever be used to decrease the number of shards, but it still -works correctly whether the number of shards is increased, decreased, or stays the same. -*/ -void calculate_split_points_by_interpolation( - size_t num_shards, - const table_shard_scheme_t &old_split_points, - table_shard_scheme_t *split_points_out) { - /* Short circuit this case because it's both trivial and common */ - if (num_shards == old_split_points.num_shards()) { - *split_points_out = old_split_points; - return; - } - split_points_out->split_points.clear(); - for (size_t split_index = 1; split_index < num_shards; ++split_index) { - double split_old_index = split_index * - (old_split_points.num_shards() / static_cast(num_shards)); - guarantee(split_old_index >= 0); - guarantee(split_old_index <= old_split_points.num_shards()); - size_t left_old_index = floor(split_old_index); - guarantee(left_old_index <= old_split_points.num_shards() - 1); - store_key_t left_key = - (left_old_index == 0) - ? store_key_t::min() - : old_split_points.split_points[left_old_index-1]; - store_key_t right_key = - (left_old_index == old_split_points.num_shards() - 1) - ? store_key_t::max() - : old_split_points.split_points[left_old_index]; - store_key_t split_key = interpolate_key(left_key, right_key, - split_old_index-left_old_index); - split_points_out->split_points.push_back(split_key); - } - ensure_distinct(&split_points_out->split_points); -} - -void calculate_split_points_intelligently( - namespace_id_t table_id, - real_reql_cluster_interface_t *reql_cluster_interface, - size_t num_shards, - const table_shard_scheme_t &old_split_points, - signal_t *interruptor, - table_shard_scheme_t *split_points_out) - THROWS_ONLY(interrupted_exc_t, failed_table_op_exc_t, no_such_table_exc_t) { - if (num_shards > old_split_points.num_shards()) { - std::map counts; - fetch_distribution(table_id, reql_cluster_interface, interruptor, &counts); - if (!calculate_split_points_with_distribution( - counts, num_shards, split_points_out)) { - /* There aren't enough documents to calculate distribution. We'll just assume - the user is going to use UUID primary keys. If we got it wrong, they will end - up with horribly unbalanced data, but it's the best we can do. */ - calculate_split_points_for_uuids(num_shards, split_points_out); - } - } else if (num_shards == old_split_points.num_shards()) { - *split_points_out = old_split_points; - } else { - calculate_split_points_by_interpolation( - num_shards, old_split_points, split_points_out); - } -} - diff --git a/src/clustering/administration/tables/split_points.hpp b/src/clustering/administration/tables/split_points.hpp deleted file mode 100644 index 922ffda83c9..00000000000 --- a/src/clustering/administration/tables/split_points.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_TABLES_SPLIT_POINTS_HPP_ -#define CLUSTERING_ADMINISTRATION_TABLES_SPLIT_POINTS_HPP_ - -#include -#include -#include - -#include "btree/keys.hpp" -#include "clustering/table_manager/table_meta_client.hpp" -#include "containers/uuid.hpp" - -class real_reql_cluster_interface_t; -class signal_t; -class table_shard_scheme_t; - -/* `fetch_distribution` fetches the distribution information from the database. */ -void fetch_distribution( - const namespace_id_t &table_id, - real_reql_cluster_interface_t *reql_cluster_interface, - signal_t *interruptor, - std::map *counts_out) - THROWS_ONLY(interrupted_exc_t, failed_table_op_exc_t, no_such_table_exc_t); - -/* `calculate_split_points_with_distribution` generates a set of split points that are -guaranteed to divide the data approximately evenly, using the results of -`fetch_distribution()`. It returns `false` if there are too few documents in the -database. */ -bool calculate_split_points_with_distribution( - const std::map &counts, - size_t num_shards, - table_shard_scheme_t *split_points_out); - -/* `calculate_split_points_for_uuids` generates a set of split points that will divide -the range of UUIDs evenly. */ -void calculate_split_points_for_uuids( - size_t num_shards, - table_shard_scheme_t *split_points_out); - -/* `calculate_split_points_by_interpolation` generates a set of split points on the -assumption that the given previous set of split points is evenly distributed. If the new -requested number of shards is different from the previous number of shards, it will use -interpolation. */ -void calculate_split_points_by_interpolation( - size_t num_shards, - const table_shard_scheme_t &old_split_points, - table_shard_scheme_t *split_points_out); - -/* `calculate_split_points_intelligently` picks one of the above methods based on its -input. If the number of shards is being increased, it takes a distribution; if the number -is being decreased, it interpolates; and if the number stays the same, it uses the old -split points. It fails if it can't read the distribution from the database. */ -void calculate_split_points_intelligently( - namespace_id_t table_id, - real_reql_cluster_interface_t *reql_cluster_interface, - size_t num_shards, - const table_shard_scheme_t &old_split_points, - signal_t *interruptor, - table_shard_scheme_t *split_points_out) - THROWS_ONLY(interrupted_exc_t, failed_table_op_exc_t, no_such_table_exc_t); - -#endif /* CLUSTERING_ADMINISTRATION_TABLES_SPLIT_POINTS_HPP_ */ - diff --git a/src/clustering/administration/tables/table_common.cc b/src/clustering/administration/tables/table_common.cc deleted file mode 100644 index 5889fc24e85..00000000000 --- a/src/clustering/administration/tables/table_common.cc +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/tables/table_common.hpp" - -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/table_manager/table_meta_client.hpp" -#include "concurrency/cross_thread_signal.hpp" - -common_table_artificial_table_backend_t::common_table_artificial_table_backend_t( - name_string_t const &table_name, - rdb_context_t *rdb_context, - lifetime_t name_resolver, - std::shared_ptr > _semilattice_view, - table_meta_client_t *_table_meta_client, - admin_identifier_format_t _identifier_format) - : timer_cfeed_artificial_table_backend_t(table_name, rdb_context, name_resolver), - semilattice_view(_semilattice_view), - table_meta_client(_table_meta_client), - identifier_format(_identifier_format) { - semilattice_view->assert_thread(); -} - -std::string common_table_artificial_table_backend_t::get_primary_key_name() { - return "id"; -} - -bool common_table_artificial_table_backend_t::read_all_rows_as_vector( - auth::user_context_t const &user_context, - signal_t *interruptor_on_caller, - std::vector *rows_out, - UNUSED admin_err_t *error_out) { - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - cluster_semilattice_metadata_t metadata = semilattice_view->get(); - std::map configs; - std::map disconnected_configs; - table_meta_client->list_configs( - &interruptor_on_home, &configs, &disconnected_configs); - rows_out->clear(); - pmap(configs.cbegin(), configs.cend(), - [&](const std::pair &pair) { - ql::datum_t db_name_or_uuid; - if (!convert_database_id_to_datum( - pair.second.config.basic.database, identifier_format, metadata, - &db_name_or_uuid, nullptr)) { - db_name_or_uuid = ql::datum_t("__deleted_database__"); - } - try { - ql::datum_t row; - format_row( - user_context, - pair.first, - pair.second, - db_name_or_uuid, - &interruptor_on_home, &row); - rows_out->push_back(row); - } catch (const no_such_table_exc_t &) { - /* The table got deleted between the call to `list_configs()` and the - call to `format_row()`. Ignore it. */ - } catch (const failed_table_op_exc_t &) { - ql::datum_t row; - format_error_row( - user_context, - pair.first, - db_name_or_uuid, - pair.second.config.basic.name, - &row); - rows_out->push_back(row); - } catch (const interrupted_exc_t &) { - /* We're handling this outside the `pmap` */ - } - }); - if (interruptor_on_home.is_pulsed()) { - throw interrupted_exc_t(); - } - for (const auto &pair : disconnected_configs) { - ql::datum_t db_name_or_uuid; - if (!convert_database_id_to_datum( - pair.second.database, identifier_format, metadata, - &db_name_or_uuid, nullptr)) { - db_name_or_uuid = ql::datum_t("__deleted_database__"); - } - ql::datum_t row; - format_error_row( - user_context, pair.first, db_name_or_uuid, pair.second.name, &row); - rows_out->push_back(row); - } - return true; -} - -bool common_table_artificial_table_backend_t::read_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor_on_caller, - ql::datum_t *row_out, - UNUSED admin_err_t *error_out) { - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - cluster_semilattice_metadata_t metadata = semilattice_view->get(); - namespace_id_t table_id; - admin_err_t dummy_error; - if (!convert_uuid_from_datum(primary_key, &table_id, &dummy_error)) { - /* If the primary key was not a valid UUID, then it must refer to a nonexistent - row. */ - *row_out = ql::datum_t(); - return true; - } - try { - table_basic_config_t basic_config; - table_meta_client->get_name(table_id, &basic_config); - - ql::datum_t db_name_or_uuid; - name_string_t db_name; - if (!convert_database_id_to_datum(basic_config.database, identifier_format, - metadata, &db_name_or_uuid, &db_name)) { - db_name_or_uuid = ql::datum_t("__deleted_database__"); - db_name = name_string_t::guarantee_valid("__deleted_database__"); - } - - table_config_and_shards_t config; - try { - table_meta_client->get_config(table_id, &interruptor_on_home, &config); - format_row( - user_context, - table_id, - config, - db_name_or_uuid, - &interruptor_on_home, - row_out); - } catch (const failed_table_op_exc_t &) { - format_error_row( - user_context, table_id, db_name_or_uuid, basic_config.name, row_out); - } - return true; - } catch (const no_such_table_exc_t &) { - *row_out = ql::datum_t(); - return true; - } -} - -void common_table_artificial_table_backend_t::format_error_row( - UNUSED auth::user_context_t const &user_context, - const namespace_id_t &table_id, - const ql::datum_t &db_name_or_uuid, - const name_string_t &table_name, - ql::datum_t *row_out) { - ql::datum_object_builder_t builder; - builder.overwrite("id", convert_uuid_to_datum(table_id)); - builder.overwrite("db", db_name_or_uuid); - builder.overwrite("name", convert_name_to_datum(table_name)); - builder.overwrite("error", - ql::datum_t("None of the replicas for this table are accessible.")); - *row_out = std::move(builder).to_datum(); -} - diff --git a/src/clustering/administration/tables/table_common.hpp b/src/clustering/administration/tables/table_common.hpp deleted file mode 100644 index 7b132d281c0..00000000000 --- a/src/clustering/administration/tables/table_common.hpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_TABLES_TABLE_COMMON_HPP_ -#define CLUSTERING_ADMINISTRATION_TABLES_TABLE_COMMON_HPP_ - -#include -#include -#include - -#include "clustering/administration/admin_op_exc.hpp" -#include "containers/lifetime.hpp" -#include "rdb_protocol/artificial_table/caching_cfeed_backend.hpp" -#include "rdb_protocol/context.hpp" -#include "rpc/semilattice/view.hpp" - -class cluster_semilattice_metadata_t; - -/* This is a base class for the `rethinkdb.table_config` and `rethinkdb.table_status` -pseudo-tables. Subclasses should implement `format_row()` and `write_row()`. */ - -class common_table_artificial_table_backend_t : - public timer_cfeed_artificial_table_backend_t -{ -public: - common_table_artificial_table_backend_t( - name_string_t const &table_name, - rdb_context_t *rdb_context, - lifetime_t name_resolver, - std::shared_ptr< semilattice_readwrite_view_t< - cluster_semilattice_metadata_t> > _semilattice_view, - table_meta_client_t *_table_meta_client, - admin_identifier_format_t _identifier_format); - - std::string get_primary_key_name(); - - bool read_all_rows_as_vector( - auth::user_context_t const &user_context, - signal_t *interruptor_on_caller, - std::vector *rows_out, - admin_err_t *error_out); - - bool read_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - signal_t *interruptor_on_caller, - ql::datum_t *row_out, - admin_err_t *error_out); - -protected: - /* This will always be called on the home thread */ - virtual void format_row( - auth::user_context_t const &user_context, - const namespace_id_t &table_id, - const table_config_and_shards_t &config, - /* In theory `db_name_or_uuid` can be computed from `config`, but the - computation is non-trivial, so it's easier if `table_common` does it for all - the subclasses. */ - const ql::datum_t &db_name_or_uuid, - signal_t *interruptor_on_home, - ql::datum_t *row_out) - THROWS_ONLY( - interrupted_exc_t, - no_such_table_exc_t, - failed_table_op_exc_t, - auth::permission_error_t) = 0; - - /* This is called for tables that we couldn't even determine the current - configuration of, because every single server for the table is disconnected. The - default implementation produces a document with an `error` field describing the - problem. */ - virtual void format_error_row( - auth::user_context_t const &user_context, - const namespace_id_t &table_id, - const ql::datum_t &db_name_or_uuid, - const name_string_t &table_name, - ql::datum_t *row_out); - - std::shared_ptr< semilattice_readwrite_view_t< - cluster_semilattice_metadata_t> > semilattice_view; - table_meta_client_t *table_meta_client; - admin_identifier_format_t identifier_format; -}; - -#endif /* CLUSTERING_ADMINISTRATION_TABLES_TABLE_COMMON_HPP_ */ - diff --git a/src/clustering/administration/tables/table_config.cc b/src/clustering/administration/tables/table_config.cc deleted file mode 100644 index 2a04057e9dd..00000000000 --- a/src/clustering/administration/tables/table_config.cc +++ /dev/null @@ -1,843 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/tables/table_config.hpp" - -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/administration/tables/generate_config.hpp" -#include "clustering/administration/tables/split_points.hpp" -#include "clustering/table_manager/table_meta_client.hpp" -#include "concurrency/cross_thread_signal.hpp" -#include "containers/archive/string_stream.hpp" -#include "rdb_protocol/terms/write_hook.hpp" - -table_config_artificial_table_backend_t::table_config_artificial_table_backend_t( - rdb_context_t *_rdb_context, - lifetime_t name_resolver, - std::shared_ptr< semilattice_readwrite_view_t< - cluster_semilattice_metadata_t> > _semilattice_view, - real_reql_cluster_interface_t *_reql_cluster_interface, - admin_identifier_format_t _identifier_format, - server_config_client_t *_server_config_client, - table_meta_client_t *_table_meta_client) - : common_table_artificial_table_backend_t( - name_string_t::guarantee_valid("table_config"), - _rdb_context, - name_resolver, - _semilattice_view, - _table_meta_client, - _identifier_format), - rdb_context(_rdb_context), - reql_cluster_interface(_reql_cluster_interface), - server_config_client(_server_config_client) { -} - -table_config_artificial_table_backend_t::~table_config_artificial_table_backend_t() { - begin_changefeed_destruction(); -} - -bool convert_server_id_from_datum( - const ql::datum_t &datum, - admin_identifier_format_t identifier_format, - server_config_client_t *server_config_client, - const server_name_map_t &old_server_names, - server_id_t *server_id_out, - server_name_map_t *server_names_out, - admin_err_t *error_out) { - if (identifier_format == admin_identifier_format_t::name) { - name_string_t name; - if (!convert_name_from_datum(datum, "server name", &name, error_out)) { - return false; - } - /* First we look up the server in `old_server_names`; if that fails, we try again - in `server_config_client->get_server_config_map()`. We prefer `old_server_names` - because that will reduce the likelihood of an accidental change if the servers' - names have changed recently. */ - size_t count = 0; - for (const auto &pair : old_server_names.names) { - if (pair.second.second == name) { - *server_id_out = pair.first; - server_names_out->names.insert(pair); - ++count; - } - } - if (count == 0) { - server_config_client->get_server_config_map()->read_all( - [&](const server_id_t &server_id, const server_config_versioned_t *config) { - if (config->config.name == name) { - *server_id_out = server_id; - server_names_out->names.insert(std::make_pair( - server_id, std::make_pair(config->version, name))); - ++count; - } - }); - } - if (count >= 2) { - *error_out = admin_err_t{ - strprintf("Server `%s` is ambiguous; there are multiple " - "servers with that name.", name.c_str()), - query_state_t::FAILED}; - return false; - } else if (count == 0) { - *error_out = admin_err_t{ - strprintf("Server `%s` does not exist or is not connected.", - name.c_str()), - query_state_t::FAILED}; - return false; - } else { - return true; - } - } else { - if (!convert_server_id_from_datum(datum, server_id_out, error_out)) { - return false; - } - /* We know the server's UUID, but we need to confirm that it exists and determine - its name. First we look up the server name in `get_server_config_map()`; if that - fails, then we look it up in `old_server_names`. We prefer - `get_server_config_map()` because the name information there is more likely to be - up-to-date. */ - bool found; - server_config_client->get_server_config_map()->read_key(*server_id_out, - [&](const server_config_versioned_t *config) { - if (config != nullptr) { - server_names_out->names.insert(std::make_pair(*server_id_out, - std::make_pair(config->version, config->config.name))); - found = true; - } else { - found = false; - } - }); - if (found) { - return true; - } - auto it = old_server_names.names.find(*server_id_out); - if (it != old_server_names.names.end()) { - server_names_out->names.insert(*it); - return true; - } - *error_out = admin_err_t{ - strprintf("There is no server with UUID `%s`.", - uuid_to_str(server_id_out->get_uuid()).c_str()), - query_state_t::FAILED}; - return false; - } -} - -ql::datum_t convert_replica_list_to_datum( - const std::set &replicas, - admin_identifier_format_t identifier_format, - const server_name_map_t &server_names) { - ql::datum_array_builder_t replicas_builder(ql::configured_limits_t::unlimited); - for (const server_id_t &replica : replicas) { - replicas_builder.add(convert_name_or_uuid_to_datum( - server_names.get(replica), replica.get_uuid(), identifier_format)); - } - return std::move(replicas_builder).to_datum(); -} - -bool convert_replica_list_from_datum( - const ql::datum_t &datum, - admin_identifier_format_t identifier_format, - server_config_client_t *server_config_client, - const server_name_map_t &old_server_names, - std::set *replicas_out, - server_name_map_t *server_names_out, - admin_err_t *error_out) { - if (datum.get_type() != ql::datum_t::R_ARRAY) { - *error_out = admin_err_t{ - "Expected an array, got " + datum.print(), - query_state_t::FAILED}; - return false; - } - replicas_out->clear(); - for (size_t i = 0; i < datum.arr_size(); ++i) { - server_id_t server_id; - if (!convert_server_id_from_datum(datum.get(i), identifier_format, - server_config_client, old_server_names, &server_id, server_names_out, - error_out)) { - return false; - } - auto pair = replicas_out->insert(server_id); - if (!pair.second) { - *error_out = admin_err_t{ - strprintf("Server `%s` is listed more than once.", - server_names_out->get(server_id).c_str()), - query_state_t::FAILED}; - return false; - } - } - return true; -} - -ql::datum_t convert_write_ack_config_to_datum( - const write_ack_config_t &config) { - switch (config) { - case write_ack_config_t::SINGLE: - return ql::datum_t("single"); - case write_ack_config_t::MAJORITY: - return ql::datum_t("majority"); - default: - unreachable(); - } -} - -bool convert_write_ack_config_from_datum( - const ql::datum_t &datum, - write_ack_config_t *config_out, - admin_err_t *error_out) { - if (datum == ql::datum_t("single")) { - *config_out = write_ack_config_t::SINGLE; - } else if (datum == ql::datum_t("majority")) { - *config_out = write_ack_config_t::MAJORITY; - } else { - *error_out = admin_err_t{ - "Expected \"single\" or \"majority\", got: " + datum.print(), - query_state_t::FAILED}; - return false; - } - return true; -} - -ql::datum_t convert_durability_to_datum( - write_durability_t durability) { - switch (durability) { - case write_durability_t::SOFT: - return ql::datum_t("soft"); - case write_durability_t::HARD: - return ql::datum_t("hard"); - case write_durability_t::INVALID: - default: - unreachable(); - } -} - -bool convert_durability_from_datum( - const ql::datum_t &datum, - write_durability_t *durability_out, - admin_err_t *error_out) { - if (datum == ql::datum_t("soft")) { - *durability_out = write_durability_t::SOFT; - } else if (datum == ql::datum_t("hard")) { - *durability_out = write_durability_t::HARD; - } else { - *error_out = admin_err_t{ - "Expected \"soft\" or \"hard\", got: " + datum.print(), - query_state_t::FAILED}; - return false; - } - return true; -} - -ql::datum_t convert_table_config_shard_to_datum( - const table_config_t::shard_t &shard, - admin_identifier_format_t identifier_format, - const server_name_map_t &server_names) { - ql::datum_object_builder_t builder; - - builder.overwrite("replicas", - convert_replica_list_to_datum( - shard.all_replicas, identifier_format, server_names)); - builder.overwrite("nonvoting_replicas", - convert_replica_list_to_datum( - shard.nonvoting_replicas, identifier_format, server_names)); - builder.overwrite("primary_replica", - convert_name_or_uuid_to_datum(server_names.get(shard.primary_replica), - shard.primary_replica.get_uuid(), identifier_format)); - - return std::move(builder).to_datum(); -} - -bool convert_table_config_shard_from_datum( - ql::datum_t datum, - admin_identifier_format_t identifier_format, - server_config_client_t *server_config_client, - const server_name_map_t &old_server_names, - table_config_t::shard_t *shard_out, - server_name_map_t *server_names_out, - admin_err_t *error_out) { - converter_from_datum_object_t converter; - if (!converter.init(datum, error_out)) { - return false; - } - - ql::datum_t all_replicas_datum; - if (!converter.get("replicas", &all_replicas_datum, error_out)) { - return false; - } - if (!convert_replica_list_from_datum(all_replicas_datum, identifier_format, - server_config_client, old_server_names, &shard_out->all_replicas, - server_names_out, error_out)) { - error_out->msg = "In `replicas`: " + error_out->msg; - return false; - } - if (shard_out->all_replicas.empty()) { - *error_out = admin_err_t{ - "You must specify at least one replica for each shard.", - query_state_t::FAILED}; - return false; - } - - ql::datum_t nonvoting_replicas_datum; - if (converter.get("nonvoting_replicas", &nonvoting_replicas_datum, error_out)) { - if (!convert_replica_list_from_datum(nonvoting_replicas_datum, identifier_format, - server_config_client, old_server_names, &shard_out->nonvoting_replicas, - server_names_out, error_out)) { - error_out->msg = "In `nonvoting_replicas`: " + error_out->msg; - return false; - } - for (const server_id_t &server : shard_out->nonvoting_replicas) { - if (shard_out->all_replicas.count(server) != 1) { - *error_out = admin_err_t{ - "Every server listed in the `nonvoting_replicas` field " - "must also appear in `replicas`.", - query_state_t::FAILED}; - return false; - } - } - } else { - shard_out->nonvoting_replicas.clear(); - } - - ql::datum_t primary_replica_datum; - if (!converter.get("primary_replica", &primary_replica_datum, error_out)) { - return false; - } - - if (!convert_server_id_from_datum(primary_replica_datum, identifier_format, - server_config_client, old_server_names, &shard_out->primary_replica, - server_names_out, error_out)) { - error_out->msg = "In `primary_replica`: " + error_out->msg; - return false; - } - if (shard_out->all_replicas.count(shard_out->primary_replica) != 1) { - *error_out = admin_err_t{ - strprintf("The server listed in the `primary_replica` field " - "(`%s`) must also appear in `replicas`.", - server_names_out->get(shard_out->primary_replica).c_str()), - query_state_t::FAILED}; - return false; - } - if (shard_out->nonvoting_replicas.count(shard_out->primary_replica) == 1) { - *error_out = admin_err_t{ - strprintf("The primary replica (`%s`) must not be a nonvoting replica.", - server_names_out->get(shard_out->primary_replica).c_str()), - query_state_t::FAILED}; - return false; - } - - if (!converter.check_no_extra_keys(error_out)) { - return false; - } - - return true; -} - -ql::datum_t convert_sindexes_to_datum( - const std::map &sindexes) { - ql::datum_array_builder_t sindexes_builder(ql::configured_limits_t::unlimited); - for (const auto &sindex : sindexes) { - sindexes_builder.add(convert_string_to_datum(sindex.first)); - } - return std::move(sindexes_builder).to_datum(); -} - -ql::datum_t convert_write_hook_to_datum( - const optional &write_hook) { - - ql::datum_t res = ql::datum_t::null(); - if (write_hook) { - write_message_t wm; - serialize( - &wm, write_hook->func); - string_stream_t stream; - int write_res = send_write_message(&stream, &wm); - - rcheck_toplevel(write_res == 0, - ql::base_exc_t::LOGIC, - "Invalid write hook."); - - ql::datum_t binary = ql::datum_t::binary( - datum_string_t(write_hook_blob_prefix + stream.str())); - res = - ql::datum_t{ - std::map{ - std::pair( - datum_string_t("function"), binary), - std::pair( - datum_string_t("query"), - ql::datum_t( - datum_string_t( - format_write_hook_query(write_hook.get()))))}}; - } - return res; -} - -bool convert_sindexes_from_datum( - ql::datum_t datum, - std::set *indexes_out, - admin_err_t *error_out) { - if (!convert_set_from_datum( - &convert_string_from_datum, false, datum, indexes_out, error_out)) { - error_out->msg = "In `indexes`: " + error_out->msg; - return false; - } - - return true; -} - -/* This is separate from `format_row()` because it needs to be publicly exposed so it can - be used to create the return value of `table.reconfigure()`. */ -ql::datum_t convert_table_config_to_datum( - namespace_id_t table_id, - const ql::datum_t &db_name_or_uuid, - const table_config_t &config, - admin_identifier_format_t identifier_format, - const server_name_map_t &server_names) { - ql::datum_object_builder_t builder; - builder.overwrite("name", convert_name_to_datum(config.basic.name)); - builder.overwrite("db", db_name_or_uuid); - builder.overwrite("id", convert_uuid_to_datum(table_id)); - builder.overwrite("indexes", convert_sindexes_to_datum(config.sindexes)); - builder.overwrite("write_hook", convert_write_hook_to_datum(config.write_hook)); - builder.overwrite("primary_key", convert_string_to_datum(config.basic.primary_key)); - builder.overwrite("shards", - convert_vector_to_datum( - [&](const table_config_t::shard_t &shard) { - return convert_table_config_shard_to_datum( - shard, identifier_format, server_names); - }, - config.shards)); - builder.overwrite("write_acks", - convert_write_ack_config_to_datum(config.write_ack_config)); - builder.overwrite("durability", - convert_durability_to_datum(config.durability)); - return std::move(builder).to_datum(); -} - -void table_config_artificial_table_backend_t::format_row( - UNUSED auth::user_context_t const &user_context, - const namespace_id_t &table_id, - const table_config_and_shards_t &config, - const ql::datum_t &db_name_or_uuid, - UNUSED signal_t *interruptor_on_home, - ql::datum_t *row_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t) { - assert_thread(); - *row_out = convert_table_config_to_datum(table_id, db_name_or_uuid, - config.config, identifier_format, config.server_names); -} - -bool convert_table_config_and_name_from_datum( - ql::datum_t datum, - bool existed_before, - const cluster_semilattice_metadata_t &all_metadata, - admin_identifier_format_t identifier_format, - server_config_client_t *server_config_client, - const table_config_and_shards_t &old_config, - table_meta_client_t *table_meta_client, - signal_t *interruptor, - namespace_id_t *id_out, - table_config_t *config_out, - server_name_map_t *server_names_out, - name_string_t *db_name_out, - admin_err_t *error_out) - THROWS_ONLY(interrupted_exc_t, admin_op_exc_t) { - /* In practice, the input will always be an object and the `id` field will always - be valid, because `artificial_table_t` will check those thing before passing the - row to `table_config_artificial_table_backend_t`. But we check them anyway for - consistency. */ - converter_from_datum_object_t converter; - if (!converter.init(datum, error_out)) { - return false; - } - - ql::datum_t name_datum; - if (!converter.get("name", &name_datum, error_out)) { - return false; - } - if (!convert_name_from_datum( - name_datum, "table name", &config_out->basic.name, error_out)) { - error_out->msg = "In `name`: " + error_out->msg; - return false; - } - - ql::datum_t db_datum; - if (!converter.get("db", &db_datum, error_out)) { - return false; - } - if (!convert_database_id_from_datum( - db_datum, identifier_format, all_metadata, &config_out->basic.database, - db_name_out, error_out)) { - return false; - } - - ql::datum_t id_datum; - if (!converter.get("id", &id_datum, error_out)) { - return false; - } - if (!convert_uuid_from_datum(id_datum, id_out, error_out)) { - error_out->msg = "In `id`: " + error_out->msg; - return false; - } - - /* As a special case, we allow the user to omit `indexes`, `primary_key`, `shards`, - `write_acks`, and/or `durability` for newly-created tables. */ - - if (converter.has("indexes")) { - ql::datum_t indexes_datum; - if (!converter.get("indexes", &indexes_datum, error_out)) { - return false; - } - std::set sindexes; - if (!convert_sindexes_from_datum(indexes_datum, &sindexes, error_out)) { - return false; - } - - if (existed_before) { - bool equal = sindexes.size() == old_config.config.sindexes.size(); - for (const auto &old_sindex : old_config.config.sindexes) { - equal &= sindexes.count(old_sindex.first) == 1; - } - if (!equal) { - error_out->msg = "The `indexes` field is read-only and can't be used to " - "create or drop indexes."; - return false; - } - config_out->sindexes = old_config.config.sindexes; - } else if (!sindexes.empty()) { - error_out->msg = "The `indexes` field is read-only and can't be used to " - "create indexes."; - return false; - } - } else { - if (existed_before) { - error_out->msg = "Expected a field named `indexes`."; - return false; - } - } - - if (existed_before || converter.has("primary_key")) { - ql::datum_t primary_key_datum; - if (!converter.get("primary_key", &primary_key_datum, error_out)) { - return false; - } - if (!convert_string_from_datum(primary_key_datum, - &config_out->basic.primary_key, error_out)) { - error_out->msg = "In `primary_key`: " + error_out->msg; - return false; - } - } else { - config_out->basic.primary_key = "id"; - } - - if (existed_before || converter.has("shards")) { - ql::datum_t shards_datum; - if (!converter.get("shards", &shards_datum, error_out)) { - return false; - } - if (!convert_vector_from_datum( - [&](ql::datum_t shard_datum, table_config_t::shard_t *shard_out, - admin_err_t *error_out_2) { - return convert_table_config_shard_from_datum( - shard_datum, identifier_format, server_config_client, - old_config.server_names, shard_out, server_names_out, - error_out_2); - }, - shards_datum, - &config_out->shards, - error_out)) { - error_out->msg = "In `shards`: " + error_out->msg; - return false; - } - if (config_out->shards.empty()) { - *error_out = admin_err_t{ - "In `shards`: You must specify at least one shard.", - query_state_t::FAILED}; - return false; - } - } else { - try { - table_generate_config( - server_config_client, nil_uuid(), table_meta_client, - table_generate_config_params_t::make_default(), table_shard_scheme_t(), - interruptor, &config_out->shards, server_names_out); - } catch (const admin_op_exc_t &msg) { - throw admin_op_exc_t( - "Unable to automatically generate configuration for " - "new table: " + std::string(msg.what()), - query_state_t::FAILED); - } catch (const no_such_table_exc_t &) { - /* This can't happen when calling `table_generate_config()` for a new table, - only when updating an existing one */ - unreachable(); - } catch (const failed_table_op_exc_t &) { - /* Same as with `no_such_table_exc_t` */ - unreachable(); - } - } - - if (existed_before || converter.has("write_acks")) { - ql::datum_t write_acks_datum; - if (!converter.get("write_acks", &write_acks_datum, error_out)) { - return false; - } - if (!convert_write_ack_config_from_datum(write_acks_datum, - &config_out->write_ack_config, error_out)) { - error_out->msg = "In `write_acks`: " + error_out->msg; - return false; - } - } else { - config_out->write_ack_config = write_ack_config_t::MAJORITY; - } - - if (existed_before || converter.has("durability")) { - ql::datum_t durability_datum; - if (!converter.get("durability", &durability_datum, error_out)) { - return false; - } - if (!convert_durability_from_datum(durability_datum, &config_out->durability, - error_out)) { - error_out->msg = "In `durability`: " + error_out->msg; - return false; - } - } else { - config_out->durability = write_durability_t::HARD; - } - - if (converter.has("write_hook")) { - ql::datum_t write_hook_datum; - if (!converter.get("write_hook", &write_hook_datum, error_out)) { - return false; - } - if (write_hook_datum.has()) { - if ((!old_config.config.write_hook && - write_hook_datum.get_type() != ql::datum_t::type_t::R_NULL ) || - write_hook_datum - != convert_write_hook_to_datum(old_config.config.write_hook)) { - error_out->msg = "The `write_hook` field is read-only and can't" \ - " be used to create or drop a write hook function."; - return false; - } - } - config_out->write_hook = old_config.config.write_hook; - } else { - if (existed_before) { - error_out->msg = "Expected a field named `write_hook`."; - return false; - } - } - - if (!converter.check_no_extra_keys(error_out)) { - return false; - } - - return true; -} - -void table_config_artificial_table_backend_t::do_modify( - const namespace_id_t &table_id, - table_config_and_shards_t &&old_config, - table_config_t &&new_config_no_shards, - server_name_map_t &&new_server_names, - const name_string_t &old_db_name, - const name_string_t &new_db_name, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t, - maybe_failed_table_op_exc_t, admin_op_exc_t) { - table_config_and_shards_t new_config; - new_config.config = std::move(new_config_no_shards); - new_config.server_names = std::move(new_server_names); - - if (new_config.config.basic.primary_key != old_config.config.basic.primary_key) { - throw admin_op_exc_t("It's illegal to change a table's primary key", - query_state_t::FAILED); - } - - if (new_config.config.basic.database != old_config.config.basic.database || - new_config.config.basic.name != old_config.config.basic.name) { - if (table_meta_client->exists( - new_config.config.basic.database, new_config.config.basic.name)) { - throw admin_op_exc_t( - strprintf( - "Can't rename table `%s.%s` to `%s.%s` because table `%s.%s` " - "already exists.", - old_db_name.c_str(), old_config.config.basic.name.c_str(), - new_db_name.c_str(), new_config.config.basic.name.c_str(), - new_db_name.c_str(), new_config.config.basic.name.c_str()), - query_state_t::FAILED); - } - } - - calculate_split_points_intelligently(table_id, reql_cluster_interface, - new_config.config.shards.size(), old_config.shard_scheme, interruptor, - &new_config.shard_scheme); - - table_config_and_shards_change_t table_config_and_shards_change( - table_config_and_shards_change_t::set_table_config_and_shards_t{ new_config }); - table_meta_client->set_config( - table_id, table_config_and_shards_change, interruptor); -} - -void table_config_artificial_table_backend_t::do_create( - const namespace_id_t &table_id, - table_config_t &&new_config_no_shards, - server_name_map_t &&new_server_names, - const name_string_t &new_db_name, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t, - maybe_failed_table_op_exc_t, admin_op_exc_t) { - table_config_and_shards_t new_config; - new_config.config = std::move(new_config_no_shards); - new_config.server_names = std::move(new_server_names); - - if (table_meta_client->exists( - new_config.config.basic.database, new_config.config.basic.name)) { - throw admin_op_exc_t( - strprintf("Table `%s.%s` already exists.", - new_db_name.c_str(), new_config.config.basic.name.c_str()), - query_state_t::FAILED); - } - - calculate_split_points_for_uuids( - new_config.config.shards.size(), &new_config.shard_scheme); - - table_meta_client->create(table_id, new_config, interruptor); -} - -bool table_config_artificial_table_backend_t::write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor_on_caller, - admin_err_t *error_out) { - /* Parse primary key */ - namespace_id_t table_id; - admin_err_t dummy_error; - if (!convert_uuid_from_datum(primary_key, &table_id, &dummy_error)) { - /* If the primary key was not a valid UUID, then it must refer to a nonexistent - row. */ - guarantee(!pkey_was_autogenerated, "auto-generated primary key should have " - "been a valid UUID string."); - table_id = nil_uuid(); - } - - cross_thread_signal_t interruptor_on_home(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - cluster_semilattice_metadata_t metadata = semilattice_view->get(); - - try { - try { - /* Fetch the name of the table and its database for error messages */ - table_basic_config_t old_basic_config; - table_meta_client->get_name(table_id, &old_basic_config); - guarantee(!pkey_was_autogenerated, "UUID collision happened"); - - user_context.require_config_permission( - rdb_context, old_basic_config.database, table_id); - - name_string_t old_db_name; - if (!convert_database_id_to_datum(old_basic_config.database, - identifier_format, metadata, nullptr, &old_db_name)) { - old_db_name = name_string_t::guarantee_valid("__deleted_database__"); - } - - if (new_value_inout->has()) { - table_config_and_shards_t old_config; - try { - table_meta_client->get_config( - table_id, &interruptor_on_home, &old_config); - } CATCH_OP_ERRORS(old_db_name, old_basic_config.name, error_out, - "Failed to retrieve the table's configuration, it was not changed.", - "Failed to retrieve the table's configuration, it was not changed.") - - table_config_t new_config; - server_name_map_t new_server_names; - namespace_id_t new_table_id; - name_string_t new_db_name; - if (!convert_table_config_and_name_from_datum(*new_value_inout, true, - metadata, identifier_format, server_config_client, - old_config, table_meta_client, &interruptor_on_home, - &new_table_id, &new_config, &new_server_names, &new_db_name, - error_out)) { - error_out->msg = "The change you're trying to make to " - "`rethinkdb.table_config` has the wrong format. " - + error_out->msg; - return false; - } - guarantee(new_table_id == table_id, "artificial_table_t shouldn't have " - "allowed the primary key to change"); - - // Verify the user is allowed to move the table to the new database. - if (new_config.basic.database != old_basic_config.database) { - user_context.require_config_permission( - rdb_context, new_config.basic.database); - } - - try { - do_modify(table_id, std::move(old_config), std::move(new_config), - std::move(new_server_names), old_db_name, new_db_name, - &interruptor_on_home); - return true; - } CATCH_OP_ERRORS(old_db_name, old_basic_config.name, error_out, - "The table's configuration was not changed.", - "The table's configuration may or may not have been changed.") - } else { - try { - table_meta_client->drop(table_id, &interruptor_on_home); - return true; - } CATCH_OP_ERRORS(old_db_name, old_basic_config.name, error_out, - "The table was not dropped.", - "The table may or may not have been dropped.") - } - } catch (const no_such_table_exc_t &) { - /* Fall through */ - } - if (new_value_inout->has()) { - if (!pkey_was_autogenerated) { - *error_out = admin_err_t{ - "There is no existing table with the given ID. To create a " - "new table by inserting into `rethinkdb.table_config`, you must let " - "the database generate the primary key automatically.", - query_state_t::FAILED}; - return false; - } - - namespace_id_t new_table_id; - table_config_t new_config; - server_name_map_t new_server_names; - name_string_t new_db_name; - if (!convert_table_config_and_name_from_datum(*new_value_inout, false, - metadata, identifier_format, server_config_client, - table_config_and_shards_t(), table_meta_client, - &interruptor_on_home, &new_table_id, &new_config, &new_server_names, - &new_db_name, error_out)) { - error_out->msg = "The change you're trying to make to " - "`rethinkdb.table_config` has the wrong format. " + error_out->msg; - return false; - } - guarantee(new_table_id == table_id, "artificial_table_t shouldn't have " - "allowed the primary key to change"); - - /* `convert_table_config_and_name_from_datum()` might have filled in missing - fields, so we need to write back the filled-in values to `new_value_inout`. - */ - *new_value_inout = convert_table_config_to_datum( - table_id, new_value_inout->get_field("db"), new_config, - identifier_format, new_server_names); - - try { - do_create(table_id, std::move(new_config), std::move(new_server_names), - new_db_name, &interruptor_on_home); - return true; - } CATCH_OP_ERRORS(new_db_name, new_config.basic.name, error_out, - "The table was not created.", - "The table may or may not have been created.") - } else { - /* The user is deleting a table that doesn't exist. Do nothing. */ - return true; - } - } catch (const admin_op_exc_t &admin_op_exc) { - *error_out = admin_op_exc.to_admin_err(); - return false; - } -} diff --git a/src/clustering/administration/tables/table_config.hpp b/src/clustering/administration/tables/table_config.hpp deleted file mode 100644 index c418704480d..00000000000 --- a/src/clustering/administration/tables/table_config.hpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_TABLES_TABLE_CONFIG_HPP_ -#define CLUSTERING_ADMINISTRATION_TABLES_TABLE_CONFIG_HPP_ - -#include -#include -#include - -#include "clustering/administration/servers/config_client.hpp" -#include "clustering/administration/tables/table_common.hpp" -#include "containers/uuid.hpp" -#include "rdb_protocol/artificial_table/backend.hpp" -#include "rpc/semilattice/view.hpp" - -class real_reql_cluster_interface_t; -class table_config_t; - -/* This is publicly exposed so that it can be used to create the return value of -`table.reconfigure()`. */ -ql::datum_t convert_table_config_to_datum( - namespace_id_t table_id, - const ql::datum_t &db_name_or_uuid, - const table_config_t &config, - admin_identifier_format_t identifier_format, - const server_name_map_t &server_names); - -ql::datum_t convert_write_hook_to_datum( - const optional &write_hook); - -class table_config_artificial_table_backend_t : - public common_table_artificial_table_backend_t -{ -public: - table_config_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - std::shared_ptr > _semilattice_view, - real_reql_cluster_interface_t *_reql_cluster_interface, - admin_identifier_format_t _identifier_format, - server_config_client_t *_server_config_client, - table_meta_client_t *_table_meta_client); - ~table_config_artificial_table_backend_t(); - - bool write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor_on_caller, - admin_err_t *error_out); - -private: - void format_row( - auth::user_context_t const &user_context, - const namespace_id_t &table_id, - const table_config_and_shards_t &config, - const ql::datum_t &db_name_or_uuid, - signal_t *interruptor_on_home, - ql::datum_t *row_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t); - - void do_modify( - const namespace_id_t &table_id, - table_config_and_shards_t &&old_config, - table_config_t &&new_config_no_shards, - server_name_map_t &&new_server_names, - const name_string_t &old_db_name, - const name_string_t &new_db_name, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t, - maybe_failed_table_op_exc_t, admin_op_exc_t); - - void do_create( - const namespace_id_t &table_id, - table_config_t &&new_config_no_shards, - server_name_map_t &&new_server_names, - const name_string_t &new_db_name, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t, - maybe_failed_table_op_exc_t, admin_op_exc_t); - - rdb_context_t *rdb_context; - real_reql_cluster_interface_t *reql_cluster_interface; - server_config_client_t *server_config_client; -}; - -#endif /* CLUSTERING_ADMINISTRATION_TABLES_TABLE_CONFIG_HPP_ */ - diff --git a/src/clustering/administration/tables/table_metadata.cc b/src/clustering/administration/tables/table_metadata.cc deleted file mode 100644 index fefcc065f4a..00000000000 --- a/src/clustering/administration/tables/table_metadata.cc +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/tables/table_metadata.hpp" - -#include "clustering/administration/tables/database_metadata.hpp" -#include "containers/archive/archive.hpp" -#include "containers/archive/boost_types.hpp" -#include "containers/archive/stl_types.hpp" -#include "containers/archive/versioned.hpp" -#include "rdb_protocol/protocol.hpp" - -std::set table_config_t::shard_t::voting_replicas() const { - std::set s; - std::set_difference( - all_replicas.begin(), all_replicas.end(), - nonvoting_replicas.begin(), nonvoting_replicas.end(), - std::inserter(s, s.end())); - return s; -} - - -key_range_t table_shard_scheme_t::get_shard_range(size_t i) const { - guarantee(i < num_shards()); - store_key_t left = (i == 0) ? store_key_t::min() : split_points[i-1]; - if (i != num_shards() - 1) { - return key_range_t( - key_range_t::closed, left, - key_range_t::open, split_points[i]); - } else { - return key_range_t( - key_range_t::closed, left, - key_range_t::none, store_key_t()); - } -} - -size_t table_shard_scheme_t::find_shard_for_key(const store_key_t &key) const { - size_t ix = 0; - while (ix < split_points.size() && key >= split_points[ix]) { - ++ix; - } - return ix; -} - -class table_config_and_shards_change_t::apply_change_visitor_t - : public boost::static_visitor { -public: - explicit apply_change_visitor_t( - table_config_and_shards_t *_table_config_and_shards) - : table_config_and_shards(_table_config_and_shards) { } - - result_type operator()( - const set_table_config_and_shards_t &set_table_config_and_shards) const { - *table_config_and_shards = - set_table_config_and_shards.new_config_and_shards; - return true; - } - - result_type operator()(const write_hook_create_t &write_hook_create) const { - table_config_and_shards->config.write_hook.set(write_hook_create.config); - return true; - } - - result_type operator()(UNUSED const write_hook_drop_t &write_hook_drop) const { - table_config_and_shards->config.write_hook = r_nullopt; - return true; - } - - result_type operator()(const sindex_create_t &sindex_create) const { - auto pair = table_config_and_shards->config.sindexes.insert( - std::make_pair(sindex_create.name, sindex_create.config)); - return pair.second; - } - - result_type operator()(const sindex_drop_t &sindex_drop) const { - auto size = table_config_and_shards->config.sindexes.erase(sindex_drop.name); - return size == 1; - } - - result_type operator()(const sindex_rename_t &sindex_rename) const { - if (table_config_and_shards->config.sindexes.count( - sindex_rename.name) == 0) { - /* The index `sindex_rename.name` does not exist. */ - return false; - } - if (sindex_rename.name != sindex_rename.new_name) { - if (table_config_and_shards->config.sindexes.count( - sindex_rename.new_name) == 1 && - sindex_rename.overwrite == false) { - /* The index `sindex_rename.new_name` already exits and should not be - overwritten. */ - return false; - } else { - table_config_and_shards->config.sindexes[sindex_rename.new_name] = - table_config_and_shards->config.sindexes.at(sindex_rename.name); - table_config_and_shards->config.sindexes.erase(sindex_rename.name); - } - } - return true; - } - -private: - table_config_and_shards_t *table_config_and_shards; -}; - -/* Note, it's important that `apply_change` does not change -`table_config_and_shards` if it returns false. */ -bool table_config_and_shards_change_t::apply_change(table_config_and_shards_t *table_config_and_shards) const { - return boost::apply_visitor( - apply_change_visitor_t(table_config_and_shards), change); -} - -bool table_config_and_shards_change_t::name_and_database_equal(const table_basic_config_t &table_basic_config) const { - const set_table_config_and_shards_t *set_table_config_and_shards = - boost::get(&change); - if (set_table_config_and_shards == nullptr) { - return true; - } else { - return set_table_config_and_shards->new_config_and_shards.config.basic.name == table_basic_config.name && - set_table_config_and_shards->new_config_and_shards.config.basic.database == table_basic_config.database; - } -} - - - -RDB_IMPL_SERIALIZABLE_3_SINCE_v2_1(table_basic_config_t, - name, database, primary_key); -RDB_IMPL_EQUALITY_COMPARABLE_3(table_basic_config_t, - name, database, primary_key); - -RDB_IMPL_SERIALIZABLE_3_SINCE_v2_1(table_config_t::shard_t, - all_replicas, nonvoting_replicas, primary_replica); -RDB_IMPL_EQUALITY_COMPARABLE_3(table_config_t::shard_t, - all_replicas, nonvoting_replicas, primary_replica); - -template -void serialize(write_message_t *wm, const table_config_t &tc) { - table_basic_config_t basic = tc.basic; - serialize(wm, basic); - - std::vector shards = tc.shards; - serialize(wm, shards); - - std::map sindexes = tc.sindexes; - serialize(wm, sindexes); - - optional write_hook = tc.write_hook; - serialize(wm, write_hook); - - write_ack_config_t write_ack_config = tc.write_ack_config; - serialize(wm, write_ack_config); - - write_durability_t durability = tc.durability; - serialize(wm, durability); -} - -INSTANTIATE_SERIALIZE_FOR_CLUSTER_AND_DISK(table_config_t); - -template -archive_result_t deserialize_table_config_pre_v2_4( - read_stream_t *s, table_config_t *tc) { - archive_result_t res; - - table_basic_config_t basic; - res = deserialize(s, &basic); - if (bad(res)) { return res; } - - std::vector shards; - res = deserialize(s, &shards); - if (bad(res)) { return res; } - - std::map sindexes; - res = deserialize(s, &sindexes); - if (bad(res)) { return res; } - - write_ack_config_t write_ack_config; - res = deserialize(s, &write_ack_config); - if (bad(res)) { return res; } - - write_durability_t durability; - res = deserialize(s, &durability); - if (bad(res)) { return res; } - - tc->basic = std::move(basic); - tc->shards = std::move(shards); - tc->sindexes = std::move(sindexes); - tc->write_ack_config = std::move(write_ack_config); - tc->durability = std::move(durability); - - return res; -} - -template -archive_result_t deserialize( - read_stream_t *s, table_config_t *tc) { - archive_result_t res; - - table_basic_config_t basic; - res = deserialize(s, &basic); - if (bad(res)) { return res; } - - std::vector shards; - res = deserialize(s, &shards); - if (bad(res)) { return res; } - - std::map sindexes; - res = deserialize(s, &sindexes); - if (bad(res)) { return res; } - - optional write_hook; - res = deserialize(s, &write_hook); - if (bad(res)) { return res; } - - write_ack_config_t write_ack_config; - res = deserialize(s, &write_ack_config); - if (bad(res)) { return res; } - - write_durability_t durability; - res = deserialize(s, &durability); - if (bad(res)) { return res; } - - *tc = table_config_t{std::move(basic), - std::move(shards), - std::move(sindexes), - std::move(write_hook), - std::move(write_ack_config), - std::move(durability)}; - - return res; -} - -template <> -archive_result_t deserialize( - read_stream_t *s, table_config_t *tc) { - return deserialize_table_config_pre_v2_4(s, tc); -} - -template <> -archive_result_t deserialize( - read_stream_t *s, table_config_t *tc) { - return deserialize_table_config_pre_v2_4(s, tc); -} - -template <> -archive_result_t deserialize( - read_stream_t *s, table_config_t *tc) { - return deserialize_table_config_pre_v2_4(s, tc); -} - -template archive_result_t deserialize( - read_stream_t *, table_config_t *); - -RDB_IMPL_EQUALITY_COMPARABLE_6(table_config_t, - basic, shards, write_hook, sindexes, write_ack_config, durability); - -RDB_IMPL_SERIALIZABLE_1_SINCE_v1_16(table_shard_scheme_t, split_points); -RDB_IMPL_EQUALITY_COMPARABLE_1(table_shard_scheme_t, split_points); - -RDB_IMPL_SERIALIZABLE_3_SINCE_v2_1(table_config_and_shards_t, - config, shard_scheme, server_names); -RDB_IMPL_EQUALITY_COMPARABLE_3(table_config_and_shards_t, - config, shard_scheme, server_names); - -RDB_IMPL_SERIALIZABLE_1_FOR_CLUSTER( - table_config_and_shards_change_t::set_table_config_and_shards_t, - new_config_and_shards); -RDB_IMPL_SERIALIZABLE_2_FOR_CLUSTER(table_config_and_shards_change_t::sindex_create_t, - name, config); -RDB_IMPL_SERIALIZABLE_1_FOR_CLUSTER(table_config_and_shards_change_t::sindex_drop_t, - name); -RDB_IMPL_SERIALIZABLE_3_FOR_CLUSTER(table_config_and_shards_change_t::sindex_rename_t, - name, new_name, overwrite); - -RDB_IMPL_SERIALIZABLE_1_FOR_CLUSTER(table_config_and_shards_change_t::write_hook_create_t, config); -RDB_IMPL_SERIALIZABLE_0_FOR_CLUSTER(table_config_and_shards_change_t::write_hook_drop_t); - -RDB_IMPL_SERIALIZABLE_1_SINCE_v1_13(database_semilattice_metadata_t, name); -RDB_IMPL_SEMILATTICE_JOINABLE_1(database_semilattice_metadata_t, name); -RDB_IMPL_EQUALITY_COMPARABLE_1(database_semilattice_metadata_t, name); - -RDB_IMPL_SERIALIZABLE_1_SINCE_v1_13(databases_semilattice_metadata_t, databases); -RDB_IMPL_SEMILATTICE_JOINABLE_1(databases_semilattice_metadata_t, databases); -RDB_IMPL_EQUALITY_COMPARABLE_1(databases_semilattice_metadata_t, databases); - diff --git a/src/clustering/administration/tables/table_metadata.hpp b/src/clustering/administration/tables/table_metadata.hpp deleted file mode 100644 index 7d6781551e2..00000000000 --- a/src/clustering/administration/tables/table_metadata.hpp +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_TABLES_TABLE_METADATA_HPP_ -#define CLUSTERING_ADMINISTRATION_TABLES_TABLE_METADATA_HPP_ - -#include -#include -#include -#include -#include - -#include "buffer_cache/types.hpp" // for `write_durability_t` -#include "clustering/administration/servers/server_metadata.hpp" -#include "clustering/generic/nonoverlapping_regions.hpp" -#include "containers/name_string.hpp" -#include "containers/uuid.hpp" -#include "rdb_protocol/protocol.hpp" -#include "rpc/connectivity/server_id.hpp" -#include "rpc/semilattice/joins/macros.hpp" -#include "rpc/serialize_macros.hpp" - -/* This is the metadata for a single table. */ - -/* `table_basic_config_t` contains the subset of the table's configuration that the -parser needs to process queries against the table. A copy of this is stored on every -thread of every server for every table. */ -class table_basic_config_t { -public: - name_string_t name; - database_id_t database; - std::string primary_key; -}; - -RDB_DECLARE_SERIALIZABLE(table_basic_config_t); -RDB_DECLARE_EQUALITY_COMPARABLE(table_basic_config_t); - -enum class write_ack_config_t { - SINGLE, - MAJORITY -}; -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE( - write_ack_config_t, - int8_t, - write_ack_config_t::SINGLE, - write_ack_config_t::MAJORITY); - -/* `table_config_t` describes the complete contents of the `rethinkdb.table_config` -artificial table. */ - -class table_config_t { -public: - class shard_t { - public: - std::set voting_replicas() const; - - /* `nonvoting_replicas` must be a subset of `all_replicas`. `primary_replica` - must be in `all_replicas` and not in `nonvoting_replicas`. */ - std::set all_replicas; - std::set nonvoting_replicas; - server_id_t primary_replica; - }; - table_basic_config_t basic; - std::vector shards; - std::map sindexes; - optional write_hook; - write_ack_config_t write_ack_config; - write_durability_t durability; -}; - -RDB_DECLARE_EQUALITY_COMPARABLE(table_config_t); - -RDB_DECLARE_SERIALIZABLE(table_config_t::shard_t); -RDB_DECLARE_EQUALITY_COMPARABLE(table_config_t::shard_t); - -class table_shard_scheme_t { -public: - std::vector split_points; - - static table_shard_scheme_t one_shard() { - return table_shard_scheme_t(); - } - - size_t num_shards() const { - return split_points.size() + 1; - } - - key_range_t get_shard_range(size_t i) const; - size_t find_shard_for_key(const store_key_t &key) const; -}; - -RDB_DECLARE_SERIALIZABLE(table_shard_scheme_t); -RDB_DECLARE_EQUALITY_COMPARABLE(table_shard_scheme_t); - -/* `table_config_and_shards_t` exists because the `table_config_t` needs to be changed in -sync with the `table_shard_scheme_t` and the server name mapping. */ - -class table_config_and_shards_t { -public: - table_config_t config; - table_shard_scheme_t shard_scheme; - - /* This contains an entry for every server mentioned in the config. The `uint64_t`s - are server config versions. */ - server_name_map_t server_names; -}; - -RDB_DECLARE_SERIALIZABLE(table_config_and_shards_t); -RDB_DECLARE_EQUALITY_COMPARABLE(table_config_and_shards_t); - -class table_config_and_shards_change_t { -public: - class set_table_config_and_shards_t { - public: - table_config_and_shards_t new_config_and_shards; - }; - - class write_hook_create_t { - public: - write_hook_config_t config; - }; - - class write_hook_drop_t { - public: - }; - - class sindex_create_t { - public: - std::string name; - sindex_config_t config; - }; - - class sindex_drop_t { - public: - std::string name; - }; - - class sindex_rename_t { - public: - std::string name; - std::string new_name; - bool overwrite; - }; - - table_config_and_shards_change_t() { } - - explicit table_config_and_shards_change_t(set_table_config_and_shards_t &&_change) - : change(std::move(_change)) { } - explicit table_config_and_shards_change_t(sindex_create_t &&_change) - : change(std::move(_change)) { } - explicit table_config_and_shards_change_t(sindex_drop_t &&_change) - : change(std::move(_change)) { } - explicit table_config_and_shards_change_t(sindex_rename_t &&_change) - : change(std::move(_change)) { } - explicit table_config_and_shards_change_t(write_hook_create_t &&_change) - : change(std::move(_change)) { } - explicit table_config_and_shards_change_t(write_hook_drop_t &&_change) - : change(std::move(_change)) { } - - - /* Note, it's important that `apply_change` does not change - `table_config_and_shards` if it returns false. */ - bool apply_change(table_config_and_shards_t *table_config_and_shards) const; - - bool name_and_database_equal(const table_basic_config_t &table_basic_config) const; - - RDB_MAKE_ME_SERIALIZABLE_1(table_config_and_shards_change_t, change); - -private: - boost::variant< - set_table_config_and_shards_t, - sindex_create_t, - sindex_drop_t, - sindex_rename_t, - write_hook_create_t, - write_hook_drop_t> change; - - class apply_change_visitor_t; -}; - -RDB_DECLARE_SERIALIZABLE(table_config_and_shards_change_t::set_table_config_and_shards_t); -RDB_DECLARE_SERIALIZABLE(table_config_and_shards_change_t::write_hook_create_t); -RDB_DECLARE_SERIALIZABLE(table_config_and_shards_change_t::write_hook_drop_t); -RDB_DECLARE_SERIALIZABLE(table_config_and_shards_change_t::sindex_create_t); -RDB_DECLARE_SERIALIZABLE(table_config_and_shards_change_t::sindex_drop_t); -RDB_DECLARE_SERIALIZABLE(table_config_and_shards_change_t::sindex_rename_t); - -#endif /* CLUSTERING_ADMINISTRATION_TABLES_TABLE_METADATA_HPP_ */ diff --git a/src/clustering/administration/tables/table_status.cc b/src/clustering/administration/tables/table_status.cc deleted file mode 100644 index 1df045e9007..00000000000 --- a/src/clustering/administration/tables/table_status.cc +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/administration/tables/table_status.hpp" - -#include - -#include "clustering/administration/datum_adapter.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "clustering/administration/metadata.hpp" -#include "clustering/table_contract/executor/exec_primary.hpp" -#include "clustering/table_manager/table_meta_client.hpp" - -table_status_artificial_table_backend_t::table_status_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - std::shared_ptr > _semilattice_view, - server_config_client_t *_server_config_client, - table_meta_client_t *_table_meta_client, - namespace_repo_t *_namespace_repo, - admin_identifier_format_t _identifier_format) - : common_table_artificial_table_backend_t( - name_string_t::guarantee_valid("table_status"), - rdb_context, - name_resolver, - _semilattice_view, - _table_meta_client, - _identifier_format), - server_config_client(_server_config_client), - namespace_repo(_namespace_repo) { -} - -table_status_artificial_table_backend_t::~table_status_artificial_table_backend_t() { - begin_changefeed_destruction(); -} - -ql::datum_t convert_replica_status_to_datum( - const server_id_t &server_id, - const char *status, - admin_identifier_format_t identifier_format, - const server_name_map_t &server_names) { - ql::datum_object_builder_t replica_builder; - replica_builder.overwrite("server", convert_name_or_uuid_to_datum( - server_names.get(server_id), server_id.get_uuid(), identifier_format)); - replica_builder.overwrite("state", ql::datum_t(status)); - return std::move(replica_builder).to_datum(); -} - -ql::datum_t convert_shard_status_to_datum( - const table_config_t::shard_t &shard, - const std::map &states, - const std::set &disconnected, - admin_identifier_format_t identifier_format, - const server_name_map_t &server_names) { - ql::datum_array_builder_t primary_replicas_builder( - ql::configured_limits_t::unlimited); - ql::datum_array_builder_t replicas_builder( - ql::configured_limits_t::unlimited); - - for (const auto &pair : states) { - if (!pair.second.primary && !pair.second.secondary && - !pair.second.transitioning) { - continue; - } - if (pair.second.primary) { - primary_replicas_builder.add(convert_name_or_uuid_to_datum( - server_names.get(pair.first), pair.first.get_uuid(), identifier_format)); - } - const char *status; - if (pair.second.backfilling) { - status = "backfilling"; - } else if (pair.second.need_quorum) { - status = "waiting_for_quorum"; - } else if (pair.second.need_primary) { - status = "waiting_for_primary"; - } else if (pair.second.transitioning) { - status = "transitioning"; - } else { - status = "ready"; - } - replicas_builder.add(convert_replica_status_to_datum( - pair.first, status, identifier_format, server_names)); - } - - for (const server_id_t &server : shard.all_replicas) { - guarantee(states.count(server) != 0 || disconnected.count(server) != 0); - if (disconnected.count(server) != 0) { - replicas_builder.add(convert_replica_status_to_datum( - server, "disconnected", identifier_format, server_names)); - } - } - - ql::datum_object_builder_t shard_builder; - shard_builder.overwrite("primary_replicas", - std::move(primary_replicas_builder).to_datum()); - shard_builder.overwrite("replicas", - std::move(replicas_builder).to_datum()); - return std::move(shard_builder).to_datum(); -} - -ql::datum_t convert_raft_leader_to_datum( - const table_status_t &status, - admin_identifier_format_t identifier_format) { - if (static_cast(status.raft_leader)) { - return convert_name_or_uuid_to_datum( - status.server_names.get(*status.raft_leader), - status.raft_leader->get_uuid(), - identifier_format); - } - - return ql::datum_t::null(); -} - -ql::datum_t convert_table_status_to_datum( - const table_status_t &status, - admin_identifier_format_t identifier_format) { - ql::datum_object_builder_t builder; - - if (status.total_loss) { - builder.overwrite("shards", ql::datum_t::null()); - } else { - ql::datum_array_builder_t shards_builder(ql::configured_limits_t::unlimited); - for (size_t i = 0; i < status.config->config.shards.size(); ++i) { - key_range_t range = status.config->shard_scheme.get_shard_range(i); - std::map states; - for (const auto &pair : status.server_shards) { - pair.second.visit( - key_range_t::right_bound_t(range.left), - range.right, - [&](const key_range_t::right_bound_t &, - const key_range_t::right_bound_t &, - const table_shard_status_t &range_states) { - states[pair.first].merge(range_states); - }); - } - shards_builder.add(convert_shard_status_to_datum( - status.config->config.shards[i], states, status.disconnected, - identifier_format, status.server_names)); - } - builder.overwrite("shards", std::move(shards_builder).to_datum()); - } - - // add raft leader information - builder.overwrite("raft_leader", - convert_raft_leader_to_datum(status, identifier_format)); - - ql::datum_object_builder_t status_builder; - status_builder.overwrite("ready_for_outdated_reads", ql::datum_t::boolean( - status.readiness >= table_readiness_t::outdated_reads)); - status_builder.overwrite("ready_for_reads", ql::datum_t::boolean( - status.readiness >= table_readiness_t::reads)); - status_builder.overwrite("ready_for_writes", ql::datum_t::boolean( - status.readiness >= table_readiness_t::writes)); - status_builder.overwrite("all_replicas_ready", ql::datum_t::boolean( - status.readiness == table_readiness_t::finished)); - builder.overwrite("status", std::move(status_builder).to_datum()); - - return std::move(builder).to_datum(); -} - -void table_status_artificial_table_backend_t::format_row( - UNUSED auth::user_context_t const &user_context, - const namespace_id_t &table_id, - const table_config_and_shards_t &config, - const ql::datum_t &db_name_or_uuid, - signal_t *interruptor_on_home, - ql::datum_t *row_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t) { - assert_thread(); - table_status_t status; - get_table_status(table_id, config, namespace_repo, table_meta_client, - server_config_client, interruptor_on_home, &status); - ql::datum_t status_datum = convert_table_status_to_datum(status, identifier_format); - ql::datum_object_builder_t builder(status_datum); - builder.overwrite("id", convert_uuid_to_datum(table_id)); - builder.overwrite("db", db_name_or_uuid); - builder.overwrite("name", convert_name_to_datum(status.config->config.basic.name)); - *row_out = std::move(builder).to_datum(); -} - -void table_status_artificial_table_backend_t::format_error_row( - UNUSED auth::user_context_t const &user_context, - const namespace_id_t &table_id, - const ql::datum_t &db_name_or_uuid, - const name_string_t &table_name, - ql::datum_t *row_out) { - assert_thread(); - table_status_t status; - status.readiness = table_readiness_t::unavailable; - status.total_loss = true; - ql::datum_t status_datum = convert_table_status_to_datum(status, identifier_format); - ql::datum_object_builder_t builder(status_datum); - builder.overwrite("id", convert_uuid_to_datum(table_id)); - builder.overwrite("db", db_name_or_uuid); - builder.overwrite("name", convert_name_to_datum(table_name)); - *row_out = std::move(builder).to_datum(); -} - -bool table_status_artificial_table_backend_t::write_row( - UNUSED auth::user_context_t const &user_context, - UNUSED ql::datum_t primary_key, - UNUSED bool pkey_was_autogenerated, - UNUSED ql::datum_t *new_value_inout, - UNUSED signal_t *interruptor_on_caller, - admin_err_t *error_out) { - *error_out = admin_err_t{ - "It's illegal to write to the `rethinkdb.table_status` table.", - query_state_t::FAILED}; - return false; -} - diff --git a/src/clustering/administration/tables/table_status.hpp b/src/clustering/administration/tables/table_status.hpp deleted file mode 100644 index 49027d97bde..00000000000 --- a/src/clustering/administration/tables/table_status.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_ADMINISTRATION_TABLES_TABLE_STATUS_HPP_ -#define CLUSTERING_ADMINISTRATION_TABLES_TABLE_STATUS_HPP_ - -#include -#include - -#include "clustering/administration/tables/calculate_status.hpp" -#include "clustering/administration/tables/table_common.hpp" - -class namespace_repo_t; -class server_config_client_t; - -ql::datum_t convert_table_status_to_datum( - const table_status_t &status, - admin_identifier_format_t identifier_format); - -class table_status_artificial_table_backend_t : - public common_table_artificial_table_backend_t -{ -public: - table_status_artificial_table_backend_t( - rdb_context_t *rdb_context, - lifetime_t name_resolver, - std::shared_ptr > _semilattice_view, - server_config_client_t *server_config_client, - table_meta_client_t *_table_meta_client, - namespace_repo_t *_namespace_repo, - admin_identifier_format_t _identifier_format); - ~table_status_artificial_table_backend_t(); - - bool write_row( - auth::user_context_t const &user_context, - ql::datum_t primary_key, - bool pkey_was_autogenerated, - ql::datum_t *new_value_inout, - signal_t *interruptor_on_caller, - admin_err_t *error_out); - -private: - void format_row( - auth::user_context_t const &user_context, - const namespace_id_t &table_id, - const table_config_and_shards_t &config, - const ql::datum_t &db_name_or_uuid, - signal_t *interruptor_on_home, - ql::datum_t *row_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t); - - void format_error_row( - auth::user_context_t const &user_context, - const namespace_id_t &table_id, - const ql::datum_t &db_name_or_uuid, - const name_string_t &table_name, - ql::datum_t *row_out); - - server_config_client_t *server_config_client; - namespace_repo_t *namespace_repo; -}; - -#endif /* CLUSTERING_ADMINISTRATION_TABLES_TABLE_STATUS_HPP_ */ - diff --git a/src/clustering/generic/minidir.hpp b/src/clustering/generic/minidir.hpp deleted file mode 100644 index 1cbc44edfe2..00000000000 --- a/src/clustering/generic/minidir.hpp +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_GENERIC_MINIDIR_HPP_ -#define CLUSTERING_GENERIC_MINIDIR_HPP_ - -#include "concurrency/fifo_enforcer.hpp" -#include "concurrency/watchable_map.hpp" -#include "containers/archive/boost_types.hpp" -#include "rpc/connectivity/cluster.hpp" -#include "rpc/mailbox/typed.hpp" - -class mailbox_manager_t; - -/* "Minidir" is short for "mini directory". The minidir classes are for broadcasting -state across the network just like the directory, but the broadcast is to a finite set of -mailboxes instead of to every connected server. This gives better performance and -sometimes better abstraction. */ - -typedef uuid_u minidir_link_id_t; - -template -class minidir_bcard_t { -public: - /* The `minidir_write_manager_t` sends messages to the `update_mailbox_t` at the - `minidir_read_manager_t`. It will send a series of messages with its `peer_id` and - a randomly generated, but consistent, `link_id`; each message will have a - `fifo_token` from the same `fifo_enforcer_source_t` created specifically for use with - that `link_id`. `closing_link` will always be `false`; `key` and `value` will be - set appropriately to add or remove values from the map. If the write manager is - destroyed or decides to stop sending messages to that particular read manager, then - it will send one final message with `closing_link` set to `true`. If contact is - lost between them, no final message will be sent, but the read manager will erase all - of the recorded key-value pairs from that peer. If the write manager decides to - start sending message again, or if contact is reestablished, then it will start over - with a new `link_id` and a new `fifo_enforcer_source_t`. */ - struct update_message_t { - peer_id_t peer_id; - minidir_link_id_t link_id; - fifo_enforcer_write_token_t fifo_token; - bool closing_link; - optional key; - optional value; - - RDB_MAKE_ME_SERIALIZABLE_6(update_message_t, peer_id, link_id, fifo_token, closing_link, key, value); - }; - typedef mailbox_t update_mailbox_t; - typename update_mailbox_t::address_t update_mailbox; - - RDB_MAKE_ME_SERIALIZABLE_1(minidir_bcard_t, update_mailbox); -}; - -/* Instantiate a `minidir_read_manager_t` on the server that's supposed to be receiving -messages, and put the return value of `get_bcard()` in the directory. Then read the -received values from `get_values()`. */ -template -class minidir_read_manager_t { -public: - explicit minidir_read_manager_t(mailbox_manager_t *mm); - - minidir_bcard_t get_bcard() { - minidir_bcard_t bc; - bc.update_mailbox = update_mailbox.get_address(); - return bc; - } - - watchable_map_t *get_values() { - return &map_var; - } - -private: - /* If we lose contact with a peer, we need to erase all of their entries from the - `map_var`. This is complicated because it may later reconnect, and there are various - race conditions. Here's how it all works: - - When `on_update()` gets the first message from a peer, it creates an entry in - `peer_map`. This entry contains an `auto_drainer_t::lock_t` holding the peer's - connection, so the peer can't disconnect and reconnect until we release the lock. - - `on_connection_change()` gets called whenever a peer connects or disconnects. We - ignore connects, but on a disconnect we remove the entry from the map and delete - it. The actual deletion is done in a coroutine, since destroying `drainer` may - block. - - Destroying the `peer_data_t` destroys all the `watchable_map_var_t::entry_t` for - links from that peer, so all the entries disappear from the map. - - If the peer reconnects, the process starts over from step 1. - */ - class peer_data_t { - public: - class link_data_t { - public: - std::map::entry_t> map; - fifo_enforcer_sink_t fifo_sink; - }; - explicit peer_data_t(auto_drainer_t::lock_t ck) : connection_keepalive(ck) { } - auto_drainer_t::lock_t connection_keepalive; - std::map > link_map; - auto_drainer_t drainer; - }; - - void on_connection_change( - const peer_id_t &peer_id, - const connectivity_cluster_t::connection_pair_t *pair); - - void on_update( - signal_t *interruptor, - const typename minidir_bcard_t::update_message_t &msg); - - mailbox_manager_t *const mailbox_manager; - - watchable_map_var_t map_var; - - std::map > peer_map; - - /* This must be destroyed after we destroy `connection_subs`, since it may spawn - coroutines that hold this. But it must be destroyed before we destroy the other - member variables, since those coroutines access the other member variables. */ - auto_drainer_t drainer; - - typename minidir_bcard_t::update_mailbox_t update_mailbox; - - watchable_map_t::all_subs_t - connection_subs; -}; - -/* Instantiate a `minidir_write_manager_t` on the server that's supposed to be sending -values. The `values` parameter to the constructor is the values to transmit, and the -`readers` parameter is where to transmit them. */ -template -class minidir_write_manager_t { -public: - minidir_write_manager_t( - mailbox_manager_t *mm, - watchable_map_t *_values, - watchable_map_t > *_readers); - ~minidir_write_manager_t(); - -private: - /* Summary of how this works: - - - `on_reader_change()` creates and destroys `link_data_t`s. When it creates the - first `link_data_t` for a given peer, it creates a `peer_data_t` to put it in. - When it creates a `link_data_t` it generates a new `link_id` and calls - `spawn_update()` for every key-value pair in `values` at that time. When it - destroys a `link_data_t` it calls `spawn_closing_link()`. When it destroys the - last `link_data_t` for a peer, it destroys the `peer_data_t`. - - - `on_connection_change()` destroys `peer_data_t`s for peers that have disconnected. - This means it destroys `link_data_t`s as a side effect. When a peer reconnects, - it calls `on_reader_change()` for every reader, to re-create the `link_data_t`s. - So the re-created `link_data_t`s will have new `link_id`s, and they will get a - new set of initial messages. - - - `on_value_change()` doesn't create or destroy `peer_data_t`s or `link_data_t`s; it - just calls `spawn_update()` for every existing `link_data_t`. - - So at startup, we initialize everything by just calling `on_reader_change()` on every - reader in the map. This takes care of setting up the `peer_data_t`s and sending the - initial messages too. */ - - class peer_data_t { - public: - class link_data_t { - public: - minidir_link_id_t link_id; - minidir_bcard_t bcard; - fifo_enforcer_source_t fifo_source; - }; - explicit peer_data_t(auto_drainer_t::lock_t ck) : connection_keepalive(ck) { } - /* We hold `connection_keepalive` to force the `connectivity_cluster_t` to notify - us that the old connection has been dropped, so we'll delete the lock, before it - starts a new connection. This prevents us getting confused. */ - auto_drainer_t::lock_t connection_keepalive; - std::map > link_map; - }; - - void on_connection_change( - const peer_id_t &peer, - const connectivity_cluster_t::connection_pair_t *pair); - void on_value_change( - const key_t &key, - const value_t *value); - void on_reader_change( - const reader_id_t &reader_id, - const minidir_bcard_t *bcard); - - void spawn_update( - typename peer_data_t::link_data_t *link_data, - const key_t &key, - const optional &value); - void spawn_closing_link( - typename peer_data_t::link_data_t *link_data); - - mailbox_manager_t *const mailbox_manager; - watchable_map_t *const values; - watchable_map_t > *const readers; - - std::map > peer_map; - bool stopping; - - /* This must be destroyed after we destroy the `subs`es, since they may spawn new - coroutines that hold locks on it. But it must be destroyed before the other member - variables, since those coroutines may access the other member variables. */ - auto_drainer_t drainer; - - typename watchable_map_t - ::all_subs_t connection_subs; - typename watchable_map_t::all_subs_t value_subs; - typename watchable_map_t >::all_subs_t - reader_subs; -}; - -#endif /* CLUSTERING_GENERIC_MINIDIR_HPP_ */ - diff --git a/src/clustering/generic/minidir.tcc b/src/clustering/generic/minidir.tcc deleted file mode 100644 index ce738d08316..00000000000 --- a/src/clustering/generic/minidir.tcc +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/generic/minidir.hpp" - -#include "concurrency/wait_any.hpp" -#include "rpc/mailbox/mailbox.hpp" - -template -minidir_read_manager_t::minidir_read_manager_t( - mailbox_manager_t *mm) : - mailbox_manager(mm), - update_mailbox(mailbox_manager, - std::bind(&minidir_read_manager_t::on_update, this, ph::_1, ph::_2)), - connection_subs(mailbox_manager->get_connectivity_cluster()->get_connections(), - std::bind(&minidir_read_manager_t::on_connection_change, this, ph::_1, ph::_2), - initial_call_t::NO) - { } - -template -void minidir_read_manager_t::on_connection_change( - const peer_id_t &peer_id, - const connectivity_cluster_t::connection_pair_t *pair) { - auto it = peer_map.find(peer_id); - if (pair == nullptr && it != peer_map.end()) { - peer_data_t *peer_data = it->second.release(); - peer_map.erase(it); - auto_drainer_t::lock_t this_keepalive = drainer.lock(); - coro_t::spawn_sometime([peer_data, this_keepalive /* important to capture */]() { - delete peer_data; - }); - } -} - -template -void minidir_read_manager_t::on_update( - signal_t *interruptor, - const typename minidir_bcard_t::update_message_t &msg) { - const peer_id_t &peer_id = msg.peer_id; - const minidir_link_id_t &link_id = msg.link_id; - fifo_enforcer_write_token_t fifo_token = msg.fifo_token; - bool closing_link = msg.closing_link; - const optional &key = msg.key; - const optional &value = msg.value; - - scoped_ptr_t *peer_data_ptr = &peer_map[peer_id]; - if (!peer_data_ptr->has()) { - /* Acquire a lock on the connection session over which this message arrived. */ - auto_drainer_t::lock_t conn_keepalive; - connectivity_cluster_t::connection_t *conn = - mailbox_manager->get_connectivity_cluster()->get_connection( - peer_id, &conn_keepalive); - if (conn == nullptr) { - /* The connection over which this message arrived was just closed. Ignore the - message. */ - return; - } - peer_data_ptr->init(new peer_data_t(conn_keepalive)); - } - peer_data_t *peer_data = peer_data_ptr->get(); - - auto_drainer_t::lock_t peer_keepalive = peer_data->drainer.lock(); - scoped_ptr_t *link_data_ptr = - &peer_data->link_map[link_id]; - if (!link_data_ptr->has()) { - link_data_ptr->init(new typename peer_data_t::link_data_t); - } - typename peer_data_t::link_data_t *link_data = link_data_ptr->get(); - - fifo_enforcer_sink_t::exit_write_t exit_write(&link_data->fifo_sink, fifo_token); - wait_any_t waiter(&exit_write, peer_keepalive.get_drain_signal()); - wait_interruptible(&waiter, interruptor); - if (peer_keepalive.get_drain_signal()->is_pulsed()) { - return; - } - if (closing_link) { - guarantee(!static_cast(key)); - guarantee(!static_cast(value)); - exit_write.end(); - peer_data->link_map.erase(link_id); - } else { - guarantee(static_cast(key)); - auto it = link_data->map.find(*key); - if (static_cast(value)) { - if (it != link_data->map.end()) { - /* We are updating an existing key */ - it->second.change([&](value_t *v) { - *v = *value; - return true; - }); - } else { - /* We are creating a new key */ - link_data->map.insert(std::make_pair(*key, - typename watchable_map_var_t::entry_t( - &map_var, *key, *value))); - } - } else { - /* We are deleting an existing key. (Or maybe the key doesn't exist, and this - message was redundant. That's possible too.) */ - if (it != link_data->map.end()) { - link_data->map.erase(it); - } - } - } -} - -template -minidir_write_manager_t::minidir_write_manager_t( - mailbox_manager_t *_mm, - watchable_map_t *_values, - watchable_map_t > *_readers) : - mailbox_manager(_mm), - values(_values), - readers(_readers), - stopping(false), - /* The `reader_subs` constructor takes care of sending all the initial messages - because we pass `YES` as the third parameter. We pass `NO` to the other two - constructors to avoid redundancy. */ - connection_subs(mailbox_manager->get_connectivity_cluster()->get_connections(), - std::bind(&minidir_write_manager_t::on_connection_change, this, ph::_1, ph::_2), - initial_call_t::NO), - value_subs(values, - std::bind(&minidir_write_manager_t::on_value_change, this, ph::_1, ph::_2), - initial_call_t::NO), - reader_subs(readers, - std::bind(&minidir_write_manager_t::on_reader_change, this, ph::_1, ph::_2), - initial_call_t::YES) - { } - -template -minidir_write_manager_t::~minidir_write_manager_t() { - ASSERT_FINITE_CORO_WAITING; - /* Send a closing message to every existing link */ - for (auto &&peer_pair : peer_map) { - for (auto &&link_pair : peer_pair.second->link_map) { - spawn_closing_link(link_pair.second.get()); - } - } - /* Delete all the links. */ - peer_map.clear(); - /* Prevent `on_reader_change()` from creating any new links while we wait for the - coroutines we just started with `spawn_closing_link()` to finish. */ - stopping = true; -} - -template -void minidir_write_manager_t::on_connection_change( - const peer_id_t &peer, - const connectivity_cluster_t::connection_pair_t *pair) { - auto it = peer_map.find(peer); - if (pair == nullptr && it != peer_map.end()) { - peer_map.erase(it); - } else if (pair != nullptr && it == peer_map.end()) { - readers->read_all(std::bind( - &minidir_write_manager_t::on_reader_change, this, ph::_1, ph::_2)); - } -} - -template -void minidir_write_manager_t::on_value_change( - const key_t &key, - const value_t *value) { - optional value_optional; - if (value != nullptr) { - value_optional.set(*value); - } - for (auto &&peer_pair : peer_map) { - for (auto &&link_pair : peer_pair.second->link_map) { - spawn_update(link_pair.second.get(), key, value_optional); - } - } -} - -template -void minidir_write_manager_t::on_reader_change( - const reader_id_t &reader_id, - const minidir_bcard_t *bcard) { - if (stopping) { - /* We're in the process of closing all our existing links. So it would be bad to - open new links here (they would never get closed) and it there's no point in - closing links. */ - return; - } - if (bcard != nullptr) { - /* Find or make an entry in the peers map */ - peer_id_t peer_id = bcard->update_mailbox.get_peer(); - auto it = peer_map.find(peer_id); - if (it == peer_map.end()) { - auto_drainer_t::lock_t connection_keepalive; - if (mailbox_manager->get_connectivity_cluster()->get_connection(peer_id, - &connection_keepalive) == nullptr) { - /* We aren't connected to this server. Don't make an entry in the peers - map. If we connect, then `on_connection_change()` will fix it. */ - return; - } - auto res = peer_map.insert(std::make_pair( - peer_id, scoped_ptr_t( - new peer_data_t(connection_keepalive)))); - it = res.first; - } - - auto jt = it->second->link_map.find(reader_id); - if (jt != it->second->link_map.end()) { - /* We already have an entry for this reader. No need to do anything. */ - return; - } - - /* Create an entry in the link map for this reader */ - typename peer_data_t::link_data_t *link_data = - new typename peer_data_t::link_data_t; - it->second->link_map[reader_id].init(link_data); - link_data->link_id = generate_uuid(); - link_data->bcard = *bcard; - - /* Send initial values to the new reader */ - values->read_all([&](const key_t &key, const value_t *value) { - this->spawn_update(link_data, key, make_optional(*value)); - }); - } else { - /* Remove the entry in the link map. Unfortunately, we don't know which peer it's - on, so we have to check them all. */ - for (auto it = peer_map.begin(); it != peer_map.end(); ++it) { - auto jt = it->second->link_map.find(reader_id); - if (jt != it->second->link_map.end()) { - spawn_closing_link(jt->second.get()); - it->second->link_map.erase(jt); - if (it->second->link_map.empty()) { - /* Garbage collect the now-empty `peer_map` entry */ - peer_map.erase(it); - } - break; - } - } - } -} - -template -void minidir_write_manager_t::spawn_update( - typename peer_data_t::link_data_t *link_data, - const key_t &key, - const optional &value) { - minidir_link_id_t link_id = link_data->link_id; - minidir_bcard_t bcard = link_data->bcard; - fifo_enforcer_write_token_t write_token = link_data->fifo_source.enter_write(); - auto_drainer_t::lock_t keepalive = drainer.lock(); - coro_t::spawn_sometime([this, keepalive /* important to capture */, bcard, link_id, - write_token, key, value] { - send(mailbox_manager, bcard.update_mailbox, - {mailbox_manager->get_me(), link_id, write_token, false, make_optional(key), value}); - }); -} - -template -void minidir_write_manager_t::spawn_closing_link( - typename peer_data_t::link_data_t *link_data) { - minidir_link_id_t link_id = link_data->link_id; - minidir_bcard_t bcard = link_data->bcard; - fifo_enforcer_write_token_t write_token = link_data->fifo_source.enter_write(); - auto_drainer_t::lock_t keepalive = drainer.lock(); - coro_t::spawn_sometime([this, keepalive /* important to capture */, bcard, link_id, - write_token] { - send(mailbox_manager, bcard.update_mailbox, - {mailbox_manager->get_me(), link_id, write_token, true, optional(), - optional()}); - }); -} - diff --git a/src/clustering/generic/multi_client_client.cc b/src/clustering/generic/multi_client_client.cc deleted file mode 100644 index 68eb06645a6..00000000000 --- a/src/clustering/generic/multi_client_client.cc +++ /dev/null @@ -1,57 +0,0 @@ -#include "clustering/generic/multi_client_client.hpp" - -#include - -#include "clustering/generic/registrant.hpp" -#include "containers/archive/boost_types.hpp" - -template -multi_client_client_t::multi_client_client_t( - mailbox_manager_t *mm, - const mc_business_card_t &server, - signal_t *interruptor) : - mailbox_manager(mm) { - - mailbox_t intro_mailbox( - mailbox_manager, - [&](signal_t *, const server_business_card_t &bc) { - intro_promise.pulse(bc); - }); - - { - const client_business_card_t client_business_card(intro_mailbox.get_address()); - - registrant.init(new registrant_t(mailbox_manager, - server.registrar, - client_business_card)); - } - - wait_interruptible(intro_promise.get_ready_signal(), interruptor); -} - -template -void multi_client_client_t::spawn_request( - const request_type &request) { - send(mailbox_manager, intro_promise.wait(), request); -} - -template -optional::client_business_card_t> > > -multi_client_client_t::extract_registrar_business_card( - const optional > &bcard) { - if (bcard) { - if (bcard.get()) { - return make_optional(make_optional(bcard.get().get().registrar)); - } else { - return make_optional(optional >()); - } - } - return r_nullopt; -} - - -#include "clustering/query_routing/metadata.hpp" - -#include "rdb_protocol/protocol.hpp" -template class multi_client_client_t; diff --git a/src/clustering/generic/multi_client_client.hpp b/src/clustering/generic/multi_client_client.hpp deleted file mode 100644 index 7420efc606b..00000000000 --- a/src/clustering/generic/multi_client_client.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CLUSTERING_GENERIC_MULTI_CLIENT_CLIENT_HPP_ -#define CLUSTERING_GENERIC_MULTI_CLIENT_CLIENT_HPP_ - -#include - -#include "clustering/generic/multi_client_metadata.hpp" -#include "concurrency/promise.hpp" -#include "containers/optional.hpp" -#include "rpc/mailbox/typed.hpp" - -template class registrant_t; -template class clone_ptr_t; -template class watchable_t; - -/* `multi_client_client_t` works with `multi_client_server_t`. On creation, it -obtains the address of a request mailbox from the server, which can be different -for each connecting client. -Requests can then be spawned through the `spawn_request` method on the client. - -Since multi_client_client_t and multi_client_server_t are little more than wrappers -around a registrant_t and registrar_t respectively, they will likely be removed in -the future. */ - -template -class multi_client_client_t { -private: - typedef multi_client_business_card_t mc_business_card_t; - typedef typename mc_business_card_t::server_business_card_t server_business_card_t; - typedef typename mc_business_card_t::client_business_card_t client_business_card_t; - -public: - multi_client_client_t( - mailbox_manager_t *mm, - const mc_business_card_t &server, - signal_t *interruptor); - - void spawn_request(const request_type &request); - -private: - static optional > > - extract_registrar_business_card( - const optional > &bcard); - - mailbox_manager_t *const mailbox_manager; - - promise_t intro_promise; - - auto_drainer_t drainer; - - scoped_ptr_t::client_business_card_t> > registrant; - - DISABLE_COPYING(multi_client_client_t); -}; - -#endif /* CLUSTERING_GENERIC_MULTI_CLIENT_CLIENT_HPP_ */ diff --git a/src/clustering/generic/multi_client_metadata.hpp b/src/clustering/generic/multi_client_metadata.hpp deleted file mode 100644 index 2cdd9465e37..00000000000 --- a/src/clustering/generic/multi_client_metadata.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_GENERIC_MULTI_CLIENT_METADATA_HPP_ -#define CLUSTERING_GENERIC_MULTI_CLIENT_METADATA_HPP_ - -#include "clustering/generic/registration_metadata.hpp" -#include "rpc/semilattice/joins/macros.hpp" - -template -class multi_client_client_business_card_t { -public: - multi_client_client_business_card_t() { } - multi_client_client_business_card_t( - const mailbox_addr_t> &ia) : - intro_addr(ia) { } - mailbox_addr_t> intro_addr; - RDB_MAKE_ME_SERIALIZABLE_1(multi_client_client_business_card_t, intro_addr); - RDB_MAKE_ME_EQUALITY_COMPARABLE_1(multi_client_client_business_card_t, intro_addr); -}; - -template -class multi_client_business_card_t { -public: - multi_client_business_card_t() { } - typedef mailbox_addr_t server_business_card_t; - typedef multi_client_client_business_card_t - client_business_card_t; - - explicit multi_client_business_card_t( - const registrar_business_card_t &r) : - registrar(r) { } - registrar_business_card_t registrar; - RDB_MAKE_ME_SERIALIZABLE_1(multi_client_business_card_t, registrar); - RDB_MAKE_ME_EQUALITY_COMPARABLE_1(multi_client_business_card_t, registrar); -}; - -#endif /* CLUSTERING_GENERIC_MULTI_CLIENT_METADATA_HPP_ */ diff --git a/src/clustering/generic/multi_client_server.hpp b/src/clustering/generic/multi_client_server.hpp deleted file mode 100644 index 851d269da72..00000000000 --- a/src/clustering/generic/multi_client_server.hpp +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_GENERIC_MULTI_CLIENT_SERVER_HPP_ -#define CLUSTERING_GENERIC_MULTI_CLIENT_SERVER_HPP_ - -#include -#include -#include -#include - -#include "clustering/generic/multi_client_metadata.hpp" -#include "clustering/generic/registrar.hpp" -#include "rpc/mailbox/typed.hpp" - -/* `multi_client_server_t` is a thin wrapper around a `registrar_t`. - A `multi_client_client_t` can connect to it to run requests of type `request_type`. - Upon registration, `multi_client_server_t` creates an instance of `registrant_type`, - and sends the address of a request mailbox over to the `multi_client_client_t`. - -Since multi_client_client_t and multi_client_server_t are little more than wrappers -around a registrant_t and registrar_t respectively, they will likely be removed in -the future. */ - -template -class multi_client_server_t { -private: - typedef typename multi_client_business_card_t::server_business_card_t server_business_card_t; - typedef typename multi_client_business_card_t::client_business_card_t client_business_card_t; - -public: - multi_client_server_t( - mailbox_manager_t *mm, - user_data_type ud) : - mailbox_manager(mm), - user_data(ud), - registrar(mailbox_manager, this) - { } - - multi_client_business_card_t get_business_card() { - return multi_client_business_card_t( - registrar.get_business_card()); - } - -private: - class client_t : public intrusive_list_node_t { - public: - client_t(multi_client_server_t *p, - const client_business_card_t &client_bc, - UNUSED signal_t *interruptor) : - registrant(p->user_data), - drainer(new auto_drainer_t), - request_mailbox(new mailbox_t(p->mailbox_manager, - std::bind(&client_t::on_request, this, ph::_1, ph::_2))) - { - send(p->mailbox_manager, client_bc.intro_addr, - server_business_card_t(request_mailbox->get_address())); - } - - ~client_t() { - request_mailbox.reset(); - drainer.reset(); - } - - private: - void on_request(signal_t *interruptor, const request_type &request) { - try { - registrant.perform_request(request, interruptor); - } catch (const interrupted_exc_t &) { - /* ignore */ - } - } - - registrant_type registrant; - - scoped_ptr_t drainer; - scoped_ptr_t > request_mailbox; - }; - - mailbox_manager_t *const mailbox_manager; - user_data_type user_data; - - registrar_t registrar; - -private: - DISABLE_COPYING(multi_client_server_t); -}; - -#endif /* CLUSTERING_GENERIC_MULTI_CLIENT_SERVER_HPP_ */ diff --git a/src/clustering/generic/nonoverlapping_regions.cc b/src/clustering/generic/nonoverlapping_regions.cc deleted file mode 100644 index 0f8684e0f5d..00000000000 --- a/src/clustering/generic/nonoverlapping_regions.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "clustering/generic/nonoverlapping_regions.hpp" - -bool valid_regions_helper(const std::vector ®ionvec, - const std::set ®ionset) { - // Disallow empty regions. - if (regionset.find(region_t()) != regionset.end()) { - return false; - } - - // Require that the regions join to the universe. - region_t should_be_universe; - region_join_result_t res = region_join(regionvec, &should_be_universe); - if (res != REGION_JOIN_OK || should_be_universe != region_t::universe()) { - return false; - } - - return true; -} - -bool nonoverlapping_regions_t::set_regions(const std::vector ®ions) { - std::set regionset(regions.begin(), regions.end()); - - // Disallow duplicate regions. - if (regionset.size() != regions.size() || !valid_regions_helper(regions, regionset)) { - return false; - } - - regions_ = regionset; - return true; -} - -bool nonoverlapping_regions_t::valid_for_sharding() const { - std::vector regionvec(regions_.begin(), regions_.end()); - - return valid_regions_helper(regionvec, regions_); -} - -// TODO: Ugh, this is O(n)! Oh well. -bool nonoverlapping_regions_t::add_region(const region_t ®ion) { - for (std::set::iterator it = regions_.begin(); - it != regions_.end(); - ++it) { - if (region_overlaps(region, *it)) { - return false; - } - } - - regions_.insert(region); - return true; -} - diff --git a/src/clustering/generic/nonoverlapping_regions.hpp b/src/clustering/generic/nonoverlapping_regions.hpp deleted file mode 100644 index a56e1687ed2..00000000000 --- a/src/clustering/generic/nonoverlapping_regions.hpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CLUSTERING_GENERIC_NONOVERLAPPING_REGIONS_HPP_ -#define CLUSTERING_GENERIC_NONOVERLAPPING_REGIONS_HPP_ - -#include -#include - -#include "containers/archive/stl_types.hpp" -#include "region/region.hpp" -#include "rpc/serialize_macros.hpp" - -class nonoverlapping_regions_t { -public: - nonoverlapping_regions_t() { } - - // Returns true upon success, false if there's an overlap or the - // regions don't join to the universal set. This is like calling - // add_region in a loop and then valid_for_sharding, only maybe without being O(n^2). - MUST_USE bool set_regions(const std::vector ®ions); - - // We already know by the public interface that no regions - // overlap. This checks that their join is the universe, and that - // there is no empty region. - bool valid_for_sharding() const; - - // TODO: Use valid_for_sharding everywhere we use add_region. - - // Returns true upon success, false if there's an overlap. This is O(n)! - MUST_USE bool add_region(const region_t ®ion); - - // Yes, both of these are const iterators. - typedef std::set::iterator iterator; - iterator begin() const { return regions_.begin(); } - iterator end() const { return regions_.end(); } - - bool empty() const { return regions_.empty(); } - size_t size() const { return regions_.size(); } - - void remove_region(iterator it) { - guarantee(it != regions_.end()); - regions_.erase(it); - } - - bool operator==(const nonoverlapping_regions_t &other) const { - return regions_ == other.regions_; - } - - RDB_MAKE_ME_SERIALIZABLE_1(nonoverlapping_regions_t, regions_); - -private: - std::set regions_; -}; - -#endif // CLUSTERING_GENERIC_NONOVERLAPPING_REGIONS_HPP_ diff --git a/src/clustering/generic/raft_core.cc b/src/clustering/generic/raft_core.cc deleted file mode 100644 index 8a2988e135e..00000000000 --- a/src/clustering/generic/raft_core.cc +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "clustering/generic/raft_core.hpp" - -RDB_IMPL_SERIALIZABLE_1_SINCE_v2_1(raft_member_id_t, uuid); -RDB_IMPL_SERIALIZABLE_2_SINCE_v2_1(raft_config_t, voting_members, non_voting_members); -RDB_IMPL_SERIALIZABLE_2_SINCE_v2_1(raft_complex_config_t, config, new_config); - -void debug_print(printf_buffer_t *buf, const raft_member_id_t &member_id) { - debug_print(buf, member_id.uuid); -} diff --git a/src/clustering/generic/raft_core.hpp b/src/clustering/generic/raft_core.hpp deleted file mode 100644 index 3bcff7ada84..00000000000 --- a/src/clustering/generic/raft_core.hpp +++ /dev/null @@ -1,1015 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_GENERIC_RAFT_CORE_HPP_ -#define CLUSTERING_GENERIC_RAFT_CORE_HPP_ - -#include -#include -#include - -#include "errors.hpp" -#include - -#include "concurrency/auto_drainer.hpp" -#include "concurrency/new_mutex.hpp" -#include "concurrency/promise.hpp" -#include "concurrency/signal.hpp" -#include "concurrency/watchable.hpp" -#include "concurrency/watchable_map.hpp" -#include "concurrency/watchdog_timer.hpp" -#include "containers/archive/optional.hpp" -#include "containers/archive/stl_types.hpp" -#include "containers/empty_value.hpp" -#include "containers/uuid.hpp" -#include "time.hpp" - -/* This file and the corresponding `raft_core.tcc` implement the Raft consensus -algorithm, as described in the paper "In Search of an Understandable Consensus Algorithm -(Extended Version)" (2014) by Diego Ongaro and John Ousterhout. Because of the complexity -and subtlety of the Raft algorithm, we follow the paper closely and refer back to it -regularly. You are advised to have a copy of the paper on hand when reading or modifying -this file. The comments also occasionally refer to Diego Ongaro's dissertation, -"Consensus: Bridging Theory and Practice" (2014), as the dissertation addresses a few -subtle points that the paper does not. - -These files only contains the basic Raft algorithm itself; they don't contain any -networking or storage logic. Instead, the algorithm uses abstract interfaces to send and -receive network messages and write data to persistent storage. This both keeps these -files as simple as possible and makes it easy to test the Raft algorithm using mocked-up -network and storage systems. - -We support both log compaction and configuration changes. Configuration changes use the -joint-configuration mechanism described in the original paper, rather than the -one-server-at-a-time mechanism described in the dissertation. - -This implementation deviates significantly from the Raft paper in that we don't actually -send a continuous stream of heartbeats from the leader to the followers. Instead, we send -a "virtual heartbeat stream"; we send a message when a server becomes leader and another -when it ceases to be leader, and we rely on the underlying networking layer to detect if -the connection has failed. - -The classes in this file are templatized on a types called `state_t`, which represents -the state machine that the Raft cluster manages. Operations on the state machine are -represented by a member type `state_t::change_t`. So `state_t::change_t` is the type that -is stored in the Raft log, and `state_t` is stored when taking a snapshot. - -`state_t` and `state_t::change_t` must satisfy the following requirements: - * Both must be default-constructable, destructable, copy- and move-constructable, copy- - and move-assignable. - * Both must support the `==` and `!=` operators. - * `state_t` must have a method `void apply_change(const change_t &)` which applies the - change to the `state_t`, mutating it in place. */ - -/* `raft_term_t` and `raft_log_index_t` are typedefs to improve the readability of the -code, by making it clearer what the meaning of a particular number is. */ -typedef uint64_t raft_term_t; -typedef uint64_t raft_log_index_t; - -/* `raft_start_election_immediately_t` is used as a hint to the `raft_member_t` -constructor, and is used to speed up new raft clusters' first elections. - -On new clusters, it should be set to YES on precisely one of the raft members. If we're -joining an existing cluster, it should be set to NO to avoid disposing a running leader. */ -enum class raft_start_election_immediately_t { - NO, - YES -}; -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE( - raft_start_election_immediately_t, - int8_t, - raft_start_election_immediately_t::NO, - raft_start_election_immediately_t::YES); - -/* Every member of the Raft cluster is identified by a `raft_member_id_t`. The Raft paper -uses integers for this purpose, but we use UUIDs because we have no reliable distributed -way of assigning integers. Note that `raft_member_id_t` is not a `server_id_t` or a -`peer_id_t`. If a single server leaves a Raft cluster and then joins again, it will use a -different `raft_member_id_t` the second time. */ -class raft_member_id_t { -public: - raft_member_id_t() : uuid(nil_uuid()) { } - explicit raft_member_id_t(uuid_u u) : uuid(u) { } - bool is_nil() const { return uuid.is_nil(); } - bool operator==(const raft_member_id_t &other) const { return uuid == other.uuid; } - bool operator!=(const raft_member_id_t &other) const { return uuid != other.uuid; } - bool operator<(const raft_member_id_t &other) const { return uuid < other.uuid; } - uuid_u uuid; -}; -RDB_DECLARE_SERIALIZABLE(raft_member_id_t); -void debug_print(printf_buffer_t *buf, const raft_member_id_t &member_id); - -/* `raft_config_t` describes the set of members that are involved in the Raft cluster. */ -class raft_config_t { -public: - /* Regular members of the Raft cluster go in `voting_members`. `non_voting_members` - is for members that should receive updates, but that don't count for voting purposes. - */ - std::set voting_members, non_voting_members; - - /* Returns a list of all members, voting and non-voting. */ - std::set get_all_members() const { - std::set members; - members.insert(voting_members.begin(), voting_members.end()); - members.insert(non_voting_members.begin(), non_voting_members.end()); - return members; - } - - /* Returns `true` if `member` is a voting or non-voting member. */ - bool is_member(const raft_member_id_t &member) const { - return voting_members.count(member) == 1 || - non_voting_members.count(member) == 1; - } - - /* Returns `true` if `members` constitutes a majority. */ - bool is_quorum(const std::set &members) const { - size_t votes = 0; - for (const raft_member_id_t &m : members) { - votes += voting_members.count(m); - } - return (votes * 2 > voting_members.size()); - } - - /* Returns `true` if the given member can act as a leader. (Mostly this exists for - consistency with `raft_complex_config_t`.) */ - bool is_valid_leader(const raft_member_id_t &member) const { - return voting_members.count(member) == 1; - } - - /* The equality and inequality operators are mostly for debugging */ - bool operator==(const raft_config_t &other) const { - return voting_members == other.voting_members && - non_voting_members == other.non_voting_members; - } - bool operator!=(const raft_config_t &other) const { - return !(*this == other); - } -}; -RDB_DECLARE_SERIALIZABLE(raft_config_t); - -/* `raft_complex_config_t` can represent either a `raft_config_t` or a joint consensus of -an old and a new `raft_config_t`. */ -class raft_complex_config_t { -public: - /* For a regular configuration, `config` holds the configuration and `new_config` is - empty. For a joint consensus configuration, `config` holds the old configuration and - `new_config` holds the new configuration. */ - raft_config_t config; - optional new_config; - - bool is_joint_consensus() const { - return static_cast(new_config); - } - - std::set get_all_members() const { - std::set members = config.get_all_members(); - if (is_joint_consensus()) { - /* Raft paper, Section 6: "Log entries are replicated to all servers in both - configurations." */ - std::set members2 = new_config->get_all_members(); - members.insert(members2.begin(), members2.end()); - } - return members; - } - - bool is_member(const raft_member_id_t &member) const { - return config.is_member(member) || - (is_joint_consensus() && new_config->is_member(member)); - } - - bool is_quorum(const std::set &members) const { - /* Raft paper, Section 6: "Agreement (for elections and entry commitment) - requires separate majorities from both the old and new configurations." */ - if (is_joint_consensus()) { - return config.is_quorum(members) && new_config->is_quorum(members); - } else { - return config.is_quorum(members); - } - } - - bool is_valid_leader(const raft_member_id_t &member) const { - /* Raft paper, Section 6: "Any server from either configuration may serve as - leader." */ - return config.is_valid_leader(member) || - (is_joint_consensus() && new_config->is_valid_leader(member)); - } - - /* The equality and inequality operators are mostly for debugging */ - bool operator==(const raft_complex_config_t &other) const { - return config == other.config && - new_config == other.new_config; - } - bool operator!=(const raft_complex_config_t &other) const { - return !(*this == other); - } -}; -RDB_DECLARE_SERIALIZABLE(raft_complex_config_t); - -enum class raft_log_entry_type_t { - /* A `regular` log entry is one with a `change_t`. So if `type` is `regular`, - then `change` has a value but `config` is empty. */ - regular, - /* A `config` log entry has a `raft_complex_config_t`. They are used to change - the cluster configuration. See Section 6 of the Raft paper. So if `type` is - `config`, then `config` has a value but `change` is empty. */ - config, - /* A `noop` log entry does nothing and carries niether a `change_t` nor a - `raft_complex_config_t`. See Section 8 of the Raft paper. */ - noop -}; -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE(raft_log_entry_type_t, int8_t, - raft_log_entry_type_t::regular, raft_log_entry_type_t::noop); - -/* `raft_log_entry_t` describes an entry in the Raft log. */ -template -class raft_log_entry_t { -public: - raft_log_entry_type_t type; - raft_term_t term; - /* Whether `change` and `config` are empty or not depends on the value of `type`. */ - optional change; - optional config; - - /* The equality and inequality operators are for testing. */ - bool operator==(const raft_log_entry_t &other) const { - return type == other.type && - term == other.term && - change == other.change && - config == other.config; - } - bool operator!=(const raft_log_entry_t &other) const { - return !(*this == other); - } - - RDB_MAKE_ME_SERIALIZABLE_4(raft_log_entry_t, type, term, change, config); -}; - -/* `raft_log_t` stores a slice of the Raft log. There are two situations where this shows -up in Raft: in an "AppendEntries RPC", and in each server's local state. The Raft paper -represents this as three separate variables, but grouping them together makes the -code clearer. */ -template -class raft_log_t { -public: - /* In an append-entries message, `prev_index` and `prev_term` correspond to the - parameters that Figure 2 of the Raft paper calls `prevLogIndex` and `prevLogTerm`, - and `entries` corresponds to the parameter that the Raft paper calls `entries`. - - In a server's local state, `prev_index` and `prev_term` correspond to the "last - included index" and "last included term" variables as described in Section 7. - `entries` corresponds to the `log` variable described in Figure 2. */ - - raft_log_index_t prev_index; - raft_term_t prev_term; - std::deque > entries; - - /* Return the latest index that is present in the log. If the log is empty, returns - the index on which the log is based. */ - raft_log_index_t get_latest_index() const { - return prev_index + entries.size(); - } - - /* Returns the term of the log entry at the given index. The index must either be - present in the log or the last index before the log. */ - raft_term_t get_entry_term(raft_log_index_t index) const { - guarantee(index >= prev_index, "the log doesn't go back this far"); - guarantee(index <= get_latest_index(), "the log doesn't go forward this far"); - if (index == prev_index) { - return prev_term; - } else { - return get_entry_ref(index).term; - } - } - - /* Returns the entry in the log at the given index. */ - const raft_log_entry_t &get_entry_ref(raft_log_index_t index) const { - guarantee(index > prev_index, "the log doesn't go back this far"); - guarantee(index <= get_latest_index(), "the log doesn't go forward this far"); - return entries[index - prev_index - 1]; - } - - /* Deletes the log entry at the given index and all entries after it. */ - void delete_entries_from(raft_log_index_t index) { - guarantee(index > prev_index, "the log doesn't go back this far"); - guarantee(index <= get_latest_index(), "the log doesn't go forward this far"); - entries.erase(entries.begin() + (index - prev_index - 1), entries.end()); - } - - /* Deletes the log entry at the given index and all entries before it. Note that - `index` may be after the last log entry. */ - void delete_entries_to(raft_log_index_t index, raft_term_t index_term) { - guarantee(index > prev_index, "the log doesn't go back this far"); - if (index <= get_latest_index()) { - guarantee(index_term == get_entry_term(index)); - entries.erase(entries.begin(), entries.begin() + (index - prev_index)); - } else { - entries.clear(); - } - prev_index = index; - prev_term = index_term; - } - - /* Appends the given entry ot the log. */ - void append(const raft_log_entry_t &entry) { - entries.push_back(entry); - } - - /* The equality and inequality operators are for testing. */ - bool operator==(const raft_log_t &other) const { - return prev_index == other.prev_index && - prev_term == other.prev_term && - entries == other.entries; - } - bool operator!=(const raft_log_t &other) const { - return !(*this == other); - } - - RDB_MAKE_ME_SERIALIZABLE_3(raft_log_t, prev_index, prev_term, entries); -}; - -/* `raft_persistent_state_t` describes the information that each member of the Raft -cluster persists to stable storage. */ -template -class raft_persistent_state_t { -public: - /* `make_initial()` returns a `raft_persistent_state_t` for a member of a new Raft - instance with starting state `initial_state` and configuration `initial_config`. The - caller must ensure that every member of the new Raft cluster starts with the same - values for these variables. */ - static raft_persistent_state_t make_initial( - const state_t &initial_state, - const raft_config_t &initial_config); - - /* The remaining members are public so that `raft_member_t` and implementations of - `raft_storage_interface_t` can access them. In general nothing else besides those two - should have to access these. */ - - /* `current_term` and `voted_for` correspond to the variables with the same names in - Figure 2 of the Raft paper. */ - raft_term_t current_term; - raft_member_id_t voted_for; - - /* `snapshot_state` corresponds to the stored snapshotted state, as described in - Section 7. */ - state_t snapshot_state; - - /* `snapshot_config` corresponds to the stored snapshotted configuration, as - described in Section 7. */ - raft_complex_config_t snapshot_config; - - /* `log.prev_index` and `log.prev_term` correspond to the "last included index" and - "last included term" as described in Section 7. `log.entries` corresponds to the - `log` variable in Figure 2. */ - raft_log_t log; - - /* This implementation deviates from the Raft paper by also persisting - `commit_index`. This ensures that the Raft committed state doesn't revert to an - earlier state if the member crashes and restarts. */ - raft_log_index_t commit_index; - - /* The equality and inequality operators are for testing. */ - bool operator==(const raft_persistent_state_t &other) const { - return current_term == other.current_term && - voted_for == other.voted_for && - snapshot_state == other.snapshot_state && - snapshot_config == other.snapshot_config && - log == other.log && - commit_index == other.commit_index; - } - bool operator!=(const raft_persistent_state_t &other) const { - return !(*this == other); - } - - RDB_MAKE_ME_SERIALIZABLE_6(raft_persistent_state_t, current_term, voted_for, - snapshot_state, snapshot_config, log, commit_index); -}; - -/* `raft_storage_interface_t` is an abstract class that `raft_member_t` uses to store -data on disk. */ -template -class raft_storage_interface_t { -public: - /* `get()` returns a stable pointer to the current state. The `raft_member_t` will - take this pointer once and then hold it. It's a const pointer; the `raft_member_t` - must use the other methods in order to change the state. */ - virtual const raft_persistent_state_t *get() = 0; - - /* These methods perform various combinations of changes on the stored state. The - changes will always be visible in the pointer returned by `get()` and also stored - safely on disk by the time the method call returns. The changes will always be - atomic. */ - - /* Set `current_term` and `voted_for`, leaving everything else alone. */ - virtual void write_current_term_and_voted_for( - raft_term_t current_term, - raft_member_id_t voted_for) = 0; - - /* Set `commit_index`, leaving everything else alone. */ - virtual void write_commit_index( - raft_log_index_t commit_index) = 0; - - /* Delete any existing log entries in the stored log after `first_replaced`, and then - copy any log entries in `source` after `first_replaced` into the stored log. */ - virtual void write_log_replace_tail( - const raft_log_t &source, - raft_log_index_t first_replaced) = 0; - - /* Append a single entry to the log. */ - virtual void write_log_append_one( - const raft_log_entry_t &entry) = 0; - - /* Overwrite `snapshot_state` and `snapshot_config`. If `erase_log` is `true`, it - erase the entire log; otherwise, only erase log entries with indexes less than or - equal to `log_prev_index`. Set `log.prev_index` and `log.prev_term` to - `log_prev_index` and `log_prev_term`. Set `commit_index`. */ - virtual void write_snapshot( - const state_t &snapshot_state, - const raft_complex_config_t &snapshot_config, - bool clear_log, - raft_log_index_t log_prev_index, - raft_term_t log_prev_term, - raft_log_index_t commit_index) = 0; - -protected: - virtual ~raft_storage_interface_t() { } -}; - -/* `raft_rpc_request_t` describes a request that one Raft member sends over the network -to another Raft member. It actually can describe one of three request types, -corresponding to the three RPCs in the Raft paper, but they're bundled together into one -type for the convenience of code that uses Raft. */ -template -class raft_rpc_request_t { -private: - template friend class raft_member_t; - - /* `request_vote_t` describes the parameters to the "RequestVote RPC" described - in Figure 2 of the Raft paper. */ - class request_vote_t { - public: - /* `term`, `candidate_id`, `last_log_index`, and `last_log_term` correspond to - the parameters with the same names in the Raft paper. */ - raft_term_t term; - raft_member_id_t candidate_id; - raft_log_index_t last_log_index; - raft_term_t last_log_term; - RDB_MAKE_ME_SERIALIZABLE_4(request_vote_t, - term, candidate_id, last_log_index, last_log_term); - }; - - /* `install_snapshot_t` describes the parameters of the "InstallSnapshot RPC" - described in Figure 13 of the Raft paper. */ - class install_snapshot_t { - public: - /* `term`, `leader_id`, `last_included_index`, and `last_included_term` - correspond to the parameters with the same names in the Raft paper. In the Raft - paper, the content of the snapshot is sent as a series of binary blobs, but we - don't want to do that; instead, we send the `state_t` and `raft_complex_config_t` - directly. So our `snapshot_state` and `snapshot_config` parameters replace the - `offset`, `data`, and `done` parameters of the Raft paper. */ - raft_term_t term; - raft_member_id_t leader_id; - raft_log_index_t last_included_index; - raft_term_t last_included_term; - state_t snapshot_state; - raft_complex_config_t snapshot_config; - RDB_MAKE_ME_SERIALIZABLE_6(install_snapshot_t, - term, leader_id, last_included_index, last_included_term, snapshot_state, - snapshot_config); - }; - - /* `append_entries_t` describes the parameters of the "AppendEntries RPC" described - in Figure 2 of the Raft paper. */ - class append_entries_t { - public: - /* `term`, `leader_id`, and `leader_commit` correspond to the parameters with the - same names in the Raft paper. `entries` corresponds to three of the paper's - variables: `prevLogIndex`, `prevLogTerm`, and `entries`. */ - raft_term_t term; - raft_member_id_t leader_id; - raft_log_t entries; - raft_log_index_t leader_commit; - RDB_MAKE_ME_SERIALIZABLE_4(append_entries_t, - term, leader_id, entries, leader_commit); - }; - - boost::variant request; - - RDB_MAKE_ME_SERIALIZABLE_1(raft_rpc_request_t, request); -}; - -/* `raft_rpc_reply_t` describes the reply to a `raft_rpc_request_t`. */ -class raft_rpc_reply_t { -private: - template friend class raft_member_t; - - /* `request_vote_t` describes the information returned from the "RequestVote - RPC" described in Figure 2 of the Raft paper. */ - class request_vote_t { - public: - raft_term_t term; - bool vote_granted; - RDB_MAKE_ME_SERIALIZABLE_2(request_vote_t, term, vote_granted); - }; - - /* `install_snapshot_t` describes in the information returned from the - "InstallSnapshot RPC" described in Figure 13 of the Raft paper. */ - class install_snapshot_t { - public: - raft_term_t term; - RDB_MAKE_ME_SERIALIZABLE_1(install_snapshot_t, term); - }; - - /* `append_entries_t` describes the information returned from the - "AppendEntries RPC" described in Figure 2 of the Raft paper. */ - class append_entries_t { - public: - raft_term_t term; - bool success; - RDB_MAKE_ME_SERIALIZABLE_2(append_entries_t, term, success); - }; - - boost::variant reply; - - RDB_MAKE_ME_SERIALIZABLE_1(raft_rpc_reply_t, reply); -}; - -/* `raft_network_interface_t` is the abstract class that `raft_member_t` uses to send -messages over the network. */ -template -class raft_network_interface_t { -public: - /* `send_rpc()` sends a message to the Raft member indicated in the `dest` field. The - message will be delivered by calling the `on_rpc()` method on the `raft_member_t` in - question. - * If the RPC is delivered successfully, `send_rpc()` will return `true`, and - the reply will be stored in `*reply_out`. - * If something goes wrong, `send_rpc()` will return `false`. The RPC may or may not - have been delivered in this case. The caller should wait until the Raft member is - present in `get_connected_members()` before trying again. - * If the interruptor is pulsed, it throws `interrupted_exc_t`. The RPC may or may - not have been delivered. */ - virtual bool send_rpc( - const raft_member_id_t &dest, - const raft_rpc_request_t &request, - signal_t *interruptor, - raft_rpc_reply_t *reply_out) = 0; - - /* `send_virtual_heartbeats()` sends a virtual continuous stream of heartbeat - messages to all other members. The stream is "virtual" in that it actually consists - of a single start message and a single stop message rather than repeated actual - messages. The other members can receive it by looking in `get_connected_members()`. - Calling `send_virtual_heartbeats()` with an empty `optional` stops the stream. - */ - virtual void send_virtual_heartbeats( - const optional &term) = 0; - - /* `get_connected_members()` has an entry for every Raft member that we think we're - currently connected to. If the member is sending virtual heartbeats, the values will - be the term it is sending heartbeats for; otherwise, the values will be empty - `optional`s. */ - virtual watchable_map_t > - *get_connected_members() = 0; - -protected: - virtual ~raft_network_interface_t() { } -}; - -/* `raft_member_t` is responsible for managing the activity of a single member of the -Raft cluster. */ -template -class raft_member_t : public home_thread_mixin_debug_only_t -{ -public: - raft_member_t( - const raft_member_id_t &this_member_id, - /* The state in `storage` must already be initialized */ - raft_storage_interface_t *storage, - raft_network_interface_t *network, - /* We'll print log messages of the form `: `. If - `log_prefix` is empty, we won't print any messages. */ - const std::string &log_prefix, - const raft_start_election_immediately_t start_election_immediately); - - ~raft_member_t(); - - /* `state_and_config_t` describes the Raft cluster's current state, configuration, - and log index all in the same struct. The reason for putting them in the same struct - is so that they can be stored in a `watchable_t` and kept in sync. */ - class state_and_config_t { - public: - state_and_config_t(raft_log_index_t _log_index, const state_t &_state, - const raft_complex_config_t &_config) : - log_index(_log_index), state(_state), config(_config) { } - raft_log_index_t log_index; - state_t state; - raft_complex_config_t config; - }; - - /* `get_committed_state()` describes the state of the Raft cluster after all - committed log entries have been applied. */ - clone_ptr_t > get_committed_state() { - assert_thread(); - return committed_state.get_watchable(); - } - - /* `get_latest_state()` describes the state of the Raft cluster if every log entry, - including uncommitted entries, has been applied. */ - clone_ptr_t > get_latest_state() { - assert_thread(); - return latest_state.get_watchable(); - } - - /* `change_lock_t` freezes the Raft member state, for example in preparation for - calling `propose_*()`. Only one `change_lock_t` can exist at a time, and while it - exists, the Raft member will not process normal traffic; so don't keep the - `change_lock_t` around longer than necessary. However, it is safe to block while - holding the `change_lock_t` if you need to. - - The point of `change_lock_t` is that `get_latest_state()` will not change while the - `change_lock_t` exists, unless the lock owner calls `propose_*()`. The state reported - by `get_latest_state()` is guaranteed to be the state that the proposed change will - be applied to. This makes it possible to atomically read the state and issue a change - conditional on the state. */ - class change_lock_t { - public: - change_lock_t(raft_member_t *parent, signal_t *interruptor); - private: - friend class raft_member_t; - new_mutex_acq_t mutex_acq; - }; - - /* `get_state_for_init()` returns a `raft_persistent_state_t` that could be used to - initialize a new member joining the Raft cluster. - A `change_lock_t` must be constructed on this `raft_member_t` before calling this. - This is a separate step so that `get_state_for_init()` doesn't need to block in - order to obtain a lock internally. */ - raft_persistent_state_t get_state_for_init( - const change_lock_t &change_lock_proof); - - /* Here's how to perform a Raft transaction: - - 1. Find a `raft_member_t` in the cluster for which `get_readiness_for_change()` - returns true. (For a config transaction, use `get_readiness_for_config_change()` - instead.) - - 2. Construct a `change_lock_t` on that `raft_member_t`. - - 3. Call `propose_*()`. You can make multiple calls to `propose_change()` and - `propose_noop()` with the same `change_lock_t`, but no more than one call to - `propose_config_change()`. - - 4. Destroy the `change_lock_t` so the Raft cluster can process your transaction. - - 5. If you need to be notified of whether your transaction succeeds or not, wait on - the `change_token_t` returned by `propose_*()`. */ - - /* These watchables indicate whether this Raft member is ready to accept changes. In - general, if these watchables are true, then `propose_*()` will probably succeed. - (However, this is not guaranteed.) If these watchables are false, don't bother trying - `propose_*()`. - - Under the hood, these are true if: - - This member is currently the leader - - This member is in contact with a quorum of followers - - We are not currently in a reconfiguration (for `get_readiness_for_config_change()`) - */ - clone_ptr_t > get_readiness_for_change() { - return readiness_for_change.get_watchable(); - } - clone_ptr_t > get_readiness_for_config_change() { - return readiness_for_config_change.get_watchable(); - } - - /* `change_token_t` is a way to track the progress of a change to the Raft cluster. - It's a promise that will be `true` if the change has been committed, and `false` if - something went wrong. If it returns `false`, the change may or may not eventually be - committed anyway. */ - class change_token_t : public promise_t { - private: - friend class raft_member_t; - change_token_t(raft_member_t *parent, raft_log_index_t index, bool is_config); - promise_t promise; - bool is_config; - multimap_insertion_sentry_t sentry; - }; - - /* `propose_change()` tries to apply a `change_t` to the cluster. - `propose_config_change()` tries to change the cluster's configuration. - `propose_noop()` executes a transaction with no side effects; this can be used to - test if this node is actually a functioning leader. - - `propose_*()` will block while the change is being initiated; this should be a - relatively quick process. - - If the change is successfully initiated, `propose_*()` will return a `change_token_t` - that you can use to monitor the progress of the change. If it is not successful, it - will return `nullptr`. See `get_readiness_for_[config_]change()` for an explanation - of when and why it will return `nullptr`. */ - scoped_ptr_t propose_change( - change_lock_t *change_lock, - const typename state_t::change_t &change); - scoped_ptr_t propose_config_change( - change_lock_t *change_lock, - const raft_config_t &new_config); - scoped_ptr_t propose_noop( - change_lock_t *change_lock); - - /* When a Raft member calls `send_rpc()` on its `raft_network_interface_t`, the RPC - is sent across the network and delivered by calling `on_rpc()` at its destination. */ - void on_rpc( - const raft_rpc_request_t &request, - raft_rpc_reply_t *reply_out); - -#ifndef NDEBUG - /* `check_invariants()` asserts that the given collection of Raft cluster members are - in a valid, consistent state. This may block, because it needs to acquire each - member's mutex, but it will not modify anything. Since this requires direct access to - each member of the Raft cluster, it's only useful for testing. */ - static void check_invariants( - const std::set *> &members); -#endif - -private: - enum class mode_t { - follower, - candidate, - leader - }; - - /* These are the minimum and maximum election timeouts. In section 5.6, the Raft - paper suggests that a typical election timeout should be somewhere between 10ms and - 500ms. We use somewhat larger numbers to reduce server traffic, at the cost of longer - periods of unavailability when a master dies. */ - const int32_t election_timeout_min_ms = 1000, - election_timeout_max_ms = 2000; - - /* To cope with i/o bottlenecks when many tables are running elections at the same - time, we additionally increase the election timeout during repeated retries, up to - the maximum value set here: */ - const int32_t election_retry_timeout_max_ms = 30000; - - /* When the number of committed entries in the log exceeds this number, we will take - a snapshot to compress them. */ - const size_t snapshot_threshold = 20; - - /* Note: Methods prefixed with `follower_`, `candidate_`, or `leader_` are methods - that are only used when in that state. This convention will hopefully make the code - slightly clearer. */ - - /* `on_rpc()` calls one of these three methods depending on what type of RPC it - received. */ - void on_request_vote_rpc( - const typename raft_rpc_request_t::request_vote_t &rpc, - raft_rpc_reply_t::request_vote_t *reply_out); - void on_install_snapshot_rpc( - const typename raft_rpc_request_t::install_snapshot_t &rpc, - raft_rpc_reply_t::install_snapshot_t *reply_out); - void on_append_entries_rpc( - const typename raft_rpc_request_t::append_entries_t &rpc, - raft_rpc_reply_t::append_entries_t *reply_out); - -#ifndef NDEBUG - /* Asserts that all of the invariants that can be checked locally hold true. This - doesn't block or modify anything. It should be safe to call it at any time (except - when in between modifying two variables that should remain consistent with each - other, of course). In general we call it whenever we acquire or release the mutex, - because we know that the variables should be consistent at those times. */ - void check_invariants(const new_mutex_acq_t *mutex_acq); -#endif - - /* `on_connected_members_change()` is called whenever the contents of - `network->get_connected_members()` changes. If we're a leader, we use it to run - `update_readiness_for_change()`. We also use it to check for higher terms in virtual - heartbeats. */ - void on_connected_members_change( - const raft_member_id_t &member_id, - const optional *value); - - /* `on_rpc_from_leader()` is a helper function that we call in response to - AppendEntries RPCs, InstallSnapshot RPCs, and virtual heartbeats. It returns `true` - if the RPC is valid, and `false` if we should reject the RPC. */ - bool on_rpc_from_leader( - const raft_member_id_t &request_leader_id, - raft_term_t request_term, - const new_mutex_acq_t *mutex_acq); - - /* `on_watchdog()` is called if we haven't heard from the leader within an election - timeout. it starts a new election by spawning `candidate_and_leader_coro()`. */ - void on_watchdog(); - - /* `apply_log_entries()` updates `state_and_config` with the entries from `log` with - indexes `first <= index <= last`. */ - static void apply_log_entries( - state_and_config_t *state_and_config, - const raft_log_t &log, - raft_log_index_t first, - raft_log_index_t last); - - /* `update_term()` sets the term to `new_term` and resets all per-term variables. - Since we often want to set `ps().voted_for` to something immediately after starting a - new term, it allows setting that to avoid an extra storage write operation. */ - void update_term(raft_term_t new_term, - raft_member_id_t new_voted_for, - const new_mutex_acq_t *mutex_acq); - - /* When we change the commit index we have to also apply changes to the state - machine. `update_commit_index()` handles that automatically. */ - void update_commit_index(raft_log_index_t new_commit_index, - const new_mutex_acq_t *mutex_acq); - - /* When we change `match_index` we might have to update `commit_index` as well. - `leader_update_match_index()` handles that automatically. */ - void leader_update_match_index( - raft_member_id_t key, - raft_log_index_t new_value, - const new_mutex_acq_t *mutex_acq); - - /* `update_readiness_for_change()` should be called whenever any of the variables - that are used to compute `readiness_for_change` or `readiness_for_config_change` are - modified. */ - void update_readiness_for_change(); - - /* `candidate_or_leader_become_follower()` moves us from the `candidate` or `leader` - state to `follower` state. It kills `candidate_and_leader_coro()` and blocks until it - exits. */ - void candidate_or_leader_become_follower(const new_mutex_acq_t *mutex_acq); - - /* `candidate_and_leader_coro()` contains most of the candidate- and leader-specific - logic. It runs in a separate coroutine for as long as we are a candidate or leader. - */ - void candidate_and_leader_coro( - /* A `new_mutex_acq_t` allocated on the heap. `candidate_and_leader_coro()` takes - ownership of it. */ - new_mutex_acq_t *mutex_acq_on_heap, - /* To make sure that `candidate_and_leader_coro` stops before the `raft_member_t` - is destroyed. This is also used by `candidate_or_leader_become_follower()` to - interrupt `candidate_and_leader_coro()`. */ - auto_drainer_t::lock_t leader_keepalive); - - /* `candidate_run_election()` is a helper function for `candidate_and_leader_coro()`. - It sends out request-vote RPCs and wait for us to get enough votes. It blocks until - we are elected or `cancel_signal` is pulsed. The caller is responsible for detecting - the case where another leader is elected and also for detecting the case where the - election times out, and pulsing `cancel_signal`. It returns `true` if we were - elected. */ - bool candidate_run_election( - /* Note that `candidate_run_election()` may temporarily release `mutex_acq`, but - it will always be holding the lock when `run_election()` returns. But if - `interruptor` is pulsed it will throw `interrupted_exc_t` and not reacquire the - lock. */ - scoped_ptr_t *mutex_acq, - signal_t *cancel_signal, - signal_t *interruptor); - - /* `leader_spawn_update_coros()` is a helper function for - `candidate_and_leader_coro()` that spawns or kills instances of `run_updates()` as - necessary to ensure that there is always one for each cluster member. */ - void leader_spawn_update_coros( - /* The value of `nextIndex` to use for each newly connected peer. */ - raft_log_index_t initial_next_index, - /* A map containing an `auto_drainer_t` for each running update coroutine. */ - std::map > *update_drainers, - const new_mutex_acq_t *mutex_acq); - - /* `leader_send_updates()` is a helper function for `candidate_and_leader_coro()`; - `leader_spawn_update_coros()` spawns one in a new coroutine for each peer. It pushes - install-snapshot RPCs and/or append-entry RPCs out to the given peer until - `update_keepalive.get_drain_signal()` is pulsed. */ - void leader_send_updates( - const raft_member_id_t &peer, - raft_log_index_t initial_next_index, - auto_drainer_t::lock_t update_keepalive); - - /* `leader_continue_reconfiguration()` is a helper function for - `candidate_and_leader_coro()`. It checks if we have completed the first phase of a - reconfiguration (by committing a joint consensus configuration) and if so, it starts - the second phase by committing the new configuration. It also checks if we have - completed the second phase and if so, it makes us step down. */ - void leader_continue_reconfiguration( - const new_mutex_acq_t *mutex_acq); - - /* `candidate_or_leader_note_term()` is a helper function for - `candidate_run_election()` and `leader_send_updates()`. If the given term is greater - than the current term, it updates the current term and interrupts - `candidate_and_leader_coro()`. It returns `true` if the term was changed. */ - bool candidate_or_leader_note_term( - raft_term_t term, - const new_mutex_acq_t *mutex_acq); - - /* `leader_append_log_entry()` is a helper for `propose_change_if_leader()` and - `propose_config_change_if_leader()`. It adds an entry to the log but doesn't wait for - the entry to be committed. */ - void leader_append_log_entry( - const raft_log_entry_t &log_entry, - const new_mutex_acq_t *mutex_acq); - - /* This is because we end up needing to access the persistent state very frequently, - and `storage->get()` is too verbose. */ - raft_persistent_state_t const &ps() { - return *storage->get(); - } - - /* The member ID of the member of the Raft cluster represented by this - `raft_member_t`. */ - const raft_member_id_t this_member_id; - - raft_storage_interface_t *const storage; - raft_network_interface_t *const network; - - const std::string log_prefix; - - /* `committed_state` describes the state after all committed log entries have been - applied. The `state` field of `committed_state` is equivalent to the "state machine" - in the Raft paper. The `log_index` field is equal to the `lastApplied` and - `commitIndex` variables in Figure 2 of the Raft paper. This implementation deviates - from the Raft paper in that the paper allows for a delay between when changes are - committed and when they are applied to the state machine, so `lastApplied` may lag - behind `commitIndex`. But we always apply changes to the state machine as soon as - they are committed, so `lastApplied` and `commitIndex` are equivalent for us. */ - watchable_variable_t committed_state; - - /* `latest_state` describes the state after all log entries, not only committed ones, - have been applied. This is publicly exposed to the user, and it's also useful because - "a server always uses the latest configuration in its log, regardless of whether the - entry is committed" (Raft paper, Section 6). Whenever `ps.log` is modified, - `latest_state` must be updated to keep in sync. */ - watchable_variable_t latest_state; - - /* Only `candidate_and_leader_coro()` should ever change `mode` */ - mode_t mode; - - /* `current_term_leader_id` is the ID of the member that is leader during this term. - If we haven't seen any node acting as leader this term, it's `nil_uuid()`. We use it - to redirect clients as described in Figure 2 and Section 8. */ - raft_member_id_t current_term_leader_id; - - /* If any member is sending us valid virtual heartbeats for `ps.current_term`, then - `virtual_heartbeat_sender` will be set to that member; otherwise it will be nil. */ - raft_member_id_t virtual_heartbeat_sender; - - /* If `virtual_heartbeat_sender` is non-empty, we'll set - `virtual_heartbeat_watchdog_blockers` to make sure that neither `watchdog` nor - `watchdog_leader` gets triggered while we're receiving virtual heartbeats. */ - scoped_ptr_t virtual_heartbeat_watchdog_blockers[2]; - - /* `match_indexes` corresponds to the `matchIndex` array described in Figure 2 of the - Raft paper. Note that it is only used if we are the leader; if we are not the leader, - then it must be empty. */ - std::map match_indexes; - - /* `readiness_for_change` and `readiness_for_config_change` track whether this member - is ready to accept changes. A member is ready for changes if it is leader and in - contact with a quorum of followers; it is ready for config changes if those - conditions are met and it is also not currently in a reconfiguration. Whenever any of - those variables changes, `update_readiness_for_change()` must be called. */ - watchable_variable_t readiness_for_change; - watchable_variable_t readiness_for_config_change; - - /* `propose_[config_]change()` inserts a `change_token_t *` into `change_tokens`. If - we stop being leader or lose contact with a majority of the cluster nodes, then all - of the change tokens will be notified that the changes they were waiting on have - failed. Whenever we commit a transaction, we also notify change tokens for success if - appropriate. If we are not leader, `change_tokens` will be empty. */ - std::multimap change_tokens; - - /* This mutex ensures that operations don't interleave in confusing ways. Each RPC - acquires this mutex when it begins and releases it when it returns. Also, if - `candidate_and_leader_coro()` is running, it holds this mutex when actively - manipulating state and releases it when waiting. In general we don't hold the mutex - when responding to an interruptor. */ - new_mutex_t mutex; - - /* This mutex assertion controls writes to the Raft log and associated state. - Specifically, anything writing to `ps().log`, `ps().snapshot_*`, `committed_state`, - or `latest_state` should hold this mutex assertion. - - If we are follower, `on_append_entries_rpc()` and `on_install_snapshot_rpc()` acquire - this temporarily; if we are candidate or leader, `candidate_and_leader_coro()` holds - this at all times. */ - mutex_assertion_t log_mutex; - - /* This makes sure that `candidate_and_leader_coro()` stops when the `raft_member_t` - is destroyed. It's in a `scoped_ptr_t` so that - `candidate_or_leader_become_follower()` can destroy it to kill - `candidate_and_leader_coro()`. */ - scoped_ptr_t leader_drainer; - - /* Occasionally we have to spawn miscellaneous coroutines. This makes sure that they - all get stopped before the `raft_member_t` is destroyed. It's in a `scoped_ptr_t` so - that the destructor can destroy it early. */ - scoped_ptr_t drainer; - - /* Whenever we get a valid RPC from a candidate, we notify `watchdog`. Whenever we - get a valid RPC from a leader, we notify both watchdogs. If `watchdog` is triggered, - we start a new election. The purpose of `watchdog_leader_only` is that to keep track - of whether we have seen a valid leader recently, so that we know whether or not to - accept RequestVote RPCs. */ - scoped_ptr_t watchdog, watchdog_leader_only; - - /* This calls `update_readiness_for_change()` whenever a peer connects or - disconnects. */ - scoped_ptr_t > - ::all_subs_t> connected_members_subs; -}; - -#endif /* CLUSTERING_GENERIC_RAFT_CORE_HPP_ */ - diff --git a/src/clustering/generic/raft_core.tcc b/src/clustering/generic/raft_core.tcc deleted file mode 100644 index b4236da2ba9..00000000000 --- a/src/clustering/generic/raft_core.tcc +++ /dev/null @@ -1,2064 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_GENERIC_RAFT_CORE_TCC_ -#define CLUSTERING_GENERIC_RAFT_CORE_TCC_ - -#include "clustering/generic/raft_core.hpp" - -#include "arch/compiler.hpp" -#include "arch/runtime/coroutines.hpp" -#include "concurrency/exponential_backoff.hpp" -#include "containers/map_sentries.hpp" -#include "logger.hpp" -#include "random.hpp" - -/* After finding myself repeatedly adding debug logging to Raft and then tearing it out -when I didn't need it anymore, I automated the process. Define `ENABLE_RAFT_DEBUG` to -print messages to stderr whenever important events happen in Raft. */ -// #define ENABLE_RAFT_DEBUG -#ifdef ENABLE_RAFT_DEBUG -#include "debug.hpp" -inline std::string show_member_id(const raft_member_id_t &mid) { - return uuid_to_str(mid.uuid).substr(0, 4); -} -#define RAFT_DEBUG(...) debugf(__VA_ARGS__) -#define RAFT_DEBUG_THIS(...) debugf("%s: %s", \ - show_member_id(this_member_id).c_str(), \ - strprintf(__VA_ARGS__).c_str()) -#define RAFT_DEBUG_VAR -#else -#define RAFT_DEBUG(...) ((void)0) -#define RAFT_DEBUG_THIS(...) ((void)0) -#define RAFT_DEBUG_VAR UNUSED -#endif /* ENABLE_RAFT_DEBUG */ - -template -raft_persistent_state_t -raft_persistent_state_t::make_initial( - const state_t &initial_state, - const raft_config_t &initial_config) { - raft_persistent_state_t ps; - /* The Raft paper indicates that `current_term` should be initialized to 0 and the - first log index is 1. */ - ps.current_term = 0; - ps.voted_for = raft_member_id_t(); - ps.snapshot_state = initial_state; - raft_complex_config_t complex_config; - complex_config.config = initial_config; - ps.snapshot_config = complex_config; - ps.log.prev_index = 0; - ps.log.prev_term = 0; - ps.commit_index = 0; - return ps; -} - -template -raft_member_t::raft_member_t( - const raft_member_id_t &_this_member_id, - raft_storage_interface_t *_storage, - raft_network_interface_t *_network, - const std::string &_log_prefix, - const raft_start_election_immediately_t start_election_immediately) : - this_member_id(_this_member_id), - storage(_storage), - network(_network), - log_prefix(_log_prefix), - /* Restore state machine from snapshot. */ - committed_state(state_and_config_t( - ps().log.prev_index, - ps().snapshot_state, - ps().snapshot_config)), - /* We'll update `latest_state` with the contents of the log shortly */ - latest_state(committed_state.get_ref()), - /* Raft paper, Section 5.2: "When servers start up, they begin as followers." */ - mode(mode_t::follower), - /* These must be false initially because we're a follower. */ - readiness_for_change(false), - readiness_for_config_change(false), - drainer(new auto_drainer_t), - /* Initialize `watchdog` in the `NOT_TRIGGERED` state, so that it will wait for an - election timeout to elapse before starting a new election, unless - _start_election_immediately is true, in which case we start an election - immediately. */ - watchdog(new watchdog_timer_t( - election_timeout_min_ms, election_timeout_max_ms, - [this]() { this->on_watchdog(); }, - start_election_immediately == raft_start_election_immediately_t::YES - ? watchdog_timer_t::state_t::TRIGGERED - : watchdog_timer_t::state_t::NOT_TRIGGERED)), - /* Initialize `watchdog_leader_only` in the `TRIGGERED` state, so that we will accept - valid RequestVote RPCs. */ - watchdog_leader_only(new watchdog_timer_t( - /* Note that we wait exactly `election_timeout_min_ms`. See the note in - `on_request_vote_rpc()` for an explanation of why this is. */ - election_timeout_min_ms, election_timeout_min_ms, - nullptr, watchdog_timer_t::state_t::TRIGGERED)), - connected_members_subs( - new watchable_map_t >::all_subs_t( - network->get_connected_members(), - std::bind(&raft_member_t::on_connected_members_change, this, ph::_1, ph::_2) - )) -{ - new_mutex_acq_t mutex_acq(&mutex); - /* Finish initializing `latest_state` */ - latest_state.apply_atomic_op([&](state_and_config_t *s) -> bool { - this->apply_log_entries( - s, this->ps().log, s->log_index + 1, this->ps().log.get_latest_index()); - return !this->ps().log.entries.empty(); - }); - /* Finish initializing `committed_state` */ - committed_state.apply_atomic_op([&](state_and_config_t *s) -> bool { - this->apply_log_entries( - s, this->ps().log, s->log_index + 1, this->ps().commit_index); - return true; - }); - DEBUG_ONLY_CODE(check_invariants(&mutex_acq)); -} - -template -raft_member_t::~raft_member_t() { - assert_thread(); - /* Now that the destructor has been called, we can safely assume that our public - methods will not be called. */ - - /* In addition unsubscribe from member change events, so we're sure to not get - those either while destructing. */ - connected_members_subs.reset(); - - new_mutex_acq_t mutex_acq(&mutex); - DEBUG_ONLY_CODE(check_invariants(&mutex_acq)); - - /* Kill `candidate_and_leader_coro()`, if it's running. We have to destroy this - before destroying the watchdogs because it may hold `watchdog_timer_t::blocker_t`s - that point to the watchdogs. */ - if (mode != mode_t::follower) { - candidate_or_leader_become_follower(&mutex_acq); - } - - /* Destroy the watchdogs so that they don't start any new actions while we're - cleaning up. We have to do this before destroying `drainer` because they might - otherwise try to lock `drainer`. We have to destroy the blockers before destroying - the watchdogs because they hold pointers to the watchdogs. */ - virtual_heartbeat_watchdog_blockers[0].reset(); - virtual_heartbeat_watchdog_blockers[1].reset(); - watchdog.reset(); - watchdog_leader_only.reset(); - - /* Destroy `drainer` to kill any miscellaneous coroutines that aren't related to - `candidate_and_leader_coro()`. */ - drainer.reset(); - - /* All the coroutines have stopped, so we can start calling member destructors. */ -} - -template -raft_persistent_state_t raft_member_t::get_state_for_init( - const change_lock_t &change_lock_proof) { - change_lock_proof.mutex_acq.guarantee_is_holding(&mutex); - /* This implementation deviates from the Raft paper in that we initialize new peers - joining the cluster by copying the log, snapshot, etc. from an existing peer, instead - of starting the new peer with a blank state. */ - raft_persistent_state_t ps_copy = ps(); - /* Clear `voted_for` since the newly-joining peer will not have voted for anything */ - ps_copy.voted_for = raft_member_id_t(); - return ps_copy; -} - -template -raft_member_t::change_lock_t::change_lock_t( - raft_member_t *parent, - signal_t *interruptor) : - mutex_acq(&parent->mutex, interruptor) { - DEBUG_ONLY_CODE(parent->check_invariants(&mutex_acq)); -} - -template -raft_member_t::change_token_t::change_token_t( - raft_member_t *_parent, - raft_log_index_t _index, - bool _is_config) : - is_config(_is_config), sentry(&_parent->change_tokens, _index, this) { } - -template -scoped_ptr_t::change_token_t> -raft_member_t::propose_change( - change_lock_t *change_lock, - const typename state_t::change_t &change) { - assert_thread(); - change_lock->mutex_acq.guarantee_is_holding(&mutex); - - if (!readiness_for_change.get_ref()) { - return scoped_ptr_t(); - } - guarantee(mode == mode_t::leader); - - /* We have to construct the change token before we create the log entry. If we are - the only member of the cluster, then `leader_append_log_entry()` will commit the - entry as well as create it; by creating the change token first, we ensure that it - will get notified if this happens. */ - raft_log_index_t log_index = ps().log.get_latest_index() + 1; - scoped_ptr_t change_token( - new change_token_t(this, log_index, false)); - - raft_log_entry_t new_entry; - new_entry.type = raft_log_entry_type_t::regular; - new_entry.change = optional(change); - new_entry.term = ps().current_term; - - leader_append_log_entry(new_entry, &change_lock->mutex_acq); - guarantee(ps().log.get_latest_index() == log_index); - - DEBUG_ONLY_CODE(check_invariants(&change_lock->mutex_acq)); - return change_token; -} - -template -scoped_ptr_t::change_token_t> -raft_member_t::propose_config_change( - change_lock_t *change_lock, - const raft_config_t &new_config) { - assert_thread(); - change_lock->mutex_acq.guarantee_is_holding(&mutex); - - if (!readiness_for_config_change.get_ref()) { - return scoped_ptr_t(); - } - guarantee(mode == mode_t::leader); - guarantee(!committed_state.get_ref().config.is_joint_consensus()); - guarantee(!latest_state.get_ref().config.is_joint_consensus()); - - raft_log_index_t log_index = ps().log.get_latest_index() + 1; - scoped_ptr_t change_token( - new change_token_t(this, log_index, true)); - - /* Raft paper, Section 6: "... the cluster first switches to a [joint consensus - configuration]" */ - raft_complex_config_t new_complex_config; - new_complex_config.config = latest_state.get_ref().config.config; - new_complex_config.new_config = optional(new_config); - - raft_log_entry_t new_entry; - new_entry.type = raft_log_entry_type_t::config; - new_entry.config = optional(new_complex_config); - new_entry.term = ps().current_term; - - leader_append_log_entry(new_entry, &change_lock->mutex_acq); - guarantee(ps().log.get_latest_index() == log_index); - - /* Now that we've put a config entry into the log, we'll have to flip - `readiness_for_config_change` */ - update_readiness_for_change(); - guarantee(!readiness_for_config_change.get_ref()); - - /* When the joint consensus is committed, `leader_continue_reconfiguration()` - will take care of initiating the second step of the reconfiguration. */ - - DEBUG_ONLY_CODE(check_invariants(&change_lock->mutex_acq)); - return change_token; -} - -template -scoped_ptr_t::change_token_t> -raft_member_t::propose_noop( - change_lock_t *change_lock) { - assert_thread(); - change_lock->mutex_acq.guarantee_is_holding(&mutex); - - if (!readiness_for_change.get_ref()) { - return scoped_ptr_t(); - } - guarantee(mode == mode_t::leader); - - raft_log_index_t log_index = ps().log.get_latest_index() + 1; - scoped_ptr_t change_token( - new change_token_t(this, log_index, false)); - - raft_log_entry_t new_entry; - new_entry.type = raft_log_entry_type_t::noop; - new_entry.term = ps().current_term; - - leader_append_log_entry(new_entry, &change_lock->mutex_acq); - guarantee(ps().log.get_latest_index() == log_index); - - DEBUG_ONLY_CODE(check_invariants(&change_lock->mutex_acq)); - return change_token; -} - -template -void raft_member_t::on_rpc( - const raft_rpc_request_t &request, - raft_rpc_reply_t *reply_out) { - typedef raft_rpc_request_t req_t; - if (const typename req_t::request_vote_t *request_vote_req = - boost::get(&request.request)) { - raft_rpc_reply_t::request_vote_t rep; - on_request_vote_rpc(*request_vote_req, &rep); - reply_out->reply = rep; - } else if (const typename req_t::install_snapshot_t *install_snapshot_req = - boost::get(&request.request)) { - raft_rpc_reply_t::install_snapshot_t rep; - on_install_snapshot_rpc(*install_snapshot_req, &rep); - reply_out->reply = rep; - } else if (const typename req_t::append_entries_t *append_entries_req = - boost::get(&request.request)) { - raft_rpc_reply_t::append_entries_t rep; - on_append_entries_rpc(*append_entries_req, &rep); - reply_out->reply = rep; - } else { - unreachable(); - } -} - -#ifndef NDEBUG -template -void raft_member_t::check_invariants( - const std::set *> &members) { - /* We acquire each member's mutex to ensure we don't catch them in invalid states */ - std::vector > mutex_acqs; - for (raft_member_t *member : members) { - signal_timer_t timeout; - timeout.start(10000); - try { - scoped_ptr_t mutex_acq( - new new_mutex_acq_t(&member->mutex, &timeout)); - /* Check each member's invariants individually */ - member->check_invariants(mutex_acq.get()); - mutex_acqs.push_back(std::move(mutex_acq)); - } catch (const interrupted_exc_t &) { - crash("Raft member mutex is gridlocked"); - } - } - - { - /* Raft paper, Figure 3: "Election Safety: at most one leader can be elected in - a given term" */ - std::set claimed; - for (raft_member_t *member : members) { - if (member->mode == mode_t::leader) { - auto pair = claimed.insert(member->ps().current_term); - guarantee(pair.second, "At most one leader can be elected in a given " - "term"); - } - } - } - - /* It's impractical to test the Leader Append-Only property described in Figure 3 - because we don't store history, so we can't tell what operations the leader is - performing on its log. */ - - { - /* Raft paper, Figure 3: "Log Matching: if two logs contain an entry with the - same index and term, then the logs are identical in all entries up through the - given index." */ - for (raft_member_t *m1 : members) { - for (raft_member_t *m2 : members) { - bool match_so_far = true; - for (raft_log_index_t i = std::max(m1->ps().log.prev_index + 1, - m2->ps().log.prev_index + 1); - i <= std::min(m1->ps().log.get_latest_index(), - m2->ps().log.get_latest_index()); - ++i) { - raft_log_entry_t e1 = m1->ps().log.get_entry_ref(i); - raft_log_entry_t e2 = m2->ps().log.get_entry_ref(i); - if (e1.term == e2.term) { - guarantee(e1.type == e2.type, - "Log matching property violated"); - guarantee(e1.change == e2.change, - "Log matching property violated"); - guarantee(e1.config == e2.config, - "Log matching property violated"); - guarantee(match_so_far, - "Log matching property violated"); - } else { - match_so_far = false; - } - } - } - } - } - - /* It's impractical to test the Leader Completeness property because we snapshot log - entries so quickly that the test would in practice be a no-op. */ - - /* Raft paper, Figure 3: "State Machine Safety: if a server has applied a log entry - at a given index to its state machine, no other server will ever apply a different - log entry for the same index." - Since we snapshot log entries immediately, in practice the part of this that's worth - testing is that if two members have the same snapshot index then they will have the - same snapshot contents. */ - for (raft_member_t *m1 : members) { - for (raft_member_t *m2 : members) { - if (m1 <= m2) { - /* Don't make unnecessary checks */ - continue; - } - if (m1->ps().log.prev_index == m2->ps().log.prev_index) { - guarantee(m1->ps().snapshot_state == m2->ps().snapshot_state); - guarantee(m1->ps().snapshot_config == m2->ps().snapshot_config); - } - } - } -} -#endif /* NDEBUG */ - -template -void raft_member_t::on_request_vote_rpc( - const typename raft_rpc_request_t::request_vote_t &request, - raft_rpc_reply_t::request_vote_t *reply_out) { - assert_thread(); - new_mutex_acq_t mutex_acq(&mutex); - DEBUG_ONLY_CODE(check_invariants(&mutex_acq)); - - /* Raft paper, Section 6: "Servers disregard RequestVote RPCs when they believe a - current leader exists... Specifically, if a server receives a RequestVote RPC within - the minimum election timeout of hearing from a current leader, it does not update its - term or grant its vote." - - We detect whether we've heard from a leader or not by checking if - `watchdog_leader_only` is triggered or not. This is the purpose of - `watchdog_leader_only`. This is also the reason why `watchdog_leader_only` is set to - wait exactly `election_timeout_min_ms` instead of a random period between - `election_timeout_min_ms` and `election_timeout_max_ms`. - - This implementation deviates from the Raft paper in that a leader will accept a - RequestVote RPC from the candidate as long as the candidate is in the latest config. - Normally this should be redundant, because if the candidate is in the latest config - then the leader should be sending it AppendEntries RPCs, and so the leader would find - out about the newer term via the AppendEntry RPC reply from the candidate. But this - implementation sends "virtual heartbeats" instead of real heartbeats, and it does not - receive replies for its virtual heartbeats. So we need another mechanism for a leader - to detect when another member has started a new term, and we're using RequestVote - RPCs for that. */ - if ((mode == mode_t::leader && - !latest_state.get_ref().config.is_member(request.candidate_id)) - || (mode == mode_t::follower && watchdog_leader_only->get_state() == - watchdog_timer_t::state_t::NOT_TRIGGERED)) { - RAFT_DEBUG_THIS("RequestVote from %s for %" PRIu64 " ignored (leader exists)\n", - show_member_id(request.candidate_id).c_str(), request.term); - reply_out->term = ps().current_term; - reply_out->vote_granted = false; - return; - } - - /* Raft paper, Figure 2: If RPC request or response contains term T > currentTerm: - set currentTerm = T, convert to follower */ - if (request.term > ps().current_term) { - if (mode != mode_t::follower) { - candidate_or_leader_become_follower(&mutex_acq); - } - update_term(request.term, raft_member_id_t(), &mutex_acq); - /* Continue processing the RPC as follower */ - } - - /* Raft paper, Figure 2: "Reply false if term < currentTerm" */ - if (request.term < ps().current_term) { - reply_out->term = ps().current_term; - reply_out->vote_granted = false; - DEBUG_ONLY_CODE(check_invariants(&mutex_acq)); - return; - } - - /* Sanity checks, not explicitly described in the Raft paper. */ - guarantee(request.candidate_id != this_member_id, "We shouldn't be requesting a " - "vote from ourself."); - if (mode != mode_t::follower) { - guarantee(ps().voted_for == this_member_id, "We should have voted for ourself " - "already."); - } - - /* Raft paper, Figure 2: "If votedFor is null or candidateId, and candidate's log is - at least as up-to-date as receiver's log, grant vote */ - - /* So if `voted_for` is neither `nil_uuid()` nor `candidate_id`, we don't grant the - vote */ - if (!ps().voted_for.is_nil() && ps().voted_for != request.candidate_id) { - reply_out->term = ps().current_term; - reply_out->vote_granted = false; - DEBUG_ONLY_CODE(check_invariants(&mutex_acq)); - return; - } - - /* Raft paper, Section 5.4.1: "Raft determines which of two logs is more up-to-date - by comparing the index and term of the last entries in the logs. If the logs have - last entries with different terms, then the log with the later term is more - up-to-date. If the logs end with the same term, then whichever log is longer is more - up-to-date." */ - bool candidate_is_at_least_as_up_to_date = - request.last_log_term > ps().log.get_entry_term(ps().log.get_latest_index()) || - (request.last_log_term == ps().log.get_entry_term(ps().log.get_latest_index()) && - request.last_log_index >= ps().log.get_latest_index()); - if (!candidate_is_at_least_as_up_to_date) { - RAFT_DEBUG_THIS("RequestVote from %s for %" PRIu64 " ignored (not up-to-date)\n", - show_member_id(request.candidate_id).c_str(), request.term); - reply_out->term = ps().current_term; - reply_out->vote_granted = false; - DEBUG_ONLY_CODE(check_invariants(&mutex_acq)); - return; - } - guarantee(ps().log.get_latest_index() >= ps().commit_index); - guarantee(request.last_log_index >= ps().commit_index); - - RAFT_DEBUG_THIS("RequestVote from %s for %" PRIu64 " granted\n", - show_member_id(request.candidate_id).c_str(), request.term); - - storage->write_current_term_and_voted_for( - ps().current_term, request.candidate_id); - - /* Raft paper, Section 5.2: "A server remains in follower state as long as it - receives valid RPCs from a leader or candidate." - So candidate RPCs are sufficient to delay the watchdog timer. It's also important - that we don't delay the watchdog timer for RPCs that we reject because they aren't - up-to-date; otherwise, you can get a situation where there is only one cluster member - that is sufficiently up-to-date to be elected leader, but it never stands for - election because it keeps hearing from other candidates. This is why this line is - below the `if (!candidate_is_at_least_as_up_to_date)` instead of above it. */ - watchdog->notify(); - - reply_out->term = ps().current_term; - reply_out->vote_granted = true; - - DEBUG_ONLY_CODE(check_invariants(&mutex_acq)); -} - -template -void raft_member_t::on_install_snapshot_rpc( - const typename raft_rpc_request_t::install_snapshot_t &request, - raft_rpc_reply_t::install_snapshot_t *reply_out) { - assert_thread(); - new_mutex_acq_t mutex_acq(&mutex); - DEBUG_ONLY_CODE(check_invariants(&mutex_acq)); - - if (!on_rpc_from_leader(request.leader_id, request.term, &mutex_acq)) { - /* Raft paper, Figure 2: term should be set to "currentTerm, for leader to update - itself" */ - reply_out->term = ps().current_term; - DEBUG_ONLY_CODE(check_invariants(&mutex_acq)); - return; - } - - mutex_assertion_t::acq_t log_mutex_acq(&log_mutex); - - /* This implementation deviates from the Raft paper in the order it does things. The - Raft paper says that the first step should be to save the snapshot, which for us - would correspond to setting `ps().snapshot_state` and `ps().snapshot_config`. But - because we only store one snapshot at a time and require that snapshot to be right - before the start of the log, that doesn't make sense for us. Instead, we save the - snapshot if and when we update the log. */ - - /* Raft paper, Figure 13: "If existing log entry has same index and term as - snapshot's last included entry, retain log entries following it and reply" */ - if (request.last_included_index <= ps().log.prev_index) { - /* The proposed snapshot starts at or before our current snapshot. It's - impossible to check if an existing log entry has the same index and term because - the snapshot's last included entry is before our most recent entry. But if that's - the case, we don't need this snapshot, so we can safely ignore it. */ - if (request.last_included_index == ps().log.prev_index) { - guarantee(request.last_included_term == ps().log.prev_term, "The entry we " - "shapshotted at was committed, so we shouldn't be receiving a different " - "committed entry at the same index."); - } - reply_out->term = ps().current_term; - DEBUG_ONLY_CODE(check_invariants(&mutex_acq)); - return; - } else if (request.last_included_index <= ps().log.get_latest_index() && - ps().log.get_entry_term(request.last_included_index) == - request.last_included_term) { - /* Raft paper, Section 7: "If ... the follower receives a snapshot that describes - a prefix of its log (due to retransmission or by mistake), then log entries - covered by the snapshot are deleted but entries following the snapshot are still - valid and must be retained. - Raft paper, Figure 13: "Save snapshot file" - (We're going slightly out of order, as described above) */ - storage->write_snapshot( - request.snapshot_state, - request.snapshot_config, - false, - request.last_included_index, - request.last_included_term, - std::max(request.last_included_index, ps().commit_index)); - guarantee(ps().log.prev_index == request.last_included_index); - guarantee(ps().log.prev_term == request.last_included_term); - - /* This implementation deviates from the Raft paper in that we may update the - state machine in this situation. The paper implies that we should reply without - updating the state machine in this case, but it's obviously safe to update the - committed index in this case, so we do that. */ - } else { - /* Raft paper, Figure 13: "Discard the entire log" - Raft paper, Figure 13: "Save snapshot file" - (We're going slightly out of order, as described above) */ - storage->write_snapshot( - request.snapshot_state, - request.snapshot_config, - true, - request.last_included_index, - request.last_included_term, - request.last_included_index); - guarantee(ps().log.prev_index == request.last_included_index); - guarantee(ps().log.prev_term == request.last_included_term); - - /* Because we modified `ps().log`, we need to update `latest_state` */ - latest_state.set_value_no_equals(state_and_config_t( - ps().log.prev_index, ps().snapshot_state, ps().snapshot_config)); - } - - /* Raft paper, Figure 13: "Reset state machine using snapshot contents" - This conditional doesn't appear in the Raft paper. It will always be true if we - discarded the entire log, but it will sometimes be false if we only discarded part of - the log, which is why this implementation needs it.*/ - if (request.last_included_index > committed_state.get_ref().log_index) { - /* Note that we already updated `ps().commit_index` earlier, as part of the same - storage write operation that changed the snapshot. */ - guarantee(ps().commit_index == request.last_included_index); - committed_state.apply_atomic_op([&](state_and_config_t *sc) -> bool { - sc->log_index = request.last_included_index; - sc->state = this->ps().snapshot_state; - sc->config = this->ps().snapshot_config; - return true; - }); - } - - reply_out->term = ps().current_term; - - DEBUG_ONLY_CODE(check_invariants(&mutex_acq)); -} - -template -void raft_member_t::on_append_entries_rpc( - const typename raft_rpc_request_t::append_entries_t &request, - raft_rpc_reply_t::append_entries_t *reply_out) { - assert_thread(); - new_mutex_acq_t mutex_acq(&mutex); - DEBUG_ONLY_CODE(check_invariants(&mutex_acq)); - - if (!on_rpc_from_leader(request.leader_id, request.term, &mutex_acq)) { - /* Raft paper, Figure 2: term should be set to "currentTerm, for leader to update - itself" */ - reply_out->term = ps().current_term; - reply_out->success = false; - DEBUG_ONLY_CODE(check_invariants(&mutex_acq)); - return; - } - - mutex_assertion_t::acq_t log_mutex_acq(&log_mutex); - - /* Raft paper, Figure 2: "Reply false if log doesn't contain an entry at prevLogIndex - whose term matches prevLogTerm" - The Raft paper doesn't explicitly say what to do if our snapshot is later than - `prevLogIndex`. But by the Leader Completeness Property, we know that the log entry - would have matched if it had not been snapshotted. */ - if (request.entries.prev_index > ps().log.get_latest_index() || - (request.entries.prev_index >= ps().log.prev_index && - ps().log.get_entry_term(request.entries.prev_index) != - request.entries.prev_term)) { - reply_out->term = ps().current_term; - reply_out->success = false; - DEBUG_ONLY_CODE(check_invariants(&mutex_acq)); - return; - } - - /* Raft paper, Figure 2: "If an existing entry conflicts with a new one (same index - but different terms), delete the existing entry and all that follow it" */ - bool conflict = false; - raft_log_index_t first_nonmatching_index; - const raft_log_index_t min_index = - std::max(request.entries.prev_index, ps().log.prev_index) + 1; - const raft_log_index_t max_index = - std::min(ps().log.get_latest_index(), request.entries.get_latest_index()); - for (first_nonmatching_index = min_index; - first_nonmatching_index <= max_index; - ++first_nonmatching_index) { - if (ps().log.get_entry_term(first_nonmatching_index) != - request.entries.get_entry_term(first_nonmatching_index)) { - conflict = true; - break; - } - } - - /* Raft paper, Figure 2: "Append any new entries not already in the log" - - Our implementation performs both the truncation of a conflicting log suffix and - appending any new entries in the same atomic operation through - `write_log_replace_tail`. - - There are three valid cases that we need to consider: - * Our own log is an extension of the log the leader sent us, or the logs are equal. - In that case we will have `conflict == false` and - `first_nonmatching_index == request.entries.get_latest_index()` - and `first_nonmatching_index <= ps().log.get_latest_index()`. - There is nothing to do for us. - * The log the leader sent us is an extension of our own log. - We will have `conflict == false` and - `first_nonmatching_index == ps().log.get_latest_index() + 1`. - In this case `write_log_replace_tail` is going to append the entries from `request` - starting at `first_nonmatching_index`. - * The log the leader sent us and our own log have some matching prefix (potentially - of length zero), followed by entries that are different between both logs. - We will have `conflict == true` and - `ps().log.prev_index < first_nonmatching_index <= ps().log.get_latest_index()`. - In this case we truncate our own log starting at `first_nonmatching_index`, and - replace it by the suffix of the `request` log starting at `first_nonmatching_index`. - */ - if (conflict || first_nonmatching_index > ps().log.get_latest_index()) { - /* The Leader Completeness property ensures that the leader has all committed log - entries. We must never truncate our log to become shorter than the current - `commit_index`. */ - guarantee(first_nonmatching_index > ps().commit_index); - storage->write_log_replace_tail( - request.entries, first_nonmatching_index); - } - - /* Because we modified `ps().log`, we need to update `latest_state`. */ - latest_state.apply_atomic_op([&](state_and_config_t *s) -> bool { - if (conflict) { - /* We deleted some log entries and then appended some. Since we have no way - of reverting a log entry on a `state_and_config_t`, we have to rewind to the - last committed state and then apply log entries from there. */ - *s = committed_state.get_ref(); - } - this->apply_log_entries( - s, this->ps().log, s->log_index + 1, this->ps().log.get_latest_index()); - return true; - }); - - /* Raft paper, Figure 2: "If leaderCommit > commitIndex, set commitIndex = min( - leaderCommit, index of last new entry)" */ - if (request.leader_commit > committed_state.get_ref().log_index) { - update_commit_index( - std::min(request.leader_commit, request.entries.get_latest_index()), - &mutex_acq); - } - - reply_out->term = ps().current_term; - reply_out->success = true; - - DEBUG_ONLY_CODE(check_invariants(&mutex_acq)); -} - -#ifndef NDEBUG -template -void raft_member_t::check_invariants( - const new_mutex_acq_t *mutex_acq) { - assert_thread(); - mutex_acq->guarantee_is_holding(&mutex); - - raft_log_index_t commit_index = committed_state.get_ref().log_index; - guarantee(commit_index == ps().commit_index, "Commit index stored in persistent " - "state should match commit index stored in committed_state watchable."); - - /* Some of these checks are copied directly from LogCabin's list of invariants. */ - - /* Checks related to newly-initialized states */ - if (ps().current_term == 0) { - guarantee(ps().voted_for.is_nil(), "If we're still in term 0, nobody has " - "started an election, so we shouldn't have voted for anybody."); - guarantee(ps().log.entries.empty(), "If we're still in term 0, we shouldn't " - "have any real log entries."); - guarantee(ps().log.get_latest_index() == 0, "If we're still in term 0, the log " - "should be empty"); - guarantee(mode == mode_t::follower, "If we're still in term 0, there shouldn't " - "have been an election, so we should be follower."); - guarantee(current_term_leader_id.is_nil(), "Term 0 shouldn't have a leader."); - } - guarantee((ps().log.prev_index == 0) == (ps().log.prev_term == 0), "There shouldn't " - "be any log entries in term 0."); - - /* Checks related to the log */ - raft_term_t latest_term_in_log = ps().log.get_entry_term(ps().log.prev_index); - for (raft_log_index_t i = ps().log.prev_index + 1; - i <= ps().log.get_latest_index(); - ++i) { - guarantee(ps().log.get_entry_ref(i).term != 0, - "There shouldn't be any log entries in term 0."); - guarantee(ps().log.get_entry_ref(i).term >= latest_term_in_log, - "Terms of log entries should monotonically increase"); - const raft_log_entry_t &entry = ps().log.get_entry_ref(i); - latest_term_in_log = entry.term; - - switch (entry.type) { - case raft_log_entry_type_t::regular: - guarantee(static_cast(entry.change), "Regular log entries should " - "carry changes"); - guarantee(!static_cast(entry.config), "Regular log entries shouldn't " - "carry configurations."); - break; - case raft_log_entry_type_t::config: - guarantee(!static_cast(entry.change), "Configuration log entries " - "shouldn't carry changes"); - guarantee(static_cast(entry.config), "Configuration log entries " - "should carry configurations."); - break; - case raft_log_entry_type_t::noop: - guarantee(!static_cast(entry.change), "Noop log entries shouldn't " - "carry changes"); - guarantee(!static_cast(entry.config), "Noop log entries" - "shouldn't carry configurations."); - break; - default: - unreachable(); - } - } - guarantee(latest_term_in_log <= ps().current_term, "There shouldn't be any log " - "entries with terms greater than current_term"); - - /* Checks related to reconfigurations */ - bool last_is_joint_consensus = ps().snapshot_config.is_joint_consensus(); - for (raft_log_index_t i = ps().log.prev_index + 1; - i <= ps().log.get_latest_index(); ++i) { - const raft_log_entry_t &entry = ps().log.get_entry_ref(i); - if (entry.type == raft_log_entry_type_t::config) { - guarantee(entry.config->is_joint_consensus() != last_is_joint_consensus, - "Joint consensus configurations should alternate with " - "non-joint-consensus configurations in the log."); - last_is_joint_consensus = entry.config->is_joint_consensus(); - } - } - - /* Checks related to the state machine and commits */ - guarantee(commit_index >= ps().log.prev_index, "All of the log entries in the " - "snapshot should be committed."); - guarantee(commit_index <= ps().log.get_latest_index(), "We shouldn't have committed " - "any entries that aren't in the log yet."); -#ifdef ENABLE_RAFT_DEBUG - /* This is potentially really slow, so we normally disable it. */ - { - state_and_config_t s( - ps().log.prev_index, ps().snapshot_state, ps().snapshot_config); - apply_log_entries(&s, ps().log, ps().log.prev_index + 1, commit_index); - guarantee(s.state == committed_state.get_ref().state, "State machine should be " - "equal to snapshot with log entries applied."); - guarantee(s.config == committed_state.get_ref().config, "Config stored with " - "state machine should be equal to snapshot with log entries applied."); - guarantee(s.log_index == committed_state.get_ref().log_index, "Applying the log " - "entries up to commit_index to the snapshot should cause its log_index to " - "be equal to commit_index."); - apply_log_entries(&s, ps().log, commit_index + 1, ps().log.get_latest_index()); - guarantee(s.state == latest_state.get_ref().state); - guarantee(s.config == latest_state.get_ref().config); - guarantee(s.log_index == latest_state.get_ref().log_index); - } -#endif /* ENABLE_RAFT_DEBUG */ - - /* Checks related to the follower/candidate/leader roles */ - - guarantee((mode == mode_t::leader) == (current_term_leader_id == this_member_id), - "mode should say we're leader iff current_term_leader_id says we're leader"); - guarantee(virtual_heartbeat_sender.is_nil() || - virtual_heartbeat_sender == current_term_leader_id, - "we shouldn't be getting virtual heartbeats from a non-leader"); - guarantee(leader_drainer.has() == (mode != mode_t::follower), - "candidate_and_leader_coro() should be running unless we're a follower"); - guarantee(mode == mode_t::leader || !readiness_for_change.get(), - "we shouldn't be accepting changes if we're not leader"); - guarantee(readiness_for_change.get() || !readiness_for_change.get(), - "we shouldn't be accepting config changes but not regular changes"); - - switch (mode) { - case mode_t::follower: - guarantee(match_indexes.empty(), "If we're a follower, `match_indexes` should " - "be empty."); - break; - case mode_t::candidate: - guarantee(current_term_leader_id.is_nil(), "if we have a leader we shouldn't be " - "having an election"); - guarantee(ps().voted_for == this_member_id, "if we're a candidate we should " - "have voted for ourself"); - guarantee(match_indexes.empty(), "If we're a candidate, `match_indexes` should " - "be empty."); - break; - case mode_t::leader: - guarantee(ps().voted_for == this_member_id, "if we're a leader we should have " - "voted for ourself"); - guarantee(latest_term_in_log == ps().current_term, "if we're a leader we should " - "have put at least the initial noop entry in the log"); - guarantee(match_indexes.at(this_member_id) == ps().log.get_latest_index(), - "If we're a leader, `match_indexes` should contain an up-to-date entry for " - "ourselves."); - break; - default: - unreachable(); - } - - /* We could test that `current_term_leader_id` is non-nil if there are any changes in - the log for the current term. But this would be false immediately after startup, so - we'd need an extra flag to detect that, and that's more work than it's worth. */ -} -#endif /* NDEBUG */ - -template -void raft_member_t::on_connected_members_change( - const raft_member_id_t &member_id, - const optional *value) { - assert_thread(); - ASSERT_NO_CORO_WAITING; - update_readiness_for_change(); - if (value != nullptr && static_cast(*value) && member_id != this_member_id) { - /* We've received a "start virtual heartbeats" message. We process the term just - like for an AppendEntries or InstallSnapshot RPC, but we don't actually append - any entries or install any snapshots. */ - raft_term_t term = **value; - auto_drainer_t::lock_t keepalive(drainer.get()); - coro_t::spawn_sometime( - [this, term, member_id, keepalive /* important to capture */]() { - try { - new_mutex_acq_t mutex_acq(&mutex, keepalive.get_drain_signal()); - DEBUG_ONLY_CODE(this->check_invariants(&mutex_acq)); - - if (!this->on_rpc_from_leader(member_id, term, &mutex_acq)) { - /* If this were a real AppendEntries or InstallSnapshot RPC, we would - send an RPC reply to the leader informing it that our term is higher - than its term. However, there's no way to send an RPC reply to a - virtual heartbeat. So the leader needs some other way to find out - that our term is higher than its term. In order to make this work, - this implementation deviates from the Raft paper in how leaders - handle RequestVote RPCs; see the comment in `on_request_vote_rpc()` - for more information. */ - DEBUG_ONLY_CODE(this->check_invariants(&mutex_acq)); - return; - } - - /* Check that the `get_connected_members()` state is still what it was - when this coroutine was spawned; we mustn't set - `virtual_heartbeat_sender` unless we are actually getting virtual - heartbeats. */ - if (network->get_connected_members()->get_key(member_id) - == make_optional(make_optional(term))) { - /* Sometimes we are called twice within the same term. */ - if (member_id != virtual_heartbeat_sender) { - guarantee(virtual_heartbeat_sender.is_nil()); - virtual_heartbeat_sender = member_id; - - /* As long as we're receiving valid virtual heartbeats, we won't - start a new election */ - virtual_heartbeat_watchdog_blockers[0].init( - new watchdog_timer_t::blocker_t(watchdog.get())); - virtual_heartbeat_watchdog_blockers[1].init( - new watchdog_timer_t::blocker_t(watchdog_leader_only.get())); - } - } - - DEBUG_ONLY_CODE(this->check_invariants(&mutex_acq)); - } catch (const interrupted_exc_t &) { - /* ignore */ - } - }); - } else if (virtual_heartbeat_sender == member_id) { - /* We've received a "stop virtual heartbeats" message, or lost contact with the - member that was sending virtual heartbeats. */ - virtual_heartbeat_sender = raft_member_id_t(); - - /* Now that we're no longer receiving virtual heartbeats, unblock the watchdogs. - */ - virtual_heartbeat_watchdog_blockers[0].reset(); - virtual_heartbeat_watchdog_blockers[1].reset(); - } -} - -template -bool raft_member_t::on_rpc_from_leader( - const raft_member_id_t &request_leader_id, - raft_term_t request_term, - const new_mutex_acq_t *mutex_acq) { - assert_thread(); - mutex_acq->guarantee_is_holding(&mutex); - - guarantee(request_leader_id != this_member_id); - - /* Raft paper, Figure 2: "If RPC request or response contains term T > currentTerm: - set currentTerm = T, convert to follower" */ - if (request_term > ps().current_term) { - RAFT_DEBUG_THIS("Install/Append from %s for %" PRIu64 "\n", - show_member_id(request_leader_id).c_str(), request_term); - if (mode != mode_t::follower) { - candidate_or_leader_become_follower(mutex_acq); - } - /* Note that we become a follower before we update the term. - We do this so that if we're currently a candidate in `candidate_run_election`, - we don't change the Raft state in the middle of its execution. - Doing so might not actually do any harm in our current implementation, but - it's probably safer to avoid the risk. - `candidate_or_leader_become_follower` will force `candidate_run_election` to - finish before we get here. */ - update_term(request_term, raft_member_id_t(), mutex_acq); - /* Continue processing the RPC as follower */ - } - - /* Raft paper, Figure 2: "Reply false if term < currentTerm (SE 5.1)" - Raft paper, Section 5.1: "If a server receives a request with a stale term number, it - rejects the request" */ - if (request_term < ps().current_term) { - return false; - } - - guarantee(request_term == ps().current_term); /* sanity check */ - - /* Raft paper, Section 5.2: "While waiting for votes, a candidate may receive an - AppendEntries RPC from another server claiming to be leader. If the leader's term - (included in its RPC) is at least as large as the candidate's current term, then the - candidate recognizes the leader as legitimate and returns to follower state." - - Note that this implementation also do this for install-snapshot RPCs, since it's - conceivably possible that the first RPC we receive from the leader may be an - install-snapshot RPC rather than an append-entries RPC. */ - if (mode == mode_t::candidate) { - RAFT_DEBUG_THIS("Install/Append from %s for %" PRIu64 "\n", - show_member_id(request_leader_id).c_str(), request_term); - candidate_or_leader_become_follower(mutex_acq); - } - - /* Raft paper, Section 5.2: "at most one candidate can win the election for a - particular term" - If we're leader, then we won the election, so it makes no sense for us to receive an - RPC from another member that thinks it's leader. */ - guarantee(mode != mode_t::leader); - - /* Raft paper, Section 5.2: "A server remains in follower state as long as it - receives valid RPCs from a leader or candidate." - So we should make a note that we received an RPC. */ - watchdog->notify(); - watchdog_leader_only->notify(); - - /* Recall that `current_term_leader_id` is set to `nil_uuid()` if we haven't seen a - leader yet this term. */ - if (current_term_leader_id.is_nil()) { - current_term_leader_id = request_leader_id; - } else { - /* Raft paper, Section 5.2: "at most one candidate can win the election for a - particular term" */ - guarantee(current_term_leader_id == request_leader_id); - } - - return true; -} - -template -void raft_member_t::on_watchdog() { - if (mode != mode_t::follower) { - /* If we're already a candidate or leader, there's no need for this. Candidates - have their own mechanism for retrying stuck elections. */ - return; - } - - /* Note that we may begin an election even if we are not a voter in our own latest - configuration. This is necessary to prevent deadlock in some configuration change - scenarios. This corner case is explicitly addressed in the Raft dissertation, section - 4.2.2: "A server that is not part of its own latest configuration should still start - new elections, as it might still be needed until the C_new entry is committed." */ - - /* We shouldn't block in this callback, so we immediately spawn a coroutine */ - auto_drainer_t::lock_t keepalive(drainer.get()); - coro_t::spawn_sometime([this, keepalive /* important to capture */]() { - try { - scoped_ptr_t mutex_acq( - new new_mutex_acq_t(&mutex, keepalive.get_drain_signal())); - DEBUG_ONLY_CODE(this->check_invariants(mutex_acq.get())); - /* Double-check that nothing has changed while the coroutine was - spawning. */ - if (mode != mode_t::follower || - watchdog->get_state() == watchdog_timer_t::state_t::NOT_TRIGGERED) { - return; - } - /* Begin an election */ - guarantee(!leader_drainer.has()); - leader_drainer.init(new auto_drainer_t); - coro_t::spawn_sometime(std::bind( - &raft_member_t::candidate_and_leader_coro, - this, - mutex_acq.release(), - auto_drainer_t::lock_t(leader_drainer.get()))); - } catch (const interrupted_exc_t &) { - /* If `keepalive.get_drain_signal()` fires, the `raft_member_t` is being - destroyed, so don't start an election. */ - } - }); -} - -template -void raft_member_t::apply_log_entries( - state_and_config_t *state_and_config, - const raft_log_t &log, - raft_log_index_t first, - raft_log_index_t last) { - guarantee(last >= first - 1); - guarantee(state_and_config->log_index + 1 == first); - for (raft_log_index_t i = first; i <= last; ++i) { - const raft_log_entry_t &e = log.get_entry_ref(i); - switch (e.type) { - case raft_log_entry_type_t::regular: - state_and_config->state.apply_change(*e.change); - break; - case raft_log_entry_type_t::config: - state_and_config->config = *e.config; - break; - case raft_log_entry_type_t::noop: - break; - default: unreachable(); - } - ++state_and_config->log_index; - } - guarantee(state_and_config->log_index == last); -} - -template -void raft_member_t::update_term( - raft_term_t new_term, - raft_member_id_t new_voted_for, - const new_mutex_acq_t *mutex_acq) { - mutex_acq->guarantee_is_holding(&mutex); - - guarantee(new_term > ps().current_term); - - /* In Figure 2, `votedFor` is defined as "candidateId that received vote in - current term (or null if none)". So when the current term changes, we have to - update `voted_for`. Normally we update it to null, but if this is a term that we - created, we updated it to our own member ID. The reason for doing this as a single - step is to save a call to `write_current_term_and_voted_for()`. */ - storage->write_current_term_and_voted_for(new_term, new_voted_for); - - /* The same logic applies to `current_term_leader_id` and - `virtual_heartbeat_sender`. */ - current_term_leader_id = raft_member_id_t(); - virtual_heartbeat_sender = raft_member_id_t(); - virtual_heartbeat_watchdog_blockers[0].reset(); - virtual_heartbeat_watchdog_blockers[1].reset(); -} - -template -void raft_member_t::update_commit_index( - raft_log_index_t new_commit_index, - const new_mutex_acq_t *mutex_acq) { - mutex_acq->guarantee_is_holding(&mutex); - - raft_log_index_t old_commit_index = committed_state.get_ref().log_index; - guarantee(new_commit_index > old_commit_index); - - /* This implementation deviates from the Raft paper in that we persist the commit - index to disk whenever it changes. This ensures that the state machine never appears - to go backwards. */ - storage->write_commit_index(new_commit_index); - - /* Raft paper, Figure 2: "If commitIndex > lastApplied: increment lastApplied, apply - log[lastApplied] to state machine" - If we are leader, updating `committed_state` will trigger several events: - * It will notify any running instances of `leader_send_updates()` to send the new - commit index to the followers. - * If the newly-committed state is a joint consensus state, it will wake - `candidate_and_leader_coro()` to start the second phase of the reconfiguration. - */ - committed_state.apply_atomic_op([&](state_and_config_t *state_and_config) -> bool { - this->apply_log_entries(state_and_config, this->ps().log, - state_and_config->log_index + 1, new_commit_index); - return true; - }); - guarantee(committed_state.get_ref().log_index == new_commit_index); - - /* Notify any change tokens that were waiting on this commit */ - for (auto it = change_tokens.begin(); - it != change_tokens.upper_bound(new_commit_index);) { - /* We might remove the entry from the map, which would invalidate the iterator. - So we have to increment the iterator now. */ - change_token_t *token = it->second; - ++it; - /* If the change token corresponds to a configuration change, it's not finished - until both phases of the config change have been committed. */ - if (token->is_config && committed_state.get_ref().config.is_joint_consensus()) { - /* We committed the first part of the config change, but not the second. */ - continue; - } - token->pulse(true); - /* This removes it from the map */ - token->sentry.reset(); - } - -#ifndef NDEBUG - /* In debug mode, snapshot randomly with 1/3 probability after each change. This is - so that the tests will exercise many different code paths. */ - bool should_take_snapshot = (randint(3) == 0); -#else - /* In release mode, snapshot when the log grows beyond a certain margin. */ - size_t num_committed_entries = new_commit_index - ps().log.prev_index; - bool should_take_snapshot = (num_committed_entries > snapshot_threshold); -#endif /* NDEBUG */ - if (should_take_snapshot) { - /* Take a snapshot as described in Section 7. - - This automatically updates `ps().log.prev_index` and `ps().log.prev_term`, which - are equivalent to the "last included index" and "last included term" described in - Section 7 of the Raft paper.*/ - storage->write_snapshot( - committed_state.get_ref().state, - committed_state.get_ref().config, - false, - new_commit_index, - ps().log.get_entry_term(new_commit_index), - new_commit_index); - } - - /* If we just committed the second step of a config change, then we might need to - flip `readiness_for_config_change` */ - update_readiness_for_change(); -} - -template -void raft_member_t::leader_update_match_index( - raft_member_id_t key, - raft_log_index_t new_value, - const new_mutex_acq_t *mutex_acq) { - guarantee(mode == mode_t::leader); - mutex_acq->guarantee_is_holding(&mutex); - - auto it = match_indexes.find(key); - guarantee(it != match_indexes.end()); - guarantee(it->second <= new_value); - it->second = new_value; - - /* Raft paper, Figure 2: "If there exists an N such that N > commitIndex, a majority - of matchIndex[i] >= N, and log[N].term == currentTerm: set commitIndex = N" */ - raft_log_index_t old_commit_index = committed_state.get_ref().log_index; - raft_log_index_t new_commit_index = old_commit_index; - for (raft_log_index_t n = old_commit_index + 1; - n <= ps().log.get_latest_index(); ++n) { - if (ps().log.get_entry_term(n) != ps().current_term) { - continue; - } - std::set approving_members; - for (auto const &pair : match_indexes) { - if (pair.second >= n) { - approving_members.insert(pair.first); - } - } - if (latest_state.get_ref().config.is_quorum(approving_members)) { - new_commit_index = n; - } else { - /* If this log entry isn't approved, no later log entry can possibly be - approved either */ - break; - } - } - if (new_commit_index != old_commit_index) { - update_commit_index(new_commit_index, mutex_acq); - } -} - -template -void raft_member_t::update_readiness_for_change() { - /* We're ready for changes if we're the leader and in contact with a majority of the - cluster. We're ready for config changes if the above conditions are true plus we're - also not in the middle of a config change. */ - if (mode == mode_t::leader) { - std::set peers; - network->get_connected_members()->read_all( - [&](const raft_member_id_t &member_id, - const optional *) { - peers.insert(member_id); - }); - if (latest_state.get_ref().config.is_quorum(peers)) { - readiness_for_change.set_value(true); - readiness_for_config_change.set_value( - !committed_state.get_ref().config.is_joint_consensus() && - !latest_state.get_ref().config.is_joint_consensus()); - return; - } - } - readiness_for_change.set_value(false); - readiness_for_config_change.set_value(false); - while (!change_tokens.empty()) { - change_tokens.begin()->second->pulse(false); - /* This will remove the change token from the map */ - change_tokens.begin()->second->sentry.reset(); - } -} - -template -void raft_member_t::candidate_or_leader_become_follower( - const new_mutex_acq_t *mutex_acq) { - mutex_acq->guarantee_is_holding(&mutex); - guarantee(mode == mode_t::candidate || mode == mode_t::leader); - guarantee(leader_drainer.has()); - - /* This will interrupt `candidate_and_leader_coro()` and block until it exits */ - leader_drainer.reset(); - - /* `candidate_and_leader_coro()` should have reset `mode` when it exited */ - guarantee(mode == mode_t::follower); -} - -template -void raft_member_t::candidate_and_leader_coro( - new_mutex_acq_t *mutex_acq_on_heap, - auto_drainer_t::lock_t leader_keepalive) { - scoped_ptr_t mutex_acq(mutex_acq_on_heap); - guarantee(mode == mode_t::follower); - mutex_acq->guarantee_is_holding(&mutex); - leader_keepalive.assert_is_holding(leader_drainer.get()); - signal_t *interruptor = leader_keepalive.get_drain_signal(); - - /* Raft paper, Section 5.2: "To begin an election, a follower increments its current - term and transitions to candidate state." - `candidate_run_election()` is what actually increments the current term. */ - mode = mode_t::candidate; - - if (!log_prefix.empty()) { - logINF("%s: Starting a new Raft election for term %" PRIu64 ".", - log_prefix.c_str(), ps().current_term + 1); - } - RAFT_DEBUG_THIS("election begun for %" PRIu64 "\n", ps().current_term + 1); - - /* While we're candidate or leader, we'll never update our log in response to an RPC. - */ - mutex_assertion_t::acq_t log_mutex_acq(&log_mutex); - - /* We increase the election timeout for subsequent elections to avoid overwhelming - i/o in case of a large number of concurrent elections in the cluster. */ - int32_t retry_election_timeout = election_timeout_max_ms; - - /* This `try` block is to catch `interrupted_exc_t` */ - try { - /* The first election won't necessarily succeed. So we loop until either we - become leader or we are interrupted. */ - while (true) { - signal_timer_t election_timeout; - /* Choose a random timeout between the current timeout and half - the current timeout. */ - election_timeout.start( - retry_election_timeout / 2 + - randuint64(retry_election_timeout / 2)); - - /* Increase the timeout exponentially (by 1.5) for the next run: */ - retry_election_timeout += retry_election_timeout / 2; - retry_election_timeout = - std::min(retry_election_timeout, election_retry_timeout_max_ms); - - /* `candidate_run_election` might temporarily reset `mutex_acq`, but it - should always re-acquire it before it returns. */ - bool elected = candidate_run_election( - &mutex_acq, &election_timeout, interruptor); - guarantee(mutex_acq.has()); - mutex_acq->guarantee_is_holding(&mutex); - if (elected) { - guarantee(mode == mode_t::leader); - break; /* exit the loop */ - } else { - guarantee(election_timeout.is_pulsed()); - /* Raft paper, Section 5.2: "... if many followers become candidates at - the same time, votes could be split so that no candidate obtains a - majority. When this happens, each candidate will time out and start a new - election by incrementing its term and initiating another round of - RequestVote RPCs." */ - - if (!log_prefix.empty()) { - logINF("%s: Raft election timed out. Starting a new election for " - "term %" PRIu64 ".", log_prefix.c_str(), ps().current_term + 1); - } - RAFT_DEBUG_THIS("election restarted for %" PRIu64 "\n", - ps().current_term + 1); - - /* Go around the `while`-loop again. */ - } - } - - /* We got elected. */ - guarantee(mode == mode_t::leader); - - if (!log_prefix.empty()) { - logINF("%s: This server is Raft leader for term %" PRIu64 ". Latest log " - "index is %" PRIu64 ".", - log_prefix.c_str(), ps().current_term, ps().log.get_latest_index()); - } - RAFT_DEBUG_THIS("won election for %" PRIu64 "\n", ps().current_term); - - guarantee(current_term_leader_id.is_nil()); - current_term_leader_id = this_member_id; - - /* Now that `mode` has switched to `mode_leader`, we might need to flip - `readiness_for_change`. */ - update_readiness_for_change(); - - /* Raft paper, Section 5.3: "When a leader first comes to power, it initializes - all nextIndex values to the index just after the last one in its log" - It's important to do this before we append the no-op to the log, so that we - replicate the no-op on the first try instead of having to try twice. */ - raft_log_index_t initial_next_index = ps().log.get_latest_index() + 1; - - /* Now that we are leader, initialize `match_indexes`. For now we just fill in - the entry for ourselves; `leader_spawn_update_coros()` will handle filling in the - entries for the other members of the cluster. */ - guarantee(match_indexes.empty(), "When we were not leader, `match_indexes` " - "should have been empty."); - match_indexes[this_member_id] = ps().log.get_latest_index(); - - /* Raft paper, Section 8: "[Raft has] each leader commit a blank no-op entry into - the log at the start of its term." - This is to ensure that we'll commit any entries that are possible to commit, - since we can't commit entries from earlier terms except by committing an entry - from our own term. */ - { - raft_log_entry_t new_entry; - new_entry.type = raft_log_entry_type_t::noop; - new_entry.term = ps().current_term; - leader_append_log_entry(new_entry, mutex_acq.get()); - } - - /* Raft paper, Section 5.2: "Leaders send periodic heartbeats (AppendEntries RPCs - that carry no log entries) to all followers in order to maintain their - authority." - We deviate from the Raft paper in that we send a single "start virtual - heartbeats" message and another "stop virtual heartbeats" message instead of a - repeated AppendEntries RPCs. If the network connection is lost, then the - `connectivity_cluster_t` will detect it using its own heartbeats and then the - `raft_network_interface_t` will interrupt the virtual heartbeat stream. */ - network->send_virtual_heartbeats(make_optional(ps().current_term)); - - /* Prevent the watchdog timers from being triggered until we are no longer - leader. */ - watchdog_timer_t::blocker_t watchdog_blocker(watchdog.get()); - watchdog_timer_t::blocker_t watchdog_leader_only_blocker( - watchdog_leader_only.get()); - - /* `update_drainers` contains an `auto_drainer_t` for each running instance of - `leader_send_updates()`. */ - std::map > update_drainers; - - while (true) { - - /* This will spawn instances of `leader_send_updates()`. The instances of - `leader_send_updates()` will then take care of sending append-entries RPCs to - the other members of the cluster, including the initial empty append-entries - RPC. */ - leader_spawn_update_coros( - initial_next_index, - &update_drainers, - mutex_acq.get()); - - /* Check if there is a committed joint consensus configuration but no entry - in the log for the second phase of the config change. If this is the case, - then we will add an entry. Also check if we have just committed a config in - which we are no longer leader, in which case we need to step down. */ - leader_continue_reconfiguration(mutex_acq.get()); - - /* Block until either a new entry is appended to the log or a new entry is - committed. If a new entry is appended to the log, we might need to re-run - `leader_spawn_update_coros()`; if a new entry is committed, we might need to - re-run `leader_continue_reconfiguration()`. */ - { - raft_log_index_t latest_log_index = latest_state.get_ref().log_index; - - /* Release the mutex while blocking so that we can receive RPCs */ - DEBUG_ONLY_CODE(check_invariants(mutex_acq.get())); - mutex_acq.reset(); - - run_until_satisfied_2( - committed_state.get_watchable(), - latest_state.get_watchable(), - [&](const state_and_config_t &cs, const state_and_config_t &ls) - -> bool { - if (cs.config.is_joint_consensus() && - ls.config.is_joint_consensus()) { - /* The fact that `cs.config` is a joint consensus indicates - that we recently committed a joint consensus configuration. - The fact that `ls.config` is a joint consensus means that we - haven't put the second phase of the reconfiguration into the - log yet. So we should do that now. */ - return true; - } - if (ls.log_index > latest_log_index) { - /* Something new was appended to the log. We should go check - if it was a configuration change. */ - return true; - } - return false; - }, - interruptor); - - /* Reacquire the mutex. Note that if `wait_interruptible()` throws - `interrupted_exc_t`, we won't reacquire the mutex. This is by design; - probably `candidate_or_leader_become_follower()` is holding the mutex. */ - mutex_acq.init(new new_mutex_acq_t(&mutex, interruptor)); - DEBUG_ONLY_CODE(check_invariants(mutex_acq.get())); - } - } - - } catch (const interrupted_exc_t &) { - /* This means either the `raft_member_t` is being destroyed, or we were told to - revert to follower state. In either case we do the same thing. */ - } - - /* Stop sending heartbeats now that we're no longer the leader. */ - network->send_virtual_heartbeats(r_nullopt); - - mode = mode_t::follower; - - /* `match_indexes` is supposed to be empty when we're not leader. This rule isn't - strictly necessary; it's basically a sanity check. */ - match_indexes.clear(); - - /* Now that `mode` is no longer `mode_leader`, we might need to flip - `readiness_for_change`. */ - update_readiness_for_change(); -} - -template -bool raft_member_t::candidate_run_election( - scoped_ptr_t *mutex_acq, - signal_t *cancel_signal, - signal_t *interruptor) { - (*mutex_acq)->guarantee_is_holding(&mutex); - guarantee(mode == mode_t::candidate); - - std::set votes_for_us; - cond_t we_won_the_election; - - /* Raft paper, Section 5.2: "To begin an election, a follower increments its current - term and transitions to candidate state. It then votes for itself." */ - update_term(ps().current_term + 1, this_member_id, mutex_acq->get()); - votes_for_us.insert(this_member_id); - if (latest_state.get_ref().config.is_quorum(votes_for_us)) { - /* In some degenerate cases, like if the cluster has only one node, then - our own vote might be enough to be elected. */ - we_won_the_election.pulse(); - } - - typename raft_rpc_request_t::request_vote_t request; - request.term = this->ps().current_term; - request.candidate_id = this_member_id; - request.last_log_index = this->ps().log.get_latest_index(); - request.last_log_term = - this->ps().log.get_entry_term(this->ps().log.get_latest_index()); - - /* Raft paper, Section 5.2: "[The candidate] issues RequestVote RPCs in parallel to - each of the other servers in the cluster." */ - std::set peers = latest_state.get_ref().config.get_all_members(); - scoped_ptr_t request_vote_drainer(new auto_drainer_t); - for (const raft_member_id_t &peer : peers) { - if (peer == this_member_id) { - /* Don't request a vote from ourself */ - continue; - } - - auto_drainer_t::lock_t request_vote_keepalive(request_vote_drainer.get()); - coro_t::spawn_sometime([this, &votes_for_us, &we_won_the_election, peer, - &request, request_vote_keepalive /* important to capture */]() { - try { - exponential_backoff_t backoff(100, 2000); - while (true) { - /* Don't bother trying to send an RPC until the peer is present in - `get_connected_members()`. */ - network->get_connected_members()->run_key_until_satisfied(peer, - [](const optional *x) { - return x != nullptr; - }, - request_vote_keepalive.get_drain_signal()); - - guarantee(this->mode == mode_t::candidate); - raft_rpc_request_t request_wrapper; - request_wrapper.request = request; - - raft_rpc_reply_t reply_wrapper; - bool ok = network->send_rpc(peer, request_wrapper, - request_vote_keepalive.get_drain_signal(), &reply_wrapper); - - if (!ok) { - /* Raft paper, Section 5.1: "Servers retry RPCs if they do not - receive a response in a timely manner" */ - backoff.failure(request_vote_keepalive.get_drain_signal()); - continue; - } - - const raft_rpc_reply_t::request_vote_t *reply = - boost::get( - &reply_wrapper.reply); - guarantee(reply != nullptr, "Got wrong type of RPC response"); - - { - new_mutex_acq_t mutex_acq_2( - &mutex, request_vote_keepalive.get_drain_signal()); - DEBUG_ONLY_CODE(this->check_invariants(&mutex_acq_2)); - /* We might soon be leader, but we shouldn't be leader quite yet - */ - guarantee(mode == mode_t::candidate); - - if (this->candidate_or_leader_note_term( - reply->term, &mutex_acq_2)) { - /* We got a response with a higher term than our current - term. `candidate_and_leader_coro()` will be interrupted soon. - */ - RAFT_DEBUG_THIS( - "got rpc reply with term %" PRIu64 " from %s\n", - reply->term, show_member_id(peer).c_str()); - return; - } - - if (reply->vote_granted) { - votes_for_us.insert(peer); - /* Raft paper, Section 5.2: "A candidate wins an election if - it receives votes from a majority of the servers in the full - cluster for the same term." */ - if (latest_state.get_ref().config.is_quorum(votes_for_us)) { - we_won_the_election.pulse_if_not_already_pulsed(); - } - break; - } - - /* Mutex is released here, so we don't hold it while sleeping */ - } - - /* If they didn't grant the vote, just keep pestering them until they - do or we are interrupted. This is necessary because followers will - temporarily reject votes if they have heard from a current leader - recently, so they might reject a vote and then later accept it in the - same term. But before we retry, we wait a bit, to avoid putting too - much traffic on the network. */ - backoff.failure(request_vote_keepalive.get_drain_signal()); - } - - } catch (const interrupted_exc_t &) { - /* Ignore since we're in a coroutine */ - return; - } - }); - } - - /* Release the mutex while we block. This is important because we might get an - append-entries RPC, and we need to let it acquire the mutex so it can call - `candidate_or_leader_become_follower()` if necessary. */ - mutex_acq->reset(); - - /* Raft paper, Section 5.2: "A candidate continues in this state until one of three - things happens: (a) it wins the election, (b) another server establishes itself as - leader, or (c) a period of time goes by with no winner." - If (b) or (c) happens then `candidate_and_leader_coro()` will pulse `interruptor`. */ - wait_any_t waiter(&we_won_the_election, cancel_signal); - wait_interruptible(&waiter, interruptor); - - mutex_acq->init(new new_mutex_acq_t(&mutex, interruptor)); - DEBUG_ONLY_CODE(check_invariants(mutex_acq->get())); - - if (we_won_the_election.is_pulsed()) { - /* Make sure that any ongoing request-vote coroutines stop, because they assert - `mode == mode_t::candidate`. */ - request_vote_drainer.reset(); - mode = mode_t::leader; - return true; - } else { - return false; - } - /* Here `request_vote_drainer` is destroyed, so we won't exit until all of the - coroutines we spawned have stopped, regardless of whether we won the election or not. - */ -} - -template -void raft_member_t::leader_spawn_update_coros( - raft_log_index_t initial_next_index, - std::map > *update_drainers, - const new_mutex_acq_t *mutex_acq) { - mutex_acq->guarantee_is_holding(&mutex); - guarantee(mode == mode_t::leader); - - /* Calculate the new configuration */ - std::set peers = latest_state.get_ref().config.get_all_members(); - - /* Spawn coroutines as necessary */ - for (const raft_member_id_t &peer : peers) { - if (peer == this_member_id) { - /* We don't need to send updates to ourself */ - continue; - } - if (update_drainers->count(peer) == 1) { - /* We already have a coroutine for this one */ - continue; - } - /* Raft paper, Figure 2: "[matchIndex is] initialized to 0" */ - match_indexes.insert(std::make_pair(peer, 0)); - - scoped_ptr_t update_drainer(new auto_drainer_t); - auto_drainer_t::lock_t update_keepalive(update_drainer.get()); - (*update_drainers)[peer] = std::move(update_drainer); - coro_t::spawn_sometime(std::bind(&raft_member_t::leader_send_updates, this, - peer, - initial_next_index, - update_keepalive)); - } - - /* Kill coroutines as necessary */ - for (auto it = update_drainers->begin(); it != update_drainers->end(); ) { - raft_member_id_t peer = it->first; - guarantee(peer != this_member_id, "We shouldn't have spawned a coroutine to " - "send updates to ourself"); - if (peers.count(peer) == 1) { - /* `peer` is still a member of the cluster, so the coroutine should stay - alive */ - ++it; - continue; - } else { - /* This will block until the update coroutine stops */ - update_drainers->erase(it++); - match_indexes.erase(peer); - } - } -} - -template -void raft_member_t::leader_send_updates( - const raft_member_id_t &peer, - raft_log_index_t initial_next_index, - auto_drainer_t::lock_t update_keepalive) { - try { - guarantee(peer != this_member_id); - guarantee(mode == mode_t::leader); - guarantee(match_indexes.count(peer) == 1); - scoped_ptr_t mutex_acq( - new new_mutex_acq_t(&mutex, update_keepalive.get_drain_signal())); - DEBUG_ONLY_CODE(check_invariants(mutex_acq.get())); - - /* `next_index` corresponds to the entry for this peer in the `nextIndex` map - described in Figure 2 of the Raft paper. Note that this is the index of the first - log entry that is not yet on the peer, not the index of the last log entry that - is currently on the peer. */ - raft_log_index_t next_index = initial_next_index; - - /* `member_commit_index` tracks the last value of `commit_index` we sent to the - member. It doesn't correspond to anything in the Raft paper. This implementation - deviates from the Raft paper slightly in that when the leader commits a log - entry, it immediately sends an append-entries RPC so that clients can commit the - log entry too. This is necessary because we use "virtual heartbeats" in place of - regular heartbeats, and virtual heartbeats don't update the commit index. */ - raft_log_index_t member_commit_index = 0; - - /* Raft paper, Figure 2: "Upon election: send initial empty AppendEntries RPCs - (heartbeat) to each server" - Setting `send_even_if_empty` will ensure that we send an RPC immediately. */ - bool send_even_if_empty = true; - - /* If RPCs fail we'll wait a bit before trying again, in addition to waiting for - the peer to appear in `get_connected_members()`. This prevents us from locking up - the CPU if the peer is in `get_connected_members()` but always fails RPCs - immediately. */ - exponential_backoff_t backoff(100, 1000); - - /* This implementation deviates slightly from the Raft paper in that the initial - message may not be an empty append-entries RPC. Because `leader_send_updates()` - runs in its own coroutine, it's possible that entries may be appended to the log - (or even committed, although this is unlikely) between when we are elected as - leader and when we go to send initial messages. So we could send an - append-entries RPC that isn't empty, or even an install-snapshot RPC. This is OK - because if our implementation is a candidate and receives an install-snapshot RPC - for the current term, it will revert to follower just as if it had received an - append-entries RPC. - - This is in a loop because after we send the initial RPC, we'll block until it's - time to send another update, and then go around the loop again. */ - while (true) { - /* Don't bother trying to send an RPC until the peer is present in - `get_connected_members()`. */ - DEBUG_ONLY_CODE(check_invariants(mutex_acq.get())); - mutex_acq.reset(); - network->get_connected_members()->run_key_until_satisfied(peer, - [](const optional *x) { - return x != nullptr; - }, - update_keepalive.get_drain_signal()); - mutex_acq.init( - new new_mutex_acq_t(&mutex, update_keepalive.get_drain_signal())); - DEBUG_ONLY_CODE(check_invariants(mutex_acq.get())); - - if (next_index <= ps().log.prev_index) { - /* The peer's log ends before our log begins. So we have to send an - install-snapshot RPC instead of an append-entries RPC. */ - - typename raft_rpc_request_t::install_snapshot_t request; - request.term = ps().current_term; - request.leader_id = this_member_id; - request.last_included_index = ps().log.prev_index; - request.last_included_term = ps().log.prev_term; - /* TODO: Maybe we should send `committed_state` instead of the snapshot. - Under the current implementation, they will always be the same; but if we - were to allow the snapshot to lag behind the committed state, sending - `committed_state` would help the follower catch up faster. */ - request.snapshot_state = ps().snapshot_state; - request.snapshot_config = ps().snapshot_config; - raft_rpc_request_t request_wrapper; - request_wrapper.request = request; - - raft_rpc_reply_t reply_wrapper; - DEBUG_ONLY_CODE(check_invariants(mutex_acq.get())); - mutex_acq.reset(); - - bool ok = network->send_rpc(peer, request_wrapper, - update_keepalive.get_drain_signal(), &reply_wrapper); - - /* If the RPC failed, do the backoff before reacquiring the mutex */ - if (!ok) { - backoff.failure(update_keepalive.get_drain_signal()); - } - - mutex_acq.init( - new new_mutex_acq_t(&mutex, update_keepalive.get_drain_signal())); - DEBUG_ONLY_CODE(check_invariants(mutex_acq.get())); - - if (!ok) { - /* Raft paper, Section 5.1: "Servers retry RPCs if they do not - receive a response in a timely manner" - This implementation deviates from the Raft paper slightly in that we - don't retry the exact same RPC necessarily. If the snapshot has been - updated, we could send a newer snapshot on our next try. */ - continue; - } - - backoff.success(); - - const raft_rpc_reply_t::install_snapshot_t *reply = - boost::get( - &reply_wrapper.reply); - guarantee(reply != nullptr, "Got wrong type of RPC response"); - - if (candidate_or_leader_note_term(reply->term, mutex_acq.get())) { - /* We got a reply with a higher term than our term. - `candidate_and_leader_coro()` will be interrupted soon. */ - RAFT_DEBUG_THIS("got rpc reply with term %" PRIu64 " from %s\n", - reply->term, show_member_id(peer).c_str()); - return; - } - - next_index = request.last_included_index + 1; - leader_update_match_index( - peer, - request.last_included_index, - mutex_acq.get()); - send_even_if_empty = false; - - } else if (next_index <= ps().log.get_latest_index() || - member_commit_index < committed_state.get_ref().log_index || - send_even_if_empty) { - /* The peer's log ends right where our log begins, or in the middle of - our log. Send an append-entries RPC. */ - - typename raft_rpc_request_t::append_entries_t request; - request.term = ps().current_term; - request.leader_id = this_member_id; - request.entries.prev_index = next_index - 1; - request.entries.prev_term = - ps().log.get_entry_term(request.entries.prev_index); - for (raft_log_index_t i = next_index; - i <= ps().log.get_latest_index(); ++i) { - request.entries.append(ps().log.get_entry_ref(i)); - } - guarantee(request.entries.get_latest_index() - == ps().log.get_latest_index()); - request.leader_commit = committed_state.get_ref().log_index; - raft_rpc_request_t request_wrapper; - request_wrapper.request = request; - - raft_rpc_reply_t reply_wrapper; - DEBUG_ONLY_CODE(check_invariants(mutex_acq.get())); - mutex_acq.reset(); - - bool ok = network->send_rpc(peer, request_wrapper, - update_keepalive.get_drain_signal(), &reply_wrapper); - - /* If the RPC failed, do the backoff before reacquiring the mutex */ - if (!ok) { - backoff.failure(update_keepalive.get_drain_signal()); - } - - mutex_acq.init( - new new_mutex_acq_t(&mutex, update_keepalive.get_drain_signal())); - DEBUG_ONLY_CODE(check_invariants(mutex_acq.get())); - - if (!ok) { - /* Raft paper, Section 5.1: "Servers retry RPCs if they do not - receive a response in a timely manner" - This implementation deviates from the Raft paper slightly in that we - don't retry the exact same RPC necessarily. If more entries have been - added to the log, the RPC we retry will include those new entries; or - if the peer falls too far behind, we might send an install-snapshot - RPC next time. */ - continue; - } - - backoff.success(); - - const raft_rpc_reply_t::append_entries_t *reply = - boost::get( - &reply_wrapper.reply); - guarantee(reply != nullptr, "Got wrong type of RPC response"); - - if (candidate_or_leader_note_term(reply->term, mutex_acq.get())) { - /* We got a reply with a higher term than our term. - `candidate_and_leader_coro()` will be interrupted soon. */ - RAFT_DEBUG_THIS("got rpc reply with term %" PRIu64 " from %s\n", - reply->term, show_member_id(peer).c_str()); - return; - } - - if (reply->success) { - /* Raft paper, Figure 2: "If successful: update nextIndex and - matchIndex for follower */ - next_index = request.entries.get_latest_index() + 1; - if (match_indexes.at(peer) < request.entries.get_latest_index()) { - leader_update_match_index( - peer, - request.entries.get_latest_index(), - mutex_acq.get()); - } - member_commit_index = request.leader_commit; - } else { - /* Raft paper, Section 5.3: "After a rejection, the leader decrements - nextIndex and retries the AppendEntries RPC. */ - --next_index; - } - send_even_if_empty = false; - - } else { - guarantee(next_index == ps().log.get_latest_index() + 1); - guarantee(member_commit_index == committed_state.get_ref().log_index); - /* OK, the peer is completely up-to-date. Wait until either an entry is - appended to the log or our commit index advances, and then go around the - loop again. */ - - DEBUG_ONLY_CODE(check_invariants(mutex_acq.get())); - mutex_acq.reset(); - - run_until_satisfied_2( - committed_state.get_watchable(), - latest_state.get_watchable(), - [&](const state_and_config_t &cs, const state_and_config_t &ls) { - return cs.log_index > member_commit_index || - ls.log_index >= next_index; - }, - update_keepalive.get_drain_signal()); - - mutex_acq.init( - new new_mutex_acq_t(&mutex, update_keepalive.get_drain_signal())); - DEBUG_ONLY_CODE(check_invariants(mutex_acq.get())); - } - } - } catch (const interrupted_exc_t &) { - /* The leader interrupted us. This could be because the `raft_member_t` is being - destroyed; because the leader is no longer leader; or because a config change - removed `peer` from the cluster. In any case, we just return. */ - } -} - -template -void raft_member_t::leader_continue_reconfiguration( - const new_mutex_acq_t *mutex_acq) { - mutex_acq->guarantee_is_holding(&mutex); - guarantee(mode == mode_t::leader); - /* Check if we recently committed a joint consensus configuration, or a configuration - in which we are no longer leader. */ - if (!committed_state.get_ref().config.is_valid_leader(this_member_id) && - !latest_state.get_ref().config.is_valid_leader(this_member_id)) { - /* Raft paper, Section 6: "...the leader steps down (returns to follower state) - once it has committed the C_new log entry." - `candidate_or_leader_note_term()` isn't designed for the purpose of making us - intentionally step down, but it contains all the right logic. This has a side - effect of incrementing `current_term`, but I don't think that's a problem. */ - RAFT_DEBUG_THIS("stepping down, new term: %" PRIu64 "\n", ps().current_term + 1); - candidate_or_leader_note_term(ps().current_term + 1, mutex_acq); - } else if (committed_state.get_ref().config.is_joint_consensus() && - latest_state.get_ref().config.is_joint_consensus()) { - /* OK, we recently committed a joint consensus configuration, but we haven't yet - appended the second part of the reconfiguration to the log (or else - `latest_state` wouldn't be a joint consensus). */ - - /* Raft paper, Section 6: "Once C_old,new has been committed... it is now safe - for the leader to create a log entry describing C_new and replicate it to the - cluster. */ - raft_complex_config_t new_config; - new_config.config = *committed_state.get_ref().config.new_config; - - raft_log_entry_t new_entry; - new_entry.type = raft_log_entry_type_t::config; - new_entry.config = optional(new_config); - new_entry.term = ps().current_term; - - leader_append_log_entry(new_entry, mutex_acq); - } -} - -template -bool raft_member_t::candidate_or_leader_note_term( - raft_term_t term, const new_mutex_acq_t *mutex_acq) { - mutex_acq->guarantee_is_holding(&mutex); - guarantee(mode != mode_t::follower); - /* Raft paper, Figure 2: If RPC request or response contains term T > currentTerm: - set currentTerm = T, convert to follower */ - if (term > ps().current_term) { - raft_term_t local_current_term = ps().current_term; - /* We have to spawn this in a separate coroutine because - `candidate_or_leader_become_follower()` blocks until - `candidate_and_leader_coro()` exits, so calling - `candidate_or_leader_become_follower()` directly would cause a deadlock. */ - auto_drainer_t::lock_t keepalive(drainer.get()); - coro_t::spawn_sometime([this, local_current_term, term, - keepalive /* important to capture */]() { - try { - new_mutex_acq_t mutex_acq_2(&mutex, keepalive.get_drain_signal()); - DEBUG_ONLY_CODE(this->check_invariants(&mutex_acq_2)); - /* Check that term hasn't already been updated between when - `candidate_or_leader_note_term()` was called and when this coroutine ran. - */ - if (this->ps().current_term == local_current_term) { - /* It's unlikely that `mode` will be `follower` at this point, but - it's possible. Suppose that we are a candidate for term T, and we - receive an RPC reply for term T+1, so we call `..._note_term()` and - this coroutine is spawned. But before this coroutine acquires the - mutex, we receive an AppendEntries RPC from a leader for term T, so - we transition to follower state. Then when this coroutine actually - executes, it will find that `current_term` has not changed, but we - are in follower state. */ - if (mode != mode_t::follower) { - this->candidate_or_leader_become_follower(&mutex_acq_2); - } - this->update_term(term, raft_member_id_t(), &mutex_acq_2); - } - DEBUG_ONLY_CODE(this->check_invariants(&mutex_acq_2)); - } catch (const interrupted_exc_t &) { - /* If `keepalive.get_drain_signal()` is pulsed, then the `raft_member_t` - is being destroyed, so it doesn't matter if we update our term; the - `candidate_or_leader_coro` will stop on its own. */ - } - }); - return true; - } else { - return false; - } -} - -template -void raft_member_t::leader_append_log_entry( - const raft_log_entry_t &log_entry, - const new_mutex_acq_t *mutex_acq) { - mutex_acq->guarantee_is_holding(&mutex); - guarantee(mode == mode_t::leader); - guarantee(log_entry.term == ps().current_term); - - /* Raft paper, Section 5.3: "The leader appends the command to its log as a new - entry..." - This will block until the log entry is safely on disk. This might be important for - correctness; it might be dangerous for us to send an AppendEntries RPC for log - entries that are not safely on disk yet. I'm not sure. */ - storage->write_log_append_one(log_entry); - - /* Raft paper, Section 5.3: "...then issues AppendEntries RPCs in parallel to each of - the other servers to replicate the entry." - Because we modified `ps().log`, we have to update `latest_state`. But this will also - have the side-effect of notifying anything that's waiting on `latest_state`; in - particular, instances of `leader_send_updates()` will wait on `latest_state` so they - can be notified when there are new log entries to send to the followers. */ - latest_state.apply_atomic_op([&](state_and_config_t *s) -> bool { - guarantee(s->log_index + 1 == this->ps().log.get_latest_index()); - this->apply_log_entries(s, this->ps().log, s->log_index + 1, s->log_index + 1); - return true; - }); - - /* Figure 2 of the Raft paper defines `matchIndexes` as the "index of highest log - entry known to be replicated on each server". Although it's not explicitly stated - anywhere, this means the leader needs to increment its entry in `match_indexes` - whenever it appends to its own log. */ - guarantee(match_indexes.at(this_member_id) + 1 == ps().log.get_latest_index()); - leader_update_match_index(this_member_id, ps().log.get_latest_index(), mutex_acq); -} - -#endif /* CLUSTERING_GENERIC_RAFT_CORE_TCC_ */ - diff --git a/src/clustering/generic/raft_network.hpp b/src/clustering/generic/raft_network.hpp deleted file mode 100644 index 0439e995192..00000000000 --- a/src/clustering/generic/raft_network.hpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_GENERIC_RAFT_NETWORK_HPP_ -#define CLUSTERING_GENERIC_RAFT_NETWORK_HPP_ - -#include "clustering/generic/raft_core.hpp" -#include "concurrency/watchable_transform.hpp" -#include "rpc/mailbox/typed.hpp" - -/* This file is for running the Raft protocol using RethinkDB's clustering primitives. -The core logic for the Raft protocol is in `raft_core.hpp`, not here. This just adds a -networking layer over `raft_core.hpp`. */ - -template -class raft_business_card_t { -public: - typedef mailbox_t, mailbox_addr_t> rpc_mailbox_t; - - typename rpc_mailbox_t::address_t rpc; - - optional virtual_heartbeats; - - RDB_MAKE_ME_SERIALIZABLE_2(raft_business_card_t, rpc, virtual_heartbeats); - RDB_MAKE_ME_EQUALITY_COMPARABLE_2(raft_business_card_t, rpc, virtual_heartbeats); -}; - -template -class raft_networked_member_t : - private raft_network_interface_t { -public: - raft_networked_member_t( - const raft_member_id_t &this_member_id, - mailbox_manager_t *mailbox_manager, - watchable_map_t > *peers, - raft_storage_interface_t *storage, - const std::string &log_prefix, - const raft_start_election_immediately_t start_election_immediately); - - clone_ptr_t > > get_business_card() { - return business_card.get_watchable(); - } - - raft_member_t *get_raft() { - return &member; - } - -private: - /* The `send_rpc()`, `send_virtual_heartbeats()`, and `get_connected_members()` - methods implement the `raft_network_interface_t` interface. */ - bool send_rpc( - const raft_member_id_t &dest, - const raft_rpc_request_t &rpc, - signal_t *interruptor, - raft_rpc_reply_t *reply_out); - void send_virtual_heartbeats( - const optional &term); - watchable_map_t > - *get_connected_members(); - - /* The `on_rpc()` methods are mailbox callbacks. */ - void on_rpc( - signal_t *interruptor, - const raft_rpc_request_t &rpc, - const mailbox_t::address_t &reply_addr); - - mailbox_manager_t *mailbox_manager; - watchable_map_t > *peers; - - /* This transforms the `watchable_map_t` that we got through our constructor into a - value suitable for returning from `get_connected_members()` */ - watchable_map_value_transform_t, optional > - peers_map_transformer; - - raft_member_t member; - - typename raft_business_card_t::rpc_mailbox_t rpc_mailbox; - - watchable_variable_t > business_card; -}; - -#endif /* CLUSTERING_GENERIC_RAFT_NETWORK_HPP_ */ - diff --git a/src/clustering/generic/raft_network.tcc b/src/clustering/generic/raft_network.tcc deleted file mode 100644 index b62654d0165..00000000000 --- a/src/clustering/generic/raft_network.tcc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_GENERIC_RAFT_NETWORK_TCC_ -#define CLUSTERING_GENERIC_RAFT_NETWORK_TCC_ - -#include "clustering/generic/raft_network.hpp" -#include "rpc/mailbox/disconnect_watcher.hpp" - -template -raft_networked_member_t::raft_networked_member_t( - const raft_member_id_t &this_member_id, - mailbox_manager_t *_mailbox_manager, - watchable_map_t > *_peers, - raft_storage_interface_t *storage, - const std::string &log_prefix, - const raft_start_election_immediately_t start_election_immediately) : - mailbox_manager(_mailbox_manager), - peers(_peers), - peers_map_transformer(peers, - [](const raft_business_card_t *value1) { - return &value1->virtual_heartbeats; - }), - member(this_member_id, storage, this, log_prefix, start_election_immediately), - rpc_mailbox(mailbox_manager, - std::bind(&raft_networked_member_t::on_rpc, this, ph::_1, ph::_2, ph::_3)), - business_card(raft_business_card_t { - rpc_mailbox.get_address(), optional() }) - { } - -template -bool raft_networked_member_t::send_rpc( - const raft_member_id_t &dest, - const raft_rpc_request_t &request, - signal_t *interruptor, - raft_rpc_reply_t *reply_out) { - /* Find the given member's mailbox address */ - optional > bcard = peers->get_key(dest); - if (!bcard.has_value()) { - /* The member is not connected */ - return false; - } - /* Send message and wait for a reply */ - disconnect_watcher_t watcher(mailbox_manager, bcard->rpc.get_peer()); - cond_t got_reply; - mailbox_t reply_mailbox( - mailbox_manager, - [&](signal_t *, raft_rpc_reply_t &&reply) { - *reply_out = reply; - got_reply.pulse(); - }); - send(mailbox_manager, bcard->rpc, request, reply_mailbox.get_address()); - wait_any_t waiter(&watcher, &got_reply); - wait_interruptible(&waiter, interruptor); - return got_reply.is_pulsed(); -} - -template -void raft_networked_member_t::send_virtual_heartbeats( - const optional &term) { - business_card.apply_atomic_op( - [&](raft_business_card_t *bcard) { - if (bcard->virtual_heartbeats != term) { - bcard->virtual_heartbeats = term; - return true; - } else { - return false; - } - }); -} - -template -watchable_map_t > * - raft_networked_member_t::get_connected_members() { - return &peers_map_transformer; -} - -template -void raft_networked_member_t::on_rpc( - UNUSED signal_t *interruptor, - const raft_rpc_request_t &request, - const mailbox_t::address_t &reply_addr) { - raft_rpc_reply_t reply; - member.on_rpc(request, &reply); - send(mailbox_manager, reply_addr, reply); -} - -#endif /* CLUSTERING_GENERIC_RAFT_NETWORK_TCC_ */ - diff --git a/src/clustering/generic/registrant.hpp b/src/clustering/generic/registrant.hpp deleted file mode 100644 index ff253d58345..00000000000 --- a/src/clustering/generic/registrant.hpp +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CLUSTERING_GENERIC_REGISTRANT_HPP_ -#define CLUSTERING_GENERIC_REGISTRANT_HPP_ - -#include -#include -#include -#include - -#include "clustering/generic/registration_metadata.hpp" -#include "containers/clone_ptr.hpp" -#include "containers/death_runner.hpp" -#include "containers/uuid.hpp" -#include "rpc/mailbox/typed.hpp" -#include "rpc/mailbox/mailbox.hpp" -#include "rpc/semilattice/view.hpp" - -template -class registrant_t { - -public: - /* This constructor registers with the given registrant. It may block for a short - time. */ - registrant_t( - mailbox_manager_t *mm, - const registrar_business_card_t &rmd, - business_card_t initial_value) : - mailbox_manager(mm), - registrar_md(rmd), - registration_id(generate_uuid()) - { - /* This will make it so that we get deregistered in our destructor. */ - deregisterer.fun = std::bind(®istrant_t::send_deregister_message, - mailbox_manager, - registrar_md.delete_mailbox, - registration_id); - - /* Send a message to register us */ - send(mailbox_manager, registrar_md.create_mailbox, - registration_id, - mailbox_manager->get_connectivity_cluster()->get_me(), - initial_value); - } - - /* The destructor deregisters us and returns immediately. It never throws - any exceptions. */ - ~registrant_t() THROWS_NOTHING { - - /* Most of the work is done by the destructor for `deregisterer` */ - } - -private: - typedef typename registrar_business_card_t::registration_id_t registration_id_t; - - /* We can't deregister in our destructor because then we wouldn't get - deregistered if we died mid-constructor. Instead, the deregistration must be - done by a separate subcomponent. `deregisterer` is that subcomponent. The - constructor sets `deregisterer.fun` to a `std::bind()` of - `send_deregister_message()`, and that deregisters things as necessary. */ - static void send_deregister_message( - mailbox_manager_t *mailbox_manager, - typename registrar_business_card_t::delete_mailbox_t::address_t addr, - registration_id_t rid) THROWS_NOTHING { - send(mailbox_manager, addr, rid); - } - death_runner_t deregisterer; - - mailbox_manager_t *mailbox_manager; - registrar_business_card_t registrar_md; - - registration_id_t registration_id; - - DISABLE_COPYING(registrant_t); -}; - -#endif /* CLUSTERING_GENERIC_REGISTRANT_HPP_ */ diff --git a/src/clustering/generic/registrar.hpp b/src/clustering/generic/registrar.hpp deleted file mode 100644 index f7fbd61f098..00000000000 --- a/src/clustering/generic/registrar.hpp +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CLUSTERING_GENERIC_REGISTRAR_HPP_ -#define CLUSTERING_GENERIC_REGISTRAR_HPP_ - -#include -#include -#include -#include - -#include "clustering/generic/registration_metadata.hpp" -#include "concurrency/coro_pool.hpp" -#include "concurrency/mutex.hpp" -#include "concurrency/promise.hpp" -#include "concurrency/queue/unlimited_fifo.hpp" -#include "concurrency/wait_any.hpp" -#include "containers/map_sentries.hpp" -#include "rpc/mailbox/disconnect_watcher.hpp" -#include "rpc/mailbox/typed.hpp" -#include "utils.hpp" - -template -class registrar_t { - -public: - registrar_t(mailbox_manager_t *mm, user_data_type co) : - mailbox_manager(mm), controller(co), - registration_destruction_pool(1, ®istration_destruction_queue, - ®istration_destruction_callback), - create_mailbox(mailbox_manager, std::bind(®istrar_t::on_create, - this, ph::_1, ph::_2, ph::_3, ph::_4)), - delete_mailbox(mailbox_manager, std::bind(®istrar_t::on_delete, - this, ph::_1, ph::_2)) - { } - - registrar_business_card_t get_business_card() { - return registrar_business_card_t( - create_mailbox.get_address(), - delete_mailbox.get_address()); - } - -private: - typedef typename registrar_business_card_t::registration_id_t - registration_id_t; - - class active_registration_t : public signal_t::subscription_t { - public: - active_registration_t( - registrar_t *_registrar, - mutex_t::acq_t &&_mutex_acq, - registration_id_t rid, - peer_id_t peer, - business_card_t business_card, - auto_drainer_t::lock_t &&_keepalive, - signal_t *interruptor) : - keepalive(std::move(_keepalive)), - registrar(_registrar), - mutex_acq(std::move(_mutex_acq)), - /* Construct a `registrant_t` to tell the controller that something has - now registered. */ - registrant(registrar->controller, business_card, interruptor), - /* Expose `deletion_cond` so that `on_delete()` can find it. */ - registration_map_sentry(®istrar->registrations, rid, &deletion_cond), - /* Begin monitoring the peer so we can disconnect when necessary. */ - peer_monitor(registrar->mailbox_manager, peer), - waiter(&deletion_cond, &peer_monitor, keepalive.get_drain_signal()) { - - /* Release the mutex, since we're done with our initial setup phase */ - { - mutex_t::acq_t doomed; - swap(mutex_acq, doomed); - } - - /* Wait till it's time to shut down */ - signal_t::subscription_t::reset(&waiter); - } - - virtual void run() { - /* Perform the destruction in a coroutine. */ - registrar->registration_destruction_queue.push([this]() { delete this; } ); - } - - private: - ~active_registration_t() { - /* The only thing that calls us should be waiter_subscription_t::run(). */ - rassert(waiter.is_pulsed()); - - /* Unsubscribe outselves from `waiter`, before we destruct it. */ - signal_t::subscription_t::reset(); - - /* Reacquire the mutex, to avoid race conditions when we're - deregistering from `deleters`. I'm not sure if there re any such race - conditions, but better safe than sorry. */ - { - mutex_t::acq_t reacquisition(®istrar->mutex); - swap(mutex_acq, reacquisition); - } - - /* `registration_map_sentry` destructor run here; `deletion_cond` cannot - be pulsed after this. */ - - /* `deletion_cond` destructor run here. */ - - /* `registrant` destructor run here; this will tell the controller that - the registration is dead and gone. */ - - /* `mutex_acq` destructor run here; it's safe to release the mutex - because we're no longer touching `updaters` or `deleters`. */ - } - - auto_drainer_t::lock_t keepalive; - registrar_t *registrar; - mutex_t::acq_t mutex_acq; - registrant_type registrant; - cond_t deletion_cond; - map_insertion_sentry_t registration_map_sentry; - disconnect_watcher_t peer_monitor; - wait_any_t waiter; - - DISABLE_COPYING(active_registration_t); - }; - - void on_create( - signal_t *interruptor, - registration_id_t rid, - peer_id_t peer, - business_card_t business_card) { - /* Grab the mutex to avoid race conditions if a message arrives at the - update mailbox or the delete mailbox while we're working. We must not - block between when `on_create()` begins and when `mutex_acq` is - constructed. */ - mutex_t::acq_t mutex_acq(&mutex); - - /* If the registrant has already deregistered but the deregistration - message arrived ahead of the registration message, it will have left a - NULL in the `registrations` map. */ - typename std::map::iterator it = registrations.find(rid); - if (it != registrations.end()) { - guarantee(it->second == nullptr); - registrations.erase(it); - return; - } - - /* Hand processing over to an active_registration_t. We use a heap - allocated object to save memory compared to keeping this coroutine - running for doing the remaining work. active_registration_t takes care - of its own destruction once the keepalive drain signal is pulsed (or one - of the other signals it waits on). */ - auto_drainer_t::lock_t keepalive(&drainer); - new active_registration_t(this, std::move(mutex_acq), rid, peer, - business_card, std::move(keepalive), interruptor); - } - - void on_delete(UNUSED signal_t *interruptor, registration_id_t rid) { - - /* Acquire the mutex so we don't race with `on_create()`. */ - mutex_t::acq_t mutex_acq(&mutex); - - /* Deliver our notification */ - typename std::map::iterator it = - registrations.find(rid); - if (it != registrations.end()) { - cond_t *deleter = it->second; - guarantee(!deleter->is_pulsed()); - deleter->pulse(); - } else { - /* We got here because the registrant was deleted right after being - created and the deregistration message arrived before the - registration message. Insert a NULL into the map so that - `on_create()` realizes it. */ - cond_t *const zero = nullptr; - std::pair value(rid, zero); - registrations.insert(value); - } - } - - mailbox_manager_t *mailbox_manager; - user_data_type controller; - - mutex_t mutex; - std::map registrations; - - unlimited_fifo_queue_t > registration_destruction_queue; - calling_callback_t registration_destruction_callback; - coro_pool_t > registration_destruction_pool; - - auto_drainer_t drainer; - - typename registrar_business_card_t::create_mailbox_t create_mailbox; - typename registrar_business_card_t::delete_mailbox_t delete_mailbox; - - DISABLE_COPYING(registrar_t); -}; - -#endif /* CLUSTERING_GENERIC_REGISTRAR_HPP_ */ diff --git a/src/clustering/generic/registration_metadata.hpp b/src/clustering/generic/registration_metadata.hpp deleted file mode 100644 index c20046e35d2..00000000000 --- a/src/clustering/generic/registration_metadata.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CLUSTERING_GENERIC_REGISTRATION_METADATA_HPP_ -#define CLUSTERING_GENERIC_REGISTRATION_METADATA_HPP_ - -#include "containers/uuid.hpp" -#include "rpc/mailbox/typed.hpp" -#include "rpc/semilattice/joins/macros.hpp" - -template -class registrar_business_card_t { -public: - typedef uuid_u registration_id_t; - - typedef mailbox_t create_mailbox_t; - typename create_mailbox_t::address_t create_mailbox; - - typedef mailbox_t delete_mailbox_t; - typename delete_mailbox_t::address_t delete_mailbox; - - registrar_business_card_t() { } - - registrar_business_card_t( - const typename create_mailbox_t::address_t &cm, - const typename delete_mailbox_t::address_t &dm) : - create_mailbox(cm), delete_mailbox(dm) - { } - - RDB_MAKE_ME_SERIALIZABLE_2(registrar_business_card_t, - create_mailbox, delete_mailbox); -}; - -template -RDB_MAKE_EQUALITY_COMPARABLE_2(registrar_business_card_t, - create_mailbox, delete_mailbox); - -#endif /* CLUSTERING_GENERIC_REGISTRATION_METADATA_HPP_ */ diff --git a/src/clustering/id_types.hpp b/src/clustering/id_types.hpp deleted file mode 100644 index 2c25bbe8dca..00000000000 --- a/src/clustering/id_types.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef CLUSTERING_ID_TYPES_HPP_ -#define CLUSTERING_ID_TYPES_HPP_ - -#include "containers/uuid.hpp" - -typedef uuid_u contract_id_t; - -#endif // CLUSTERING_ID_TYPES_HPP_ diff --git a/src/clustering/immediate_consistency/backfill_item_seq.hpp b/src/clustering/immediate_consistency/backfill_item_seq.hpp deleted file mode 100644 index d890a3c18ce..00000000000 --- a/src/clustering/immediate_consistency/backfill_item_seq.hpp +++ /dev/null @@ -1,178 +0,0 @@ -#ifndef CLUSTERING_IMMEDIATE_CONSISTENCY_BACKFILL_ITEM_SEQ_HPP_ -#define CLUSTERING_IMMEDIATE_CONSISTENCY_BACKFILL_ITEM_SEQ_HPP_ - -#include - -#include "btree/keys.hpp" -#include "region/region.hpp" - -/* A `backfill_item_seq_t` contains all of the `backfill_{pre_}item_t`s in some range of -the key-space. The items are stored in lexicographical order. - -It keeps track of the left and right bounds of the range it's supposed to apply to. For -example, if a `backfill_item_seq_t` contains items with keys `A`, `G`, and `J` and -applies to the range from `A` to `M`, that implies that there won't ever be a backfill -item at e.g. `B` or `L`, but it doesn't imply anything about whether there will be a -backfill item at `X`. - -It also keeps a running total of the combined mem size of the backfill items it contains. -*/ - -template -class backfill_item_seq_t { -public: - /* Initializes an undefined seq that cannot be used for anything. */ - backfill_item_seq_t() { } - - /* Initializes an empty seq with a zero-width region at the given location */ - backfill_item_seq_t( - uint64_t _beg_hash, uint64_t _end_hash, key_range_t::right_bound_t key) : - beg_hash(_beg_hash), end_hash(_end_hash), left_key(key), right_key(key), - mem_size(0) { } - - /* Returns the left and right bounds of the space this `backfill_item_seq_t` applies - to. These may be equal, in which case the sequence must be empty. */ - key_range_t::right_bound_t get_left_key() const { return left_key; } - key_range_t::right_bound_t get_right_key() const { return right_key; } - - /* The `backfill_item_seq_t` is associated with a hash range for sanity-checking - purposes. */ - uint64_t get_beg_hash() const { return beg_hash; } - uint64_t get_end_hash() const { return end_hash; } - - region_t get_region() const { - if (left_key == right_key) { - return region_t::empty(); - } else { - key_range_t kr; - kr.left = left_key.key(); - kr.right = right_key; - return region_t(beg_hash, end_hash, kr); - } - } - - size_t get_mem_size() const { return mem_size; } - - typename std::list::const_iterator begin() const { return items.begin(); } - typename std::list::const_iterator end() const { return items.end(); } - - /* `empty_of_items()` returns `true` if there aren't any items in the seq; but the - seq's domain might still be non-empty. `empty_domain()` returns `true` if the seq's - domain is empty. `empty_domain()` implies `empty_of_items()`. */ - bool empty_of_items() const { return items.empty(); } - bool empty_domain() const { return left_key == right_key; } - - const item_t &front() const { return items.front(); } - const item_t &back() const { return items.back(); } - - /* Deletes the leftmost item in the seq, shrinking the seq's domain to after the end - of the item that was removed. */ - void pop_front() { - left_key = items.front().get_range().right; - mem_size -= items.front().get_mem_size(); - items.pop_front(); - } - - /* Transfers the item at the {left,right} end of this seq to the {right,left} end of - the other seq, shrinking the seq's domain to {after the end,before the beginning} of - the item that was removed. */ - void pop_front_into(backfill_item_seq_t *other) { - guarantee(beg_hash == other->beg_hash && end_hash == other->end_hash); - guarantee(get_left_key() == other->get_right_key()); - size_t item_size = items.front().get_mem_size(); - left_key = items.front().get_range().right; - other->right_key = left_key; - mem_size -= item_size; - other->mem_size += item_size; - other->items.splice(other->items.end(), items, items.begin()); - } - - void pop_back_into(backfill_item_seq_t *other) { - guarantee(beg_hash == other->beg_hash && end_hash == other->end_hash); - guarantee(get_right_key() == other->get_left_key()); - size_t item_size = items.back().get_mem_size(); - right_key = key_range_t::right_bound_t(items.front().get_range().left); - other->left_key = right_key; - mem_size -= item_size; - other->mem_size += item_size; - other->items.splice(other->items.begin(), items, --items.end()); - } - - /* Deletes the part of the seq that is to the left of the key, changing the seq's - left bound to the given location. If a single backfill item spans the key, that item - will be split. */ - void delete_to_key(const key_range_t::right_bound_t &cut) { - guarantee(cut >= get_left_key()); - guarantee(cut <= get_right_key()); - while (!items.empty()) { - key_range_t range = items.front().get_range(); - if (range.right <= cut) { - mem_size -= items.front().get_mem_size(); - items.pop_front(); - } else if (key_range_t::right_bound_t(range.left) >= cut) { - break; - } else { - range.left = cut.key(); - mem_size -= items.front().get_mem_size(); - items.front().mask_in_place(range); - mem_size += items.front().get_mem_size(); - break; - } - } - left_key = cut; - } - - /* Appends an item to the end of the seq, expanding the seq's domain on the right. - Atoms must be appended in lexicographical order, so calling `push_back()` implicitly - states that there are no items between the previous end of the seq and `item`. */ - void push_back(item_t &&item) { - key_range_t item_range = item.get_range(); - guarantee(key_range_t::right_bound_t(item_range.left) >= right_key); - right_key = item_range.right; - mem_size += item.get_mem_size(); - items.push_back(std::move(item)); - } - - /* Expands the seq's domain on the right to the given key, explicitly stating that - there are no items between the previous end of the seq and `bound`. */ - void push_back_nothing(const key_range_t::right_bound_t &bound) { - guarantee(bound >= right_key); - right_key = bound; - } - - /* Expands the seq's domain on the left to the given key, explicitly stating that - there are no items between the previous beginning of the seq and `bound`. */ - void push_front_nothing(const key_range_t::right_bound_t &bound) { - guarantee(bound <= left_key); - left_key = bound; - } - - /* Concatenates two `backfill_item_seq_t`s. Their domains must be adjacent. */ - void concat(backfill_item_seq_t &&other) { - guarantee(beg_hash == other.beg_hash && end_hash == other.end_hash); - guarantee(right_key == other.left_key); - right_key = other.right_key; - mem_size += other.mem_size; - items.splice(items.end(), std::move(other.items)); - } - -private: - /* A `backfill_item_seq_t` has a `region_t`, with one oddity: even when the region - is empty, the `backfill_item_seq_t` still has a meaningful left and right bound. This - is why this is stored as four separate variables instead of a `region_t`. We use two - `key_range_t::right_bound_t`s instead of a `key_range_t` so that we can represent - a zero-width region after the last key. */ - uint64_t beg_hash, end_hash; - key_range_t::right_bound_t left_key, right_key; - - /* The sum of `a.get_mem_size()` over all the items */ - size_t mem_size; - - std::list items; - - RDB_MAKE_ME_SERIALIZABLE_6(backfill_item_seq_t, - beg_hash, end_hash, left_key, right_key, mem_size, items); -}; - -#endif /* CLUSTERING_IMMEDIATE_CONSISTENCY_BACKFILL_ITEM_SEQ_HPP_ */ - diff --git a/src/clustering/immediate_consistency/backfill_metadata.cc b/src/clustering/immediate_consistency/backfill_metadata.cc deleted file mode 100644 index 2b5b5306258..00000000000 --- a/src/clustering/immediate_consistency/backfill_metadata.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/immediate_consistency/backfill_metadata.hpp" - -backfill_config_t::backfill_config_t() : - item_queue_mem_size(4 * MEGABYTE), - item_chunk_mem_size(100 * KILOBYTE), - pre_item_queue_mem_size(4 * MEGABYTE), - pre_item_chunk_mem_size(100 * KILOBYTE) - { } - -RDB_IMPL_SERIALIZABLE_4_FOR_CLUSTER(backfill_config_t, - item_queue_mem_size, item_chunk_mem_size, pre_item_queue_mem_size, - pre_item_chunk_mem_size); - -RDB_IMPL_SERIALIZABLE_8_FOR_CLUSTER(backfiller_bcard_t::intro_2_t, - common_version, final_version_history, pre_items_mailbox, begin_session_mailbox, - end_session_mailbox, ack_items_mailbox, num_changes_estimate, progress_estimator); - -RDB_IMPL_SERIALIZABLE_7_FOR_CLUSTER(backfiller_bcard_t::intro_1_t, - config, initial_version, initial_version_history, intro_mailbox, items_mailbox, - ack_end_session_mailbox, ack_pre_items_mailbox); - -RDB_IMPL_SERIALIZABLE_2_FOR_CLUSTER(backfiller_bcard_t, region, registrar); -RDB_IMPL_EQUALITY_COMPARABLE_2(backfiller_bcard_t, region, registrar); - -RDB_IMPL_SERIALIZABLE_3_FOR_CLUSTER(replica_bcard_t, - synchronize_mailbox, branch_id, backfiller_bcard); -RDB_IMPL_EQUALITY_COMPARABLE_3(replica_bcard_t, - synchronize_mailbox, branch_id, backfiller_bcard); diff --git a/src/clustering/immediate_consistency/backfill_metadata.hpp b/src/clustering/immediate_consistency/backfill_metadata.hpp deleted file mode 100644 index 03416a64310..00000000000 --- a/src/clustering/immediate_consistency/backfill_metadata.hpp +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_IMMEDIATE_CONSISTENCY_BACKFILL_METADATA_HPP_ -#define CLUSTERING_IMMEDIATE_CONSISTENCY_BACKFILL_METADATA_HPP_ - -#include "btree/backfill_types.hpp" -#include "clustering/generic/registration_metadata.hpp" -#include "clustering/immediate_consistency/backfill_item_seq.hpp" -#include "clustering/immediate_consistency/history.hpp" -#include "concurrency/fifo_enforcer.hpp" -#include "rdb_protocol/distribution_progress.hpp" -#include "rpc/mailbox/typed.hpp" - -/* `backfill_config_t` contains parameters for tuning the backfill's performance and -memory usage. Mostly it's for unit testing purposes; currently we always use fixed values -in release mode, but the unit tests use smaller queue sizes to trigger different edge -cases. */ -class backfill_config_t { -public: - /* The default constructor assigns reasonable defaults */ - backfill_config_t(); - - /* The maximum amount of RAM that can be used for the items queued in memory on the - backfillee. */ - size_t item_queue_mem_size; - - /* The maximum size, in bytes, of a chunk of items sent over the network from the - backfiller to the backfillee. */ - size_t item_chunk_mem_size; - - /* The maximum amount of RAM that can be used for the pre-items queued in memory on - the backfiller. */ - size_t pre_item_queue_mem_size; - - /* The maximum size, in bytes, of a chunk of pre-items sent over the network from the - backfillee to the backfiller. */ - size_t pre_item_chunk_mem_size; -}; - -RDB_DECLARE_SERIALIZABLE(backfill_config_t); - -/* The backfiller publishes a `backfiller_bcard_t` which the backfillee uses to contact -it. The member types of `backfiller_bcard_t` describe the communications protocol between -the backfiller and the backfillee. - -The backfillee begins by registering with the `registrar_t` described by the `registrar` -member of the `backfiller_bcard_t`. When it registers, it sends an `intro_1_t`, which -contains its initial version and a set of mailbox addresses. The initial version map -implicitly defines the range that the backfill will apply to. The backfiller compares the -backfillee's initial version with its own version and finds their closest common -ancestor. Then it sends back an `intro_2_t` via the `intro_1_t::intro_mailbox`. The -`intro_2_t` contains the common version which the backfiller calculated, along with its -own set of mailbox addresses. It also includes the branch history for the branch that the -backfiller is currently on. The backfiller is guaranteed not to change branches during -the backfill, so this is the only time branch history needs to be transmitted from the -backfiller to the backfillee. - -Once the initial handshake is completed, two processes happen in parallel: the backfillee -transmits `backfill_pre_item_t`s to the backfiller, and the backfiller transmits -`backfill_item_t`s to the backfillee. These processes both proceed from left to right -lexicographically, and they are constrained to proceed at approximately the same pace: -The backfiller needs to know the pre-items for a given range before it can compute the -backfill items for that range, so the item traversal can't go ahead of the pre-item -traversal. The pre-item traversal can't go too far ahead of the item traversal because -the size of the pre-item queue is limited. - -The item traversal process is broken into a series of sessions. (See `backfillee.hpp` for -an explanation of why; the sessions correspond to calls to `backfillee_t::go()`.) Each -session individually proceeds from left to right through some range of the key-space. The -sessions cannot overlap temporally, in the sense that no more than one session can exist -at the same time; but they may overlap in key-space, in the sense that the same key may -be backfilled multiple times as part of multiple sessions. The steps of each session are -as follows: - -1. The backfillee begins a session by sending a message to the backfiller's -`begin_session_mailbox_t`, indicating where the session should start. The first session -must start at the left-hand side of the backfill range; subsequent sessions can start -anywhere between the left-hand side of the range and the furthest point where any session -has ended. - -2. The backfiller starts traversing the B-tree; as it does so, it sends messages to the -`items_mailbox_t` on the backfillee. These messages form a contiguous series of -`backfill_item_seq_t`s in lexicographical order. Each message includes the corresponding -metainfo. The backfillee applies the items to its B-tree. - -3. As the backfillee applies each item, it sends messages to the `ack_items_mailbox_t` on -the backfiller. The backfiller uses this as flow control; it limits the total mem size of -all of the items that it has sent but not received an acknowledgement for. (Note: The -backfillee must acknowledge every item, even if the session is interrupted.) - -4a. The backfillee may decide to interrupt the session. It does this by sending a message -to the `end_session_mailbox_t` on the backfiller, which responds by sending a message to -the `ack_end_session_mailbox_t` on the backfillee. The backfillee must finish applying -any items it receives before the `ack_end_session_mailbox_t` message arrives. So the -backfiller knows that if it sends a message, the backfillee will apply it, even if the -session is interrupted (but not if the backfill is interrupted). Once the backfillee -applies all the backfill items and receives the `ack_end_session_mailbox_t`, the session -is over; go back to step 1. - -4b. Alternatively, the backfiller might finish traversing the entire backfill range. When -the backfillee receives a `backfill_item_seq_t` that reaches all the way to the end of -the range, then it sends a message to the `end_session_mailbox_t` on the backfiller, to -which the backfiller responds with a message to the `ack_end_session_mailbox_t`, just -like in the other case. However, this doesn't mean that the backfill is over; the -backfillee may still choose to start another session from earlier in the key-range. - -The pre-item traversal process is much simpler than the item traversal process. The -backfillee sends a series of messages to the `pre_items_mailbox_t` on the backfiller; -these messages form a contiguous series of `backfill_pre_item_t`s in lexicographical -order. The pre items are stored in a queue on the backfiller. As the backfiller traverses -each part of the key space for the first time, it consumes the corresponding pre-items -from the queue, freeing up space in the queue. It notifies the backfillee that there's -space in the pre items queue by sending messages to the `ack_pre_items_mailbox_t` on the -backfillee. A subsequent session might go over the same region of the key-space again; -but in this case, the backfiller doesn't need the pre items for that region of the key- -space, because it knows that the backfillee must have applied the items that it sent when -it traversed that part of the key-space the first time, and therefore there are no -changes present on the backfillee but not the backfiller for that key range. */ -class backfiller_bcard_t { -public: - /* All messages between the backfiller and the backfillee have - `fifo_enforcer_write_token_t`s attached, which are used to keep them in order. - Sometimes this isn't strictly necessary, since sometimes messages can be reordered - without breaking anything; but it makes things simpler. */ - - typedef mailbox_t< - fifo_enforcer_write_token_t, - backfill_item_seq_t - > pre_items_mailbox_t; - - typedef mailbox_t< - fifo_enforcer_write_token_t, - key_range_t::right_bound_t - > begin_session_mailbox_t; - - typedef mailbox_t< - fifo_enforcer_write_token_t - > end_session_mailbox_t; - - typedef mailbox_t< - fifo_enforcer_write_token_t, - /* This `size_t` has the same meaning as the numbers returned by - `backfill_item_t::get_mem_size()`. */ - size_t - > ack_items_mailbox_t; - - class intro_2_t { - public: - /* The backfillee uses this to determine where to send pre-items from. */ - region_map_t common_version; - - /* This is the branch history corresponding to the `version_t`s that will be sent - later. */ - branch_history_t final_version_history; - - /* These mailboxes are used to conduct the actual backfill itself. */ - pre_items_mailbox_t::address_t pre_items_mailbox; - begin_session_mailbox_t::address_t begin_session_mailbox; - end_session_mailbox_t::address_t end_session_mailbox; - ack_items_mailbox_t::address_t ack_items_mailbox; - - /* This is used to determine the backfill priority. */ - uint64_t num_changes_estimate; - - /* This is used to estimate backfill progress. */ - distribution_progress_estimator_t progress_estimator; - }; - - typedef mailbox_t< - fifo_enforcer_write_token_t, - /* The `region_map_t` and the `backfill_item_seq_t` have the same region. */ - region_map_t, - backfill_item_seq_t - > items_mailbox_t; - - typedef mailbox_t< - fifo_enforcer_write_token_t - > ack_end_session_mailbox_t; - - typedef mailbox_t< - fifo_enforcer_write_token_t, - /* This `size_t` has the same meaning as the numbers returned by - `backfill_pre_item_t::get_mem_size()`. */ - size_t - > ack_pre_items_mailbox_t; - - class intro_1_t { - public: - backfill_config_t config; - region_map_t initial_version; - branch_history_t initial_version_history; - mailbox_t::address_t intro_mailbox; - items_mailbox_t::address_t items_mailbox; - ack_end_session_mailbox_t::address_t ack_end_session_mailbox; - ack_pre_items_mailbox_t::address_t ack_pre_items_mailbox; - }; - - /* This `region_t` describes the region that the backfiller applies to. Backfill - requests must cover a subset of this region's key-space, and they must cover exactly - the same part of the hash-space as this region. */ - region_t region; - - registrar_business_card_t registrar; -}; - -RDB_DECLARE_SERIALIZABLE(backfiller_bcard_t::intro_2_t); -RDB_DECLARE_SERIALIZABLE(backfiller_bcard_t::intro_1_t); -RDB_DECLARE_SERIALIZABLE(backfiller_bcard_t); -RDB_DECLARE_EQUALITY_COMPARABLE(backfiller_bcard_t); - -/* The `replica_t` publishes a `replica_bcard_t`, which the `remote_replicator_client_t` -uses to synchronize the backfills with the stream of writes it's getting from the -`primary_dispatcher_t`. So this isn't exactly part of the backfill itself, but it's in -this file for lack of a better place to put it. */ -struct replica_bcard_t { - /* This mailbox is used to ensure that the replica is at least as up to date as the - timestamp. The second argument is used as an ack mailbox; the replica will send a - reply there once it's at least as up to date as the timestamp. */ - typedef mailbox_t< - state_timestamp_t, - mailbox_addr_t<> - > synchronize_mailbox_t; - - synchronize_mailbox_t::address_t synchronize_mailbox; - branch_id_t branch_id; - backfiller_bcard_t backfiller_bcard; -}; - -RDB_DECLARE_SERIALIZABLE(replica_bcard_t); -RDB_DECLARE_EQUALITY_COMPARABLE(replica_bcard_t); - -#endif /* CLUSTERING_IMMEDIATE_CONSISTENCY_BACKFILL_METADATA_HPP_ */ - diff --git a/src/clustering/immediate_consistency/backfill_throttler.hpp b/src/clustering/immediate_consistency/backfill_throttler.hpp deleted file mode 100644 index d41db462ece..00000000000 --- a/src/clustering/immediate_consistency/backfill_throttler.hpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_IMMEDIATE_CONSISTENCY_BACKFILL_THROTTLER_HPP_ -#define CLUSTERING_IMMEDIATE_CONSISTENCY_BACKFILL_THROTTLER_HPP_ - -#include - -#include "concurrency/interruptor.hpp" -#include "concurrency/new_semaphore.hpp" -#include "containers/scoped.hpp" -#include "rpc/connectivity/peer_id.hpp" -#include "threading.hpp" - -/* `backfill_throttler_t` controls which backfills are allowed to run when. It can block -backfills from starting and also preempt already-running backfills. It's abstract to make -unit testing easier; the concrete implementation used in production is always -`standard_backfill_throttler_t`. */ - -class backfill_throttler_t : public home_thread_mixin_t { -public: - class priority_t { - public: - /* Backfills are marked as "critical" if the availability of a table depends on - them. */ - enum class critical_t { NO, YES }; - bool operator<(const priority_t &other) const { - /* Process critical backfills before non-critical backfills */ - if (other.critical == critical_t::YES && critical == critical_t::NO) { - return true; - } else if (other.critical == critical_t::NO && critical == critical_t::YES) { - return false; - } else { - /* If backfills are equally critical, prioritize the one that is likely - to finish faster */ - return num_changes > other.num_changes; - } - } - critical_t critical; - uint64_t num_changes; - }; - - /* Every backfill constructs a `lock_t` before starting. If the lock's preempt signal - is pulsed, the backfill will pause, destroy the `lock_t`, and construct another - `lock_t` before resuming. */ - class lock_t : public home_thread_mixin_t { - public: - lock_t(backfill_throttler_t *p, - const priority_t &_priority, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t) : - priority(_priority), parent(p) - { - parent->enter(this, interruptor); - } - ~lock_t() { - parent->exit(this); - } - signal_t *get_preempt_signal() { - return &preempt_signal; - } - const priority_t priority; - private: - friend class backfill_throttler_t; - backfill_throttler_t *parent; - cond_t preempt_signal; - }; - -protected: - friend class lock_t; - - virtual ~backfill_throttler_t() { } - - virtual void enter(lock_t *lock, signal_t *interruptor) = 0; - virtual void exit(lock_t *lock) = 0; - - /* This allows subclasses to signal locks' preempt signals even though - `preempt_signal` is a private member of `lock_t` */ - void preempt(lock_t *lock) { - lock->preempt_signal.pulse(); - } -}; - -#endif // CLUSTERING_IMMEDIATE_CONSISTENCY_BACKFILL_THROTTLER_HPP_ diff --git a/src/clustering/immediate_consistency/backfillee.cc b/src/clustering/immediate_consistency/backfillee.cc deleted file mode 100644 index 21e63607ba6..00000000000 --- a/src/clustering/immediate_consistency/backfillee.cc +++ /dev/null @@ -1,531 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/immediate_consistency/backfillee.hpp" - -#include "arch/timing.hpp" -#include "assignment_sentry.hpp" -#include "clustering/immediate_consistency/history.hpp" -#include "concurrency/cross_thread_signal.hpp" -#include "concurrency/wait_any.hpp" - -/* `ITEM_ACK_INTERVAL_MS` controls how often we send acknowledgements back to the -backfiller. If it's too short, we'll waste resources sending lots of tiny -acknowledgements; if it's too long, the pipeline might stall. */ -static const int ITEM_ACK_INTERVAL_MS = 100; - -/* `backfillee_t::session_t` contains all the bits and pieces for managing a single -backfill session. It's impossible to have multiple sessions running at once, so in -principle this could have been implemented as some member variables on `backfillee_t`; -but collecting the variables into an object makes it easier to reason about. - -Here's how `backfillee_t` and `session_t` interact: `backfillee_t` creates the -`session_t`. The `session_t` is responsible for sending the begin-session, end-session, -and ack-items messages to the backfiller. Whenever the backfiller sends messages to the -`backfillee_t`'s mailboxes, the `backfillee_t` passes the messages on to the `session_t` -by calling `on_items()` and `on_ack_end_session()`. The `backfillee_t` calls -`wait_done()` in a separate coroutine to wait for the session to finish, then destroys -the session after it returns. If the backfillee is destroyed, then `wait_done()` is -interrupted and the backfillee calls the `session_t` destructor. */ -class backfillee_t::session_t { -public: - session_t( - backfillee_t *_parent, - const key_range_t::right_bound_t &_threshold, - callback_t *_callback) : - parent(_parent), - threshold(_threshold), - callback(_callback), - callback_returned_false(false), - items(_parent->store->get_region().beg, _parent->store->get_region().end, - threshold), - items_mem_size_unacked(0), - sent_end_session(false), - metainfo(region_map_t::empty()), - metainfo_binary(region_map_t::empty()), - pulse_when_items_arrive(nullptr) - { - coro_t::spawn_sometime(std::bind( - &session_t::run, this, drainer.lock())); - } - - ~session_t() { - guarantee(!done_cond.is_pulsed() || items_mem_size_unacked == 0, - "we seem to have leaked some semaphore credits"); - - parent->progress_tracker->is_ready = true; - } - - /* `backfillee_t()` calls these callbacks when it receives messages from the - backfiller via the corresponding mailboxes. */ - void on_items( - region_map_t &&metainfo_chunk, - backfill_item_seq_t &&chunk) { - rassert(metainfo_chunk.get_domain() == chunk.get_region()); - items_mem_size_unacked += chunk.get_mem_size(); - items.concat(std::move(chunk)); - metainfo.extend_keys_right(std::move(metainfo_chunk)); - metainfo_binary = from_version_map(metainfo); - if (pulse_when_items_arrive != nullptr) { - pulse_when_items_arrive->pulse_if_not_already_pulsed(); - } - } - void on_ack_end_session() { - guarantee(sent_end_session); - got_ack_end_session.pulse(); - } - - void wait_done(signal_t *interruptor) { - wait_interruptible(&done_cond, interruptor); - guarantee(threshold == parent->store->get_region().inner.right - || callback_returned_false); - guarantee(got_ack_end_session.is_pulsed()); - } - -private: - /* `run()` runs in a separate coroutine for the duration of the session's existence. - It does the main work of the session: draining backfill items from the `items` queue - and passing them to the store. */ - void run(auto_drainer_t::lock_t keepalive) { - with_priority_t p(CORO_PRIORITY_BACKFILL_RECEIVER); - try { - send(parent->mailbox_manager, parent->intro.begin_session_mailbox, - parent->fifo_source.enter_write(), threshold); - - /* Loop until we reach the end of the backfill range. */ - while (threshold != parent->store->get_region().inner.right) { - /* Wait until we receive some items from the backfiller so we have - something to do, or the session is terminated. */ - while (items.empty_domain()) { - /* Make sure that the backfiller isn't waiting for us to ack more - items, or else we'd wait forever. This also ensures that if we're - ending the session, we'll ack every item instead of leaking semaphore - credits. */ - send_ack_items(); - - /* `send_ack_items()` could block, so we have to check again */ - if (!items.empty_domain()) { - break; - } - - if (got_ack_end_session.is_pulsed()) { - /* The callback returned false, so we sent an end-session message - to the backfiller, and it replied; then we drained the `items` - queue. So this session is over. */ - guarantee(callback_returned_false); - parent->progress_tracker->is_ready = true; - done_cond.pulse(); - return; - } - - /* Wait for more items to arrive */ - cond_t cond; - assignment_sentry_t sentry( - &pulse_when_items_arrive, &cond); - wait_any_t waiter(&cond, &got_ack_end_session); - wait_interruptible(&waiter, keepalive.get_drain_signal()); - } - - /* Set up a `region_t` describing the range that still needs to be - backfilled */ - region_t subregion = parent->store->get_region(); - subregion.inner.left = threshold.key(); - - /* Copy items from `items` into the store until we finish the backfill - range or we run out of items */ - class producer_t : public store_view_t::backfill_item_producer_t { - public: - explicit producer_t(session_t *_parent) : parent(_parent) { - coro_t::spawn_sometime(std::bind( - &producer_t::ack_periodically, this, drainer.lock())); - } - /* `next_item()`, `get_metainfo()`, and `on_commit()` will be called - by `receive_backfill()`. */ - continue_bool_t next_item( - bool *is_item_out, - backfill_item_t *item_out, - key_range_t::right_bound_t *empty_range_out) THROWS_NOTHING { - if (!parent->items.empty_of_items()) { - /* This is the common case. */ - *is_item_out = true; - *item_out = parent->items.front(); - parent->items.pop_front(); - return continue_bool_t::CONTINUE; - } else if (!parent->items.empty_domain()) { - /* There aren't any more items left in the queue, but there's - still a part of the key-space that we know there aren't any - items in yet. We can ask the store to apply the metainfo to - that part of the key-space. */ - *is_item_out = false; - *empty_range_out = parent->items.get_right_key(); - parent->items.delete_to_key(*empty_range_out); - return continue_bool_t::CONTINUE; - } else { - /* We ran out of items. Break out of `receive_backfill()` so - we can block and wait for more items. (We don't want to block - in this callback because the caller might be holding locks in - the B-tree.) */ - return continue_bool_t::ABORT; - } - } - const region_map_t *get_metainfo() THROWS_NOTHING { - return &parent->metainfo_binary; - } - void on_commit(const key_range_t::right_bound_t &new_threshold) - THROWS_NOTHING { - /* `on_commit()` might get called multiple times even after - `on_progress()` returns false, because calling - `send_end_session_message()` sets into motion a long chain of - events that eventually ends with the session being interrupted, - but might take a while. But we want to hide that complexity from - the callack, so we just skip calling `on_progress()` again if it - has returned `false` before. */ - if (!parent->callback_returned_false) { - region_t mask = parent->parent->store->get_region(); - mask.inner.left = parent->threshold.key(); - mask.inner.right = new_threshold; - if (!parent->callback->on_progress( - parent->metainfo.mask(mask))) { - parent->callback_returned_false = true; - parent->send_end_session_message(); - } - - if (!new_threshold.unbounded) { - const distribution_progress_estimator_t &estimator = - parent->parent->intro.progress_estimator; - parent->parent->progress_tracker->progress = - estimator.estimate_progress(new_threshold.key()); - } else { - parent->parent->progress_tracker->progress = 1.0; - } - } - parent->threshold = new_threshold; - } - private: - /* `ack_periodically()` calls `session_t::send_ack_items()` every so - often during the backfill, so that the backfiller will keep sending - us items as they consume them and so ideally the `items` queue won't - ever bottom out before we're done. */ - void ack_periodically(auto_drainer_t::lock_t keepalive2) { - try { - while (true) { - nap(ITEM_ACK_INTERVAL_MS, keepalive2.get_drain_signal()); - parent->send_ack_items(); - } - } catch (const interrupted_exc_t &) { - /* ignore */ - } - } - session_t *parent; - auto_drainer_t drainer; - } producer(this); - - parent->store->receive_backfill( - subregion, &producer, keepalive.get_drain_signal()); - } - /* We reached the end of the range to be backfilled. The callback may or may - not have returned `false` at some point along the way. */ - guarantee(items.empty_domain()); - - /* Make sure that we acknowledged every single item that the backfiller sent - us */ - send_ack_items(); - - if (!callback_returned_false) { - /* Do the handshake to end the session. It's a little bit redundant in - this case (because the backfiller knows that it sent us items all the way - to the end of the range, so we're not giving it any new information here) - but we do it anyway just to be consistent. */ - send_end_session_message(); - } else { - /* We already initiated the end-of-session handshake back when the - callback first returned false. */ - } - - wait_interruptible(&got_ack_end_session, keepalive.get_drain_signal()); - guarantee(items.empty_domain()); - parent->progress_tracker->is_ready = true; - done_cond.pulse(); - - } catch (const interrupted_exc_t &) { - /* The backfillee was destroyed, if the session is restarted `is_ready` will - be set to false again. */ - parent->progress_tracker->is_ready = true; - } - } - - /* `send_ack_items()` lets the backfiller know the total mem size of the items we've - consumed since the last call to `send_ack_items()`, so it knows when it's safe to - send more items. */ - void send_ack_items() { - guarantee(items_mem_size_unacked >= items.get_mem_size()); - size_t diff = items_mem_size_unacked - items.get_mem_size(); - if (diff != 0) { - items_mem_size_unacked -= diff; - send(parent->mailbox_manager, parent->intro.ack_items_mailbox, - parent->fifo_source.enter_write(), diff); - } - } - - void send_end_session_message() { - guarantee(!sent_end_session); - sent_end_session = true; - send(parent->mailbox_manager, parent->intro.end_session_mailbox, - parent->fifo_source.enter_write()); - } - - backfillee_t *const parent; - - /* `threshold` describes the current location that this session has reached. It - starts at the threshold that was passed to `go()` and proceeds to the right. */ - key_range_t::right_bound_t threshold; - - callback_t *const callback; - bool callback_returned_false; - - /* `items` is a queue containing all of the backfill items that we've received from - the backfiller during this session. Incoming items are appended to the right-hand - side; we consume items from the left-hand side and apply them to the B-tree. */ - backfill_item_seq_t items; - - /* `items_mem_size_unacked` describes what the backfiller thinks the total mem size - of `items` is. `send_ack_items()` uses this to calculate what to send. */ - size_t items_mem_size_unacked; - - /* `sent_end_session` is true if we've sent a message to the backfiller to end the - session. `got_ack_end_session` is pulsed if we got an acknowledgement, so that no - more items can possible be added to our queue. */ - bool sent_end_session; - cond_t got_ack_end_session; - - /* `metainfo` contains all the metainfo we've received from the backfiller. It always - extends from wherever we started the backfill to the right-hand side of `items`. */ - region_map_t metainfo; - - /* `metainfo_binary` is just `metainfo` in `binary_blob_t` form. */ - region_map_t metainfo_binary; - - /* `run()` puts a `cond_t` here when it needs to wait for more backfill items to be - appended to `items`. `on_items()` pulses it when it appends more backfill items to - `items`. */ - cond_t *pulse_when_items_arrive; - - /* `run()` pulses this when the session is completely over */ - cond_t done_cond; - - /* `drainer` must be destroyed before anything else, because it stops `run()`. */ - auto_drainer_t drainer; -}; - -backfillee_t::backfillee_t( - mailbox_manager_t *_mailbox_manager, - branch_history_manager_t *_branch_history_manager, - store_view_t *_store, - const backfiller_bcard_t &backfiller, - const backfill_config_t &_backfill_config, - backfill_progress_tracker_t::progress_tracker_t *_progress_tracker, - signal_t *interruptor) : - mailbox_manager(_mailbox_manager), - branch_history_manager(_branch_history_manager), - store(_store), - backfill_config(_backfill_config), - progress_tracker(_progress_tracker), - pre_item_throttler(backfill_config.pre_item_queue_mem_size), - pre_item_throttler_acq(&pre_item_throttler, 0), - current_session(nullptr), - session_interrupted(false), - items_mailbox(mailbox_manager, - std::bind(&backfillee_t::on_items, this, ph::_1, ph::_2, ph::_3, ph::_4)), - ack_end_session_mailbox(mailbox_manager, - std::bind(&backfillee_t::on_ack_end_session, this, ph::_1, ph::_2)), - ack_pre_items_mailbox(mailbox_manager, - std::bind(&backfillee_t::on_ack_pre_items, this, ph::_1, ph::_2, ph::_3)) -{ - guarantee(region_is_superset(backfiller.region, store->get_region())); - guarantee(store->get_region().beg == backfiller.region.beg); - guarantee(store->get_region().end == backfiller.region.end); - - backfiller_bcard_t::intro_1_t our_intro; - our_intro.config = backfill_config; - our_intro.ack_pre_items_mailbox = ack_pre_items_mailbox.get_address(); - our_intro.items_mailbox = items_mailbox.get_address(); - our_intro.ack_end_session_mailbox = ack_end_session_mailbox.get_address(); - - /* Fetch the `initial_version` and `initial_version_history` fields for the - `intro_1_t` that we'll send to the backfiller */ - { - read_token_t read_token; - store->new_read_token(&read_token); - our_intro.initial_version = to_version_map(store->get_metainfo( - order_token_t::ignore.with_read_mode(), &read_token, store->get_region(), - interruptor)); - } - { - on_thread_t thread_switcher(branch_history_manager->home_thread()); - branch_history_manager->export_branch_history( - our_intro.initial_version, - &our_intro.initial_version_history); - } - - /* Set up a mailbox to receive the `intro_2_t` from the backfiller */ - cond_t got_intro; - mailbox_t intro_mailbox( - mailbox_manager, - [&](signal_t *, const backfiller_bcard_t::intro_2_t &i) { - intro = i; - got_intro.pulse(); - }); - our_intro.intro_mailbox = intro_mailbox.get_address(); - - /* Send the `intro_1_t` to the backfiller and wait for it to send back the - `intro_2_t` */ - registrant.init(new registrant_t( - mailbox_manager, backfiller.registrar, our_intro)); - wait_interruptible(&got_intro, interruptor); - - /* Record the branch history we got from the backfiller */ - { - on_thread_t thread_switcher(branch_history_manager->home_thread()); - branch_history_manager->import_branch_history(intro.final_version_history); - } - - /* Spawn the coroutine that will stream pre-items to the backfiller. */ - coro_t::spawn_sometime( - std::bind(&backfillee_t::send_pre_items, this, drainer.lock())); -} - -backfillee_t::~backfillee_t() { - /* This destructor is declared in the `.cc` file because we need to have the full - definition of `session_t` in scope for the `scoped_ptr_t` to work. */ -} - -uint64_t backfillee_t::get_num_changes_estimate() { - return intro.num_changes_estimate; -} - -void backfillee_t::go( - callback_t *callback, - const key_range_t::right_bound_t &threshold, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t) { - guarantee(current_session == nullptr); - guarantee(!session_interrupted); - session_t session(this, threshold, callback); - current_session = &session; - try { - session.wait_done(interruptor); - current_session = nullptr; - } catch (const interrupted_exc_t &) { - current_session = nullptr; - session_interrupted = true; - throw; - } -} - -void backfillee_t::on_items( - signal_t *interruptor, - const fifo_enforcer_write_token_t &fifo_token, - region_map_t &&version, - backfill_item_seq_t &&chunk) { - fifo_enforcer_sink_t::exit_write_t exit_write(&fifo_sink, fifo_token); - wait_interruptible(&exit_write, interruptor); - if (session_interrupted) { - return; - } - guarantee(current_session != nullptr); - current_session->on_items(std::move(version), std::move(chunk)); -} - -void backfillee_t::on_ack_end_session( - signal_t *interruptor, - const fifo_enforcer_write_token_t &fifo_token) { - fifo_enforcer_sink_t::exit_write_t exit_write(&fifo_sink, fifo_token); - wait_interruptible(&exit_write, interruptor); - if (session_interrupted) { - return; - } - guarantee(current_session != nullptr); - current_session->on_ack_end_session(); -} - -void backfillee_t::send_pre_items(auto_drainer_t::lock_t keepalive) { - with_priority_t p(CORO_PRIORITY_BACKFILL_RECEIVER); - try { - key_range_t::right_bound_t pre_item_sent_threshold( - store->get_region().inner.left); - while (pre_item_sent_threshold != store->get_region().inner.right) { - /* Wait until there's room in the semaphore for the chunk we're about to - process */ - new_semaphore_in_line_t sem_acq( - &pre_item_throttler, backfill_config.pre_item_chunk_mem_size); - wait_interruptible( - sem_acq.acquisition_signal(), keepalive.get_drain_signal()); - - /* Set up a `region_t` describing the range that still needs to be - backfilled */ - region_t subregion = store->get_region(); - subregion.inner.left = pre_item_sent_threshold.key(); - - /* Copy pre-items from the store into `chunk ` until the total size hits - `backfill_config.pre_item_chunk_mem_size` or we finish the range */ - backfill_item_seq_t chunk( - store->get_region().beg, store->get_region().end, - pre_item_sent_threshold); - - class consumer_t : public store_view_t::backfill_pre_item_consumer_t { - public: - consumer_t( - backfill_item_seq_t *_chunk, - const backfill_config_t *_config) : - chunk(_chunk), config(_config) { } - continue_bool_t on_pre_item(backfill_pre_item_t &&item) THROWS_NOTHING { - chunk->push_back(std::move(item)); - if (chunk->get_mem_size() < config->pre_item_chunk_mem_size) { - return continue_bool_t::CONTINUE; - } else { - return continue_bool_t::ABORT; - } - } - continue_bool_t on_empty_range( - const key_range_t::right_bound_t &threshold) THROWS_NOTHING { - chunk->push_back_nothing(threshold); - return continue_bool_t::CONTINUE; - } - backfill_item_seq_t *chunk; - backfill_config_t const *const config; - } callback(&chunk, &backfill_config); - - store->send_backfill_pre(intro.common_version.mask(subregion), &callback, - keepalive.get_drain_signal()); - - /* Adjust for the fact that `chunk.get_mem_size()` isn't precisely equal to - `pre_item_chunk_mem_size`, and then transfer the semaphore ownership. */ - sem_acq.change_count(chunk.get_mem_size()); - pre_item_throttler_acq.transfer_in(std::move(sem_acq)); - - /* Send the chunk over the network */ - send(mailbox_manager, intro.pre_items_mailbox, - fifo_source.enter_write(), chunk); - - /* Update `progress` */ - guarantee(chunk.get_left_key() == pre_item_sent_threshold); - pre_item_sent_threshold = chunk.get_right_key(); - } - } catch (const interrupted_exc_t &) { - /* The `backfillee_t` was deleted; the backfill is being aborted. */ - } -} - -void backfillee_t::on_ack_pre_items( - signal_t *interruptor, - const fifo_enforcer_write_token_t &fifo_token, - size_t mem_size) { - /* Ordering of these messages is actually irrelevant; we just pass through the fifo - sink for consistency */ - fifo_enforcer_sink_t::exit_write_t exit_write(&fifo_sink, fifo_token); - wait_interruptible(&exit_write, interruptor); - - /* Shrink `pre_item_throttler_acq` so that `send_pre_items()` can acquire the - semaphore again. */ - guarantee(static_cast(mem_size) <= pre_item_throttler_acq.count()); - pre_item_throttler_acq.change_count(pre_item_throttler_acq.count() - mem_size); -} - diff --git a/src/clustering/immediate_consistency/backfillee.hpp b/src/clustering/immediate_consistency/backfillee.hpp deleted file mode 100644 index 6f91626708f..00000000000 --- a/src/clustering/immediate_consistency/backfillee.hpp +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_IMMEDIATE_CONSISTENCY_BACKFILLEE_HPP_ -#define CLUSTERING_IMMEDIATE_CONSISTENCY_BACKFILLEE_HPP_ - -#include "clustering/generic/registrant.hpp" -#include "clustering/immediate_consistency/history.hpp" -#include "clustering/immediate_consistency/backfill_metadata.hpp" -#include "clustering/table_manager/backfill_progress_tracker.hpp" -#include "concurrency/new_mutex.hpp" -#include "rpc/connectivity/peer_id.hpp" -#include "store_view.hpp" - -/* `backfillee_t` is responsible for replicating data from a `backfiller_t` on some other -server to this server. Its interface is a bit complicated because the -`remote_replicator_client_t` needs to start applying writes before the backfill is -finished, so it needs to know when the `backfillee_t` reaches a certain point and also to -change the `backfillee_t`'s minimum version during the backfill. Here are the details: - -1. When `backfillee_t` is constructed, it takes ownership of the entire region covered by -`store`. Nothing else is allowed to write to this region. - -2. Call `go()` to start the backfill. - -3. The backfill will proceed from left to right in lexicographical key order. - -4. The state that is backfilled is guaranteed to be at least as up-to-date as the state -that the other server had at the time that `go()` was called. - -5. Whenever the backfill finishes processing a range of keys, it calls `on_progress()` on -the callback, with a `region_t` representing that range. - -6. If `on_progress()` returns `true`, then all keys in that region are "released" by the -`backfillee_t`, meaning that it will not write to that region, and other things are now -allowed to write to that region. If `on_progress()` returns `false`, then the backfiller -retains ownership of that region of keys. - -7. The backfill continues until `on_progress()` returns `false` or the backfill is -completed. If the backfill is aborted early by returning `false`, the caller can call -`go()` again to resume the backfill. The effect of this is to reset the minimum state -that the backfill is guaranteed to go to; any keys backfilled during the new call to -`go()` are guaranteed to be at least as up-to-date as the other server's state when the -new call to `go()` happens. */ - -class backfillee_t : public home_thread_mixin_debug_only_t { -private: - /* `session_t` represents a session within this backfill; the definition is hidden to - keep this header file simple. */ - class session_t; - -public: - class callback_t { - public: - virtual bool on_progress( - const region_map_t &chunk) THROWS_NOTHING = 0; - protected: - virtual ~callback_t() { } - }; - - /* `backfillee_t()` blocks while it establishes a connection with the `backfiller_t` - and does some other setup work. It doesn't block during the main duration of the - backfill. The region to be backfilled will be `_store->get_region()`. */ - backfillee_t( - mailbox_manager_t *_mailbox_manager, - branch_history_manager_t *_branch_history_manager, - store_view_t *_store, - const backfiller_bcard_t &backfiller, - const backfill_config_t &backfill_config, - backfill_progress_tracker_t::progress_tracker_t *progress_tracker, - signal_t *interruptor); - ~backfillee_t(); - - /* Returns an estimate of the number of keys that will need to be retransmitted - during this backfill. */ - uint64_t get_num_changes_estimate(); - - /* Begins a backfill session. All keys from `start_point` onward will be - re-backfilled. For the first call, `start_point` must be the left-hand side of the - backfiller's region; for subsequent calls, `start_point` must be between the last - point for which `on_progress()` returned `true` and the last point for which - `on_progress()` returned `false`. - - Pulsing `interruptor` invalidates the `backfillee_t`. - - Durability guarantees: The data is not necessarily safely on disk when - `on_progress()` is called, but it will be safely on disk by the time `go()` returns. - */ - void go( - callback_t *callback, - const key_range_t::right_bound_t &start_point, - signal_t *interruptor) THROWS_ONLY(interrupted_exc_t); - -private: - /* `on_items()`, `on_ack_end_session()`, and `on_ack_pre_items()` are mailbox - callbacks. */ - - void on_items( - signal_t *interruptor, - const fifo_enforcer_write_token_t &fifo_token, - region_map_t &&version, - backfill_item_seq_t &&chunk); - - void on_ack_end_session( - signal_t *interruptor, - const fifo_enforcer_write_token_t &fifo_token); - - /* `send_pre_items()` is spawned by the `backfillee_t` constructor in a separate - coroutine. It's responsible for traversing the B-tree and sending pre items to the - backfiller. */ - void send_pre_items( - auto_drainer_t::lock_t keepalive); - - void on_ack_pre_items( - signal_t *interruptor, - const fifo_enforcer_write_token_t &fifo_token, - size_t chunk_size); - - mailbox_manager_t *const mailbox_manager; - branch_history_manager_t *const branch_history_manager; - store_view_t *const store; - backfill_config_t const backfill_config; - backfill_progress_tracker_t::progress_tracker_t *const progress_tracker; - - backfiller_bcard_t::intro_2_t intro; - - /* `fifo_source` is used to attach order tokens to the messages we send to the - backfiller. `fifo_sink` is used to interpret the order tokens on messages we receive - from the backfiller. */ - fifo_enforcer_source_t fifo_source; - fifo_enforcer_sink_t fifo_sink; - - /* `pre_item_throttler` limits the total mem size of the pre-items that are allowed - to queue up on the backfiller. `pre_item_throttler_acq` always holds - `pre_item_throttler`, but its `count` changes to reflect the total mem size that's - currently in the queue. */ - new_semaphore_t pre_item_throttler; - new_semaphore_in_line_t pre_item_throttler_acq; - - /* Between sessions, `current_session` is `nullptr` and `session_interrupted` is - `false`. During a session, `current_session` is valid and `session_interrupted` is - `false`. If the last session was interrupted, `current_session` is `nullptr` and - `session_interrupted` is `true`. */ - session_t *current_session; - bool session_interrupted; - - /* Destructor order matters here; `drainer` and `*_mailbox` must be destroyed before - the other members of `backfillee_t` because they can spawn coroutines that access the - other members. It's not strictly necessary to destroy `registrant` before destroying - the other things, but it's nice because that way the backfiller won't waste time - sending messages to the mailboxes as they're being destroyed. */ - auto_drainer_t drainer; - backfiller_bcard_t::items_mailbox_t items_mailbox; - backfiller_bcard_t::ack_end_session_mailbox_t ack_end_session_mailbox; - backfiller_bcard_t::ack_pre_items_mailbox_t ack_pre_items_mailbox; - scoped_ptr_t > registrant; -}; - -#endif /* CLUSTERING_IMMEDIATE_CONSISTENCY_BACKFILLEE_HPP_ */ diff --git a/src/clustering/immediate_consistency/backfiller.cc b/src/clustering/immediate_consistency/backfiller.cc deleted file mode 100644 index 198e3cde53e..00000000000 --- a/src/clustering/immediate_consistency/backfiller.cc +++ /dev/null @@ -1,500 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/immediate_consistency/backfiller.hpp" - -#include "assignment_sentry.hpp" -#include "clustering/immediate_consistency/history.hpp" -#include "rdb_protocol/distribution_progress.hpp" -#include "rdb_protocol/protocol.hpp" -#include "store_view.hpp" - -backfiller_t::backfiller_t( - mailbox_manager_t *_mailbox_manager, - branch_history_manager_t *_branch_history_manager, - store_view_t *_store) : - mailbox_manager(_mailbox_manager), - branch_history_manager(_branch_history_manager), - store(_store), - registrar(mailbox_manager, this) - { } - -backfiller_t::client_t::client_t( - backfiller_t *_parent, - const backfiller_bcard_t::intro_1_t &_intro, - signal_t *interruptor) : - parent(_parent), - intro(_intro), - full_region(intro.initial_version.get_domain()), - pre_items(full_region.beg, full_region.end, - key_range_t::right_bound_t(full_region.inner.left)), - item_throttler(intro.config.item_queue_mem_size), - item_throttler_acq(&item_throttler, 0), - pre_items_mailbox(parent->mailbox_manager, - std::bind(&client_t::on_pre_items, this, ph::_1, ph::_2, ph::_3)), - begin_session_mailbox(parent->mailbox_manager, - std::bind(&client_t::on_begin_session, this, ph::_1, ph::_2, ph::_3)), - end_session_mailbox(parent->mailbox_manager, - std::bind(&client_t::on_end_session, this, ph::_1, ph::_2)), - ack_items_mailbox(parent->mailbox_manager, - std::bind(&client_t::on_ack_items, this, ph::_1, ph::_2, ph::_3)) -{ - /* Fetch our current state from the superblock metainfo */ - region_map_t our_version; - { - read_token_t read_token; - parent->store->new_read_token(&read_token); - our_version = to_version_map(parent->store->get_metainfo( - order_token_t::ignore.with_read_mode(), &read_token, - parent->store->get_region(), interruptor)); - } - - /* Compute the common ancestor of `intro.initial_version` and `our_version`, storing - it in `common_version`. And while we're on the branch history manager's thread, - retrieve the branch history for `our_version`. */ - branch_history_t our_version_history; - { - on_thread_t thread_switcher(parent->branch_history_manager->home_thread()); - - branch_history_combiner_t combined_history( - parent->branch_history_manager, - &intro.initial_version_history); - - common_version = intro.initial_version.map_multi( - intro.initial_version.get_domain(), - [&](const region_t ®ion1, const version_t &version1) { - return our_version.map_multi(region1, - [&](const region_t ®ion2, const version_t &version2) { - try { - return version_find_common( - &combined_history, version1, version2, region2) - .map(region2, - [](const version_t &v) { return v.timestamp; }); - } catch (const missing_branch_exc_t &) { - /* If we don't have enough information to determine the - common ancestor of the two versions, act as if it's zero. - This will always produce correct results but it might make us - do more backfilling than necessary. */ - return region_map_t( - region2, state_timestamp_t::zero()); - } - }); - }); - - /* Also copy out the current value of the branch history. We only send the branch - history once at the beginning of the backfill; this is OK because the underlying - store isn't allowed to transition to a new branch while the `backfiller_t` - exists. */ - parent->branch_history_manager->export_branch_history( - our_version, &our_version_history); - } - - /* Fetch the key distribution from the store, this is used by the backfillee to - calculate the progress of backfill jobs. */ - distribution_progress_estimator_t progress_estimator(parent->store, interruptor); - - /* Estimate the total number of changes that will need to be backfilled, by comparing - `our_version`, `intro.initial_version`, and `common_version`. We estimate the number - of changes as the largest version difference. In theory we could be smarter by - cross-referencing with `distribution_counts`, but it's not worth the trouble for now. - */ - uint64_t num_changes_estimate = 0; - our_version.visit(full_region, [&](const region_t &r1, const version_t &v1) { - intro.initial_version.visit(r1, [&](const region_t &r2, const version_t &v2) { - common_version.visit(r2, [&](const region_t &, const state_timestamp_t &b) { - guarantee(v1.timestamp >= b); - guarantee(v2.timestamp >= b); - uint64_t backfiller_changes = v1.timestamp.count_changes(b); - uint64_t backfillee_changes = v2.timestamp.count_changes(b); - uint64_t total_changes = backfiller_changes + backfillee_changes; - num_changes_estimate = std::max(num_changes_estimate, total_changes); - }); - }); - }); - - /* Send the computed common ancestor to the backfillee, along with the mailboxes it - can use to contact us. */ - backfiller_bcard_t::intro_2_t our_intro; - our_intro.common_version = common_version; - our_intro.final_version_history = std::move(our_version_history); - our_intro.pre_items_mailbox = pre_items_mailbox.get_address(); - our_intro.begin_session_mailbox = begin_session_mailbox.get_address(); - our_intro.end_session_mailbox = end_session_mailbox.get_address(); - our_intro.ack_items_mailbox = ack_items_mailbox.get_address(); - our_intro.num_changes_estimate = num_changes_estimate; - our_intro.progress_estimator = std::move(progress_estimator); - send(parent->mailbox_manager, intro.intro_mailbox, our_intro); -} - -/* `item_seq_pre_item_producer_t` is a `backfill_pre_item_producer_t` that reads from a -`backfill_item_seq_t`. It keeps track of its position in the seq by -moving the elements of the seq to an internal seq; it restores the seq to its original -state when it goes out of scope. */ -class item_seq_pre_item_producer_t : public store_view_t::backfill_pre_item_producer_t { -public: - item_seq_pre_item_producer_t( - backfill_item_seq_t *_pi, - const key_range_t::right_bound_t &_c) : - pre_items(_pi), - temp_buf(_pi->get_beg_hash(), _pi->get_end_hash(), _pi->get_left_key()), - last_cursor(_c) - { - guarantee(pre_items->get_left_key() <= last_cursor); - guarantee(pre_items->get_right_key() >= last_cursor); - guarantee(pre_items->empty_of_items() - || pre_items->front().range.right > last_cursor, - "We're trying to start iteration after the first pre-item. This probably " - "indicates that we skipped backfilling part of the key-space."); - } - ~item_seq_pre_item_producer_t() { - temp_buf.concat(std::move(*pre_items)); - *pre_items = std::move(temp_buf); - } - continue_bool_t consume_range( - key_range_t::right_bound_t *cursor_inout, - const key_range_t::right_bound_t &limit, - const std::function &callback) { - guarantee(last_cursor == *cursor_inout); - if (*cursor_inout == pre_items->get_right_key()) { - return continue_bool_t::ABORT; - } - rassert(pre_items->empty_of_items() - || pre_items->front().range.right >= *cursor_inout); - while (true) { - if (pre_items->empty_of_items()) { - *cursor_inout = std::min(limit, pre_items->get_right_key()); - break; - } - if (key_range_t::right_bound_t(pre_items->front().range.left) < limit) { - callback(pre_items->front()); - } else { - *cursor_inout = limit; - break; - } - if (pre_items->front().range.right <= limit) { - pre_items->pop_front_into(&temp_buf); - } else { - *cursor_inout = limit; - break; - } - } - rassert(*cursor_inout <= pre_items->get_right_key()); - rassert(last_cursor < *cursor_inout); - last_cursor = *cursor_inout; - return continue_bool_t::CONTINUE; - } - bool try_consume_empty_range(const key_range_t &range) { - guarantee(key_range_t::right_bound_t(range.left) == last_cursor); - if (pre_items->get_right_key() >= range.right && - (pre_items->empty_of_items() || - key_range_t::right_bound_t(pre_items->front().range.left) - >= range.right)) { - last_cursor = range.right; - return true; - } else { - return false; - } - } - void rewind(const key_range_t::right_bound_t &point) { - guarantee(last_cursor >= point); - last_cursor = point; - while (!temp_buf.empty_of_items() && temp_buf.back().range.right > point) { - temp_buf.pop_back_into(pre_items); - } - } -private: - /* `pre_items` is the sequence we're reading from */ - backfill_item_seq_t *pre_items; - - /* Temporary storage for pre-items that have been consumed */ - backfill_item_seq_t temp_buf; - - /* Our current position in the sequence, used to sanity-check the method calls we're - getting. */ - key_range_t::right_bound_t last_cursor; -}; - -/* When the `client_t` receives a begin-session message from the backfillee, it creates a -`session_t`. The `session_t` is responsible for sending items to the backfillee. When the -session is over, the backfillee will send an end-session message to the `client_t`, which -will destroy the `session_t` and then send an ack-end-session message back to the -backfillee. */ -class backfiller_t::client_t::session_t { -public: - session_t(client_t *_parent, const key_range_t::right_bound_t &_threshold) : - parent(_parent), threshold(_threshold), pulse_when_pre_items_arrive(nullptr) - { - guarantee(parent->pre_items.empty_of_items() || - key_range_t::right_bound_t(parent->pre_items.front().range.left) - >= threshold, - "Every key must be backfilled at least once; it's not OK to start a new " - "session after the end of the furthest-right previous session"); - coro_t::spawn_sometime(std::bind(&session_t::run, this, drainer.lock())); - } - - /* Every time the `client_t` receives more pre-items from the backfillee, it calls - `on_pre_items()` to notify us. */ - void on_pre_items() { - if (pulse_when_pre_items_arrive != nullptr) { - pulse_when_pre_items_arrive->pulse_if_not_already_pulsed(); - } - } - -private: - void run(auto_drainer_t::lock_t keepalive) { - with_priority_t p(CORO_PRIORITY_BACKFILL_SENDER); - try { - while (threshold != parent->full_region.inner.right) { - /* Wait until there's room in the semaphore for the chunk we're about to - process. - We acquire the maximum size that we want to put in this chunk first, - and then adjust the semaphore acquisition to the actual size of the - chunk later. */ - new_semaphore_in_line_t sem_acq( - &parent->item_throttler, parent->intro.config.item_chunk_mem_size); - wait_interruptible( - sem_acq.acquisition_signal(), keepalive.get_drain_signal()); - - /* Wait until we have some pre items, or else we won't be able to make - any progress on the backfill */ - while (parent->pre_items.get_right_key() == threshold) { - cond_t cond; - assignment_sentry_t sentry( - &pulse_when_pre_items_arrive, &cond); - wait_interruptible(&cond, keepalive.get_drain_signal()); - } - - /* Set up a `region_t` describing the range that still needs to be - backfilled */ - region_t subregion = parent->full_region; - subregion.inner.left = threshold.key(); - - /* Copy items from the store into `chunk` until the total size hits - `item_chunk_mem_size`; we finish the backfill range; or we run out of - pre-items. */ - - backfill_item_seq_t chunk( - parent->full_region.beg, parent->full_region.end, - threshold); - region_map_t metainfo = region_map_t::empty(); - - { - /* `producer` will pop items off of `parent->pre_items` as - `send_backfill()` requests them, but it will restore them all when it - goes out of scope. Restoring them is important because when we - interrupt `send_backfill()`, it might not yet have produced items - corresponding to all of the pre-items it consumed, so those pre-items - might be needed again in a later call to `send_backfill()`. It's a - little sketchy that `producer` modifies `pre_items` like this, but - it's safe because only one `session_t` can exist at once and because - `client_t::on_pre_items` only touches the right-hand end of - `parent->pre_items`. */ - item_seq_pre_item_producer_t producer( - &parent->pre_items, threshold); - - /* `consumer_t` is responsible for receiving backfill items and - the corresponding metainfo from `send_backfill()` and storing them in - `chunk` and in `metainfo`. */ - class consumer_t : public store_view_t::backfill_item_consumer_t { - public: - consumer_t( - backfill_item_seq_t *_chunk, - region_map_t *_metainfo) : - chunk(_chunk), metainfo(_metainfo) { - guarantee(chunk->get_mem_size() == 0); - } - void on_item( - const region_map_t &item_metainfo, - backfill_item_t &&item) THROWS_NOTHING { - rassert(key_range_t::right_bound_t(item.range.left) >= - chunk->get_right_key()); - rassert(!item.range.is_empty()); - on_metainfo(item_metainfo, item.range.right); - chunk->push_back(std::move(item)); - } - void on_empty_range( - const region_map_t &range_metainfo, - const key_range_t::right_bound_t &new_threshold) - THROWS_NOTHING { - rassert(new_threshold >= chunk->get_right_key()); - on_metainfo(range_metainfo, new_threshold); - chunk->push_back_nothing(new_threshold); - } - private: - void on_metainfo( - const region_map_t &new_metainfo, - const key_range_t::right_bound_t &new_threshold) { - if (new_threshold == chunk->get_right_key()) { - /* This is a no-op. But if `chunk->get_right_key()` is - already unbounded, calling `key()` on it will crash. So - the normal code path might break on certain no-ops and we - should just return instead. */ - return; - } - region_t mask; - mask.beg = chunk->get_beg_hash(); - mask.end = chunk->get_end_hash(); - mask.inner.left = chunk->get_right_key().key(); - mask.inner.right = new_threshold; - metainfo->extend_keys_right( - to_version_map(new_metainfo.mask(mask))); - } - backfill_item_seq_t *const chunk; - region_map_t *const metainfo; - } consumer(&chunk, &metainfo); - - backfill_item_memory_tracker_t memory_tracker( - parent->intro.config.item_chunk_mem_size); - - parent->parent->store->send_backfill( - parent->common_version.mask(subregion), - &producer, &consumer, &memory_tracker, - keepalive.get_drain_signal()); - - /* `producer` goes out of scope here, so it restores `pre_items` to - what it was before */ - } - - /* Check if we actually got a non-empty chunk; if we got an empty chunk - there's no point in sending it over the network. Note that we use - `empty_domain()` instead of `empty_of_items()`, because the knowledge - that there are no backfill items in the given range is still very useful - information for the backfillee to have. */ - if (!chunk.empty_domain()) { - /* Adjust for the fact that `chunk.get_mem_size()` isn't precisely - equal to `item_chunk_mem_size`, and then transfer the semaphore - ownership. */ - sem_acq.change_count(chunk.get_mem_size()); - parent->item_throttler_acq.transfer_in(std::move(sem_acq)); - - /* Update `threshold` */ - guarantee(chunk.get_left_key() == threshold); - threshold = chunk.get_right_key(); - - /* Note: It's essential that we update `common_version` and - `pre_items` if and only if we send the chunk over the network. So - we shouldn't e.g. check the interruptor in between. This is because - we want to make sure that `common_version` and `pre_items` accurately - represent the state of the backfillee after it applies all the chunks - we've sent. */ - try { - /* Send the chunk over the network */ - send(parent->parent->mailbox_manager, - parent->intro.items_mailbox, - parent->fifo_source.enter_write(), metainfo, chunk); - - /* Update `common_version` to reflect the changes that will - happen on the backfillee in response to the chunk */ - parent->common_version.update( - metainfo.map(metainfo.get_domain(), - [](const version_t &v) { return v.timestamp; })); - - /* Discard pre-items we don't need anymore. This has two - purposes: it saves memory, and it keeps `pre_items` consistent - with `common_version`. However, we note that the domain of the - `pre_items` seq still starts at the left-hand end of the range to - be backfilled, representing that there are no pre items there. - This is correct because after the backfillee applies the item we - just sent, it won't have any divergent data relative to us in - that region. */ - size_t old_size = parent->pre_items.get_mem_size(); - parent->pre_items.delete_to_key(threshold); - parent->pre_items.push_front_nothing( - key_range_t::right_bound_t(parent->full_region.inner.left)); - size_t new_size = parent->pre_items.get_mem_size(); - - /* Notify the backfiller that it's OK to send us more pre items. - - Note that the way we acknowledge pre-items is different from how - the backfillee acknowledges items; the backfillee acknowledges - items periodically during the call to `receive_backfill()`, but - we wait until after `send_backfill()` returns to acknowledge the - pre-items. This way of doing things is simpler; the reason we - can't do things the same way in the backfillee is because the - call to `receive_backfill()` might run for a long time, but our - call to `send_backfill()` will only go until it fills up the - chunk. */ - if (old_size != new_size) { - send(parent->parent->mailbox_manager, - parent->intro.ack_pre_items_mailbox, - parent->fifo_source.enter_write(), old_size - new_size); - } - } catch (const interrupted_exc_t &) { - /* This is just a sanity check in case someone unthinkingly makes - something interruptible in the above code block */ - crash("We shouldn't be interrupted during this block"); - } - } - } - } catch (const interrupted_exc_t &) { - /* The backfillee sent us a stop message; or the backfillee was destroyed; or - the backfiller was destroyed. */ - } - } - - client_t *const parent; - - /* This is the current location we've backfilled up to. Initially it's set to the - point that the backfillee sent us with the begin-session message, and it moves right - from there. */ - key_range_t::right_bound_t threshold; - - /* This exists if we're waiting for more pre items. `on_pre_items()` pulses it if it - exists. */ - cond_t *pulse_when_pre_items_arrive; - - /* Destructor order matters here: `drainer` must be destroyed before the other member - variables because `drainer` stops `run()`, which accesses the other member - variables. */ - auto_drainer_t drainer; -}; - -void backfiller_t::client_t::on_begin_session( - signal_t *interruptor, - const fifo_enforcer_write_token_t &write_token, - const key_range_t::right_bound_t &threshold) { - fifo_enforcer_sink_t::exit_write_t exit_write(&fifo_sink, write_token); - wait_interruptible(&exit_write, interruptor); - - guarantee(!current_session.has()); - current_session.init(new session_t(this, threshold)); -} - -void backfiller_t::client_t::on_end_session( - signal_t *interruptor, - const fifo_enforcer_write_token_t &write_token) { - fifo_enforcer_sink_t::exit_write_t exit_write(&fifo_sink, write_token); - wait_interruptible(&exit_write, interruptor); - - guarantee(current_session.has()); - current_session.reset(); - - /* `session_t`'s destructor won't return until it's done sending items over the - network, so we can be sure that the ack-end-session message comes after all of the - items that were part of the session. */ - send(parent->mailbox_manager, intro.ack_end_session_mailbox, - fifo_source.enter_write()); -} - -void backfiller_t::client_t::on_ack_items( - signal_t *interruptor, - const fifo_enforcer_write_token_t &write_token, - size_t mem_size) { - fifo_enforcer_sink_t::exit_write_t exit_write(&fifo_sink, write_token); - wait_interruptible(&exit_write, interruptor); - - guarantee(static_cast(mem_size) <= item_throttler_acq.count()); - item_throttler_acq.change_count(item_throttler_acq.count() - mem_size); -} - -void backfiller_t::client_t::on_pre_items( - signal_t *interruptor, - const fifo_enforcer_write_token_t &write_token, - backfill_item_seq_t &&chunk) { - fifo_enforcer_sink_t::exit_write_t exit_write(&fifo_sink, write_token); - wait_interruptible(&exit_write, interruptor); - - pre_items.concat(std::move(chunk)); - if (current_session.has()) { - current_session->on_pre_items(); - } -} - diff --git a/src/clustering/immediate_consistency/backfiller.hpp b/src/clustering/immediate_consistency/backfiller.hpp deleted file mode 100644 index dcb7297d527..00000000000 --- a/src/clustering/immediate_consistency/backfiller.hpp +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_IMMEDIATE_CONSISTENCY_BACKFILLER_HPP_ -#define CLUSTERING_IMMEDIATE_CONSISTENCY_BACKFILLER_HPP_ - -#include -#include - -#include "clustering/generic/registrar.hpp" -#include "clustering/immediate_consistency/history.hpp" -#include "clustering/immediate_consistency/backfill_metadata.hpp" -#include "concurrency/new_semaphore.hpp" -#include "store_view.hpp" - -class backfill_progress_tracker_t; - -/* `backfiller_t` is responsible for copying the given store's state to other servers via -`backfillee_t`. - -It assumes that if the state of the underlying store changes, the only change will be to -apply writes along the current branch. In particular, it might break if the underlying -store receives a backfill, changes branches, or erases data while the `backfiller_t` -exists. (If the underlying store is a `store_subview_t`, it's OK if other changes happen -to the underlying store's underlying store outside of the region covered by the -`store_subview_t`.) */ - -class backfiller_t : public home_thread_mixin_debug_only_t { -public: - backfiller_t(mailbox_manager_t *_mailbox_manager, - branch_history_manager_t *_branch_history_manager, - store_view_t *_store); - - backfiller_bcard_t get_business_card() { - return backfiller_bcard_t { - store->get_region(), - registrar.get_business_card() }; - } - -private: - /* A `client_t` is created for every backfill that's in progress. */ - class client_t { - public: - client_t(backfiller_t *, const backfiller_bcard_t::intro_1_t &intro, signal_t *); - - private: - /* `session_t` represents a session within this backfill; the definition is - hidden to keep this header file simple. */ - class session_t; - - void on_begin_session( - signal_t *interruptor, - const fifo_enforcer_write_token_t &write_token, - const key_range_t::right_bound_t &threshold); - void on_end_session( - signal_t *interruptor, - const fifo_enforcer_write_token_t &write_token); - void on_ack_items( - signal_t *interruptor, - const fifo_enforcer_write_token_t &write_token, - size_t mem_size); - void on_pre_items( - signal_t *interruptor, - const fifo_enforcer_write_token_t &write_token, - backfill_item_seq_t &&chunk); - - backfiller_t *const parent; - - /* `intro` is what we received from the backfillee in the initial handshake. */ - backfiller_bcard_t::intro_1_t const intro; - - /* `full_region` is the entire region that this backfill applies to. It's the - same as `intro.initial_version.get_domain()`. */ - region_t const full_region; - - /* `fifo_source` is used to attach order tokens to the messages we send to the - backfillee. `fifo_sink` is used to interpret the order tokens on messages we - receive from the backfillee. */ - fifo_enforcer_source_t fifo_source; - fifo_enforcer_sink_t fifo_sink; - - /* `common_version` and `pre_items` together describe the state of the - backfillee's B-tree relative to ours. Initially, `common_version` is set to the - timestamp of the common ancestor of our B-tree's version and the backfillee's - B-tree's version, and `pre_items` contains backfill pre items describing which - keys on the backfillee have changed since the common ancestor (*see note). When a - backfill session covers a certain region for the first time, it consumes the - corresponding part of `pre_items` and updates `common_version` to be whatever - version it sends to the backfillee. So if a subsequent backfill session backfills - the same region, then it only backfills things that have changed since the first - backfill session. - - * Note: Actually, `pre_items` is initially empty. As we stream pre-items from - the backfillee, they are appended to the right-hand side. So it conceptually - contains all the pre items, but in reality it's filled in incrementally. */ - region_map_t common_version; - backfill_item_seq_t pre_items; - - /* `item_throttler` limits the total mem size of the backfill items that are - allowed to queue up on the backfillee. `item_throttler_acq` always holds - `item_throttler`, but its `count` changes to reflect the total mem size that's - currently in the queue. */ - new_semaphore_t item_throttler; - new_semaphore_in_line_t item_throttler_acq; - - scoped_ptr_t current_session; - - backfiller_bcard_t::pre_items_mailbox_t pre_items_mailbox; - backfiller_bcard_t::begin_session_mailbox_t begin_session_mailbox; - backfiller_bcard_t::end_session_mailbox_t end_session_mailbox; - backfiller_bcard_t::ack_items_mailbox_t ack_items_mailbox; - }; - - mailbox_manager_t *const mailbox_manager; - branch_history_manager_t *const branch_history_manager; - store_view_t *const store; - - registrar_t registrar; - - DISABLE_COPYING(backfiller_t); -}; - -#endif /* CLUSTERING_IMMEDIATE_CONSISTENCY_BACKFILLER_HPP_ */ diff --git a/src/clustering/immediate_consistency/history.cc b/src/clustering/immediate_consistency/history.cc deleted file mode 100644 index 22d05e47b93..00000000000 --- a/src/clustering/immediate_consistency/history.cc +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/immediate_consistency/history.hpp" - -#include - -#include "containers/binary_blob.hpp" - -RDB_IMPL_SERIALIZABLE_2_SINCE_v1_13(version_t, branch, timestamp); - -region_map_t to_version_map(const region_map_t &blob_map) { - return blob_map.map(blob_map.get_domain(), - [&](const binary_blob_t &b) -> version_t { - return binary_blob_t::get(b); - }); -} - -region_map_t from_version_map(const region_map_t &vers_map) { - return vers_map.map(vers_map.get_domain(), &binary_blob_t::make); -} - -RDB_IMPL_SERIALIZABLE_2_SINCE_v2_1(branch_birth_certificate_t, - initial_timestamp, origin); -RDB_IMPL_EQUALITY_COMPARABLE_2(branch_birth_certificate_t, - initial_timestamp, origin); - -void branch_history_reader_t::export_branch_history( - const branch_id_t &branch, branch_history_t *out) const THROWS_NOTHING { - if (out->branches.count(branch) == 1) { - return; - } - std::set to_process {branch}; - std::set done; - while (!to_process.empty()) { - branch_id_t next = *to_process.begin(); - to_process.erase(to_process.begin()); - done.insert(next); - if (is_branch_known(next)) { - try { - auto res = out->branches.insert(std::make_pair(next, get_branch(next))); - guarantee(res.second); - res.first->second.origin.visit(res.first->second.origin.get_domain(), - [&](const region_t &, const version_t &vers) { - if (!vers.branch.is_nil() && out->branches.count(vers.branch) == 0 && - done.count(vers.branch) == 0) { - to_process.insert(vers.branch); - } - }); - } catch (const missing_branch_exc_t &) { - crash("get_branch() failed even after is_branch_known() returned true"); - } - } - } -} - -void branch_history_reader_t::export_branch_history( - const region_map_t ®ion_map, branch_history_t *out) - const THROWS_NOTHING { - region_map.visit(region_map.get_domain(), - [&](const region_t &, const version_t &vers) { - if (!vers.branch.is_nil()) { - export_branch_history(vers.branch, out); - } - }); -} - -branch_birth_certificate_t branch_history_t::get_branch(const branch_id_t &branch) const - THROWS_ONLY(missing_branch_exc_t) { - auto it = branches.find(branch); - if (it == branches.end()) { - throw missing_branch_exc_t(); - } else { - return it->second; - } -} - -bool branch_history_t::is_branch_known(const branch_id_t &branch) const THROWS_NOTHING { - return branches.count(branch) != 0; -} - -RDB_IMPL_SERIALIZABLE_1_SINCE_v2_1(branch_history_t, branches); -RDB_IMPL_EQUALITY_COMPARABLE_1(branch_history_t, branches); - -bool version_is_ancestor( - const branch_history_reader_t *bh, - const version_t &ancestor, - const version_t &descendent, - const region_t &relevant_region) - THROWS_ONLY(missing_branch_exc_t) { - std::stack > stack; - stack.push(std::make_pair(relevant_region, descendent)); - bool missing = false; - while (!stack.empty()) { - region_t reg = stack.top().first; - version_t vers = stack.top().second; - stack.pop(); - if (vers.branch == ancestor.branch && vers.timestamp >= ancestor.timestamp) { - /* OK, this part matches; we don't need to do any further checking here */ - } else if (vers.timestamp < ancestor.timestamp) { - /* This part definitely doesn't match */ - return false; - } else { - branch_birth_certificate_t birth_certificate; - try { - birth_certificate = bh->get_branch(vers.branch); - birth_certificate.origin.visit(reg, - [&](const region_t &subreg, const version_t &subvers) { - stack.push(std::make_pair(subreg, subvers)); - }); - } catch (const missing_branch_exc_t &) { - /* We don't want to throw `missing_branch_exc_t` yet because we might - later encounter some part that definitely isn't a descendent of - `ancestor`. In other words, there might still be a possibility to return - `false`. */ - missing = true; - } - } - } - if (missing) { - throw missing_branch_exc_t(); - } else { - return true; - } -} - -/* Arbitrary comparison operator for `version_t`, so we can make sets of them. */ -class version_set_less_t { -public: - bool operator()(const version_t &a, const version_t &b) const { - return std::tie(a.branch, a.timestamp) < std::tie(b.branch, b.timestamp); - } -}; - -region_map_t version_find_common( - const branch_history_reader_t *bh, - const version_t &initial_v1, - const version_t &initial_v2, - const region_t &initial_region) - THROWS_ONLY(missing_branch_exc_t) { - /* In order to limit recursion depth in pathological cases, we use a heap-stack - instead of the real stack. `stack` stores the sub-regions left to process and - `result` stores the sub-regions that we've already solved. We repeatedly pop - fragments from `stack`; for each fragment, we either add one or more new - sub-fragments to `stack`, or add an entry to `result`. After each iteration of the - loop, the union of the regions in `stack` and the regions in `result` is - `initial_region`. */ - class fragment_t { - public: - /* The region that this fragment applies to */ - region_t r; - /* The versions we're trying to find the common ancestor of in the region */ - version_t v1, v2; - /* Versions equivalent to `v1` and `v2` in the region, which the initial values - of `v1` or `v2` are descended from. We need to track these to deal with an - awkward corner case; see below. */ - std::set v1_equiv, v2_equiv; - }; - std::stack stack; - std::vector result_regions; - std::vector result_versions; - fragment_t initial_fragment; - initial_fragment.r = initial_region; - initial_fragment.v1 = initial_v1; - initial_fragment.v2 = initial_v2; - stack.push(initial_fragment); - while (!stack.empty()) { - fragment_t x = stack.top(); - stack.pop(); - if (x.v1.branch == x.v2.branch) { - /* This is the base case; we got to two versions which are on the same - branch, so one of them is the common ancestor of both. */ - version_t common(x.v1.branch, std::min(x.v1.timestamp, x.v2.timestamp)); - result_regions.push_back(x.r); - result_versions.push_back(common); - } else if (x.v1_equiv.count(x.v2) == 1) { - /* Alternative base case: `v1` and `v2` are equivalent. */ - result_regions.push_back(x.r); - result_versions.push_back(x.v2); - } else if (x.v2_equiv.count(x.v1) == 1) { - result_regions.push_back(x.r); - result_versions.push_back(x.v1); - } else if (x.v1 == version_t::zero() || x.v2 == version_t::zero()) { - /* Conceptually, this case is a subset of the next case. We handle it - separately because `bh->get_branch()` crashes on a nil branch ID, so we'd - have to make the next case implementation more complicated. */ - result_regions.push_back(x.r); - result_versions.push_back(version_t::zero()); - } else { - /* The versions are on two separate branches. */ - branch_birth_certificate_t b1 = bh->get_branch(x.v1.branch); - branch_birth_certificate_t b2 = bh->get_branch(x.v2.branch); - /* To simplify the algorithm, make sure that `b1` has a later start point or - the two branches have equal start points */ - if (b1.initial_timestamp < b2.initial_timestamp) { - std::swap(x.v1, x.v2); - std::swap(x.v1_equiv, x.v2_equiv); - std::swap(b1, b2); - } - /* One of two things are true (we don't know which): - 1. `b2` is not descended from `b1`. In this case, recursing into `b1`'s - parents will get us closer to the common ancestor without taking us past - it. - 2. The common ancestor is `b1`'s start point. (Or, if `v1` is `b1`'s start - point, the common ancestor might be in `v1_equiv`.) In this case, - recursing from `b1` to its parents will take `v1` past the common - ancestor. But this is OK, because the common ancestor will still be - recorded in `v1_equiv`. */ - - /* Prepare the new value of `v1_equiv`. We know that `b1`'s start point - belongs in `v1_equiv`. And if `x.v1` is equal to `b1`'s start point, then we - should also include the previous values of `v1_equiv`. */ - std::set v1_equiv; - v1_equiv.insert(version_t(x.v1.branch, b1.initial_timestamp)); - if (x.v1.timestamp == b1.initial_timestamp) { - v1_equiv.insert(x.v1_equiv.begin(), x.v1_equiv.end()); - } - - /* OK, now recurse to `b1`'s parents. */ - guarantee(region_is_superset(b1.origin.get_domain(), x.r)); - b1.origin.visit(x.r, - [&](const region_t ®, const version_t &vers) { - stack.push({reg, vers, x.v2, v1_equiv, x.v2_equiv}); - }); - } - } - return region_map_t::from_unordered_fragments( - std::move(result_regions), std::move(result_versions)); -} - -region_map_t version_find_branch_common( - const branch_history_reader_t *bh, - const version_t &version, - const branch_id_t &branch, - const region_t &relevant_region) - THROWS_ONLY(missing_branch_exc_t) { - version_t branch_version; - if (branch.is_nil()) { - branch_version = version_t::zero(); - } else { - branch_version = version_t(branch, state_timestamp_t::max()); - } - return version_find_common(bh, version, branch_version, relevant_region); -} - -branch_birth_certificate_t branch_history_combiner_t::get_branch( - const branch_id_t &branch) const THROWS_ONLY(missing_branch_exc_t) { - if (r1->is_branch_known(branch)) { - return r1->get_branch(branch); - } else { - return r2->get_branch(branch); - } -} - -bool branch_history_combiner_t::is_branch_known( - const branch_id_t &branch) const THROWS_NOTHING { - return r1->is_branch_known(branch) || r2->is_branch_known(branch); -} - diff --git a/src/clustering/immediate_consistency/history.hpp b/src/clustering/immediate_consistency/history.hpp deleted file mode 100644 index 6dcfac19012..00000000000 --- a/src/clustering/immediate_consistency/history.hpp +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_IMMEDIATE_CONSISTENCY_HISTORY_HPP_ -#define CLUSTERING_IMMEDIATE_CONSISTENCY_HISTORY_HPP_ - -#include -#include -#include - -#include "concurrency/signal.hpp" -#include "containers/uuid.hpp" -#include "region/region_map.hpp" -#include "rpc/semilattice/joins/macros.hpp" -#include "rpc/serialize_macros.hpp" -#include "timestamps.hpp" -#include "utils.hpp" - -class binary_blob_t; - -/* The type `version_t` uniquely identifies the state of some region of a RethinkDB table -at some point in time. Every read operation that passes through a `broadcaster_t` will -get all its data from the version that the broadcaster is at at the time that the read -arrives. Every write operation that passes through a `broadcaster_t` will transition the -data from one `version_t` to the next. - -`version_t` internally consists of two parts: a `branch_id_t` and a timestamp. For the -`version_t::zero()`, which is the version of the initial empty database, the -`branch_id_t` is nil and the timestamp is 0. Any other version than `version_t::zero()` -belongs to a `broadcaster_t`. The `branch_id_t` will be a new UUID that was generated -when the `broadcaster_t` was created, and the timestamp will be a number that the -`broadcaster_t` increments every time a write operation passes through it. (Warning: The -timestamp is usually not zero for a new `broadcaster_t`.) */ - -class version_t { -public: - version_t() { } - version_t(branch_id_t bid, state_timestamp_t ts) : - branch(bid), timestamp(ts) { } - static version_t zero() { - return version_t(nil_uuid(), state_timestamp_t::zero()); - } - - bool operator==(const version_t &v) const{ - return branch == v.branch && timestamp == v.timestamp; - } - bool operator!=(const version_t &v) const { - return !(*this == v); - } - - branch_id_t branch; - state_timestamp_t timestamp; -}; - -RDB_DECLARE_SERIALIZABLE(version_t); - -inline void debug_print(printf_buffer_t *buf, const version_t& v) { - buf->appendf("v{"); - debug_print(buf, v.branch); - buf->appendf(", "); - debug_print(buf, v.timestamp); - buf->appendf("}"); -} - -region_map_t to_version_map(const region_map_t &blob_map); -region_map_t from_version_map(const region_map_t &version_map); - -/* The state of the database at the time that the `broadcaster_t` was created and the -sequence of writes that pass through a `broadcaster_t` are collectively referred to as a -"branch". Except for the "fake branch" identified by a nil UUID, there is a 1:1 -relationship between branches and `broadcaster_t`s. - -Thus, the branches form a directed acyclic graph. The branch created by the first primary -"descends" from `version_t::zero()`. When a primary is created as a replacement for an -unavailable primary, its branch descends from the branch of the unavailable primary. When -shards are split or merged, the newly-created branches may descend from part of a single -existing branch or from part of several existing branches. - -When a primary is created, it publishes a `branch_birth_certificate_t` that describes its -newly-created branch. */ - -class branch_birth_certificate_t { -public: - /* The region that the branch applies to. This is the same as the region of the - `broadcaster_t` that corresponds to the branch. Every write to the branch must affect - only some (non-proper) subset of this region. */ - region_t get_region() const { - return origin.get_domain(); - } - - /* The timestamp of the first state on the branch. `version_t(branch_id, - initial_timestamp)` describes the same B-tree state as `origin`. `initial_timestamp` - is always equal to the maximum of the timestamps in `origin`. */ - state_timestamp_t initial_timestamp; - - /* The state of the meta-info of the B-tree when the `broadcaster_t` was - constructed. */ - region_map_t origin; -}; - -RDB_DECLARE_SERIALIZABLE(branch_birth_certificate_t); -RDB_DECLARE_EQUALITY_COMPARABLE(branch_birth_certificate_t); - -/* `missing_branch_exc_t` is thrown if we try to fetch a birth certificate for a branch -that doesn't exist. This can happen if the branch history GC cleans up the branch while a -B-tree still exists that points at it, which is uncommon but not impossible. */ - -class missing_branch_exc_t : public std::runtime_error { -public: - missing_branch_exc_t() : - std::runtime_error("tried to look up a branch that is not in the history") { } -}; - -/* A `branch_history_t` is a map from `branch_id_t` to `branch_birth_certificate_t`. -`branch_history_reader_t` is an interface that allows reading from a `branch_history_t` -or something equivalent; this is because otherwise we might need to copy -`branch_history_t`, which may be expensive. Note that `branch_history_t` subclasses -from `branch_history_reader_t`. This is so we can pass `branch_history_t` to the many -functions that take a `branch_history_reader_t *`. */ - -class branch_history_t; - -class branch_history_reader_t { -public: - /* Returns information about one specific branch. Throws `missing_branch_exc_t` if we - don't have a record for this branch. */ - virtual branch_birth_certificate_t get_branch(const branch_id_t &branch) - const THROWS_ONLY(missing_branch_exc_t) = 0; - - /* Checks whether a given branch id is known. */ - virtual bool is_branch_known(const branch_id_t &branch) const THROWS_NOTHING = 0; - - /* Copies records related to the given branch and all its known ancestors into `out`. - `out`. The reason this mutates `out` instead of returning a `branch_history_t` is so - that you can call it several times with different branches that share history; they - will re-use records that they share. */ - void export_branch_history( - const branch_id_t &branch, branch_history_t *out) const THROWS_NOTHING; - - /* Convenience function that finds all records related to the given version - map and copies them into `out` */ - void export_branch_history( - const region_map_t ®ion_map, branch_history_t *out) - const THROWS_NOTHING; - -protected: - virtual ~branch_history_reader_t() { } -}; - -class branch_history_t : public branch_history_reader_t { -public: - branch_birth_certificate_t get_branch(const branch_id_t &branch) - const THROWS_ONLY(missing_branch_exc_t); - bool is_branch_known(const branch_id_t &branch) const THROWS_NOTHING; - - std::map branches; -}; -RDB_DECLARE_SERIALIZABLE(branch_history_t); -RDB_DECLARE_EQUALITY_COMPARABLE(branch_history_t); - -/* These are the key functions that we use to do lookups in the branch history. */ - -/* `version_is_ancestor()` returns `true` if every key in `relevant_region` of the table -passed through `ancestor` version on the way to `descendent` version. Also returns true -if `ancestor` and `descendent` are the same version. Throws `missing_branch_exc_t` if the -question can't be answered because some history is missing. */ -bool version_is_ancestor( - const branch_history_reader_t *bh, - const version_t &ancestor, - const version_t &descendent, - const region_t &relevant_region) - THROWS_ONLY(missing_branch_exc_t); - -/* `version_find_common()` finds the last common ancestor of two other versions. The -result may be different for different sub-regions, so it returns a `region_map_t`. Throws -`missing_branch_exc_t` if the question can't be answered because some history is missing. -*/ -region_map_t version_find_common( - const branch_history_reader_t *bh, - const version_t &v1, - const version_t &v2, - const region_t &relevant_region) - THROWS_ONLY(missing_branch_exc_t); - -/* `version_find_branch_common()` is like `version_find_common()` but in place of one of -the versions, it uses the latest version on the given branch. */ -region_map_t version_find_branch_common( - const branch_history_reader_t *bh, - const version_t &version, - const branch_id_t &branch, - const region_t &relevant_region) - THROWS_ONLY(missing_branch_exc_t); - -/* `branch_history_combiner_t` is a `branch_history_reader_t` that reads from two or more -other `branch_history_reader_t`s. */ -class branch_history_combiner_t : public branch_history_reader_t { -public: - branch_history_combiner_t( - const branch_history_reader_t *_r1, - const branch_history_reader_t *_r2) - : r1(_r1), r2(_r2) { } - branch_birth_certificate_t get_branch(const branch_id_t& branch) - const THROWS_ONLY(missing_branch_exc_t); - bool is_branch_known(const branch_id_t &branch) const THROWS_NOTHING; -private: - const branch_history_reader_t *r1, *r2; -}; - -/* `branch_history_manager_t` is a `branch_history_reader_t` with the addition of methods -to add branches to the branch history. This is used for a branch history which is backed -to disk. */ -class branch_history_manager_t : - public branch_history_reader_t, - public home_thread_mixin_t -{ -public: - /* Adds a new branch to the database. Blocks until it is safely on disk. Blocks to - avoid a race condition where we write the branch ID to a B-tree's metainfo, crash - before flushing the `branch_birth_certificate_t` to disk, and then cannot find the - `branch_birth_certificate_t` upon restarting. If it already exists, this is a no-op. - */ - virtual void create_branch( - branch_id_t branch_id, - const branch_birth_certificate_t &bc) - THROWS_NOTHING = 0; - - /* Like `create_branch` but for all the records in a `branch_history_t`, atomically. - */ - virtual void import_branch_history( - const branch_history_t &new_records) - THROWS_NOTHING = 0; - - /* `prepare_gc()` fills `branches` with the IDs of all known branches. `perform_gc()` - deletes any branches whose IDs are in `remove_branches`. The standard procedure is to - call `prepare_gc()`, delete the live branches from the set, then call `perform_gc()`. - */ - virtual void prepare_gc( - std::set *branches_out) - THROWS_NOTHING = 0; - virtual void perform_gc( - const std::set &remove_branches) - THROWS_NOTHING = 0; - -protected: - virtual ~branch_history_manager_t() { } -}; - -#endif /* CLUSTERING_IMMEDIATE_CONSISTENCY_HISTORY_HPP_ */ diff --git a/src/clustering/immediate_consistency/local_replicator.cc b/src/clustering/immediate_consistency/local_replicator.cc deleted file mode 100644 index 11bf80e6bb2..00000000000 --- a/src/clustering/immediate_consistency/local_replicator.cc +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/immediate_consistency/local_replicator.hpp" - -#include "concurrency/cross_thread_signal.hpp" - -local_replicator_t::local_replicator_t( - mailbox_manager_t *mailbox_manager, - const server_id_t &server_id, - primary_dispatcher_t *primary, - store_view_t *_store, - branch_history_manager_t *bhm, - signal_t *interruptor) : - store(_store), - replica( - mailbox_manager, - store, - bhm, - primary->get_branch_id(), - primary->get_branch_birth_certificate().initial_timestamp) -{ - order_source_t order_source; - -#ifndef NDEBUG - /* Make sure the store's initial value matches the initial value in the branch birth - certificate (just as a sanity check) */ - read_token_t read_token; - store->new_read_token(&read_token); - region_map_t origin = to_version_map(store->get_metainfo( - order_source.check_in("local_replica_t(read)").with_read_mode(), - &read_token, store->get_region(), interruptor)); - guarantee(origin == primary->get_branch_birth_certificate().origin); - guarantee(store->get_region() == - primary->get_branch_birth_certificate().get_region()); -#endif - - /* Store the new branch in the branch history manager. We have to do this before we - put the branch in the store's metainfo so that if we crash, the branch will never be - in the metainfo but not the branch history manager. */ - { - on_thread_t thread_switcher(bhm->home_thread()); - bhm->create_branch( - primary->get_branch_id(), - primary->get_branch_birth_certificate()); - } - - /* Initialize the metainfo to the new branch. Note we use hard durability, to avoid - the potential for subtle bugs. */ - write_token_t write_token; - store->new_write_token(&write_token); - store->set_metainfo( - region_map_t( - store->get_region(), - binary_blob_t(version_t( - primary->get_branch_id(), - primary->get_branch_birth_certificate().initial_timestamp))), - order_source.check_in("local_replica_t(write)"), - &write_token, - write_durability_t::HARD, - interruptor); - - state_timestamp_t first_timestamp; - registration = make_scoped( - primary, this, server_id, 2.0, &first_timestamp); - guarantee(first_timestamp == - primary->get_branch_birth_certificate().initial_timestamp); - registration->mark_ready(); -} - -local_replicator_t::~local_replicator_t() { - // This has to happen before `note_reshard` to make sure outstanding reads complete. - registration.reset(); - /* Since `local_replicator_t` is the primary dispatchee, changefeeds are always - routed here. But when the primary changes we need to shut off the changefeed. This - destructor is a good place to do it. */ - store->note_reshard(store->get_region()); -} - -void local_replicator_t::do_read( - const read_t &read, - state_timestamp_t min_timestamp, - signal_t *interruptor, - read_response_t *response_out) { - replica.do_read(read, min_timestamp, interruptor, response_out); -} - -void local_replicator_t::do_write_sync( - const write_t &write, - state_timestamp_t timestamp, - order_token_t order_token, - write_durability_t durability, - signal_t *interruptor, - write_response_t *response_out) { - replica.do_write( - write, timestamp, order_token, durability, - interruptor, response_out); -} - -void local_replicator_t::do_write_async( - const write_t &write, - state_timestamp_t timestamp, - order_token_t order_token, - signal_t *interruptor) { - write_response_t dummy; - replica.do_write( - write, timestamp, order_token, write_durability_t::SOFT, - interruptor, &dummy); -} - -void local_replicator_t::do_dummy_write( - signal_t *interruptor, - write_response_t *response_out) { - replica.do_dummy_write(interruptor, response_out); -} - - diff --git a/src/clustering/immediate_consistency/local_replicator.hpp b/src/clustering/immediate_consistency/local_replicator.hpp deleted file mode 100644 index 42f035bcae4..00000000000 --- a/src/clustering/immediate_consistency/local_replicator.hpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_IMMEDIATE_CONSISTENCY_LOCAL_REPLICATOR_HPP_ -#define CLUSTERING_IMMEDIATE_CONSISTENCY_LOCAL_REPLICATOR_HPP_ - -#include "clustering/immediate_consistency/history.hpp" -#include "clustering/immediate_consistency/primary_dispatcher.hpp" -#include "clustering/immediate_consistency/replica.hpp" -#include "store_view.hpp" - -/* `local_replicator_t` receives writes from a `primary_dispatcher_t` on the same server -and applies them directly to a `store_t` on the same server. It doesn't receive a -backfill at startup; instead, it asserts that the store's state is equal to the starting -state of the `primary_dispatcher_t`'s branch, and then sets the metainfo to point at the -starting state of the branch. So it must be created before any writes have happened on -the `primary_dispatcher_t`. - -There is one `local_replicator_t` on the primary replica server of each shard. -`primary_execution_t` constructs it. */ - -class local_replicator_t : public primary_dispatcher_t::dispatchee_t { -public: - local_replicator_t( - mailbox_manager_t *mailbox_manager, - const server_id_t &server_id, - primary_dispatcher_t *primary, - store_view_t *store, - branch_history_manager_t *bhm, - signal_t *interruptor); - - /* This destructor can block */ - ~local_replicator_t(); - - replica_bcard_t get_replica_bcard() { - return replica.get_replica_bcard(); - } - - bool is_primary() const { - return true; - } - - void do_read( - const read_t &read, - state_timestamp_t min_timestamp, - signal_t *interruptor, - read_response_t *response_out); - - void do_write_sync( - const write_t &write, - state_timestamp_t timestamp, - order_token_t order_token, - write_durability_t durability, - signal_t *interruptor, - write_response_t *response_out); - - void do_write_async( - const write_t &write, - state_timestamp_t timestamp, - order_token_t order_token, - signal_t *interruptor); - - void do_dummy_write( - signal_t *interruptor, - write_response_t *response_out); - -private: - store_view_t *const store; - branch_id_t const branch_id; - - replica_t replica; - - scoped_ptr_t registration; -}; - -#endif /* CLUSTERING_IMMEDIATE_CONSISTENCY_LOCAL_REPLICA_HPP_ */ - diff --git a/src/clustering/immediate_consistency/primary_dispatcher.cc b/src/clustering/immediate_consistency/primary_dispatcher.cc deleted file mode 100644 index 8eab9d4e8e8..00000000000 --- a/src/clustering/immediate_consistency/primary_dispatcher.cc +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/immediate_consistency/primary_dispatcher.hpp" - -/* Limits how many writes should be sent to a dispatchee at once. */ -const size_t DISPATCH_WRITES_CORO_POOL_SIZE = 64; - -primary_dispatcher_t::dispatchee_registration_t::dispatchee_registration_t( - primary_dispatcher_t *_parent, - dispatchee_t *_dispatchee, - const server_id_t &_server_id, - double _priority, - state_timestamp_t *first_timestamp_out) : - parent(_parent), - dispatchee(_dispatchee), - server_id(_server_id), - priority(_priority), - is_ready(false), - queue_count_membership( - &parent->perfmon_collection, - &queue_count, - uuid_to_str(server_id.get_uuid()) + "_broadcast_queue_count"), - background_write_queue(&queue_count), - background_write_workers( - DISPATCH_WRITES_CORO_POOL_SIZE, - &background_write_queue, - &background_write_caller), - latest_acked_write(state_timestamp_t::zero()) -{ - parent->assert_thread(); - - /* Grab mutex so we don't race with writes that are starting or finishing. */ - DEBUG_VAR mutex_assertion_t::acq_t acq(&parent->mutex); - ASSERT_FINITE_CORO_WAITING; - parent->dispatchees[this] = auto_drainer_t::lock_t(&drainer); - *first_timestamp_out = parent->current_timestamp; -} - -primary_dispatcher_t::dispatchee_registration_t::~dispatchee_registration_t() { - DEBUG_VAR mutex_assertion_t::acq_t acq(&parent->mutex); - ASSERT_FINITE_CORO_WAITING; - parent->assert_thread(); - parent->dispatchees.erase(this); - if (is_ready) { - parent->refresh_ready_dispatchees_as_set(); - } -} - -void primary_dispatcher_t::dispatchee_registration_t::mark_ready() { - DEBUG_VAR mutex_assertion_t::acq_t acq(&parent->mutex); - ASSERT_FINITE_CORO_WAITING; - guarantee(!is_ready); - is_ready = true; - parent->refresh_ready_dispatchees_as_set(); -} - -primary_dispatcher_t::write_callback_t::write_callback_t() : write(nullptr) { } - -primary_dispatcher_t::write_callback_t::~write_callback_t() { - if (write != nullptr) { - guarantee(write->callback == this); - write->callback = nullptr; - } -} - -primary_dispatcher_t::primary_dispatcher_t( - perfmon_collection_t *parent_perfmon_collection, - const region_map_t &base_version) : - perfmon_membership(parent_perfmon_collection, &perfmon_collection, "broadcaster"), - ready_dispatchees_as_set(std::set()) -{ - current_timestamp = state_timestamp_t::zero(); - base_version.visit(base_version.get_domain(), - [&](const region_t &, const version_t &v) { - current_timestamp = std::max(v.timestamp, current_timestamp); - }); - most_recent_acked_write_timestamp = current_timestamp; - - branch_id = generate_uuid(); - branch_bc.origin = base_version; - branch_bc.initial_timestamp = current_timestamp; -} - -void primary_dispatcher_t::read( - const read_t &_read, - fifo_enforcer_sink_t::exit_read_t *lock, - order_token_t order_token, - signal_t *interruptor, - read_response_t *response_out) - THROWS_ONLY(cannot_perform_query_exc_t, interrupted_exc_t) { - assert_thread(); - rassert(region_is_superset(branch_bc.get_region(), _read.get_region())); - order_token.assert_read_mode(); - - dispatchee_registration_t *dispatchee = nullptr; - auto_drainer_t::lock_t dispatchee_lock; - state_timestamp_t min_timestamp; - - { - wait_interruptible(lock, interruptor); - mutex_assertion_t::acq_t mutex_acq(&mutex); - lock->end(); - - /* Prefer the dispatchee with the highest acknowledged write version - (to reduce the risk that the read has to wait for a write). If multiple - ones are equal, use priority as a tie-breaker. */ - std::pair best; - for (const auto &pair : dispatchees) { - dispatchee_registration_t *d = pair.first; - if (!d->is_ready) { - continue; - } - if (_read.route_to_primary() && !d->dispatchee->is_primary()) { - continue; - } - if (dispatchee == nullptr || - std::make_pair(d->latest_acked_write, d->priority) > best) { - dispatchee = d; - dispatchee_lock = pair.second; - best = std::make_pair(d->latest_acked_write, d->priority); - } - } - - rassert(dispatchee != nullptr, "Primary replica should always be ready"); - if (dispatchee == nullptr) { - throw cannot_perform_query_exc_t( - "No replicas are ready for reading.", - query_state_t::FAILED); - } - - order_checkpoint.check_through(order_token); - min_timestamp = most_recent_acked_write_timestamp; - } - - try { - wait_any_t interruptor2(dispatchee_lock.get_drain_signal(), interruptor); - dispatchee->dispatchee->do_read(_read, min_timestamp, &interruptor2, response_out); - } catch (const interrupted_exc_t &) { - if (interruptor->is_pulsed()) { - throw; - } else { - throw cannot_perform_query_exc_t( - "Lost contact with replica during read.", - query_state_t::INDETERMINATE); - } - } -} - -void primary_dispatcher_t::spawn_write( - const write_t &write, - order_token_t order_token, - write_callback_t *cb) { - ASSERT_FINITE_CORO_WAITING; - assert_thread(); - rassert(region_is_superset(branch_bc.get_region(), write.get_region())); - order_token.assert_write_mode(); - - DEBUG_VAR mutex_assertion_t::acq_t mutex_acq(&mutex); - - write_durability_t durability; - switch (write.durability()) { - case DURABILITY_REQUIREMENT_DEFAULT: - durability = cb->get_default_write_durability(); - break; - case DURABILITY_REQUIREMENT_SOFT: - durability = write_durability_t::SOFT; - break; - case DURABILITY_REQUIREMENT_HARD: - durability = write_durability_t::HARD; - break; - default: - unreachable(); - } - - /* Assign a new timestamp to the write, unless it's a dummy write. */ - if (boost::get(&write.write) == nullptr) { - current_timestamp = current_timestamp.next(); - } - - counted_t incomplete_write = make_counted( - write, - current_timestamp, - order_checkpoint.check_through(order_token), - durability, - cb); - - // You can't reuse the same callback for two writes. - guarantee(cb->write == nullptr); - cb->write = incomplete_write.get(); - - for (const auto &pair : dispatchees) { - pair.first->background_write_queue.push( - std::bind(&primary_dispatcher_t::background_write, this, - pair.first, pair.second, incomplete_write)); - } -} - -primary_dispatcher_t::incomplete_write_t::incomplete_write_t( - const write_t &w, state_timestamp_t ts, order_token_t ot, - write_durability_t dur, write_callback_t *cb) : - write(w), timestamp(ts), order_token(ot), durability(dur), callback(cb) - { } - -primary_dispatcher_t::incomplete_write_t::~incomplete_write_t() { - if (callback != nullptr) { - guarantee(callback->write == this); - callback->write = nullptr; - callback->on_end(); - } -} - -void primary_dispatcher_t::background_write( - dispatchee_registration_t *dispatchee, - auto_drainer_t::lock_t dispatchee_lock, - counted_t write) THROWS_NOTHING { - try { - /* Use a special path for dummy writes. - dummy writes are used to check the table status, so we don't want to generate - unnecessary disk writes or block on previous writes. - We also never send a dummy write to a non-ready dispatchee, since - there's no reason for queueing them up (they are no-ops anyway). */ - if (boost::get(&write->write.write) != nullptr) { - if (dispatchee->is_ready) { - write_response_t response; - dispatchee->dispatchee->do_dummy_write( - dispatchee_lock.get_drain_signal(), &response); - if (write->callback != nullptr) { - guarantee(write->callback->write == write.get()); - write->callback->on_ack(dispatchee->server_id, std::move(response)); - } - } - return; - } - - if (dispatchee->is_ready) { - write_response_t response; - dispatchee->dispatchee->do_write_sync( - write->write, write->timestamp, write->order_token, write->durability, - dispatchee_lock.get_drain_signal(), &response); - - /* Update latest acked write on the distpatchee so we can route queries - to the fastest replica and avoid blocking there. */ - dispatchee->latest_acked_write = - std::max(dispatchee->latest_acked_write, write->timestamp); - - /* The write could potentially get acked when we call `on_ack()` on the - callback. So make sure all reads started after this point will see this - write. This is more conservative than necessary, since the write might not - actually be acked to the client just because it was acked by this mirror; but - this is the easiest thing to do. */ - most_recent_acked_write_timestamp - = std::max(most_recent_acked_write_timestamp, write->timestamp); - - if (write->callback != nullptr) { - guarantee(write->callback->write == write.get()); - write->callback->on_ack(dispatchee->server_id, std::move(response)); - } - } else { - dispatchee->dispatchee->do_write_async( - write->write, write->timestamp, write->order_token, - dispatchee_lock.get_drain_signal()); - } - } catch (const interrupted_exc_t &) { - /* ignore */ - } -} - -void primary_dispatcher_t::refresh_ready_dispatchees_as_set() { - /* Note that it's possible that we'll have multiple dispatchees with the same server - ID. This won't happen during normal operation, but it can happen temporarily during - a transition. For example, a secondary might disconnect and reconnect as a different - dispatchee before we realize that its original dispatchee is no longer valid. */ - std::set ready; - for (const auto &pair : dispatchees) { - if (pair.first->is_ready) { - ready.insert(pair.first->server_id); - } - } - ready_dispatchees_as_set.set_value(ready); -} - diff --git a/src/clustering/immediate_consistency/primary_dispatcher.hpp b/src/clustering/immediate_consistency/primary_dispatcher.hpp deleted file mode 100644 index 5dc37c520ea..00000000000 --- a/src/clustering/immediate_consistency/primary_dispatcher.hpp +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_IMMEDIATE_CONSISTENCY_PRIMARY_DISPATCHER_HPP_ -#define CLUSTERING_IMMEDIATE_CONSISTENCY_PRIMARY_DISPATCHER_HPP_ - -#include "clustering/immediate_consistency/history.hpp" -#include "concurrency/coro_pool.hpp" -#include "concurrency/queue/unlimited_fifo.hpp" -#include "concurrency/watchable.hpp" -#include "rdb_protocol/protocol.hpp" -#include "rpc/connectivity/server_id.hpp" - -/* The job of the `primary_dispatcher_t` is: -- Take in reads and writes -- Assign timestamps to the reads and writes -- Distribute the reads and writes to the replicas -- Collect the results and return them to the caller - -It takes in reads and writes via its `read()` and `spawn_write()` methods. Replicas -register for reads and writes by subclassing `dispatchee_t` and constructing a -`dispatchee_registration_t` object. - -There is one `primary_dispatcher_t` for each shard; it's located on the primary -replica server. `primary_execution_t` constructs it. */ - -class primary_dispatcher_t : public home_thread_mixin_debug_only_t { -private: - class incomplete_write_t; - -public: - /* `dispatchee_t` represents a replica from the `primary_dispatcher_t`'s point of - view. The subclass might run the reads and writes directly, or it might send them - over the network to a different server. */ - class dispatchee_t { - public: - /* This value is used to determine where reads that have `route_to_primary()` - set are routed to. */ - virtual bool is_primary() const = 0; - - virtual void do_read( - const read_t &read, - state_timestamp_t min_timestamp, - signal_t *interruptor, - read_response_t *response_out) = 0; - - /* `do_write_sync()` blocks until the write has been performed with the given - durability level, and it produces a `write_response_t`. `do_write_async()` makes - no promises about when it returns and it does not return a response. However, - `do_write_async()` may block in order to limit the rate at which the - `primary_dispatcher_t` sends writes. */ - virtual void do_write_sync( - const write_t &write, - state_timestamp_t timestamp, - order_token_t order_token, - write_durability_t durability, - signal_t *interruptor, - write_response_t *response_out) = 0; - virtual void do_write_async( - const write_t &write, - state_timestamp_t timestamp, - order_token_t order_token, - signal_t *interruptor) = 0; - virtual void do_dummy_write( - signal_t *interruptor, - write_response_t *response_out) = 0; - protected: - virtual ~dispatchee_t() { } - }; - - /* `dispatchee_registration_t` is a sentry object that signs the dispatchee up for - reads and writes. */ - class dispatchee_registration_t { - public: - dispatchee_registration_t( - primary_dispatcher_t *parent, - dispatchee_t *dispatchee, - /* `server_id` is used for reporting acks to the `write_callback_t`. */ - const server_id_t &server_id, - /* `priority` is used for deciding which of several dispatchees should handle - a read. The local dispatchee has priority 2.0 and remote dispatchees have - priority 1.0. */ - double priority, - /* `*first_timestamp_out` will be set to whatever was the latest timestamp - value when the `dispatchee_registration_t` was constructed. */ - state_timestamp_t *first_timestamp_out); - - /* Destroying the `dispatchee_registration_t` will interrupt any running read or - write operations on this dispatchee. */ - ~dispatchee_registration_t(); - - /* Initially, dispatchees will only receive `do_write_async()` calls. But after - `mark_ready()` is called, the `primary_dispatcher_t` may also send them reads, - `do_write_sync()`, and `do_dummy_write()` calls. */ - void mark_ready(); - - private: - friend class primary_dispatcher_t; - - primary_dispatcher_t *const parent; - dispatchee_t *const dispatchee; - server_id_t const server_id; - double const priority; - - bool is_ready; - - perfmon_counter_t queue_count; - perfmon_membership_t queue_count_membership; - - /* TODO: Maybe this should be a queue of `incomplete_write_t`s instead of a queue - of `std::function`s. */ - unlimited_fifo_queue_t > background_write_queue; - calling_callback_t background_write_caller; - coro_pool_t > background_write_workers; - - /* This is the timestamp of the latest write for which a `do_write_sync()` call - has completed. We use this to pick the most up-to-date dispatchee to send reads - to. */ - state_timestamp_t latest_acked_write; - - auto_drainer_t drainer; - }; - - class write_callback_t { - public: - write_callback_t(); - /* `get_default_write_durability()` returns the write durability that this write - should use if the write itself didn't specify. */ - virtual write_durability_t get_default_write_durability() = 0; - /* Every time the write is acked, `on_ack()` is called. When no more replicas - will ack the write, `on_end()` is called. */ - virtual void on_ack(const server_id_t &, write_response_t &&) = 0; - virtual void on_end() = 0; - - protected: - virtual ~write_callback_t(); - - private: - friend class primary_dispatcher_t; - /* This is so that if the write callback is destroyed before `on_end()` is - called, it will get deregistered. */ - incomplete_write_t *write; - }; - - /* There is a 1:1 relationship between `primary_dispatcher_t`s and branches. When the - `primary_dispatcher_t` is constructed, it constructs a branch using the given region - map as the origin. You can get the branch's ID and birth certificate via the - `get_branch_id()` and `get_branch_birth_certificate()` methods. */ - - primary_dispatcher_t( - perfmon_collection_t *parent_perfmon_collection, - const region_map_t &base_version); - - branch_id_t get_branch_id() { - return branch_id; - } - branch_birth_certificate_t get_branch_birth_certificate() { - return branch_bc; - } - - /* `read()` performs the given read, blocking until the read is complete. */ - void read( - const read_t &r, - fifo_enforcer_sink_t::exit_read_t *lock, - order_token_t tok, - signal_t *interruptor, - read_response_t *response_out) - THROWS_ONLY(cannot_perform_query_exc_t, interrupted_exc_t); - - /* Unlike `read()`, `spawn_write()` returns as soon as the write has begun and - replies asynchronously via a callback. It will not block. If the `write_callback_t` - is destroyed while the write is still in progress, its destructor will automatically - deregister it so that no segfaults will happen. */ - void spawn_write( - const write_t &w, - order_token_t tok, - write_callback_t *cb); - - clone_ptr_t > > get_ready_dispatchees() { - return ready_dispatchees_as_set.get_watchable(); - } - -private: - /* `incomplete_write_t` bundles all of the information related to a given write into - a single struct. When it is destroyed, it calls `on_end()` on the callback. */ - class incomplete_write_t : public single_threaded_countable_t { - public: - incomplete_write_t( - const write_t &w, state_timestamp_t ts, order_token_t ot, - write_durability_t durability, write_callback_t *cb); - ~incomplete_write_t(); - write_t write; - state_timestamp_t timestamp; - order_token_t order_token; - write_durability_t durability; - write_callback_t *callback; - }; - - void background_write( - dispatchee_registration_t *dispatchee, - auto_drainer_t::lock_t dispatchee_lock, - counted_t write) THROWS_NOTHING; - - void refresh_ready_dispatchees_as_set(); - - branch_id_t branch_id; - branch_birth_certificate_t branch_bc; - - perfmon_collection_t perfmon_collection; - perfmon_membership_t perfmon_membership; - - mutex_assertion_t mutex; - - state_timestamp_t current_timestamp; - order_checkpoint_t order_checkpoint; - - /* Once we ack a write, we must make sure that every read that's initiated after that - will see the result of the write. We use this timestamp to keep track of the most - recent acked write and produce `min_timestamp_token_t`s from it whenever we send a - read to a listener. */ - state_timestamp_t most_recent_acked_write_timestamp; - - std::map dispatchees; - - /* This is just a set that contains the peer ID of each dispatchee in `dispatchees` - that's readable. We store it separately so we can expose it to code that needs to - know which replicas are available. */ - watchable_variable_t > ready_dispatchees_as_set; - - DISABLE_COPYING(primary_dispatcher_t); -}; - -#endif /* CLUSTERING_IMMEDIATE_CONSISTENCY_PRIMARY_QUERY_ROUTER_HPP_ */ - diff --git a/src/clustering/immediate_consistency/remote_replicator_client.cc b/src/clustering/immediate_consistency/remote_replicator_client.cc deleted file mode 100644 index 4b6c8b1c3f2..00000000000 --- a/src/clustering/immediate_consistency/remote_replicator_client.cc +++ /dev/null @@ -1,495 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/immediate_consistency/remote_replicator_client.hpp" - -#include "clustering/immediate_consistency/backfill_throttler.hpp" -#include "clustering/immediate_consistency/backfillee.hpp" -#include "clustering/table_manager/backfill_progress_tracker.hpp" -#include "stl_utils.hpp" -#include "store_view.hpp" - -class remote_replicator_client_t::timestamp_range_tracker_t { -public: - timestamp_range_tracker_t( - const region_t &_store_region, - state_timestamp_t _prev_timestamp) : - store_region(_store_region), - prev_timestamp(_prev_timestamp) { } - - /* Records that the backfill has copied values up to the given timestamp for the - given range. */ - void record_backfill(const region_t ®ion, const state_timestamp_t &ts) { - rassert(region.beg == store_region.beg); - rassert(region.end == store_region.end); - rassert(key_range_t::right_bound_t(region.inner.left) == - (entries.empty() - ? key_range_t::right_bound_t(store_region.inner.left) - : entries.back().first)); - rassert(region.inner.right <= store_region.inner.right); - if (entries.empty() || entries.back().second != ts) { - entries.push_back(std::make_pair(region.inner.right, ts)); - } else { - /* Rather than making a second entry with the same timestamp, coalesce - the two entries. */ - entries.back().first = region.inner.right; - } - } - - /* Records that the streaming write with the given timestamp has been applied in the - given region. */ - void record_write(const region_t ®ion, state_timestamp_t ts) { - rassert(ts == prev_timestamp.next()); - prev_timestamp = ts; - if (region_is_empty(region)) { - rassert(entries.empty() || entries[0].second >= ts); - return; - } - rassert(region.beg == store_region.beg); - rassert(region.end == store_region.end); - rassert(region.inner.left == store_region.inner.left); - rassert(region.inner.right == entries[0].first); - rassert(entries[0].second.next() == ts); - entries[0].second = ts; - if (entries.size() > 1 && entries[1].second == ts) { - entries.pop_front(); - } - } - - /* Returns the timestamp of the last streaming write processed. */ - state_timestamp_t get_prev_timestamp() const { - return prev_timestamp; - } - - /* Returns the highest timestamp present in the map */ - state_timestamp_t get_max_timestamp() const { - if (entries.empty()) { - return prev_timestamp; - } else { - return entries.back().second; - } - } - - /* Returns the rightmost point we've backfilled to so far */ - key_range_t::right_bound_t get_backfill_threshold() const { - if (entries.empty()) { - return key_range_t::right_bound_t(store_region.inner.left); - } else { - return entries.back().first; - } - } - - /* Returns `true` if the timestamp is consistent throughout the entire region */ - bool is_homogeneous() const { - return !entries.empty() && entries[0].first == store_region.inner.right; - } - - /* Given the next streaming write, extracts the part of it that should be applied - such that the streaming write and the backfill together will neither skip nor - duplicate any change. If the backfill hasn't sent us any changes with timestamps - equal to or greater than the next write, we wouldn't be able to determine where - to clip the write, so this will crash. */ - void clip_next_write_backfilling( - state_timestamp_t timestamp, region_t *region_out) { - guarantee(can_clip_next_write_backfilling()); - clip_next_write_paused(timestamp, region_out); - } - - /* Returns `true` if the backfill has sent us some changes with timestamps equal - to or greater than the next write. */ - bool can_clip_next_write_backfilling() const { - return !entries.empty() && - (entries.size() > 1 || entries[0].first == store_region.inner.right); - } - - /* Similar to `clip_next_write_backfilling()`, except that if the backfill - hasn't sent us any changes with timestamps equal to or greater than the next - write, it will assume that the timestamps in all yet-to-be-backfilled regions - will be equal to or greater than the next write. Therefore, this is only safe to - use in `PAUSED` mode; when we exit `PAUSED` mode, we'll ensure that the backfill - resumes from a timestamp equal to or greater than the next write. */ - void clip_next_write_paused( - state_timestamp_t timestamp, region_t *region_out) { - guarantee(timestamp == prev_timestamp.next(), "sanity check failed"); - if (entries.empty() || entries[0].second > prev_timestamp) { - *region_out = region_t::empty(); - return; - } - guarantee(entries[0].second == prev_timestamp); - *region_out = store_region; - region_out->inner.right = entries.front().first; - } - -private: - /* Same as `remote_replicator_client_t::store_->get_region()`. */ - region_t store_region; - - /* The timestamp of the last streaming write that has been applied. */ - state_timestamp_t prev_timestamp; - - /* Each entry in `entries` represents a possibly-empty range. The right bound of - the range is the `right_bound_t` on the entry; the left bound of the range is the - `right_bound_t` on the previous entry, or `store_region.inner.left` for the very - first entry. - - The entries' `right_bound_t`s and timestamps will be strictly monotonically - increasing, but the difference between adjacent entries' timestamps may be more - than one. */ - std::deque > entries; -}; - -remote_replicator_client_t::remote_replicator_client_t( - backfill_throttler_t *backfill_throttler, - const backfill_config_t &backfill_config, - backfill_progress_tracker_t *backfill_progress_tracker, - mailbox_manager_t *mailbox_manager, - const server_id_t &server_id, - backfill_throttler_t::priority_t::critical_t is_critical_priority, - - const branch_id_t &branch_id, - const remote_replicator_server_bcard_t &remote_replicator_server_bcard, - const replica_bcard_t &replica_bcard, - const server_id_t &primary_server_id, - - store_view_t *store, - branch_history_manager_t *branch_history_manager, - - signal_t *interruptor) THROWS_ONLY(interrupted_exc_t) : - - mailbox_manager_(mailbox_manager), - store_(store), - region_(store->get_region()), - branch_id_(branch_id), - mode_(backfill_mode_t::PAUSED), - - next_write_waiter_(nullptr), - - write_async_mailbox_(mailbox_manager, - std::bind(&remote_replicator_client_t::on_write_async, this, - ph::_1, ph::_2, ph::_3, ph::_4, ph::_5)), - write_sync_mailbox_(mailbox_manager, - std::bind(&remote_replicator_client_t::on_write_sync, this, - ph::_1, ph::_2, ph::_3, ph::_4, ph::_5, ph::_6)), - dummy_write_mailbox_(mailbox_manager, - std::bind(&remote_replicator_client_t::on_dummy_write, this, - ph::_1, ph::_2)), - read_mailbox_(mailbox_manager, - std::bind(&remote_replicator_client_t::on_read, this, - ph::_1, ph::_2, ph::_3, ph::_4)) -{ - guarantee(remote_replicator_server_bcard.branch == branch_id); - guarantee(remote_replicator_server_bcard.region == region_); - - auto progress_tracker = - backfill_progress_tracker->insert_progress_tracker(region_); - progress_tracker->is_ready = false; - progress_tracker->start_time = current_microtime(); - progress_tracker->source_server_id = primary_server_id; - progress_tracker->progress = 0.0; - - /* If the store is currently constructing a secondary index, wait until it finishes - before we start the backfill. We'll also check again periodically during the - backfill. */ - store->wait_until_ok_to_receive_backfill(interruptor); - - /* Subscribe to the stream of writes coming from the primary */ - remote_replicator_client_intro_t intro; - { - cond_t got_intro; - remote_replicator_client_bcard_t::intro_mailbox_t intro_mailbox( - mailbox_manager, - [&](signal_t *, const remote_replicator_client_intro_t &i) { - intro = i; - mode_ = backfill_mode_t::PAUSED; - timestamp_enforcer_.init(new timestamp_enforcer_t( - intro.streaming_begin_timestamp)); - tracker_.init(new timestamp_range_tracker_t( - region_, intro.streaming_begin_timestamp)); - got_intro.pulse(); - }); - remote_replicator_client_bcard_t our_bcard { - server_id, - intro_mailbox.get_address(), - write_async_mailbox_.get_address(), - write_sync_mailbox_.get_address(), - dummy_write_mailbox_.get_address(), - read_mailbox_.get_address() }; - registrant_.init(new registrant_t( - mailbox_manager, remote_replicator_server_bcard.registrar, our_bcard)); - wait_interruptible(&got_intro, interruptor); - registered_.pulse(); - } - - /* OK, now we're streaming writes from the primary, but they're being discarded as - they arrive because `tracker_` indicates that nothing has been backfilled. */ - - backfillee_t backfillee(mailbox_manager, branch_history_manager, store, - replica_bcard.backfiller_bcard, backfill_config, progress_tracker, interruptor); - - while (tracker_->get_backfill_threshold() != region_.inner.right) { - - /* If the store is currently constructing a secondary index, wait until it - finishes before we do the next phase of the backfill. This is the correct phase - of the backfill cycle at which to wait because we aren't currently receiving - anything from the backfiller and we aren't piling up changes in any queues. */ - store->wait_until_ok_to_receive_backfill(interruptor); - - /* Acquire the backfill throttler lock. */ - backfill_throttler_t::priority_t priority; - priority.critical = is_critical_priority; - priority.num_changes = backfillee.get_num_changes_estimate(); - backfill_throttler_t::lock_t backfill_throttler_lock( - backfill_throttler, priority, interruptor); - - state_timestamp_t backfill_start_timestamp; - { - mutex_assertion_t::acq_t mutex_assertion_acq(&mutex_assertion_); - guarantee(mode_ == backfill_mode_t::PAUSED); - mode_ = backfill_mode_t::BACKFILLING; - backfill_start_timestamp = - timestamp_enforcer_->get_latest_all_before_completed(); - rassert(backfill_start_timestamp == tracker_->get_prev_timestamp()); - } - - /* Block until backfiller reaches `backfill_start_timestamp`, to ensure that the - backfill end timestamp will be at least `backfill_start_timestamp`. In the first - time through the loop, this is important to ensure that there isn't any gap - between the backfilled data and the streamed writes; in subsequent iterations, - this is important to ensure that if we threw away part of a streamed change while - in `PAUSED` mode, we'll definitely get that part of that write as part of the - backfill. */ - { - cond_t backfiller_is_up_to_date; - mailbox_t<> ack_mbox( - mailbox_manager, - [&](signal_t *) { backfiller_is_up_to_date.pulse(); }); - send(mailbox_manager, replica_bcard.synchronize_mailbox, - backfill_start_timestamp, ack_mbox.get_address()); - wait_interruptible(&backfiller_is_up_to_date, interruptor); - } - - /* Backfill in lexicographical order until we finish or the backfill throttler - lock tells us to pause again */ - class callback_t : public backfillee_t::callback_t { - public: - callback_t(remote_replicator_client_t *p, signal_t *ps) : - parent(p), preempt_signal(ps) { } - bool on_progress(const region_map_t &chunk) THROWS_NOTHING { - mutex_assertion_t::acq_t mutex_assertion_acq(&parent->mutex_assertion_); - chunk.visit(chunk.get_domain(), - [&](const region_t ®, const version_t &vers) { - parent->tracker_->record_backfill(reg, vers.timestamp); - }); - if (parent->next_write_can_proceed(&mutex_assertion_acq)) { - if (parent->next_write_waiter_ != nullptr) { - parent->next_write_waiter_->pulse_if_not_already_pulsed(); - } - } - /* If the backfill throttler is telling us to pause, or it is no longer - ok to backfill because of secondary index construction, then interrupt - `backfillee.go()` */ - return parent->store_->check_ok_to_receive_backfill() - && !preempt_signal->is_pulsed(); - } - remote_replicator_client_t *parent; - signal_t *preempt_signal; - } callback(this, backfill_throttler_lock.get_preempt_signal()); - - backfillee.go( - &callback, - tracker_->get_backfill_threshold(), - interruptor); - - if (tracker_->get_backfill_threshold() != region_.inner.right) { - /* Switch mode to `PAUSED` so that writes can proceed while we wait to - reacquire the throttler lock */ - mutex_assertion_t::acq_t mutex_assertion_acq(&mutex_assertion_); - guarantee(mode_ == backfill_mode_t::BACKFILLING); - mode_ = backfill_mode_t::PAUSED; - if (next_write_waiter_ != nullptr) { - /* Writes can always proceed immediately in `PAUSED` mode */ - next_write_waiter_->pulse_if_not_already_pulsed(); - } - } - } - - /* Wait until writes execute up to the point where the backfill left us, so that - `tracker_->is_homogeneous()` will be `true`. */ - timestamp_enforcer_->wait_all_before(tracker_->get_max_timestamp(), interruptor); - - { - /* Lock out writes again because some of these final operations might block */ - rwlock_acq_t cleanup_rwlock_acq(&cleanup_rwlock_, access_t::write, interruptor); - mutex_assertion_t::acq_t mutex_assertion_acq(&mutex_assertion_); - - guarantee(tracker_->is_homogeneous()); - guarantee(tracker_->get_prev_timestamp() == - timestamp_enforcer_->get_latest_all_before_completed()); - -#ifndef NDEBUG - /* Sanity check that the store's metainfo is all on the correct branch and - all at the correct timestamp */ - read_token_t read_token; - store->new_read_token(&read_token); - region_map_t version = to_version_map(store->get_metainfo( - order_token_t::ignore.with_read_mode(), &read_token, region_, - interruptor)); - version_t expect(branch_id, - timestamp_enforcer_->get_latest_all_before_completed()); - version.visit(region_, - [&](const region_t ®ion, const version_t &actual) { - rassert(actual == expect, "Expected version %s for sub-range %s, but " - "got version %s.", debug_strprint(expect).c_str(), - debug_strprint(region).c_str(), debug_strprint(actual).c_str()); - }); -#endif - - /* Now we're completely up-to-date and synchronized with the primary, it's time - to create a `replica_t`. */ - replica_.init(new replica_t(mailbox_manager_, store_, branch_history_manager, - branch_id, timestamp_enforcer_->get_latest_all_before_completed())); - - tracker_.reset(); /* we don't need `tracker_` anymore */ - mode_ = backfill_mode_t::STREAMING; - - if (next_write_waiter_ != nullptr) { - /* Writes can always proceed immediately in `STREAMING` mode */ - next_write_waiter_->pulse_if_not_already_pulsed(); - } - } - - /* Now that we're completely up-to-date, tell the primary that it's OK to send us - reads and synchronous writes */ - send(mailbox_manager, intro.ready_mailbox); -} - -remote_replicator_client_t::~remote_replicator_client_t() { - /* The destructor is declared here instead of the header file so that we can see the - destructor for `timestamp_range_tracker_t` */ -} - -void remote_replicator_client_t::on_write_async( - signal_t *interruptor, - write_t &&write, - state_timestamp_t timestamp, - order_token_t order_token, - const mailbox_t<>::address_t &ack_addr) - THROWS_ONLY(interrupted_exc_t) { - wait_interruptible(®istered_, interruptor); - - timestamp_enforcer_->wait_all_before(timestamp.pred(), interruptor); - - rwlock_acq_t cleanup_rwlock_acq(&cleanup_rwlock_, access_t::read, interruptor); - - mutex_assertion_t::acq_t mutex_assertion_acq(&mutex_assertion_); - while (!next_write_can_proceed(&mutex_assertion_acq)) { - cond_t cond; - guarantee(next_write_waiter_ == nullptr); - assignment_sentry_t cond_sentry(&next_write_waiter_, &cond); - mutex_assertion_acq.reset(); - wait_interruptible(&cond, interruptor); - mutex_assertion_acq.reset(&mutex_assertion_); - } - - if (mode_ == backfill_mode_t::STREAMING) { - /* Once the constructor is done, all writes will take this branch; it's the - common case. */ - - timestamp_enforcer_->complete(timestamp); - - /* Release the locks before we start the slow part */ - mutex_assertion_acq.reset(); - cleanup_rwlock_acq.reset(); - - write_response_t dummy_response; - replica_->do_write(write, timestamp, order_token, write_durability_t::SOFT, - interruptor, &dummy_response); - } else { - region_t clip_region; - if (mode_ == backfill_mode_t::PAUSED) { - tracker_->clip_next_write_paused(timestamp, &clip_region); - } else { - tracker_->clip_next_write_backfilling(timestamp, &clip_region); - } - tracker_->record_write(clip_region, timestamp); - write_token_t token; - store_->new_write_token(&token); - timestamp_enforcer_->complete(timestamp); - - /* Release the locks before we start the slow part */ - mutex_assertion_acq.reset(); - cleanup_rwlock_acq.reset(); - - if (!region_is_empty(clip_region)) { - region_map_t new_metainfo( - clip_region, binary_blob_t(version_t(branch_id_, timestamp))); - write_t subwrite; - if (write.shard(clip_region, &subwrite)) { -#ifndef NDEBUG - metainfo_checker_t checker(clip_region, - [&](const region_t &, const binary_blob_t &bb) { - rassert(bb == binary_blob_t( - version_t(branch_id_, timestamp.pred()))); - }); -#endif - write_response_t dummy_response; - store_->write(DEBUG_ONLY(checker, ) new_metainfo, subwrite, - &dummy_response, write_durability_t::SOFT, timestamp, order_token, - &token, interruptor); - } else { - /* The write doesn't actually affect any keys in this region, but we - still have to update the metainfo for consistency's sake. */ - store_->set_metainfo(new_metainfo, order_token, &token, - write_durability_t::SOFT, interruptor); - } - } - } - - send(mailbox_manager_, ack_addr); -} - -void remote_replicator_client_t::on_write_sync( - signal_t *interruptor, - const write_t &write, - state_timestamp_t timestamp, - order_token_t order_token, - write_durability_t durability, - const mailbox_t::address_t &ack_addr) - THROWS_ONLY(interrupted_exc_t) { - /* The current implementation of the dispatcher will never send us an async write - once it's started sending sync writes, but we don't want to rely on that detail, so - we pass sync writes through the timestamp enforcer too. */ - timestamp_enforcer_->complete(timestamp); - - write_response_t response; - replica_->do_write( - write, timestamp, order_token, durability, - interruptor, &response); - send(mailbox_manager_, ack_addr, response); -} - -void remote_replicator_client_t::on_dummy_write( - signal_t *interruptor, - const mailbox_t::address_t &ack_addr) - THROWS_ONLY(interrupted_exc_t) { - write_response_t response; - replica_->do_dummy_write(interruptor, &response); - send(mailbox_manager_, ack_addr, response); -} - -void remote_replicator_client_t::on_read( - signal_t *interruptor, - const read_t &read, - state_timestamp_t min_timestamp, - const mailbox_t::address_t &ack_addr) - THROWS_ONLY(interrupted_exc_t) { - read_response_t response; - replica_->do_read(read, min_timestamp, interruptor, &response); - send(mailbox_manager_, ack_addr, response); -} - -bool remote_replicator_client_t::next_write_can_proceed( - mutex_assertion_t::acq_t *mutex_assertion_acq) { - mutex_assertion_acq->assert_is_holding(&mutex_assertion_); - return mode_ != backfill_mode_t::BACKFILLING || - tracker_->can_clip_next_write_backfilling(); -} - diff --git a/src/clustering/immediate_consistency/remote_replicator_client.hpp b/src/clustering/immediate_consistency/remote_replicator_client.hpp deleted file mode 100644 index 66b17a15109..00000000000 --- a/src/clustering/immediate_consistency/remote_replicator_client.hpp +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_IMMEDIATE_CONSISTENCY_REMOTE_REPLICATOR_CLIENT_HPP_ -#define CLUSTERING_IMMEDIATE_CONSISTENCY_REMOTE_REPLICATOR_CLIENT_HPP_ - -#include - -#include "clustering/generic/registrant.hpp" -#include "clustering/immediate_consistency/backfill_throttler.hpp" -#include "clustering/immediate_consistency/remote_replicator_metadata.hpp" -#include "clustering/immediate_consistency/replica.hpp" -#include "concurrency/coro_pool.hpp" -#include "concurrency/queue/disk_backed_queue_wrapper.hpp" -#include "concurrency/semaphore.hpp" - -class backfill_progress_tracker_t; - -/* `remote_replicator_client_t` contacts a `remote_replicator_server_t` on another server -to sign up for writes to a given shard, and then applies them to a `store_t` on the same -server. It also performs a backfill to initialize the state of the `store_t`, and handles -the transition between the backfill and the streaming writes. - -There is one `remote_replicator_client_t` on each secondary replica server of each shard. -`secondary_execution_t` constructs it. */ - -class remote_replicator_client_t { -public: - /* Here's how the backfill works: - - 1. We sign up for a stream of writes from the `remote_replicator_server_t`. Initially - we let the writes queue up (in the form of blocked coroutines) as they arrive. - 2. We start backfilling from the `replica_t`. We ensure that the `replica_t` is on - the same branch as the `remote_replicator_server_t`, and that the backfill's end - timestamp is greater than or equal to the stream's begin timestamp. - 3. Suppose that the backfill sends us the range [-inf, K) at timestamp T and the - range [K, L) at timestamp T+1. Then we unblock the streaming write with timestamp - T+1 but only apply it to range [-inf, K). Then when the backfill sneds us the - range [L, M) at timestamp T+2 we unblock the streaming write with timestamp T+2 - and apply it to the range [-inf, L). This way, every change gets applied exactly - once, either as part of a streaming write or as part of a backfill. This - procedure relies on the fact that the backfill proceeds from left to right - monotonically and the versions it sends increase monotonically in timestamps. - 4. Any given write only remains queued up for as long as it takes for it to be - applied on the `replica_t` and then for another chunk with the updated timestamp - to be sent over the network. - 5. If the `backfill_throttler_t` tells us to pause, then while the backfill is paused - we unconditionally apply writes to the area we've already backfilled. When the - pause ends, we resume the backfill but ensure that all future backfilled chunks - have timestamps greater than or equal to the timestamp of the last write - backfilled during the pause. This is because during the pause we already - discarded the parts of the streaming writes that applied to the unbackfilled - area, so we have to receive those changes as part of the backfill or we won't - get them at all. - - The `remote_replicator_client_t` constructor blocks until this entire process is - complete. The backfilled data will be safely flushed to disk by the time it returns. - */ - - remote_replicator_client_t( - backfill_throttler_t *backfill_throttler, - const backfill_config_t &backfill_config, - backfill_progress_tracker_t *backfill_progress_tracker, - mailbox_manager_t *mailbox_manager, - const server_id_t &server_id, - backfill_throttler_t::priority_t::critical_t is_critical_priority, - - const branch_id_t &branch_id, - const remote_replicator_server_bcard_t &remote_replicator_server_bcard, - const replica_bcard_t &replica_bcard, - const server_id_t &primary_server_id, - - store_view_t *store, - branch_history_manager_t *branch_history_manager, - - signal_t *interruptor) THROWS_ONLY(interrupted_exc_t); - - ~remote_replicator_client_t(); - -private: - class timestamp_range_tracker_t; - - /* `on_write_async()`, `on_write_sync()`, `on_dummy_write()`, and `on_read()` - are mailbox callbacks for `write_async_mailbox_`, `write_sync_mailbox_`, - `dummy_write_mailbox_` and `read_mailbox_`. */ - void on_write_async( - signal_t *interruptor, - write_t &&write, - state_timestamp_t timestamp, - order_token_t order_token, - const mailbox_t<>::address_t &ack_addr) - THROWS_ONLY(interrupted_exc_t); - - void on_write_sync( - signal_t *interruptor, - const write_t &write, - state_timestamp_t timestamp, - order_token_t order_token, - write_durability_t durability, - const mailbox_t::address_t &ack_addr) - THROWS_ONLY(interrupted_exc_t); - - void on_dummy_write( - signal_t *interruptor, - const mailbox_t::address_t &ack_addr) - THROWS_ONLY(interrupted_exc_t); - - void on_read( - signal_t *interruptor, - const read_t &read, - state_timestamp_t min_timestamp, - const mailbox_t::address_t &ack_addr) - THROWS_ONLY(interrupted_exc_t); - - mailbox_manager_t *const mailbox_manager_; - store_view_t *const store_; - region_t const region_; /* same as `store_->get_region()` */ - branch_id_t const branch_id_; - - /* During the constructor, we alternate between `PAUSED` and `BACKFILLING`. When the - constructor is done, we switch to `STREAMING` mode and stay there. */ - enum class backfill_mode_t { - /* We haven't backfilled completely, but we aren't currently backfilling either. - Usually this means we're waiting for the backfill throttler. However, we will - still accept streaming writes in the regions we've already backfilled. */ - PAUSED, - /* We're actively receiving backfill items over the network. Whenever the - backfill reaches a given timestamp, we'll also apply all writes at or before that - timestamp; this is mediated by `backfilling_thresholds_`. */ - BACKFILLING, - /* We finished backfilling and we're just applying writes as they arrive. */ - STREAMING - }; - backfill_mode_t mode_; - - /* `timestamp_range_tracker_t` is essentially a `region_map_t`, - but in a different format and optimized for this specific use case. The domain of - `tracker_` is the region that has been backfilled thus far; the values are equal to - the current timestamps in the B-tree metainfo. `tracker_` is used to make sure that - every change gets applied either as a streaming change or as a backfilled change but - not as both. `tracker_` exists only during the backfill; it gets destroyed after the - backfill is over. */ - scoped_ptr_t tracker_; - - /* Returns `true` if the next write can be applied now, instead of having to wait for - the backfill to make more progress. */ - bool next_write_can_proceed(mutex_assertion_t::acq_t *mutex_acq); - - /* If the next write cannot proceed, it will set `next_write_waiter_` and wait for it - to be pulsed. */ - cond_t *next_write_waiter_; - - /* `timestamp_enforcer_` is used to order writes as they arrive. `replica_` will do - its own ordering of writes, so `timestamp_enforcer_` is only important during the - constructor before `replica_` has been created. */ - scoped_ptr_t timestamp_enforcer_; - - /* `replica_` is created at the end of the constructor, once the backfill is over. */ - scoped_ptr_t replica_; - - /* `mutex_assertion_` protects `mode_`, `tracker_`, `next_write_waiter_`, - `timestamp_enforcer_`, and `replica_`; but we aren't particularly careful about - always acquiring it before accessing those variables. */ - mutex_assertion_t mutex_assertion_; - - /* `registered_` is pulsed once `timestamp_enforcer_` is set up. So - `on_write_async()` has to wait for it before proceeding. */ - cond_t registered_; - - /* `cleanup_rwlock_` is used to temporarily lock out writes when doing the very last - phase of the backfill. Writes acquire it in read mode; the last phase of the backfill - acquires it in write mode. */ - rwlock_t cleanup_rwlock_; - - remote_replicator_client_bcard_t::write_async_mailbox_t write_async_mailbox_; - remote_replicator_client_bcard_t::write_sync_mailbox_t write_sync_mailbox_; - remote_replicator_client_bcard_t::dummy_write_mailbox_t dummy_write_mailbox_; - remote_replicator_client_bcard_t::read_mailbox_t read_mailbox_; - - /* We use `registrant_` to subscribe to a stream of reads and writes from the - dispatcher via the `remote_replicator_server_t`. */ - scoped_ptr_t > registrant_; -}; - -#endif /* CLUSTERING_IMMEDIATE_CONSISTENCY_REMOTE_REPLICATOR_HPP_ */ - diff --git a/src/clustering/immediate_consistency/remote_replicator_metadata.cc b/src/clustering/immediate_consistency/remote_replicator_metadata.cc deleted file mode 100644 index 52e40610b12..00000000000 --- a/src/clustering/immediate_consistency/remote_replicator_metadata.cc +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/immediate_consistency/remote_replicator_metadata.hpp" - -RDB_IMPL_SERIALIZABLE_2_FOR_CLUSTER( - remote_replicator_client_intro_t, - streaming_begin_timestamp, ready_mailbox); -RDB_IMPL_SERIALIZABLE_6_FOR_CLUSTER( - remote_replicator_client_bcard_t, - server_id, intro_mailbox, write_async_mailbox, write_sync_mailbox, - dummy_write_mailbox, read_mailbox); -RDB_IMPL_SERIALIZABLE_3_FOR_CLUSTER( - remote_replicator_server_bcard_t, - branch, region, registrar); - diff --git a/src/clustering/immediate_consistency/remote_replicator_metadata.hpp b/src/clustering/immediate_consistency/remote_replicator_metadata.hpp deleted file mode 100644 index b62c60909f1..00000000000 --- a/src/clustering/immediate_consistency/remote_replicator_metadata.hpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_IMMEDIATE_CONSISTENCY_REMOTE_REPLICATOR_METADATA_HPP_ -#define CLUSTERING_IMMEDIATE_CONSISTENCY_REMOTE_REPLICATOR_METADATA_HPP_ - -#include "clustering/generic/registration_metadata.hpp" -#include "clustering/immediate_consistency/history.hpp" -#include "rdb_protocol/protocol.hpp" -#include "rpc/connectivity/server_id.hpp" - -class remote_replicator_client_intro_t { -public: - typedef mailbox_t<> ready_mailbox_t; - - state_timestamp_t streaming_begin_timestamp; - ready_mailbox_t::address_t ready_mailbox; -}; - -RDB_DECLARE_SERIALIZABLE(remote_replicator_client_intro_t); - -class remote_replicator_client_bcard_t { -public: - typedef mailbox_t< - remote_replicator_client_intro_t - > intro_mailbox_t; - typedef mailbox_t< - write_t, state_timestamp_t, order_token_t, - mailbox_t<>::address_t - > write_async_mailbox_t; - typedef mailbox_t< - write_t, state_timestamp_t, order_token_t, write_durability_t, - mailbox_t::address_t - > write_sync_mailbox_t; - typedef mailbox_t< - mailbox_t::address_t - > dummy_write_mailbox_t; - typedef mailbox_t< - read_t, state_timestamp_t, - mailbox_t::address_t - > read_mailbox_t; - - server_id_t server_id; - intro_mailbox_t::address_t intro_mailbox; - write_async_mailbox_t::address_t write_async_mailbox; - write_sync_mailbox_t::address_t write_sync_mailbox; - dummy_write_mailbox_t::address_t dummy_write_mailbox; - read_mailbox_t::address_t read_mailbox; -}; - -RDB_DECLARE_SERIALIZABLE(remote_replicator_client_bcard_t); - -class remote_replicator_server_bcard_t { -public: - branch_id_t branch; - region_t region; - registrar_business_card_t registrar; -}; - -RDB_DECLARE_SERIALIZABLE(remote_replicator_server_bcard_t); - -#endif /* CLUSTERING_IMMEDIATE_CONSISTENCY_REMOTE_REPLICATOR_METADATA_HPP_ */ - diff --git a/src/clustering/immediate_consistency/remote_replicator_server.cc b/src/clustering/immediate_consistency/remote_replicator_server.cc deleted file mode 100644 index 82ffc4f88c3..00000000000 --- a/src/clustering/immediate_consistency/remote_replicator_server.cc +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/immediate_consistency/remote_replicator_server.hpp" - -remote_replicator_server_t::remote_replicator_server_t( - mailbox_manager_t *_mailbox_manager, - primary_dispatcher_t *_primary) : - mailbox_manager(_mailbox_manager), - primary(_primary), - registrar(mailbox_manager, this) - { } - -remote_replicator_server_t::proxy_replica_t::proxy_replica_t( - remote_replicator_server_t *_parent, - const remote_replicator_client_bcard_t &_client_bcard, - UNUSED signal_t *interruptor) : - client_bcard(_client_bcard), parent(_parent), is_ready(false), - ready_mailbox( - parent->mailbox_manager, - std::bind(&proxy_replica_t::on_ready, this, ph::_1)) -{ - state_timestamp_t first_timestamp; - registration = make_scoped( - parent->primary, this, client_bcard.server_id, 1.0, &first_timestamp); - send(parent->mailbox_manager, client_bcard.intro_mailbox, - remote_replicator_client_intro_t { - first_timestamp, - ready_mailbox.get_address() }); -} - -void remote_replicator_server_t::proxy_replica_t::do_read( - const read_t &read, - state_timestamp_t min_timestamp, - signal_t *interruptor, - read_response_t *response_out) { - guarantee(is_ready); - cond_t got_response; - mailbox_t response_mailbox( - parent->mailbox_manager, - [&](signal_t *, const read_response_t &response) { - *response_out = response; - got_response.pulse(); - }); - send(parent->mailbox_manager, client_bcard.read_mailbox, - read, min_timestamp, response_mailbox.get_address()); - wait_interruptible(&got_response, interruptor); -} - -void remote_replicator_server_t::proxy_replica_t::do_write_sync( - const write_t &write, - state_timestamp_t timestamp, - order_token_t order_token, - write_durability_t durability, - signal_t *interruptor, - write_response_t *response_out) { - guarantee(is_ready); - cond_t got_response; - mailbox_t response_mailbox( - parent->mailbox_manager, - [&](signal_t *, const write_response_t &response) { - *response_out = response; - got_response.pulse(); - }); - send(parent->mailbox_manager, client_bcard.write_sync_mailbox, - write, timestamp, order_token, durability, response_mailbox.get_address()); - wait_interruptible(&got_response, interruptor); -} - -void remote_replicator_server_t::proxy_replica_t::do_dummy_write( - signal_t *interruptor, - write_response_t *response_out) { - guarantee(is_ready); - cond_t got_response; - mailbox_t response_mailbox( - parent->mailbox_manager, - [&](signal_t *, const write_response_t &response) { - *response_out = response; - got_response.pulse(); - }); - send(parent->mailbox_manager, client_bcard.dummy_write_mailbox, - response_mailbox.get_address()); - wait_interruptible(&got_response, interruptor); -} - -void remote_replicator_server_t::proxy_replica_t::do_write_async( - const write_t &write, - state_timestamp_t timestamp, - order_token_t order_token, - signal_t *interruptor) { - cond_t got_ack; - mailbox_t<> ack_mailbox( - parent->mailbox_manager, - [&](signal_t *) { got_ack.pulse(); }); - send(parent->mailbox_manager, client_bcard.write_async_mailbox, - write, timestamp, order_token, ack_mailbox.get_address()); - wait_interruptible(&got_ack, interruptor); -} - -void remote_replicator_server_t::proxy_replica_t::on_ready(signal_t *) { - // Can't block here, or we would need an auto drainer. - ASSERT_FINITE_CORO_WAITING; - guarantee(!is_ready); - is_ready = true; - registration->mark_ready(); -} - diff --git a/src/clustering/immediate_consistency/remote_replicator_server.hpp b/src/clustering/immediate_consistency/remote_replicator_server.hpp deleted file mode 100644 index e601e25cf75..00000000000 --- a/src/clustering/immediate_consistency/remote_replicator_server.hpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_IMMEDIATE_CONSISTENCY_REMOTE_REPLICATOR_SERVER_HPP_ -#define CLUSTERING_IMMEDIATE_CONSISTENCY_REMOTE_REPLICATOR_SERVER_HPP_ - -#include "clustering/generic/registrar.hpp" -#include "clustering/immediate_consistency/primary_dispatcher.hpp" -#include "clustering/immediate_consistency/remote_replicator_metadata.hpp" - -/* `remote_replicator_server_t` takes reads and writes from the `primary_dispatcher_t` -and sends them over the network to `remote_replicator_client_t`s on other machines. - -There is one `remote_replicator_server_t` per shard. It lives on the primary replica -server with the `primary_dispatcher_t`. */ - -class remote_replicator_server_t { -public: - remote_replicator_server_t( - mailbox_manager_t *mailbox_manager, - primary_dispatcher_t *primary); - - remote_replicator_server_bcard_t get_bcard() { - return remote_replicator_server_bcard_t { - primary->get_branch_id(), - primary->get_branch_birth_certificate().get_region(), - registrar.get_business_card() }; - } - -private: - /* Whenever a `remote_replicator_client_t` connects, the `registrar` will construct a - `proxy_replica_t` to represent it. */ - class proxy_replica_t : public primary_dispatcher_t::dispatchee_t { - public: - proxy_replica_t( - remote_replicator_server_t *parent, - const remote_replicator_client_bcard_t &client_bcard, - signal_t *interruptor); - - bool is_primary() const { - return false; - } - - void do_read( - const read_t &read, - state_timestamp_t min_timestamp, - signal_t *interruptor, - read_response_t *response_out); - - void do_write_sync( - const write_t &write, - state_timestamp_t timestamp, - order_token_t order_token, - write_durability_t durability, - signal_t *interruptor, - write_response_t *response_out); - - void do_write_async( - const write_t &write, - state_timestamp_t timestamp, - order_token_t order_token, - signal_t *interruptor); - - void do_dummy_write( - signal_t *interruptor, - write_response_t *response_out); - - private: - void on_ready(signal_t *interruptor); - - remote_replicator_client_bcard_t client_bcard; - remote_replicator_server_t *parent; - bool is_ready; - - // The destruction order matters: The `ready_mailbox` callback assumes - // that `registration` is still valid. - scoped_ptr_t registration; - remote_replicator_client_intro_t::ready_mailbox_t ready_mailbox; - }; - - mailbox_manager_t *mailbox_manager; - primary_dispatcher_t *primary; - - registrar_t< - remote_replicator_client_bcard_t, - remote_replicator_server_t *, - proxy_replica_t> registrar; -}; - -#endif /* CLUSTERING_IMMEDIATE_CONSISTENCY_REMOTE_REPLICATOR_SERVER_HPP_ */ - diff --git a/src/clustering/immediate_consistency/replica.cc b/src/clustering/immediate_consistency/replica.cc deleted file mode 100644 index c888a533919..00000000000 --- a/src/clustering/immediate_consistency/replica.cc +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/immediate_consistency/replica.hpp" - -#include "rdb_protocol/protocol.hpp" -#include "store_view.hpp" - -replica_t::replica_t( - mailbox_manager_t *_mailbox_manager, - store_view_t *_store, - branch_history_manager_t *_bhm, - const branch_id_t &_branch_id, - state_timestamp_t _timestamp) : - mailbox_manager(_mailbox_manager), - store(_store), - branch_id(_branch_id), - start_enforcer(_timestamp), - end_enforcer(_timestamp), - backfiller(_mailbox_manager, _bhm, _store), - synchronize_mailbox(mailbox_manager, - std::bind(&replica_t::on_synchronize, this, ph::_1, ph::_2, ph::_3)) - { } - -void replica_t::do_read( - const read_t &read, - state_timestamp_t min_timestamp, - signal_t *interruptor, - read_response_t *response_out) { - assert_thread(); - rassert(region_is_superset(store->get_region(), read.get_region())); - rassert(!region_is_empty(read.get_region())); - - if (boost::get(&read.read) != nullptr) { - read_response_t response; - response.response = dummy_read_response_t(); - response.n_shards = 1; - *response_out = std::move(response); - return; - } - - // Wait until all writes that the read needs to see have completed. - end_enforcer.wait_all_before(min_timestamp, interruptor); - - // Leave the token empty. We're enforcing ordering ourselves through `end_enforcer`. - read_token_t read_token; - -#ifndef NDEBUG - metainfo_checker_t metainfo_checker(store->get_region(), - [](const region_t &, const binary_blob_t &) { }); -#endif - - // Perform the operation - store->read( - DEBUG_ONLY(metainfo_checker, ) - read, - response_out, - &read_token, - interruptor); -} - -void replica_t::do_write( - const write_t &write, - state_timestamp_t timestamp, - order_token_t order_token, - write_durability_t durability, - signal_t *interruptor, - write_response_t *response_out) { - assert_thread(); - rassert(region_is_superset(store->get_region(), write.get_region())); - rassert(!region_is_empty(write.get_region())); - order_token.assert_write_mode(); - - write_token_t write_token; - { - /* Wait until it's our turn to go. */ - start_enforcer.wait_all_before(timestamp.pred(), interruptor); - - /* Claim a write token. This determines the order in which writes will be - processed by the underlying store. */ - store->new_write_token(&write_token); - - /* Notify the next write (if any) that it's their turn to go. */ - start_enforcer.complete(timestamp); - } - -#ifndef NDEBUG - metainfo_checker_t metainfo_checker(store->get_region(), - [&](const region_t &, const binary_blob_t &bb) { - rassert(binary_blob_t::get(bb).timestamp <= timestamp.pred()); - }); -#endif - - // Perform the operation - store->write( - DEBUG_ONLY(metainfo_checker, ) - region_map_t( - store->get_region(), - binary_blob_t(version_t(branch_id, timestamp))), - write, - response_out, - durability, - timestamp, - order_token, - &write_token, - interruptor); - - /* Notify reads that were waiting for this write that it's OK to go */ - end_enforcer.complete(timestamp); -} - -void replica_t::do_dummy_write( - UNUSED signal_t *interruptor, - write_response_t *response_out) { - assert_thread(); - response_out->response = dummy_write_response_t(); -} - -void replica_t::on_synchronize( - signal_t *interruptor, - state_timestamp_t timestamp, - mailbox_t<>::address_t ack_addr) { - end_enforcer.wait_all_before(timestamp, interruptor); - send(mailbox_manager, ack_addr); -} - diff --git a/src/clustering/immediate_consistency/replica.hpp b/src/clustering/immediate_consistency/replica.hpp deleted file mode 100644 index d54e0eddada..00000000000 --- a/src/clustering/immediate_consistency/replica.hpp +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_IMMEDIATE_CONSISTENCY_REPLICA_HPP_ -#define CLUSTERING_IMMEDIATE_CONSISTENCY_REPLICA_HPP_ - -#include "clustering/immediate_consistency/backfill_metadata.hpp" -#include "clustering/immediate_consistency/backfiller.hpp" -#include "concurrency/timestamp_enforcer.hpp" - -/* `replica_t` represents a replica of a shard which is currently tracking changes to a -given branch. `local_replicator_t` and `remote_replicator_client_t` construct a -`replica_t` after they finish initializing the state of the `store_t` to match the -ongoing writes from the `primary_dispatcher_t`. `replica_t` takes care of actually -applying those ongoing writes and reads to the `store_t`. It also takes care of providing -backfills to new `remote_replicator_client_t`s that are trying to start up. */ - -class replica_t : public home_thread_mixin_debug_only_t { -public: - replica_t( - mailbox_manager_t *mailbox_manager, - store_view_t *store, - branch_history_manager_t *bhm, - const branch_id_t &branch_id, - state_timestamp_t timestamp); - - replica_bcard_t get_replica_bcard() { - return replica_bcard_t { - synchronize_mailbox.get_address(), - branch_id, - backfiller.get_business_card() - }; - } - - void do_read( - const read_t &read, - state_timestamp_t token, - signal_t *interruptor, - read_response_t *response_out); - - /* Warning: If you interrupt `do_write()`, the `replica_t` will be left in an - undefined state, and you should destroy the `replica_t` soon after. */ - void do_write( - const write_t &write, - state_timestamp_t timestamp, - order_token_t order_token, - write_durability_t durability, - signal_t *interruptor, - write_response_t *response_out); - - void do_dummy_write( - signal_t *interruptor, - write_response_t *response_out); - -private: - void on_synchronize( - signal_t *interruptor, - state_timestamp_t timestamp, - mailbox_t<>::address_t); - - mailbox_manager_t *const mailbox_manager; - store_view_t *const store; - branch_id_t const branch_id; - - /* A timestamp is completed in `start_enforcer` when the corresponding write has - acquired a token from the store, and in `end_enforcer` when the corresponding write - has completed. */ - timestamp_enforcer_t start_enforcer, end_enforcer; - - backfiller_t backfiller; - - replica_bcard_t::synchronize_mailbox_t synchronize_mailbox; -}; - -#endif /* CLUSTERING_IMMEDIATE_CONSISTENCY_REPLICA_HPP_ */ - diff --git a/src/clustering/immediate_consistency/standard_backfill_throttler.cc b/src/clustering/immediate_consistency/standard_backfill_throttler.cc deleted file mode 100644 index 4f81d3dc696..00000000000 --- a/src/clustering/immediate_consistency/standard_backfill_throttler.cc +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/immediate_consistency/standard_backfill_throttler.hpp" - -#include "concurrency/cross_thread_signal.hpp" -#include "concurrency/wait_any.hpp" - -static const size_t max_active_backfills = 8; - -standard_backfill_throttler_t::~standard_backfill_throttler_t() { - guarantee(active.empty()); - guarantee(waiting.empty()); -} - -void standard_backfill_throttler_t::enter(lock_t *lock, signal_t *interruptor_on_lock) { - cross_thread_signal_t interruptor_on_home(interruptor_on_lock, home_thread()); - on_thread_t thread_switcher(home_thread()); - scoped_ptr_t mutex_acq( - new new_mutex_acq_t(&mutex, &interruptor_on_home)); - - if (active.size() < max_active_backfills) { - /* There is no contention, so we can start right away */ - active.insert(std::make_pair(lock->priority, lock)); - - } else { - /* Insert `cond` into the waiting queue. */ - cond_t cond; - auto it = waiting.insert(std::make_pair( - lock->priority, std::make_pair(lock, &cond))); - - /* If there's a lower-priority backfill that's already active, preempt it. This - only pulses the preempt signal; it doesn't immediately remove it from `active`. - */ - auto w_it = waiting.rbegin(); - auto a_it = active.begin(); - while (w_it != waiting.rend() && a_it != active.end() - && a_it->first < w_it->first) { - { - on_thread_t thread_switcher_2(a_it->second->home_thread()); - if (!a_it->second->get_preempt_signal()->is_pulsed()) { - preempt(a_it->second); - } - } - ++w_it; - ++a_it; - } - - /* Release the mutex before we wait for the cond */ - mutex_acq.reset(); - - /* Wait until it's our turn to go. Note that we don't throw `interrupted_exc_t` - because we still need to clean up. */ - wait_any_t waiter(&cond, &interruptor_on_home); - waiter.wait_lazily_unordered(); - - mutex_acq.init(new new_mutex_acq_t(&mutex)); /* again, not interruptible */ - ASSERT_NO_CORO_WAITING; - if (cond.is_pulsed()) { - guarantee(active.count(std::make_pair(lock->priority, lock)) == 1); - } else { - waiting.erase(it); - throw interrupted_exc_t(); - } - } -} - -void standard_backfill_throttler_t::exit(lock_t *lock) { - on_thread_t thread_switcher(home_thread()); - new_mutex_acq_t acq(&mutex); - ASSERT_NO_CORO_WAITING; - - /* Find the entry corresponding to this particular backfill. */ - auto it = active.find(std::make_pair(lock->priority, lock)); - guarantee(it != active.end()); - active.erase(it); - - /* Find the highest-priority backfill that's waiting to start */ - auto jt = waiting.end(); - if (jt != waiting.begin()) { - --jt; - - /* Pulse the `cond_t` so that `enter()` can return */ - jt->second.second->pulse(); - - /* Transfer the backfill from `active` to `waiting` */ - active.insert(std::make_pair(jt->first, jt->second.first)); - waiting.erase(jt); - } -} - diff --git a/src/clustering/immediate_consistency/standard_backfill_throttler.hpp b/src/clustering/immediate_consistency/standard_backfill_throttler.hpp deleted file mode 100644 index 21141c5e9e2..00000000000 --- a/src/clustering/immediate_consistency/standard_backfill_throttler.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_IMMEDIATE_CONSISTENCY_STANDARD_BACKFILL_THROTTLER_HPP_ -#define CLUSTERING_IMMEDIATE_CONSISTENCY_STANDARD_BACKFILL_THROTTLER_HPP_ - -#include - -#include "clustering/immediate_consistency/backfill_throttler.hpp" -#include "concurrency/new_mutex.hpp" - -/* `standard_backfill_throttler_t` is the `backfill_throttler_t` that is used in -production. It allows a fixed number of backfills total (currently 8); if there are more -than 8 backfills trying to run, it will always allow the highest-priority backfills to go -first, preempting the lower-priority backfills if necessary. */ - -class standard_backfill_throttler_t : public backfill_throttler_t { -public: - standard_backfill_throttler_t() { } - ~standard_backfill_throttler_t(); - -private: - void enter(lock_t *lock, signal_t *interruptor); - void exit(lock_t *lock); - - std::multimap > waiting; - std::set > active; - - new_mutex_t mutex; -}; - -#endif /* CLUSTERING_IMMEDIATE_CONSISTENCY_STANDARD_BACKFILL_THROTTLER_HPP_ */ - diff --git a/src/clustering/query_routing/direct_query_server.cc b/src/clustering/query_routing/direct_query_server.cc deleted file mode 100644 index 85416882a2b..00000000000 --- a/src/clustering/query_routing/direct_query_server.cc +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/query_routing/direct_query_server.hpp" - -#include "protocol_api.hpp" -#include "store_view.hpp" - -direct_query_server_t::direct_query_server_t( - mailbox_manager_t *mm, - store_view_t *svs_) : - mailbox_manager(mm), - svs(svs_), - read_mailbox(mm, std::bind(&direct_query_server_t::on_read, this, - ph::_1, ph::_2, ph::_3)) - { } - -direct_query_bcard_t direct_query_server_t::get_bcard() { - return direct_query_bcard_t(read_mailbox.get_address()); -} - -void direct_query_server_t::on_read( - signal_t *interruptor, - const read_t &read, - const mailbox_addr_t &cont) { - - // Shortcut: Dummy reads for checking table status are fulfilled - // without hitting the store. - if (boost::get(&read.read) != nullptr) { - read_response_t response; - response.response = dummy_read_response_t(); - response.n_shards = 1; - send(mailbox_manager, cont, response); - return; - } - - try { - /* Leave the token empty. We're not actually interested in ordering here. */ - read_token_t token; - -#ifndef NDEBUG - metainfo_checker_t metainfo_checker(svs->get_region(), - [](const region_t &, const binary_blob_t &) { }); -#endif - - read_response_t response; - svs->read(DEBUG_ONLY(metainfo_checker, ) - read, - &response, - &token, - interruptor); - send(mailbox_manager, cont, response); - } catch (const interrupted_exc_t &) { - /* ignore */ - } -} diff --git a/src/clustering/query_routing/direct_query_server.hpp b/src/clustering/query_routing/direct_query_server.hpp deleted file mode 100644 index 401b7f66fec..00000000000 --- a/src/clustering/query_routing/direct_query_server.hpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_QUERY_ROUTING_DIRECT_QUERY_SERVER_HPP_ -#define CLUSTERING_QUERY_ROUTING_DIRECT_QUERY_SERVER_HPP_ - -#include "clustering/query_routing/metadata.hpp" -#include "concurrency/fifo_checker.hpp" - -class store_view_t; - -/* For each primary or secondary replica of each shard, there is a -`direct_query_server_t`. The `direct_query_server_t` allows the `table_query_server_t` to -bypass the `broadcaster_t` and read directly from the B-tree itself. This reduces network -traffic and is possible even when the primary replica is unavailable, but the data it -returns might be out of date. */ - -class direct_query_server_t { -public: - direct_query_server_t( - mailbox_manager_t *mm, - store_view_t *svs); - - direct_query_bcard_t get_bcard(); - -private: - void on_read( - signal_t *interruptor, - const read_t &, - const mailbox_addr_t &); - - mailbox_manager_t *mailbox_manager; - store_view_t *svs; - - order_source_t order_source; // TODO: order_token_t::ignore - - direct_query_bcard_t::read_mailbox_t read_mailbox; -}; - -#endif /* CLUSTERING_QUERY_ROUTING_DIRECT_QUERY_SERVER_HPP_ */ - diff --git a/src/clustering/query_routing/metadata.cc b/src/clustering/query_routing/metadata.cc deleted file mode 100644 index 9f2f6ceca22..00000000000 --- a/src/clustering/query_routing/metadata.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/query_routing/metadata.hpp" - -#include "containers/archive/optional.hpp" -#include "containers/archive/stl_types.hpp" -#include "containers/archive/versioned.hpp" - -RDB_IMPL_SERIALIZABLE_4_FOR_CLUSTER( - primary_query_bcard_t::read_request_t, - read, order_token, fifo_token, cont_addr); - -RDB_IMPL_SERIALIZABLE_4_FOR_CLUSTER( - primary_query_bcard_t::write_request_t, write, order_token, fifo_token, cont_addr); - -RDB_IMPL_SERIALIZABLE_2_FOR_CLUSTER(primary_query_bcard_t, region, multi_client); - -RDB_IMPL_EQUALITY_COMPARABLE_2(primary_query_bcard_t, region, multi_client); - -RDB_IMPL_SERIALIZABLE_1_FOR_CLUSTER(direct_query_bcard_t, read_mailbox); -RDB_IMPL_EQUALITY_COMPARABLE_1(direct_query_bcard_t, read_mailbox); - -RDB_IMPL_SERIALIZABLE_3_FOR_CLUSTER(table_query_bcard_t, region, primary, direct); -RDB_IMPL_EQUALITY_COMPARABLE_3(table_query_bcard_t, region, primary, direct); - diff --git a/src/clustering/query_routing/metadata.hpp b/src/clustering/query_routing/metadata.hpp deleted file mode 100644 index 7aeb2b52eb8..00000000000 --- a/src/clustering/query_routing/metadata.hpp +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_QUERY_ROUTING_METADATA_HPP_ -#define CLUSTERING_QUERY_ROUTING_METADATA_HPP_ - -#include - -#include "errors.hpp" -#include - -#include "clustering/generic/multi_client_metadata.hpp" -#include "clustering/generic/registration_metadata.hpp" -#include "concurrency/fifo_checker.hpp" -#include "concurrency/fifo_enforcer.hpp" -#include "rdb_protocol/protocol.hpp" -#include "rpc/mailbox/typed.hpp" - -/* There is one `primary_query_bcard_t` per branch. It's created by the primary; parsers -use it to find the master. */ - -class primary_query_bcard_t { -public: - class read_request_t { - public: - read_request_t() { } - read_request_t( - const read_t &r, - order_token_t ot, - fifo_enforcer_read_token_t ft, - const mailbox_addr_t> &ca) : - read(r), order_token(ot), fifo_token(ft), cont_addr(ca) { } - read_t read; - order_token_t order_token; - fifo_enforcer_read_token_t fifo_token; - mailbox_addr_t> cont_addr; - }; - - class write_request_t { - public: - write_request_t() { } - write_request_t( - const write_t &w, - order_token_t ot, - fifo_enforcer_write_token_t ft, - const mailbox_addr_t> &ca) : - write(w), order_token(ot), fifo_token(ft), cont_addr(ca) { } - write_t write; - order_token_t order_token; - fifo_enforcer_write_token_t fifo_token; - mailbox_addr_t> cont_addr; - }; - - typedef boost::variant< read_request_t, write_request_t > request_t; - - primary_query_bcard_t() { } - primary_query_bcard_t( - const region_t &r, - const multi_client_business_card_t &mc) - : region(r), multi_client(mc) { } - - /* The region that this master covers */ - region_t region; - - /* Contact info for the master itself */ - multi_client_business_card_t multi_client; -}; - -RDB_DECLARE_SERIALIZABLE_FOR_CLUSTER(primary_query_bcard_t::read_request_t); - -RDB_DECLARE_SERIALIZABLE_FOR_CLUSTER(primary_query_bcard_t::write_request_t); - -RDB_DECLARE_SERIALIZABLE(primary_query_bcard_t); -RDB_DECLARE_EQUALITY_COMPARABLE(primary_query_bcard_t); - -/* Each replica exposes a `direct_query_bcard_t` for each shard that it is a primary or -secondary replica for. */ - -class direct_query_bcard_t { -public: - typedef mailbox_t> read_mailbox_t; - - direct_query_bcard_t() { } - explicit direct_query_bcard_t(const read_mailbox_t::address_t &rm) : read_mailbox(rm) { } - - read_mailbox_t::address_t read_mailbox; -}; - -RDB_DECLARE_SERIALIZABLE(direct_query_bcard_t); -RDB_DECLARE_EQUALITY_COMPARABLE(direct_query_bcard_t); - -/* `table_query_bcard_t` wraps the `primary_query_bcard_t` and/or `direct_query_bcard_t` -into a single object for convenience. */ - -class table_query_bcard_t { -public: - region_t region; - optional primary; - optional direct; -}; - -RDB_DECLARE_SERIALIZABLE(table_query_bcard_t); -RDB_DECLARE_EQUALITY_COMPARABLE(table_query_bcard_t); - -#endif /* CLUSTERING_QUERY_ROUTING_METADATA_HPP_ */ - diff --git a/src/clustering/query_routing/primary_query_client.cc b/src/clustering/query_routing/primary_query_client.cc deleted file mode 100644 index 0124a7c345e..00000000000 --- a/src/clustering/query_routing/primary_query_client.cc +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/query_routing/primary_query_client.hpp" - -#include - -#include - -#include "arch/timing.hpp" -#include "concurrency/promise.hpp" -#include "containers/archive/boost_types.hpp" -#include "rdb_protocol/protocol.hpp" - -// TODO: Was this macro supposed to be used? -// #define THROTTLE_THRESHOLD 200 - -primary_query_client_t::primary_query_client_t( - mailbox_manager_t *mm, - const primary_query_bcard_t &master, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t) : - mailbox_manager(mm), - region(master.region), - multi_client_client( - mailbox_manager, - master.multi_client, - interruptor) - { } - -void primary_query_client_t::new_read_token(fifo_enforcer_sink_t::exit_read_t *out) { - out->begin(&internal_fifo_sink, internal_fifo_source.enter_read()); -} - -void primary_query_client_t::read( - const read_t &_read, - read_response_t *response, - order_token_t otok, - fifo_enforcer_sink_t::exit_read_t *token, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, cannot_perform_query_exc_t) { - rassert(region_is_superset(region, _read.get_region())); - otok.assert_read_mode(); - - promise_t > - result_or_failure; - mailbox_t> - result_or_failure_mailbox( - mailbox_manager, - [&](signal_t *, const boost::variant &res) { - result_or_failure.pulse(res); - }); - - wait_interruptible(token, interruptor); - fifo_enforcer_read_token_t token_for_master = source_for_master.enter_read(); - token->end(); - - primary_query_bcard_t::read_request_t read_request( - _read, - otok, - token_for_master, - result_or_failure_mailbox.get_address()); - - multi_client_client.spawn_request(read_request); - - wait_interruptible(result_or_failure.get_ready_signal(), interruptor); - - if (const cannot_perform_query_exc_t *error - = boost::get(&result_or_failure.wait())) { - throw *error; - } else if (const read_response_t *result = - boost::get( - &result_or_failure.wait())) { - *response = *result; - } else { - unreachable(); - } -} - -void primary_query_client_t::new_write_token(fifo_enforcer_sink_t::exit_write_t *out) { - out->begin(&internal_fifo_sink, internal_fifo_source.enter_write()); -} - -void primary_query_client_t::write( - const write_t &_write, - write_response_t *response, - order_token_t otok, - fifo_enforcer_sink_t::exit_write_t *token, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, cannot_perform_query_exc_t) { - rassert(region_is_superset(region, _write.get_region())); - otok.assert_write_mode(); - - promise_t > result_or_failure; - mailbox_t> result_or_failure_mailbox( - mailbox_manager, - [&](signal_t *, const boost::variant &res) { - result_or_failure.pulse(res); - }); - - wait_interruptible(token, interruptor); - fifo_enforcer_write_token_t token_for_master = source_for_master.enter_write(); - token->end(); - - primary_query_bcard_t::write_request_t write_request( - _write, - otok, - token_for_master, - result_or_failure_mailbox.get_address()); - - multi_client_client.spawn_request(write_request); - - wait_interruptible(result_or_failure.get_ready_signal(), interruptor); - - if (const cannot_perform_query_exc_t *error - = boost::get(&result_or_failure.wait())) { - throw *error; - throw cannot_perform_query_exc_t(*error); - } else if (const write_response_t *result = - boost::get(&result_or_failure.wait())) { - *response = *result; - } else { - unreachable(); - } -} diff --git a/src/clustering/query_routing/primary_query_client.hpp b/src/clustering/query_routing/primary_query_client.hpp deleted file mode 100644 index d989abe9508..00000000000 --- a/src/clustering/query_routing/primary_query_client.hpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_QUERY_ROUTING_PRIMARY_QUERY_CLIENT_HPP_ -#define CLUSTERING_QUERY_ROUTING_PRIMARY_QUERY_CLIENT_HPP_ - -#include "clustering/generic/multi_client_client.hpp" -#include "clustering/generic/registrant.hpp" -#include "clustering/query_routing/metadata.hpp" -#include "protocol_api.hpp" - -/* `primary_query_client_t` is responsible for sending queries to -`primary_query_server_t`. It is instantiated by `table_query_client_t`. The -`primary_query_client_t` internally contains a `multi_client_client_t` that works -with the `multi_client_server_t` in the `primary_query_server_t` to ensure the -ordering of read and write queries that are being sent to the primary. */ - -class primary_query_client_t : public home_thread_mixin_debug_only_t { -public: - primary_query_client_t( - mailbox_manager_t *mm, - const primary_query_bcard_t &master, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t); - - region_t get_region() { - return region; - } - - void new_read_token(fifo_enforcer_sink_t::exit_read_t *out); - - void read( - const read_t &read, - read_response_t *response, - order_token_t otok, - fifo_enforcer_sink_t::exit_read_t *token, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, cannot_perform_query_exc_t); - - void new_write_token(fifo_enforcer_sink_t::exit_write_t *out); - - void write( - const write_t &write, - write_response_t *response, - order_token_t otok, - fifo_enforcer_sink_t::exit_write_t *token, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, cannot_perform_query_exc_t); - -private: - typedef multi_client_business_card_t< - primary_query_bcard_t::request_t - > mc_business_card_t; - - void on_allocation(int); - - mailbox_manager_t *mailbox_manager; - - region_t region; - fifo_enforcer_source_t internal_fifo_source; - fifo_enforcer_sink_t internal_fifo_sink; - - fifo_enforcer_source_t source_for_master; - - multi_client_client_t multi_client_client; - - DISABLE_COPYING(primary_query_client_t); -}; - -#endif /* CLUSTERING_QUERY_ROUTING_PRIMARY_QUERY_CLIENT_HPP_ */ diff --git a/src/clustering/query_routing/primary_query_server.cc b/src/clustering/query_routing/primary_query_server.cc deleted file mode 100644 index 0c923695b7e..00000000000 --- a/src/clustering/query_routing/primary_query_server.cc +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/query_routing/primary_query_server.hpp" - -#include "clustering/administration/admin_op_exc.hpp" -#include "containers/archive/boost_types.hpp" - -primary_query_server_t::primary_query_server_t( - mailbox_manager_t *mm, region_t r, query_callback_t *cb) - : mailbox_manager(mm), - query_callback(cb), - region(r), - multi_client_server(mm, this) { -} - -primary_query_server_t::~primary_query_server_t() { - shutdown_cond.pulse(); -} - -primary_query_bcard_t primary_query_server_t::get_bcard() { - return primary_query_bcard_t( - region, multi_client_server.get_business_card()); -} - -void primary_query_server_t::client_t::perform_request( - const primary_query_bcard_t::request_t &request, - UNUSED signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t) -{ - /* Note that we only pay attention to `parent->shutdown_cond`; we don't ever use - `interruptor` for anything. This is because as soon as we return, the multi-throttler - tickets will be returned to the free pool; but if we interrupt - `query_callback_t::on_*()`, it might not immediately release its resources. So if - clients repeatedly connected and disconnected, and if we interrupted the call to - `on_*()` every time, then we would use far more resources than we are supposed to. So - we wait for requests to finish on their own before returning the tickets to the free - pool. */ - - if (parent->shutdown_cond.is_pulsed()) { - return; - } - - try { - if (const primary_query_bcard_t::read_request_t *read = - boost::get(&request)) { - - read->order_token.assert_read_mode(); - fifo_enforcer_sink_t::exit_read_t exiter(&fifo_sink, read->fifo_token); - boost::variant reply - = read_response_t(); - admin_err_t error; - bool ok = parent->query_callback->on_read( - read->read, &exiter, read->order_token, &parent->shutdown_cond, - boost::get(&reply), &error); - if (!ok) { - reply = cannot_perform_query_exc_t(error.msg, error.query_state); - } - send(parent->mailbox_manager, read->cont_addr, reply); - - } else if (const primary_query_bcard_t::write_request_t *write = - boost::get(&request)) { - - write->order_token.assert_write_mode(); - fifo_enforcer_sink_t::exit_write_t exiter(&fifo_sink, write->fifo_token); - boost::variant reply - = write_response_t(); - admin_err_t error; - bool ok = parent->query_callback->on_write( - write->write, &exiter, write->order_token, &parent->shutdown_cond, - boost::get(&reply), &error); - if (!ok) { - reply = cannot_perform_query_exc_t(error.msg, error.query_state); - } - send(parent->mailbox_manager, write->cont_addr, reply); - - } else { - unreachable(); - } - } catch (const interrupted_exc_t &) { - /* We have to trap the `interrupted_exc_t` because we're listening on - `parent->shutdown_cond`, not on `interruptor`. It would be illegal for us to - throw `interrupted_exc_t` when `interruptor` was not pulsed. So instead we just - return normally. */ - } -} - diff --git a/src/clustering/query_routing/primary_query_server.hpp b/src/clustering/query_routing/primary_query_server.hpp deleted file mode 100644 index ea2688f6feb..00000000000 --- a/src/clustering/query_routing/primary_query_server.hpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_QUERY_ROUTING_PRIMARY_QUERY_SERVER_HPP_ -#define CLUSTERING_QUERY_ROUTING_PRIMARY_QUERY_SERVER_HPP_ - -#include -#include -#include -#include - -#include "clustering/generic/multi_client_server.hpp" -#include "clustering/query_routing/metadata.hpp" - -/* Each shard has a `primary_query_server_t` on its primary replica server. The -`primary_query_server_t` is responsible for receiving queries from the servers that the -clients connect to and forwarding those queries to the `query_callback_t`. Specifically, -the class `primary_query_client_t`, which is instantiated by `table_query_client_t`, -sends the queries to the `primary_query_server_t`. - -`primary_query_server_t` internally contains a `multi_client_server_t`, which is -responsible for managing clients from the different `primary_query_client_t`s. -We use it in combination with `primary_query_server_t::client_t` to ensure the -ordering of requests that originate from a given client. */ - -class primary_query_server_t { -public: - class query_callback_t { - public: - virtual bool on_write( - const write_t &request, - fifo_enforcer_sink_t::exit_write_t *exiter, - order_token_t order_token, - signal_t *interruptor, - write_response_t *response_out, - admin_err_t *error_out) = 0; - virtual bool on_read( - const read_t &request, - fifo_enforcer_sink_t::exit_read_t *exiter, - order_token_t order_token, - signal_t *interruptor, - read_response_t *response_out, - admin_err_t *error_out) = 0; - protected: - virtual ~query_callback_t() { } - }; - - primary_query_server_t( - mailbox_manager_t *mm, region_t r, query_callback_t *callback); - ~primary_query_server_t(); - - primary_query_bcard_t get_bcard(); - -private: - class client_t { - public: - explicit client_t(primary_query_server_t *p) : - parent(p) { } - void perform_request( - const primary_query_bcard_t::request_t &, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t); - private: - primary_query_server_t *parent; - fifo_enforcer_sink_t fifo_sink; - }; - - mailbox_manager_t *const mailbox_manager; - query_callback_t *const query_callback; - region_t const region; - - /* See note in `client_t::perform_request()` for what this is about */ - cond_t shutdown_cond; - - multi_client_server_t< - primary_query_bcard_t::request_t, - primary_query_server_t *, - client_t - > multi_client_server; - - DISABLE_COPYING(primary_query_server_t); -}; - -#endif /* CLUSTERING_QUERY_ROUTING_PRIMARY_QUERY_SERVER_HPP_ */ diff --git a/src/clustering/query_routing/table_query_client.cc b/src/clustering/query_routing/table_query_client.cc deleted file mode 100644 index 490e2dde32b..00000000000 --- a/src/clustering/query_routing/table_query_client.cc +++ /dev/null @@ -1,597 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/query_routing/table_query_client.hpp" - -#include - -#include "clustering/query_routing/primary_query_client.hpp" -#include "clustering/table_contract/cpu_sharding.hpp" -#include "clustering/table_manager/multi_table_manager.hpp" -#include "clustering/table_manager/table_meta_client.hpp" -#include "concurrency/cross_thread_signal.hpp" -#include "concurrency/fifo_enforcer.hpp" -#include "concurrency/watchable.hpp" -#include "rdb_protocol/env.hpp" - -table_query_client_t::table_query_client_t( - const namespace_id_t &_table_id, - mailbox_manager_t *mm, - watchable_map_t, table_query_bcard_t> *d, - multi_table_manager_t *mtm, - rdb_context_t *_ctx, - table_meta_client_t *table_meta_client) - : table_id(_table_id), - mailbox_manager(mm), - directory(d), - multi_table_manager(mtm), - ctx(_ctx), - m_table_meta_client(table_meta_client), - start_count(0), - starting_up(true), - subs(directory, - std::bind(&table_query_client_t::update_registrant, - this, ph::_1, ph::_2), - initial_call_t::YES) { - rassert(ctx != nullptr); - starting_up = false; - if (start_count == 0) { - start_cond.pulse(); - } -} - -bool table_query_client_t::check_readiness(table_readiness_t readiness, - signal_t *interruptor) { - rassert(readiness != table_readiness_t::finished, - "Cannot check for the 'finished' state with namespace_interface_t."); - try { - switch (readiness) { - case table_readiness_t::outdated_reads: - { - read_response_t res; - read_t r(dummy_read_t(), profile_bool_t::DONT_PROFILE, - read_mode_t::OUTDATED); - read( - auth::user_context_t(auth::permissions_t(tribool::True, tribool::False, tribool::False, tribool::False)), - r, - &res, - order_token_t::ignore, - interruptor); - } - break; - case table_readiness_t::reads: - { - read_response_t res; - read_t r(dummy_read_t(), profile_bool_t::DONT_PROFILE, - read_mode_t::SINGLE); - read( - auth::user_context_t(auth::permissions_t(tribool::True, tribool::False, tribool::False, tribool::False)), - r, - &res, - order_token_t::ignore, - interruptor); - } - break; - case table_readiness_t::finished: // Fallthrough in release mode, better than a crash - case table_readiness_t::writes: - { - write_response_t res; - write_t w(dummy_write_t(), profile_bool_t::DONT_PROFILE, - ql::configured_limits_t::unlimited); - write( - auth::user_context_t(auth::permissions_t(tribool::True, tribool::True, tribool::False, tribool::False)), - w, - &res, - order_token_t::ignore, - interruptor); - } - break; - case table_readiness_t::unavailable: - // Do nothing - all tables are always >= unavailable - break; - default: - unreachable(); - } - } catch (const cannot_perform_query_exc_t &) { - return false; - } - return true; -} - -void table_query_client_t::read( - auth::user_context_t const &user_context, - const read_t &r, - read_response_t *response, - order_token_t order_token, - signal_t *interruptor) - THROWS_ONLY( - interrupted_exc_t, cannot_perform_query_exc_t, auth::permission_error_t) { - table_basic_config_t table_basic_config; - try { - m_table_meta_client->get_name(table_id, &table_basic_config); - } catch (no_such_table_exc_t const &) { - throw cannot_perform_query_exc_t( - "Failed to retrieve the table configuration", query_state_t::FAILED); - } - - user_context.require_read_permission(ctx, table_basic_config.database, table_id); - - order_token.assert_read_mode(); - if (r.read_mode == read_mode_t::OUTDATED) { - guarantee(!r.route_to_primary()); - /* This seems kind of silly. We do it this way because - `dispatch_outdated_read` needs to be able to see `outdated_read_info_t`, - which is defined in the `private` section. */ - dispatch_outdated_read(r, response, interruptor); - } else if (r.read_mode == read_mode_t::DEBUG_DIRECT) { - guarantee(!r.route_to_primary()); - dispatch_debug_direct_read(r, response, interruptor); - } else { - dispatch_immediate_op( - &primary_query_client_t::new_read_token, - &primary_query_client_t::read, - r, response, order_token, interruptor); - } -} - -void table_query_client_t::write( - auth::user_context_t const &user_context, - const write_t &w, - write_response_t *response, - order_token_t order_token, - signal_t *interruptor) - THROWS_ONLY( - interrupted_exc_t, cannot_perform_query_exc_t, auth::permission_error_t) { - table_basic_config_t table_basic_config; - try { - m_table_meta_client->get_name(table_id, &table_basic_config); - } catch (no_such_table_exc_t const &) { - throw cannot_perform_query_exc_t( - "Failed to retrieve the table configuration", query_state_t::FAILED); - } - - user_context.require_write_permission(ctx, table_basic_config.database, table_id); - - order_token.assert_write_mode(); - dispatch_immediate_op( - &primary_query_client_t::new_write_token, - &primary_query_client_t::write, - w, response, order_token, interruptor); -} - -std::set table_query_client_t::get_sharding_scheme() - THROWS_ONLY(cannot_perform_query_exc_t) { - std::vector s; - relationships.visit(relationships.get_domain(), - [&](const region_t &, const std::set &rels) { - for (relationship_t *rel : rels) { - s.push_back(rel->region); - } - }); - region_t whole; - region_join_result_t res = region_join(s, &whole); - if (res != REGION_JOIN_OK || whole != region_t::universe()) { - throw cannot_perform_query_exc_t("cannot compute sharding scheme " - "because primary replicas are " - "unavailable or duplicated", - query_state_t::FAILED); - } - return std::set( - std::make_move_iterator(s.begin()), - std::make_move_iterator(s.end())); -} - -template -void table_query_client_t::dispatch_immediate_op( - /* `how_to_make_token` and `how_to_run_query` have type pointer-to- - member-function. */ - void (primary_query_client_t::*how_to_make_token)( - fifo_enforcer_token_type *), - void (primary_query_client_t::*how_to_run_query)( - const op_type &, - op_response_type *, - order_token_t, - fifo_enforcer_token_type *, - signal_t *) THROWS_ONLY(interrupted_exc_t, cannot_perform_query_exc_t), - const op_type &op, - op_response_type *response, - order_token_t order_token, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, cannot_perform_query_exc_t) { - - if (interruptor->is_pulsed()) throw interrupted_exc_t(); - - std::vector > > - primaries_to_contact; - scoped_ptr_t > - new_op_info(new immediate_op_info_t()); - relationships.visit(region_t::universe(), - [&](const region_t ®, const std::set &rels) { - if (op.shard(reg, &new_op_info->sharded_op)) { - relationship_t *chosen_relationship = nullptr; - for (auto jt = rels.begin(); jt != rels.end(); ++jt) { - // If some shards are currently intersecting (this should only happen - // temporarily while resharding). `reg` might be a subset of the region - // of the relationships in `rels`. - // We need to test for that, so we don't send operations to a shard with - // the wrong boundaries. - if ((*jt)->primary_client && (*jt)->region == reg) { - if (chosen_relationship) { - throw cannot_perform_query_exc_t( - "too many primary replicas available", - query_state_t::FAILED); - } - chosen_relationship = *jt; - } - } - if (!chosen_relationship) { - throw cannot_perform_query_exc_t( - strprintf("primary replica for shard %s not available", - key_range_to_string(reg.inner).c_str()), - query_state_t::FAILED); - } - new_op_info->primary_client = chosen_relationship->primary_client; - (new_op_info->primary_client->*how_to_make_token)( - &new_op_info->enforcement_token); - new_op_info->keepalive = auto_drainer_t::lock_t( - &chosen_relationship->drainer); - primaries_to_contact.push_back(std::move(new_op_info)); - new_op_info.init( - new immediate_op_info_t()); - } - }); - - std::vector results(primaries_to_contact.size()); - std::vector > - failures(primaries_to_contact.size()); - pmap(primaries_to_contact.size(), std::bind( - &table_query_client_t::template perform_immediate_op< - op_type, fifo_enforcer_token_type, op_response_type>, - this, - how_to_run_query, - &primaries_to_contact, - &results, - &failures, - order_token, - ph::_1, - interruptor)); - - if (interruptor->is_pulsed()) throw interrupted_exc_t(); - - bool seen_non_failure = false; - optional first_failure; - for (size_t i = 0; i < primaries_to_contact.size(); ++i) { - if (failures[i]) { - switch (failures[i]->get_query_state()) { - case query_state_t::FAILED: - if (!first_failure) first_failure = failures[i]; - break; - case query_state_t::INDETERMINATE: throw *failures[i]; - default: unreachable(); - } - } else { - seen_non_failure = true; - } - if (seen_non_failure && first_failure) { - // If we got different responses from the different shards, we - // default to the safest error type. - throw cannot_perform_query_exc_t( - first_failure->what(), query_state_t::INDETERMINATE); - } - } - if (first_failure) { - guarantee(!seen_non_failure); - throw *first_failure; - } - - op.unshard(results.data(), results.size(), response, ctx, interruptor); -} - -template -void table_query_client_t::perform_immediate_op( - void (primary_query_client_t::*how_to_run_query)( - const op_type &, - op_response_type *, - order_token_t, - fifo_enforcer_token_type *, - signal_t *) - /* THROWS_ONLY(interrupted_exc_t, resource_lost_exc_t, - cannot_perform_query_exc_t) */, - std::vector > > - *primaries_to_contact, - std::vector *results, - std::vector > *failures, - order_token_t order_token, - size_t i, - signal_t *interruptor) - THROWS_NOTHING -{ - immediate_op_info_t *primary_to_contact - = (*primaries_to_contact)[i].get(); - - try { - wait_any_t waiter(primary_to_contact->keepalive.get_drain_signal(), interruptor); - (primary_to_contact->primary_client->*how_to_run_query)( - primary_to_contact->sharded_op, - &results->at(i), - order_token, - &primary_to_contact->enforcement_token, - &waiter); - } catch (const cannot_perform_query_exc_t& e) { - (*failures)[i].set(e); - } catch (const interrupted_exc_t&) { - if (interruptor->is_pulsed()) { - /* Return immediately. `dispatch_immediate_op()` will notice that the - interruptor has been pulsed. */ - return; - } else { - /* `keepalive.get_drain_signal()` was pulsed because the other server - disconnected or stopped being a primary */ - (*failures)[i].set(cannot_perform_query_exc_t( - "lost contact with primary replica", - query_state_t::INDETERMINATE)); - } - } -} - -void table_query_client_t::dispatch_outdated_read( - const read_t &op, - read_response_t *response, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, cannot_perform_query_exc_t) { - - if (interruptor->is_pulsed()) throw interrupted_exc_t(); - - std::vector > replicas_to_contact; - - scoped_ptr_t new_op_info(new outdated_read_info_t()); - relationships.visit(region_t::universe(), - [&](const region_t ®ion, const std::set &rels) { - if (op.shard(region, &new_op_info->sharded_op)) { - std::vector potential_relationships; - relationship_t *chosen_relationship = nullptr; - for (auto jt = rels.begin(); jt != rels.end(); ++jt) { - // See the comment in `dispatch_immediate_op` about why we need to - // check that `region` and the relationship's region are the same. - if ((*jt)->direct_bcard != nullptr && (*jt)->region == region) { - if ((*jt)->is_local) { - chosen_relationship = *jt; - break; - } else { - potential_relationships.push_back(*jt); - } - } - } - if (!chosen_relationship && !potential_relationships.empty()) { - chosen_relationship - = potential_relationships[randint(potential_relationships.size())]; - } - if (!chosen_relationship) { - /* Don't bother looking for masters; if there are no direct - readers, there won't be any masters either. */ - throw cannot_perform_query_exc_t( - "no replica is available", - query_state_t::FAILED); - } - new_op_info->direct_bcard = chosen_relationship->direct_bcard; - new_op_info->keepalive = auto_drainer_t::lock_t( - &chosen_relationship->drainer); - replicas_to_contact.push_back(std::move(new_op_info)); - new_op_info.init(new outdated_read_info_t()); - } - }); - - std::vector results(replicas_to_contact.size()); - std::vector failures(replicas_to_contact.size()); - pmap(replicas_to_contact.size(), - std::bind(&table_query_client_t::perform_outdated_read, this, - &replicas_to_contact, &results, &failures, ph::_1, interruptor)); - - if (interruptor->is_pulsed()) throw interrupted_exc_t(); - - for (size_t i = 0; i < replicas_to_contact.size(); ++i) { - if (!failures[i].empty()) { - // Reads are never indeterminate. - throw cannot_perform_query_exc_t(failures[i], query_state_t::FAILED); - } - } - - op.unshard(results.data(), results.size(), response, ctx, interruptor); -} - -void table_query_client_t::perform_outdated_read( - std::vector > *replicas_to_contact, - std::vector *results, - std::vector *failures, - size_t i, - signal_t *interruptor) THROWS_NOTHING { - outdated_read_info_t *replica_to_contact = (*replicas_to_contact)[i].get(); - - try { - cond_t done; - mailbox_t cont(mailbox_manager, - [&](signal_t *, const read_response_t &res) { - results->at(i) = res; - done.pulse(); - }); - - send(mailbox_manager, - replica_to_contact->direct_bcard->read_mailbox, - replica_to_contact->sharded_op, - cont.get_address()); - wait_any_t waiter(replica_to_contact->keepalive.get_drain_signal(), &done); - wait_interruptible(&waiter, interruptor); - if (!done.is_pulsed()) { - /* `wait_interruptible()` returned because - `replica_to_contact->keepalive.get_drain_signal()` was pulsed */ - failures->at(i).assign("lost contact with replica"); - } - } catch (const interrupted_exc_t &) { - /* Return immediately. `dispatch_immediate_op()` will notice that the - interruptor has been pulsed. */ - } -} - -void table_query_client_t::dispatch_debug_direct_read( - const read_t &op, - read_response_t *response, - signal_t *interruptor_on_caller) - THROWS_ONLY(interrupted_exc_t, cannot_perform_query_exc_t) { - cross_thread_signal_t interruptor_on_mtm( - interruptor_on_caller, multi_table_manager->home_thread()); - on_thread_t thread_switcher(multi_table_manager->home_thread()); - multi_table_manager->visit_table(table_id, &interruptor_on_mtm, access_t::read, - [&](multistore_ptr_t *multistore, table_manager_t *) { - if (multistore == nullptr) { - throw cannot_perform_query_exc_t( - "This server does not have any data available for the given table.", - query_state_t::FAILED); - } - std::vector responses; - pmap(CPU_SHARDING_FACTOR, [&](size_t shard_number) { - try { - region_t region = cpu_sharding_subspace(shard_number); - read_t subread; - if (!op.shard(region, &subread)) { - return; - } - read_response_t subresponse; - { - store_view_t *store = - multistore->get_cpu_sharded_store(shard_number); - cross_thread_signal_t interruptor_on_store( - &interruptor_on_mtm, store->home_thread()); - on_thread_t thread_switcher_2(store->home_thread()); -#ifndef NDEBUG - metainfo_checker_t checker( - region, [](const region_t &, const binary_blob_t &) {}); -#endif /* NDEBUG */ - read_token_t token; - store->new_read_token(&token); - store->read( - DEBUG_ONLY(checker, ) - subread, &subresponse, &token, - &interruptor_on_store); - } - responses.push_back(subresponse); - } catch (const interrupted_exc_t &) { - /* ignore; we'll catch it outside the pmap */ - } - }); - if (interruptor_on_mtm.is_pulsed()) { - throw interrupted_exc_t(); - } - op.unshard(responses.data(), responses.size(), response, ctx, - &interruptor_on_mtm); - }); -} - -void table_query_client_t::update_registrant( - const std::pair &key, - const table_query_bcard_t *bcard) { - auto it = coro_stoppers.find(key); - if (bcard == nullptr && it != coro_stoppers.end()) { - it->second->pulse_if_not_already_pulsed(); - } else if (bcard != nullptr && it == coro_stoppers.end()) { - coro_stoppers.insert(std::make_pair(key, make_scoped())); - if (starting_up) { - start_count++; - } - coro_t::spawn_sometime(std::bind( - &table_query_client_t::relationship_coroutine, this, - key, *bcard, starting_up, relationship_coroutine_auto_drainer.lock())); - } -} - -template -class region_map_set_membership_t { -public: - region_map_set_membership_t(region_map_t > *m, const region_t &r, const value_t &v) : - map(m), region(r), value(v) { - // Note that `visit_mutable` might split the region up if there are existing - // partially overlapping entries (e.g. during a rebalance). - // Once those entries go away, `region_map_t` should automatically merge the - // regions again. - map->visit_mutable(region, [&](const region_t &, std::set *set) { - rassert(set->count(value) == 0); - set->insert(value); - }); - } - ~region_map_set_membership_t() { - map->visit_mutable(region, [&](const region_t &, std::set *set) { - rassert(set->count(value) == 1); - set->erase(value); - }); - } -private: - region_map_t > *map; - region_t region; - value_t value; -}; - -void table_query_client_t::relationship_coroutine( - const std::pair &key, - const table_query_bcard_t &bcard, - bool is_start, - auto_drainer_t::lock_t lock) THROWS_NOTHING { - try { - wait_any_t stop_signal(lock.get_drain_signal(), coro_stoppers.at(key).get()); - - relationship_t relationship_record; - relationship_record.is_local = - (key.first == mailbox_manager->get_connectivity_cluster()->get_me()); - relationship_record.region = bcard.region; - - scoped_ptr_t primary_client; - if (static_cast(bcard.primary)) { - primary_client.init(new primary_query_client_t( - mailbox_manager, *bcard.primary, &stop_signal)); - relationship_record.primary_client = primary_client.get(); - } else { - relationship_record.primary_client = nullptr; - } - - if (static_cast(bcard.direct)) { - relationship_record.direct_bcard = &*bcard.direct; - } else { - relationship_record.direct_bcard = nullptr; - } - - region_map_set_membership_t relationship_map_insertion( - &relationships, bcard.region, &relationship_record); - - if (is_start) { - guarantee(start_count > 0); - start_count--; - if (start_count == 0) { - start_cond.pulse(); - } - is_start = false; - } - - stop_signal.wait_lazily_unordered(); - } catch (const interrupted_exc_t &) { - /* do nothing */ - } - - if (is_start) { - guarantee(start_count > 0); - start_count--; - if (start_count == 0) { - start_cond.pulse(); - } - } - - coro_stoppers.erase(key); - - /* If we disconnect and then reconnect quickly, then `update_registrant()` won't - spawn a new coroutine because the old entry is still present in `coro_stoppers`. So - we have to manually call `update_registrant()` again to spawn a new coroutine in this - case. */ - if (!lock.get_drain_signal()->is_pulsed()) { - directory->read_key(key, - [&](const table_query_bcard_t *new_bcard) { - update_registrant(key, new_bcard); - }); - } -} - - diff --git a/src/clustering/query_routing/table_query_client.hpp b/src/clustering/query_routing/table_query_client.hpp deleted file mode 100644 index d1be9a831dc..00000000000 --- a/src/clustering/query_routing/table_query_client.hpp +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_QUERY_ROUTING_TABLE_QUERY_CLIENT_HPP_ -#define CLUSTERING_QUERY_ROUTING_TABLE_QUERY_CLIENT_HPP_ - -#include - -#include -#include -#include -#include - -#include "clustering/administration/auth/permission_error.hpp" -#include "clustering/query_routing/metadata.hpp" -#include "containers/clone_ptr.hpp" -#include "concurrency/fifo_enforcer.hpp" -#include "concurrency/watchable_map.hpp" -#include "protocol_api.hpp" -#include "rdb_protocol/protocol.hpp" - -class multi_table_manager_t; -class primary_query_client_t; -class table_meta_client_t; - -/* `table_query_client_t` is responsible for sending queries to the cluster. It -instantiates `primary_query_client_t` and `direct_query_client_t` internally; it covers -the entire table whereas they cover single shards. */ - -class table_query_client_t : public namespace_interface_t { -public: - table_query_client_t( - const namespace_id_t &table_id, - mailbox_manager_t *mm, - watchable_map_t, table_query_bcard_t> - *directory, - multi_table_manager_t *multi_table_manager, - rdb_context_t *, - table_meta_client_t *table_meta_client); - - /* Returns a signal that will be pulsed when we have either successfully connected - or tried and failed to connect to every primary replica that was present at the time - that the constructor was called. This is to avoid the case where we get errors like - "lost contact with primary replica" when really we just haven't finished connecting - yet. */ - signal_t *get_initial_ready_signal() { - return &start_cond; - } - - bool check_readiness(table_readiness_t readiness, signal_t *interruptor); - - void read( - auth::user_context_t const &user_context, - const read_t &r, - read_response_t *response, - order_token_t order_token, - signal_t *interruptor) - THROWS_ONLY( - interrupted_exc_t, cannot_perform_query_exc_t, auth::permission_error_t); - - void write( - auth::user_context_t const &user_context, - const write_t &w, - write_response_t *response, - order_token_t order_token, - signal_t *interruptor) - THROWS_ONLY( - interrupted_exc_t, cannot_perform_query_exc_t, auth::permission_error_t); - - std::set get_sharding_scheme() THROWS_ONLY(cannot_perform_query_exc_t); - -private: - class relationship_t { - public: - bool is_local; - region_t region; - primary_query_client_t *primary_client; - const direct_query_bcard_t *direct_bcard; - auto_drainer_t drainer; - }; - - /* The code for handling immediate reads is 99% the same as the code for - handling writes, so it's factored out into the `dispatch_immediate_op()` - function. */ - - template - class immediate_op_info_t { - public: - op_type sharded_op; - primary_query_client_t *primary_client; - fifo_enforcer_token_type enforcement_token; - auto_drainer_t::lock_t keepalive; - }; - - class outdated_read_info_t { - public: - read_t sharded_op; - const direct_query_bcard_t *direct_bcard; - auto_drainer_t::lock_t keepalive; - }; - - template - void dispatch_immediate_op( - /* `how_to_make_token` and `how_to_run_query` have type pointer-to-member-function. */ - void (primary_query_client_t::*how_to_make_token)(fifo_enforcer_token_type *), - void (primary_query_client_t::*how_to_run_query)(const op_type &, op_response_type *response, order_token_t, fifo_enforcer_token_type *, signal_t *) THROWS_ONLY(interrupted_exc_t, cannot_perform_query_exc_t), - const op_type &op, - op_response_type *response, - order_token_t order_token, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, cannot_perform_query_exc_t); - - // The exception specification of `how_to_run_query` is commented out in - // order to bypass a clang bug seen on certain versions for OS X, or perhaps - // fix a confusing violation of C++. This function is called only by - // dispatch_immediate_op, which still has the exception specification. - template - void perform_immediate_op( - void (primary_query_client_t::*how_to_run_query)(const op_type &, op_response_type *, order_token_t, fifo_enforcer_token_type *, signal_t *) /* THROWS_ONLY(interrupted_exc_t, cannot_perform_query_exc_t) */, - std::vector > > *masters_to_contact, - std::vector *results, - std::vector > *failures, - order_token_t order_token, - size_t i, - signal_t *interruptor) - THROWS_NOTHING; - - void dispatch_outdated_read( - const read_t &op, - read_response_t *response, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, cannot_perform_query_exc_t); - - void perform_outdated_read( - std::vector > *direct_readers_to_contact, - std::vector *results, - std::vector *failures, - size_t i, - signal_t *interruptor) - THROWS_NOTHING; - - void dispatch_debug_direct_read( - const read_t &op, - read_response_t *response, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, cannot_perform_query_exc_t); - - void update_registrant(const std::pair &key, - const table_query_bcard_t *bcard); - - void relationship_coroutine( - const std::pair &key, - const table_query_bcard_t &bcard, - bool is_start, - auto_drainer_t::lock_t lock) THROWS_NOTHING; - - namespace_id_t const table_id; - mailbox_manager_t *const mailbox_manager; - watchable_map_t, table_query_bcard_t> - *const directory; - multi_table_manager_t *const multi_table_manager; - rdb_context_t *const ctx; - table_meta_client_t *m_table_meta_client; - - std::map, scoped_ptr_t > coro_stoppers; - region_map_t > relationships; - - /* `start_cond` will be pulsed when we have either successfully connected to - or tried and failed to connect to every peer present when the constructor - was called. `start_count` is the number of peers we're still waiting for. - `starting_up` is true during the constructor so `update_registrants()` knows to - increment `start_count` when it finds a peer. */ - cond_t start_cond; - int start_count; - bool starting_up; - - auto_drainer_t relationship_coroutine_auto_drainer; - - watchable_map_t, table_query_bcard_t>::all_subs_t subs; - - DISABLE_COPYING(table_query_client_t); -}; - -#endif /* CLUSTERING_QUERY_ROUTING_TABLE_QUERY_CLIENT_HPP_ */ diff --git a/src/clustering/table_contract/branch_history_gc.cc b/src/clustering/table_contract/branch_history_gc.cc deleted file mode 100644 index b83d012a489..00000000000 --- a/src/clustering/table_contract/branch_history_gc.cc +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_contract/branch_history_gc.hpp" - -#include "logger.hpp" - -void copy_branch_history_for_branch( - const branch_id_t &root, - const branch_history_t &source, - const table_raft_state_t &old_state, - bool ignore_missing, - branch_history_t *add_branches_out) { - const branch_history_t &existing = old_state.branch_history; - std::set todo; - if (existing.branches.count(root) == 0 && - add_branches_out->branches.count(root) == 0) { - todo.insert(root); - } - while (!todo.empty()) { - branch_id_t b = *todo.begin(); - todo.erase(todo.begin()); - const auto bc_it = source.branches.find(b); - if (bc_it == source.branches.end()) { - rassert(ignore_missing); - if (!ignore_missing) { - /* In release mode we just log a message if a birth certificate - is missing, and hope that we can recover from this by ignoring - the branch. */ - logERR("Ignoring missing branch `%s`. This probably means that " - "there is a bug in RethinkDB. Please report this at " - "https://github.com/rethinkdb/rethinkdb/issues/", - uuid_to_str(b).c_str()); - } - continue; - } - const branch_birth_certificate_t &bc = bc_it->second; - add_branches_out->branches.insert(std::make_pair(b, bc)); - bc.origin.visit(bc.origin.get_domain(), - [&](const region_t &, const version_t &version) { - if (version != version_t::zero() && - existing.branches.count(version.branch) == 0 && - add_branches_out->branches.count(version.branch) == 0) { - todo.insert(version.branch); - } - }); - } -} - -void mark_all_ancestors_live( - const branch_id_t &root, - const region_t ®ion, - const branch_history_reader_t *branch_reader, - std::set *remove_branches_out) { - guarantee(!root.is_nil()); - std::multimap todo; - std::set done; - todo.insert(std::make_pair(root, region)); - while (!todo.empty()) { - std::pair next = *todo.begin(); - todo.erase(todo.begin()); - done.insert(next.first); - remove_branches_out->erase(next.first); - branch_reader->get_branch(next.first).origin.visit(next.second, - [&](const region_t &subregion, const version_t &version) { - if (version != version_t::zero() && - branch_reader->is_branch_known(version.branch) && - done.count(version.branch) == 0) { - todo.insert(std::make_pair(version.branch, subregion)); - } - }); - } -} - -void mark_ancestors_since_base_live( - const branch_id_t &root, - const region_t ®ion, - const branch_history_reader_t *branch_reader, - const branch_history_reader_t *base, - std::set *remove_branches_out) { - guarantee(!root.is_nil()); - std::multimap todo; - std::set done; - todo.insert(std::make_pair(root, region)); - while (!todo.empty()) { - std::pair next = *todo.begin(); - todo.erase(todo.begin()); - done.insert(next.first); - remove_branches_out->erase(next.first); - bool next_in_base = base->is_branch_known(next.first); - branch_reader->get_branch(next.first).origin.visit(next.second, - [&](const region_t &subregion, const version_t &version) { - if (version != version_t::zero() && - branch_reader->is_branch_known(version.branch) && - done.count(version.branch) == 0) { - bool version_in_base = base->is_branch_known(version.branch); - if (!(next_in_base && !version_in_base)) { - todo.insert(std::make_pair(version.branch, subregion)); - } - } - }); - } -} - diff --git a/src/clustering/table_contract/branch_history_gc.hpp b/src/clustering/table_contract/branch_history_gc.hpp deleted file mode 100644 index 02d996223f4..00000000000 --- a/src/clustering/table_contract/branch_history_gc.hpp +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_CONTRACT_BRANCH_HISTORY_GC_HPP_ -#define CLUSTERING_TABLE_CONTRACT_BRANCH_HISTORY_GC_HPP_ - -#include "clustering/table_contract/contract_metadata.hpp" - -/* The branch history is stored in the Raft state and again on disk on each replica. Each -replica's B-tree metainfo refers to some branches, and the `current_branches` field in -the Raft state also refers to branches. When backfilling, we use the branch history to -find the relationship between the backfiller's B-tree metainfo state and the backfillee's -B-tree metainfo state. The coordinator uses the branch history to find the relationship -between the replicas' B-tree metainfo states and the branches in the contracts in the -Raft state. - -For performance reasons, we don't want the branch history to grow without bound. But we -want to make sure to preserve enough history to calculate the relationships between the -branches. So for each shard, the coordinator computes the common ancestor of the branches -in the contracts and the branches in the replicas' B-tree metainfos; it keeps only those -branches that are along the path between that common ancestor and the branches in the -contracts. The coordinator can compute that common ancestor using the replicas' contract -acks. Each individual replica also keeps those branches that are along the path from the -common ancestor to its current B-tree metainfo branches. The contract executor on each -replica determines the common ancestor by looking at the list of branches in the Raft -state. */ - -/* Whenever `calculate_contracts()` is going to add a new entry to `current_branches`, it -calls `copy_branch_history_for_branch()` to add the branch history for the newly-added -branch. */ -void copy_branch_history_for_branch( - const branch_id_t &new_branch, - const branch_history_t &source, - const table_raft_state_t &old_state, - bool ignore_missing, - branch_history_t *add_branches_out); - -/* `mark_all_ancestors_live()` removes `branch` and all of its ancestors in the given -region from `remove_branches_out`. */ -void mark_all_ancestors_live( - const branch_id_t &root, - const region_t ®ion, - const branch_history_reader_t *branch_reader, - std::set *remove_branches_out); - -/* `mark_ancestors_since_base_live()` traces the ancestry of `root` back until it finds a -branch in `base`, then keeps tracing back until it finds a branch not in `base`. All of -the branches except the last one will be removed from `remove_branches_out`. - -For example, suppose that the branch history looks like this: - zero -> A -> B -> C -> D -> E -> root -Suppose that branches `C` and `D` are in `base`. Then `root`, `C`, `D`, and `E` would be -removed from `remove_branches_out`. But if `base` were empty, then `A` and `B` would also -be removed. - -This is used on the contract executor to clean up the branch history. `root` will point -to the branch that is currently in use, and `base` will point to the Raft state branch -history. */ -void mark_ancestors_since_base_live( - const branch_id_t &root, - const region_t ®ion, - const branch_history_reader_t *reader, - const branch_history_reader_t *base, - std::set *remove_branches_out); - -#endif /* CLUSTERING_TABLE_CONTRACT_BRANCH_HISTORY_GC_HPP_ */ - diff --git a/src/clustering/table_contract/contract_metadata.cc b/src/clustering/table_contract/contract_metadata.cc deleted file mode 100644 index c64c382b120..00000000000 --- a/src/clustering/table_contract/contract_metadata.cc +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_contract/contract_metadata.hpp" - -#include "clustering/table_contract/cpu_sharding.hpp" -#include "stl_utils.hpp" - -#ifndef NDEBUG -void contract_t::sanity_check() const { - if (static_cast(primary)) { - guarantee(replicas.count(primary->server) == 1); - if (static_cast(primary->hand_over) - && !primary->hand_over->get_uuid().is_nil()) { - guarantee(replicas.count(*primary->hand_over) == 1); - } - } - for (const server_id_t &s : voters) { - guarantee(replicas.count(s) == 1); - } -} -#endif /* NDEBUG */ - -RDB_IMPL_EQUALITY_COMPARABLE_2( - contract_t::primary_t, server, hand_over); -RDB_IMPL_EQUALITY_COMPARABLE_5( - contract_t, replicas, voters, temp_voters, primary, after_emergency_repair); - -RDB_IMPL_SERIALIZABLE_2_SINCE_v2_1( - contract_t::primary_t, server, hand_over); -RDB_IMPL_SERIALIZABLE_5_SINCE_v2_1( - contract_t, replicas, voters, temp_voters, primary, after_emergency_repair); - -#ifndef NDEBUG -void contract_ack_t::sanity_check( - const server_id_t &server, - const contract_id_t &contract_id, - const table_raft_state_t &raft_state) const { - const region_t ®ion = raft_state.contracts.at(contract_id).first; - const contract_t &contract = raft_state.contracts.at(contract_id).second; - - guarantee(contract.replicas.count(server) == 1, - "A server sent an ack for a contract that it wasn't a replica for."); - - bool ack_says_primary = state == state_t::primary_need_branch || - state == state_t::primary_in_progress || state == state_t::primary_ready; - bool contract_says_primary = static_cast(contract.primary) && - contract.primary->server == server; - guarantee(ack_says_primary == contract_says_primary, - "The contract says a server should be primary, but it sent a non-primary ack."); - - guarantee((state == state_t::primary_need_branch) == static_cast(branch), - "branch_id should be present iff state is primary_need_branch"); - guarantee((state == state_t::secondary_need_primary) == static_cast(version), - "version should be present iff state is secondary_need_primary"); - guarantee(!static_cast(version) || version->get_domain() == region, - "version has wrong region"); - - bool is_voter = contract.voters.count(server) == 1 || - (static_cast(contract.temp_voters) && - contract.temp_voters->count(server) == 1); - if (!contract.after_emergency_repair && is_voter) { - try { - if (state == state_t::primary_need_branch) { - branch_history_combiner_t combiner( - &raft_state.branch_history, &branch_history); - version_t branch_initial_version( - *branch, - branch_history.get_branch(*branch).initial_timestamp); - raft_state.current_branches.visit(region, - [&](const region_t &subregion, const branch_id_t &cur_branch) { - version_find_branch_common( - &combiner, branch_initial_version, cur_branch, subregion); - }); - } else if (state == state_t::secondary_need_primary) { - branch_history_combiner_t combiner( - &raft_state.branch_history, &branch_history); - version->visit(region, - [&](const region_t &subregion, const version_t &subversion) { - raft_state.current_branches.visit(subregion, - [&](const region_t &subsubregion, const branch_id_t &cur_branch) { - version_find_branch_common( - &combiner, subversion, cur_branch, subsubregion); - }); - }); - } - } catch (const missing_branch_exc_t &) { - crash("Branch history is missing pieces"); - } - } -} -#endif /* NDEBUG */ - -RDB_IMPL_SERIALIZABLE_4_FOR_CLUSTER( - contract_ack_t, state, version, branch, branch_history); -RDB_IMPL_EQUALITY_COMPARABLE_4( - contract_ack_t, state, version, branch, branch_history); - -void table_raft_state_t::apply_change(const table_raft_state_t::change_t &change) { - class visitor_t : public boost::static_visitor { - public: - void operator()(const change_t::set_table_config_t &set_config_change) { - state->config = set_config_change.new_config; - } - void operator()(const change_t::new_contracts_t &new_contracts_change) { - for (const contract_id_t &cid : new_contracts_change.remove_contracts) { - state->contracts.erase(cid); - } - state->contracts.insert( - new_contracts_change.add_contracts.begin(), - new_contracts_change.add_contracts.end()); - for (const branch_id_t &bid : new_contracts_change.remove_branches) { - state->branch_history.branches.erase(bid); - } - state->branch_history.branches.insert( - new_contracts_change.add_branches.branches.begin(), - new_contracts_change.add_branches.branches.end()); - for (const auto ®ion_branch : - new_contracts_change.register_current_branches) { - state->current_branches.update(region_branch.first, region_branch.second); - } - for (const server_id_t &sid : new_contracts_change.remove_server_names) { - state->server_names.names.erase(sid); - } - state->server_names.names.insert( - new_contracts_change.add_server_names.names.begin(), - new_contracts_change.add_server_names.names.end()); - } - void operator()(const change_t::new_member_ids_t &new_member_ids_change) { - for (const server_id_t &sid : new_member_ids_change.remove_member_ids) { - state->member_ids.erase(sid); - } - state->member_ids.insert( - new_member_ids_change.add_member_ids.begin(), - new_member_ids_change.add_member_ids.end()); - } - void operator()(const change_t::new_server_names_t &new_server_names) { - for (const auto &pair : new_server_names.config_and_shards.names) { - auto name_it = state->config.server_names.names.find(pair.first); - guarantee(name_it != state->config.server_names.names.end()); - name_it->second = pair.second; - } - for (const auto &pair : new_server_names.raft_state.names) { - auto name_it = state->server_names.names.find(pair.first); - guarantee(name_it != state->server_names.names.end()); - name_it->second = pair.second; - } - } - table_raft_state_t *state; - } visitor; - visitor.state = this; - boost::apply_visitor(visitor, change.v); - DEBUG_ONLY_CODE(sanity_check()); -} - -#ifndef NDEBUG -void table_raft_state_t::sanity_check() const { - std::set all_replicas; - for (const auto &pair : contracts) { - pair.second.second.sanity_check(); - for (const server_id_t &replica : pair.second.second.replicas) { - all_replicas.insert(replica); - guarantee(server_names.names.count(replica) == 1); - } - } - for (const auto &pair : server_names.names) { - guarantee(all_replicas.count(pair.first) == 1); - } -} -#endif /* NDEBUG */ - -RDB_IMPL_EQUALITY_COMPARABLE_1( - table_raft_state_t::change_t::set_table_config_t, new_config); -RDB_IMPL_EQUALITY_COMPARABLE_7( - table_raft_state_t::change_t::new_contracts_t, - remove_contracts, add_contracts, register_current_branches, add_branches, - remove_branches, remove_server_names, add_server_names); -RDB_IMPL_EQUALITY_COMPARABLE_2( - table_raft_state_t::change_t::new_member_ids_t, remove_member_ids, add_member_ids); -RDB_IMPL_EQUALITY_COMPARABLE_2( - table_raft_state_t::change_t::new_server_names_t, config_and_shards, raft_state); -RDB_IMPL_EQUALITY_COMPARABLE_1( - table_raft_state_t::change_t, v); -RDB_IMPL_EQUALITY_COMPARABLE_6( - table_raft_state_t, config, contracts, branch_history, current_branches, - member_ids, server_names); - -RDB_IMPL_SERIALIZABLE_1_SINCE_v2_1( - table_raft_state_t::change_t::set_table_config_t, new_config); -RDB_IMPL_SERIALIZABLE_7_SINCE_v2_1( - table_raft_state_t::change_t::new_contracts_t, - remove_contracts, add_contracts, remove_branches, add_branches, - register_current_branches, remove_server_names, add_server_names); -RDB_IMPL_SERIALIZABLE_2_SINCE_v2_1( - table_raft_state_t::change_t::new_member_ids_t, - remove_member_ids, add_member_ids); -RDB_IMPL_SERIALIZABLE_2_SINCE_v2_1( - table_raft_state_t::change_t::new_server_names_t, raft_state, config_and_shards); -RDB_IMPL_SERIALIZABLE_1_SINCE_v2_1( - table_raft_state_t::change_t, v); -RDB_IMPL_SERIALIZABLE_6_SINCE_v2_1( - table_raft_state_t, config, contracts, branch_history, current_branches, - member_ids, server_names); - -table_raft_state_t make_new_table_raft_state( - const table_config_and_shards_t &config) { - table_raft_state_t state; - state.config = config; - for (size_t i = 0; i < config.shard_scheme.num_shards(); ++i) { - const table_config_t::shard_t &shard_conf = config.config.shards[i]; - contract_t contract; - contract.replicas = shard_conf.all_replicas; - contract.voters = shard_conf.voting_replicas(); - if (!shard_conf.primary_replica.get_uuid().is_nil()) { - contract.primary.set( - contract_t::primary_t { shard_conf.primary_replica, r_nullopt }); - } - contract.after_emergency_repair = false; - for (size_t j = 0; j < CPU_SHARDING_FACTOR; ++j) { - region_t region = region_intersection( - region_t(config.shard_scheme.get_shard_range(i)), - cpu_sharding_subspace(j)); - state.contracts.insert(std::make_pair(generate_uuid(), - std::make_pair(region, contract))); - } - for (const server_id_t &server_id : shard_conf.all_replicas) { - if (state.member_ids.count(server_id) == 0) { - state.member_ids[server_id] = raft_member_id_t(generate_uuid()); - } - } - } - state.server_names = config.server_names; - DEBUG_ONLY_CODE(state.sanity_check()); - return state; -} - -RDB_IMPL_EQUALITY_COMPARABLE_6(table_shard_status_t, - primary, secondary, need_primary, need_quorum, backfilling, transitioning); -RDB_IMPL_SERIALIZABLE_6_FOR_CLUSTER(table_shard_status_t, - primary, secondary, need_primary, need_quorum, backfilling, transitioning); - -void debug_print(printf_buffer_t *buf, const contract_t::primary_t &primary) { - buf->appendf("primary_t { server = "); - debug_print(buf, primary.server); - buf->appendf(" hand_over = "); - debug_print(buf, primary.hand_over); - buf->appendf(" }"); -} - -void debug_print(printf_buffer_t *buf, const contract_t &contract) { - buf->appendf("contract_t { replicas = "); - debug_print(buf, contract.replicas); - buf->appendf(" voters = "); - debug_print(buf, contract.voters); - buf->appendf(" temp_voters = "); - debug_print(buf, contract.temp_voters); - buf->appendf(" primary = "); - debug_print(buf, contract.primary); - buf->appendf(" }"); -} - -const char *contract_ack_state_to_string(contract_ack_t::state_t state) { - typedef contract_ack_t::state_t state_t; - switch (state) { - case state_t::primary_need_branch: return "primary_need_branch"; - case state_t::primary_in_progress: return "primary_in_progress"; - case state_t::primary_ready: return "primary_ready"; - case state_t::secondary_need_primary: return "secondary_need_primary"; - case state_t::secondary_backfilling: return "secondary_backfilling"; - case state_t::secondary_streaming: return "secondary_streaming"; - default: unreachable(); - } -} - -void debug_print(printf_buffer_t *buf, const contract_ack_t &ack) { - buf->appendf("contract_ack_t { state = %s", - contract_ack_state_to_string(ack.state)); - buf->appendf(" version = "); - debug_print(buf, ack.version); - buf->appendf(" branch = "); - debug_print(buf, ack.branch); - buf->appendf(" branch_history.size = "); - debug_print(buf, ack.branch_history.branches.size()); - buf->appendf(" }"); -} - -void debug_print(printf_buffer_t *buf, const table_raft_state_t &state) { - buf->appendf("table_raft_state_t { config = ..."); - buf->appendf(" contracts = "); - debug_print(buf, state.contracts); - buf->appendf(" branch_history.size = "); - debug_print(buf, state.branch_history.branches.size()); - buf->appendf(" current_branches = "); - debug_print(buf, state.current_branches); - buf->appendf(" member_ids = "); - debug_print(buf, state.member_ids); - buf->appendf(" server_names = "); - debug_print(buf, state.server_names.names); - buf->appendf(" }"); -} - diff --git a/src/clustering/table_contract/contract_metadata.hpp b/src/clustering/table_contract/contract_metadata.hpp deleted file mode 100644 index 0651611c910..00000000000 --- a/src/clustering/table_contract/contract_metadata.hpp +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_CONTRACT_CONTRACT_METADATA_HPP_ -#define CLUSTERING_TABLE_CONTRACT_CONTRACT_METADATA_HPP_ - -#include -#include -#include - -#include "clustering/administration/tables/table_metadata.hpp" -#include "clustering/generic/raft_core.hpp" -#include "clustering/id_types.hpp" -#include "clustering/immediate_consistency/history.hpp" -#include "containers/optional.hpp" -#include "region/region_map.hpp" -#include "rpc/semilattice/joins/macros.hpp" /* for EQUALITY_COMPARABLE macros */ - -/* `table_raft_state_t` is the type of the state that is stored in each table's Raft -instance. The most important part is a collection of `contract_t`s, which describe the -current state of the different shards. Each replica has a `contract_executor_t` for each -table, which watches the `contract_t`s and performs backfills, accepts queries, etc. in -response to what the `contract_t`s say. In addition, the `contract_executor_t` sends -`contract_ack_t`s back to the table's `contract_coordinator_t`, which initiates Raft -transactions to update the `contract_t`s as necessary to perform auto-failover, implement -the user's configuration changes, and so on. - -The name `contract_t` comes from the fact that it controls the future behavior of the -`contract_coordinator_t` and the `contract_executor_t`, so it's like a "contract" between -them. The user can also be thought of as a party to the "contract", because the contract -guarantees that writes won't be discarded once they've been acked to the user. The -analogy is a bit of a stretch. */ - -class contract_t; -class contract_ack_t; -class table_raft_state_t; - -/* We provide the following consistency guarantee: Suppose the client sends a write W -which affects some region R of the table, and the cluster acknowledges that W has been -performed. Then every future read will see W, even if servers die or auto-failover -happens, unless the administrator issues a manual override. (This assumes that the reads -and writes are not acked to the client until a majority of voters have replied.) - -To provide this guarantee, we maintain a bunch of invariants. Let V be the `version_t` -which W creates. Let `c` be the `contract_t` in the Raft state for some region R' which -overlaps R, and let `b` be the value of `current_branches` in the Raft state for some -region R'' that overlaps R. Then the following are always true unless the user issues -a manual override: -1. A majority of `c.voters` have versions on disk which descend from V (or are V) in the - intersection of R and R'. -2. If `c.primary` is present, then `c.primary->server` has a version on disk which - descends from V (or is V) in the intersection of R and R'. -3. The latest version on `b` descends from V (or is V) in the intersection of R and R''. -*/ - -class contract_t { -public: - class primary_t { - public: - /* The server that's supposed to be primary. */ - server_id_t server; - /* If we're switching to another primary, then `hand_over` is the server ID of - the server we're switching to. */ - optional hand_over; - }; - - contract_t() : after_emergency_repair(false) { } - -#ifndef NDEBUG - void sanity_check() const; -#endif /* NDEBUG */ - - bool is_voter(const server_id_t &s) const { - return voters.count(s) == 1 || - (static_cast(temp_voters) && temp_voters->count(s) == 1); - } - - /* `replicas` is all the servers that are replicas for this table, whether voting or - non-voting. `voters` is a subset of `replicas` that just contains the voting - replicas. If we're in the middle of a transition between two sets of voters, then - `temp_voters` will contain the new set. */ - std::set replicas; - std::set voters; - optional > temp_voters; - - /* `primary` contains the server that's supposed to be primary. If we're in the - middle of a transition between two primaries, then `primary` will be empty. */ - optional primary; - - /* `after_emergency_repair` is set to `true` when we conduct an emergency repair. - When it's `true` we'll use a different algorithm for choosing primary replicas. Once - the table has stabilized after the repair, we'll set it back to `false`. */ - bool after_emergency_repair; -}; - -RDB_DECLARE_EQUALITY_COMPARABLE(contract_t::primary_t); -RDB_DECLARE_EQUALITY_COMPARABLE(contract_t); -RDB_DECLARE_SERIALIZABLE(contract_t::primary_t); -RDB_DECLARE_SERIALIZABLE(contract_t); - -/* Each contract is tagged with a `contract_id_t`. If the contract changes in any way, it -gets a new ID. All the `contract_ack_t`s are tagged with the contract ID that they are -responding to. This way, the coordinator knows exactly which contract the executor is -acking. - -In order to facilitiate CPU sharding, each contract's region must apply to exactly one -CPU shard. */ - -// Defined in id_types.hpp. -// typedef uuid_u contract_id_t; - -/* The `contract_executor_t` looks at what each `contract_t` says about its server ID, -and reacts according to the following rules: - -If `server_id == contract.primary->server`: -- Serve backfills to servers that request them -- If `contract.primary->hand_over`, then don't handle any queries. If - `*contract.primary->hand_over` is up to date, then ack `primary_ready`. Otherwise, - ack `primary_in_progress`. -- If `contract.branch` isn't a branch ID that we just now created, then don't handle any - queries and report `primary_need_branch` with a branch ID. -- If `contract.primary->hand_over` is empty and `contract.branch` is the branch that we - created, do handle queries. Ack writes to the client when a majority of `voters` and - `temp_voters` (if present) have them on disk. If every write that happened before the - last time the contract changed has been replicated to a majority of `voters` and - `temp_voters` (if present), then ack `primary_ready`. Otherwise, ack - `primary_in_progress`. - -Otherwise, if `server_id` is in `contract.replicas`: -- If `contract.primary` is empty or we can't contact `contract.primary->server`, then ack - `secondary_need_primary`. -- Otherwise, backfill data and then stream writes from `contract.primary->server`. While - backfilling, ack `secondary_backfilling`; when done backfilling, ack - `secondary_streaming`. - -If `server_id` is not in `contract.replicas`: -- Delete all data. -- Don't send an ack. -*/ - -class contract_ack_t { -public: - enum class state_t { - primary_need_branch, - primary_in_progress, - primary_ready, - secondary_need_primary, - secondary_backfilling, - secondary_streaming - }; - - contract_ack_t() { } - explicit contract_ack_t(state_t s) : state(s) { } - -#ifndef NDEBUG - void sanity_check( - const server_id_t &server, - const contract_id_t &contract_id, - const table_raft_state_t &raft_state) const; -#endif - - state_t state; - - /* This is non-empty if `state` is `secondary_need_primary`. */ - optional > version; - - /* This is non-empty if `state` is `primary_need_branch`. - When a new primary is first instantiated in response to a new contract_t, it - generates a new branch ID and sends it to the coordinator through this field. - The coordinator will then registers the branch and update the `current_branches` - field of the Raft state. */ - optional branch; - - /* This contains information about all branches mentioned in `version` or `branch` */ - branch_history_t branch_history; -}; -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE( - contract_ack_t::state_t, int8_t, - contract_ack_t::state_t::primary_need_branch, - contract_ack_t::state_t::secondary_streaming); -RDB_DECLARE_SERIALIZABLE(contract_ack_t); -RDB_DECLARE_EQUALITY_COMPARABLE(contract_ack_t); - -/* `table_raft_state_t` is the datum that each table's Raft cluster manages. The -`raft_member_t` type is templatized on a template paramter called `state_t`, and -`table_raft_state_t` is the concrete value that it's instantiated to. */ - -class table_raft_state_t { -public: - class change_t { - public: - class set_table_config_t { - public: - table_config_and_shards_t new_config; - }; - - class new_contracts_t { - public: - std::set remove_contracts; - std::map > add_contracts; - std::map register_current_branches; - branch_history_t add_branches; - std::set remove_branches; - std::set remove_server_names; - server_name_map_t add_server_names; - }; - - class new_member_ids_t { - public: - std::set remove_member_ids; - std::map add_member_ids; - }; - - class new_server_names_t { - public: - server_name_map_t config_and_shards; - server_name_map_t raft_state; - }; - - change_t() { } - template change_t(T &&t) : v(std::move(t)) { } - - boost::variant< - set_table_config_t, - new_contracts_t, - new_member_ids_t, - new_server_names_t> v; - }; - - table_raft_state_t() - : current_branches(region_t::universe(), nil_uuid()) { } - - void apply_change(const change_t &c); - -#ifndef NDEBUG - void sanity_check() const; -#endif /* NDEBUG */ - - /* `config` is the latest user-specified config. The user can freely read and modify - this at any time. */ - table_config_and_shards_t config; - - /* `contracts` is the `contract_t`s for the table, along with the region each one - applies to. `contract_coordinator_t` reads and writes it; `contract_executor_t` reads - it. */ - std::map > contracts; - - /* `current_branches` contains the most recently acked branch IDs for a given - region. It gets updated by the coordinator when a new primary registers its - branch with the coordinator through a `contract_ack_t`. - The coordinator uses this to find out which replica has the most up to date - data version for a given range (see `break_ack_into_fragments()` in - coordinator.cc). */ - region_map_t current_branches; - - /* `branch_history` contains all of the branches that appear in `current_branches`, - plus their ancestors going back to the common ancestor with the data version stored - on the replicas. The idea is that if a replica sends us its current version and its - branch history, we can combine the replica's branch history with this branch history - to form a complete picture of the relationship of the replica's current data version - to the contract's `branch`. */ - branch_history_t branch_history; - - /* `member_ids` assigns a Raft member ID to each server that's supposed to be part of - the Raft cluster for this table. `contract_coordinator_t` writes it; - `multi_table_manager_t` reads it and uses that information to add and remove servers - to the Raft cluster. */ - std::map member_ids; - - /* `server_names` contains the server name of every server that's mentioned in a - contract. This is used to display `server_status`. */ - server_name_map_t server_names; -}; - -RDB_DECLARE_EQUALITY_COMPARABLE(table_raft_state_t::change_t::set_table_config_t); -RDB_DECLARE_EQUALITY_COMPARABLE(table_raft_state_t::change_t::new_contracts_t); -RDB_DECLARE_EQUALITY_COMPARABLE(table_raft_state_t::change_t::new_member_ids_t); -RDB_DECLARE_EQUALITY_COMPARABLE(table_raft_state_t::change_t::new_server_names_t); -RDB_DECLARE_EQUALITY_COMPARABLE(table_raft_state_t::change_t); -RDB_DECLARE_EQUALITY_COMPARABLE(table_raft_state_t); -RDB_DECLARE_SERIALIZABLE(table_raft_state_t::change_t::set_table_config_t); -RDB_DECLARE_SERIALIZABLE(table_raft_state_t::change_t::new_contracts_t); -RDB_DECLARE_SERIALIZABLE(table_raft_state_t::change_t::new_member_ids_t); -RDB_DECLARE_SERIALIZABLE(table_raft_state_t::change_t::new_server_names_t); -RDB_DECLARE_SERIALIZABLE(table_raft_state_t::change_t); -RDB_DECLARE_SERIALIZABLE(table_raft_state_t); - -/* Returns a `table_raft_state_t` for a newly-created table with the given configuration. -*/ -table_raft_state_t make_new_table_raft_state( - const table_config_and_shards_t &config); - -/* `table_shard_status_t` describes the current state of the server with respect to some -range of the key-space. It's used for producing `rethinkdb.table_status`. It's designed -so that `table_shard_status_t`s from different hash-shards or different ranges can be -meaningfully combined. */ -class table_shard_status_t { -public: - table_shard_status_t() : primary(false), secondary(false), need_primary(false), - need_quorum(false), backfilling(false), transitioning(false) { } - void merge(const table_shard_status_t &other) { - primary |= other.primary; - secondary |= other.secondary; - need_primary |= other.need_primary; - need_quorum |= other.need_quorum; - backfilling |= other.backfilling; - transitioning |= other.transitioning; - } - bool primary; /* server is primary */ - bool secondary; /* server is secondary */ - bool need_primary; /* server is secondary and waiting for primary */ - bool need_quorum; /* server is primary and waiting for branch */ - bool backfilling; /* server is receiving a backfill */ - bool transitioning; /* server is in a contract, but has no ack */ -}; -RDB_DECLARE_EQUALITY_COMPARABLE(table_shard_status_t); -RDB_DECLARE_SERIALIZABLE(table_shard_status_t); - -void debug_print(printf_buffer_t *buf, const contract_t::primary_t &primary); -void debug_print(printf_buffer_t *buf, const contract_t &contract); -void debug_print(printf_buffer_t *buf, const contract_ack_t &ack); -void debug_print(printf_buffer_t *buf, const table_raft_state_t &state); - -#endif /* CLUSTERING_TABLE_CONTRACT_CONTRACT_METADATA_HPP_ */ - diff --git a/src/clustering/table_contract/coordinator/calculate_contracts.cc b/src/clustering/table_contract/coordinator/calculate_contracts.cc deleted file mode 100644 index c22ceb3205e..00000000000 --- a/src/clustering/table_contract/coordinator/calculate_contracts.cc +++ /dev/null @@ -1,762 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_contract/coordinator/calculate_contracts.hpp" - -#include "clustering/table_contract/branch_history_gc.hpp" -#include "clustering/table_contract/cpu_sharding.hpp" -#include "logger.hpp" - -/* A `contract_ack_t` is not necessarily homogeneous. It may have different `version_t`s -for different regions, and a region with a single `version_t` may need to be split -further depending on the branch history. Since `calculate_contract()` assumes it's -processing a homogeneous input, we need to break the `contract_ack_t` into homogeneous -pieces. `contract_ack_frag_t` is like a homogeneous version of `contract_ack_t`; in place -of the `region_map_t` it has a single `state_timestamp_t`. Use -`break_ack_into_fragments()` to convert a `contract_ack_t` into a -`region_map_t`. */ - -class contract_ack_frag_t { -public: - bool operator==(const contract_ack_frag_t &x) const { - return state == x.state && version == x.version && - common_ancestor == x.common_ancestor && branch == x.branch; - } - bool operator!=(const contract_ack_frag_t &x) const { - return !(*this == x); - } - - /* `state` and `branch` are the same as the corresponding fields of the original - `contract_ack_t` */ - contract_ack_t::state_t state; - optional branch; - - /* `version` is the value of the `version` field of the original `contract_ack_t` for - the specific sub-region this fragment applies to. */ - optional version; - - /* `common_ancestor` is the timestamp of the last common ancestor of the original - `contract_ack_t` and the `current_branch` in the Raft state for the specific - sub-region this fragment applies to. If `version` is blank, this will always be - blank; if `version` is present, `common_ancestor` might or might not be blank - depending on whether we expect to use the value. */ - optional common_ancestor; -}; - -region_map_t break_ack_into_fragments( - const region_t ®ion, - const contract_ack_t &ack, - const region_map_t ¤t_branches, - const branch_history_reader_t *raft_branch_history, - bool compute_common_ancestor) { - contract_ack_frag_t base_frag; - base_frag.state = ack.state; - base_frag.branch = ack.branch; - if (!static_cast(ack.version)) { - return region_map_t(region, base_frag); - } else if (compute_common_ancestor) { - branch_history_combiner_t combined_branch_history( - raft_branch_history, &ack.branch_history); - /* Fragment over branches and then over versions within each branch. */ - return ack.version->map_multi(region, - [&](const region_t &ack_version_reg, const version_t &vers) { - base_frag.version = make_optional(vers); - return current_branches.map_multi(ack_version_reg, - [&](const region_t &branch_reg, const branch_id_t &branch) { - region_map_t points_on_canonical_branch; - try { - points_on_canonical_branch = - version_find_branch_common(&combined_branch_history, - vers, branch, branch_reg); - } catch (const missing_branch_exc_t &) { -#ifndef NDEBUG - crash("Branch history is incomplete"); -#else - logERR("The branch history is incomplete. This probably means " - "that there is a bug in RethinkDB. Please report this " - "at https://github.com/rethinkdb/rethinkdb/issues/ ."); - /* Recover by using the root branch */ - points_on_canonical_branch = - region_map_t(region_t::universe(), - version_t::zero()); -#endif - } - return points_on_canonical_branch.map(branch_reg, - [&](const version_t &common_vers) { - base_frag.common_ancestor = - make_optional(common_vers.timestamp); - return base_frag; - }); - }); - }); - } else { - return ack.version->map(region, - [&](const version_t &vers) { - base_frag.version = make_optional(vers); - return base_frag; - }); - } -} - -/* `invisible_to_majority_of_set()` returns `true` if `target` definitely cannot be seen -by a majority of the servers in `judges`. If we can't see one of the servers in `judges`, -we'll assume it can see `target` to reduce spurious failoves. */ -bool invisible_to_majority_of_set( - const server_id_t &target, - const std::set &judges, - watchable_map_t, empty_value_t> * - connections_map) { - size_t count = 0; - for (const server_id_t &s : judges) { - if (connections_map->get_key(std::make_pair(s, target)).has_value() || - !connections_map->get_key(std::make_pair(s, s)).has_value()) { - ++count; - } - } - return !(count > judges.size() / 2); -} - -/* A small helper function for `calculate_contract()` to test whether a given -replica is currently streaming. */ -bool is_streaming( - const contract_t &old_c, - const std::map &acks, - server_id_t server) { - auto it = acks.find(server); - if (it != acks.end() && - (it->second.state == contract_ack_t::state_t::secondary_streaming || - (static_cast(old_c.primary) && - old_c.primary->server == server))) { - return true; - } else { - return false; - } -} - -/* `calculate_contract()` calculates a new contract for a region. Whenever any of the -inputs changes, the coordinator will call `update_contract()` to compute a contract for -each range of keys. The new contract will often be the same as the old, in which case it -doesn't get a new contract ID. */ -contract_t calculate_contract( - /* The old contract that contains this region. */ - const contract_t &old_c, - /* The user-specified configuration for the shard containing this region. */ - const table_config_t::shard_t &config, - /* Contract acks from replicas regarding `old_c`. If a replica hasn't sent us an - ack *specifically* for `old_c`, it won't appear in this map; we don't include - acks for contracts that were in the same region before `old_c`. */ - const std::map &acks, - /* This `watchable_map_t` will have an entry for (X, Y) if we can see server X - and server X can see server Y. */ - watchable_map_t, empty_value_t> * - connections_map) { - - contract_t new_c = old_c; - - /* If there are new servers in `config.all_replicas`, add them to `c.replicas` */ - new_c.replicas.insert(config.all_replicas.begin(), config.all_replicas.end()); - - /* If there is a mismatch between `config.voting_replicas()` and `c.voters`, then - add and/or remove voters until both sets match. - There are three important restrictions we have to consider here: - 1. We should not remove voters if that would reduce the total number of voters - below the minimum of the size of the current voter set and the size of the - configured voter set. Consider the case where a user wants to replace - a few replicas by different ones. Without this restriction, we would often - remove all the old replicas from the voters set before enough of the new - replicas would become streaming to replace them. - 2. To increase availability in some scenarios, we also don't remove replicas - if that would mean being left with less than a majority of streaming - replicas. - 3. Only replicas that are streaming are guaranteed to have a complete branch - history. Once we make a replica voting, some parts of our logic assume that - the branch history is intact (most importantly our call to - `break_ack_into_fragments()` in `calculate_all_contracts()`). - To avoid this condition, we only add a replica to the voters list after it - has started streaming. - See https://github.com/rethinkdb/rethinkdb/issues/4866 for more details. */ - std::set config_voting_replicas = config.voting_replicas(); - if (!static_cast(old_c.temp_voters) && - old_c.voters != config_voting_replicas) { - bool voters_changed = false; - std::set new_voters = old_c.voters; - - /* Step 1: Check if we can add any voters */ - for (const server_id_t &server : config_voting_replicas) { - if (old_c.voters.count(server)) { - /* The replica is already a voter */ - continue; - } - if (is_streaming(old_c, acks, server)) { - /* The replica is streaming. We can add it to the voter set. */ - new_voters.insert(server); - voters_changed = true; - } - } - - /* Step 2: Remove voters */ - /* We try to remove non-streaming replicas first before we start removing - streaming ones to maximize availability in case a replica fails. */ - std::list to_remove; - for (const server_id_t &server : new_voters) { - if (config_voting_replicas.count(server) > 0) { - /* The replica should remain a voter */ - continue; - } - if (is_streaming(old_c, acks, server)) { - /* The replica is streaming. Put it at the end of the `to_remove` - list. */ - to_remove.push_back(server); - } else { - /* The replica is not streaming. Removing it doesn't hurt - availability, so we put it at the front of the `to_remove` list. */ - to_remove.push_front(server); - } - } - - size_t num_streaming = 0; - for (const server_id_t &server : new_voters) { - if (is_streaming(old_c, acks, server)) { - ++num_streaming; - } - } - size_t min_voters_size = std::min(old_c.voters.size(), - config_voting_replicas.size()); - for (const server_id_t &server_to_remove : to_remove) { - /* Check if we can remove more voters without going below - `min_voters_size`, and without losing a majority of streaming voters. */ - size_t remaining_streaming = is_streaming(old_c, acks, server_to_remove) - ? num_streaming - 1 - : num_streaming; - size_t remaining_total = new_voters.size() - 1; - bool would_lose_majority = remaining_streaming <= remaining_total / 2; - if (would_lose_majority || new_voters.size() <= min_voters_size) { - break; - } - new_voters.erase(server_to_remove); - voters_changed = true; - } - - /* Step 3: If anything changed, stage the new voter set into `temp_voters` */ - rassert(voters_changed == (old_c.voters != new_voters)); - if (voters_changed) { - new_c.temp_voters.set(new_voters); - } - } - - /* If we already initiated a voter change by setting `temp_voters`, it might be time - to commit that change by setting `voters` to `temp_voters`. */ - if (static_cast(old_c.temp_voters)) { - /* Before we change `voters`, we have to make sure that we'll preserve the - invariant that every acked write is on a majority of `voters`. This is mostly the - job of the primary; it will not report `primary_ready` unless it is requiring - acks from a majority of both `voters` and `temp_voters` before acking writes to - the client, *and* it has ensured that every write that was acked before that - policy was implemented has been backfilled to a majority of `temp_voters`. So we - can't switch voters unless the primary reports `primary_ready`. - See `primary_execution_t::is_contract_ackable` for the detailed semantics of - the `primary_ready` state. */ - if (static_cast(old_c.primary) && - acks.count(old_c.primary->server) == 1 && - acks.at(old_c.primary->server).state == - contract_ack_t::state_t::primary_ready) { - /* The `acks` we just checked are based on `old_c`, so we really shouldn't - commit any different set of `temp_voters`. */ - guarantee(new_c.temp_voters == old_c.temp_voters); - /* OK, it's safe to commit. */ - new_c.voters = *new_c.temp_voters; - new_c.temp_voters.reset(); - } - } - - /* `visible_voters` includes all members of `voters` and `temp_voters` which could be - visible to a majority of `voters` (and `temp_voters`, if `temp_voters` exists). Note - that if the coordinator can't see server X, it will assume server X can see every - other server; this reduces spurious failovers when the coordinator loses contact with - other servers. */ - std::set visible_voters; - for (const server_id_t &server : new_c.replicas) { - if (new_c.voters.count(server) == 0 && - (!static_cast(new_c.temp_voters) || - new_c.temp_voters->count(server) == 0)) { - continue; - } - if (invisible_to_majority_of_set(server, new_c.voters, connections_map)) { - continue; - } - if (static_cast(new_c.temp_voters)) { - if (invisible_to_majority_of_set( - server, *new_c.temp_voters, connections_map)) { - continue; - } - } - visible_voters.insert(server); - } - - /* If a server was removed from `config.replicas` and `c.voters` but it's still in - `c.replicas`, then remove it. And if it's primary, then make it not be primary. */ - bool should_kill_primary = false; - for (const server_id_t &server : old_c.replicas) { - if (config.all_replicas.count(server) == 0 && - new_c.voters.count(server) == 0 && - (!static_cast(new_c.temp_voters) || - new_c.temp_voters->count(server) == 0)) { - new_c.replicas.erase(server); - if (static_cast(old_c.primary) && old_c.primary->server == server) { - /* Actual killing happens further down */ - should_kill_primary = true; - } - } - } - - /* If we don't have a primary, choose a primary. Servers are not eligible to be a - primary unless they are carrying every acked write. There will be at least one - eligible server if and only if we have reports from a majority of `new_c.voters`. - - In addition, we must choose `config.primary_replica` if it is eligible. If - `config.primary_replica` has not sent an ack, we must wait for the failover timeout - to elapse before electing a different replica. This is to make sure that we won't - elect the wrong replica simply because the user's designated primary took a little - longer to send the ack. */ - if (!static_cast(old_c.primary) && !old_c.after_emergency_repair) { - /* We have an invariant that every acked write must be on the path from the root - of the branch history to `old_c.branch`. So we project each voter's state onto - that path, then sort them by position along the path. Any voter that is at least - as up to date, according to that metric, as more than half of the voters - (including itself) is eligible. We also take into account whether a server is - visible to its peers when deciding which server to select. */ - - /* First, collect the states from the servers, and sort them by how up-to-date - they are. Note that we use the server ID as a secondary sorting key. This mean we - tend to pick the same server if we run the algorithm twice; this helps to reduce - unnecessary fragmentation. */ - std::vector > sorted_candidates; - for (const server_id_t &server : new_c.voters) { - if (acks.count(server) == 1 && acks.at(server).state == - contract_ack_t::state_t::secondary_need_primary) { - sorted_candidates.push_back( - std::make_pair(*(acks.at(server).common_ancestor), server)); - } - } - std::sort(sorted_candidates.begin(), sorted_candidates.end()); - - /* Second, determine which servers are eligible to become primary on the basis of - their data and their visibility to their peers. */ - std::vector eligible_candidates; - for (size_t i = 0; i < sorted_candidates.size(); ++i) { - server_id_t server = sorted_candidates[i].second; - /* If the server is not visible to more than half of its peers, then it is - not eligible to be primary */ - if (visible_voters.count(server) == 0) { - continue; - } - /* `up_to_date_count` is the number of servers that `server` is at least as - up-to-date as. We know `server` must be at least as up-to-date as itself and - all of the servers that are earlier in the list. */ - size_t up_to_date_count = i + 1; - /* If there are several servers with the same timestamp, they will appear - together in the list. So `server` may be at least as up-to-date as some of - the servers that appear after it in the list. */ - while (up_to_date_count < sorted_candidates.size() && - sorted_candidates[up_to_date_count].first == - sorted_candidates[i].first) { - ++up_to_date_count; - } - /* OK, now `up_to_date_count` is the number of servers that this server is - at least as up-to-date as. */ - if (up_to_date_count > new_c.voters.size() / 2) { - eligible_candidates.push_back(server); - } - } - - /* OK, now we can pick a primary. */ - auto it = std::find(eligible_candidates.begin(), eligible_candidates.end(), - config.primary_replica); - if (it != eligible_candidates.end()) { - /* The user's designated primary is eligible, so use it. */ - contract_t::primary_t p; - p.server = config.primary_replica; - new_c.primary.set(p); - } else if (!eligible_candidates.empty()) { - /* The user's designated primary is ineligible. We have to decide if we'll - wait for the user's designated primary to become eligible, or use one of the - other eligible candidates. */ - if (!config.primary_replica.get_uuid().is_nil() && - visible_voters.count(config.primary_replica) == 1 && - acks.count(config.primary_replica) == 0) { - /* The user's designated primary is visible to a majority of its peers, - and the only reason it was disqualified is because we haven't seen an ack - from it yet. So we'll wait for it to send in an ack rather than electing - a different primary. */ - } else { - /* We won't wait for it. */ - contract_t::primary_t p; - /* `eligible_candidates` is ordered by how up-to-date they are */ - p.server = eligible_candidates.back(); - new_c.primary.set(p); - } - } - - } else if (!static_cast(old_c.primary) && old_c.after_emergency_repair) { - /* If we recently completed an emergency repair operation, we use a different - procedure to pick the primary: we require all voters to be present, and then we - pick the voter with the highest version, regardless of `current_branch`. The - reason we can't take `current_branch` into account is that the emergency repair - might create gaps in the branch history, so we can't reliably compute each - replica's position on `current_branch`. The reason we require all replicas to be - present is to make the emergency repair safer. For example, imagine if a table - has three voters, A, B, and C. Suppose that voter B is lagging behind the others, - and voter C is removed by emergency repair. Then the normal algorithm could pick - voter B as the primary, even though it's missing some data. */ - server_id_t best_primary = server_id_t::from_server_uuid(nil_uuid()); - state_timestamp_t best_timestamp = state_timestamp_t::zero(); - bool all_present = true; - for (const server_id_t &server : new_c.voters) { - if (acks.count(server) == 1 && acks.at(server).state == - contract_ack_t::state_t::secondary_need_primary) { - state_timestamp_t timestamp = acks.at(server).version->timestamp; - if (best_primary.get_uuid().is_nil() || timestamp > best_timestamp) { - best_primary = server; - best_timestamp = timestamp; - } - } else { - all_present = false; - break; - } - } - if (all_present) { - contract_t::primary_t p; - p.server = best_primary; - new_c.primary.set(p); - } - } - - /* Sometimes we already have a primary, but we need to pick a different one. There - are three such situations: - - The existing primary is disconnected - - The existing primary isn't `config.primary_replica`, and `config.primary_replica` - is ready to take over the role - - `config.primary_replica` isn't ready to take over the role, but the existing - primary isn't even supposed to be a replica anymore. - In the first situation, we'll simply remove `c.primary`. In the second and third - situations, we'll first set `c.primary->warm_shutdown` to `true`, and then only - once the primary acknowledges that, we'll remove `c.primary`. Either way, once the - replicas acknowledge the contract in which we removed `c.primary`, the logic earlier - in this function will select a new primary. Note that we can't go straight from the - old primary to the new one; we need a majority of replicas to promise to stop - receiving updates from the old primary before it's safe to elect a new one. */ - if (static_cast(old_c.primary)) { - /* Note we already checked for the case where the old primary wasn't supposed to - be a replica. If this is so, then `should_kill_primary` will already be set to - `true`. */ - - /* Check if we need to do an auto-failover. The precise form of this condition - isn't important for correctness. If we do an auto-failover when the primary isn't - actually dead, or don't do an auto-failover when the primary is actually dead, - the worst that will happen is we'll lose availability. */ - if (!should_kill_primary && visible_voters.count(old_c.primary->server) == 0) { - should_kill_primary = true; - } - - if (should_kill_primary) { - new_c.primary.reset(); - } else if (old_c.primary->server != config.primary_replica) { - /* The old primary is still a valid replica, but it isn't equal to - `config.primary_replica`. So we have to do a hand-over to ensure that after - we kill the primary, `config.primary_replica` will be a valid candidate. */ - - if (old_c.primary->hand_over != - make_optional(config.primary_replica)) { - /* We haven't started the hand-over yet, or we're in the middle of a - hand-over to a different primary. */ - if (acks.count(config.primary_replica) == 1 && - acks.at(config.primary_replica).state == - contract_ack_t::state_t::secondary_streaming && - visible_voters.count(config.primary_replica) == 1) { - /* The new primary is ready, so begin the hand-over. */ - new_c.primary->hand_over.set(config.primary_replica); - } else { - /* We're not ready to switch to the new primary yet. */ - if (static_cast(old_c.primary->hand_over)) { - /* We were in the middle of a hand over to a different primary, - and then the user changed `config.primary_replica`. But the new - primary isn't ready yet, so cancel the old hand-over. (This is - very uncommon.) */ - new_c.primary->hand_over.reset(); - } - } - } else { - /* We're already in the process of handing over to the new primary. */ - if (acks.count(old_c.primary->server) == 1 && - acks.at(old_c.primary->server).state == - contract_ack_t::state_t::primary_ready) { - /* The hand over is complete. Now it's safe to stop the old primary. - The new primary will be started later, after a majority of the - replicas acknowledge that they are no longer listening for writes - from the old primary. - See `primary_execution_t::is_contract_ackable` for a detailed - explanation of what the `primary_ready` state implies. */ - new_c.primary.reset(); - } else if (visible_voters.count(config.primary_replica) == 0) { - /* Something went wrong with the new primary before the hand-over was - complete. So abort the hand-over. */ - new_c.primary->hand_over.reset(); - } - } - } else { - if (static_cast(old_c.primary->hand_over)) { - /* We were in the middle of a hand over, but then the user changed - `config.primary_replica` back to what it was before. (This is very - uncommon.) */ - new_c.primary->hand_over.reset(); - } - } - } - - return new_c; -} - -/* `calculate_all_contracts()` is sort of like `calculate_contract()` except that it -applies to the whole set of contracts instead of to a single contract. It takes the -inputs that `calculate_contract()` needs, but in sharded form; then breaks the key space -into small enough chunks that the inputs are homogeneous across each chunk; then calls -`calculate_contract()` on each chunk. - -The output is in the form of a diff instead of a set of new contracts. We need a diff to -put in the `table_raft_state_t::change_t::new_contracts_t`, and we need to compute the -diff anyway in order to reuse contract IDs for contracts that haven't changed, so it -makes sense to combine those two diff processes. */ -void calculate_all_contracts( - const table_raft_state_t &old_state, - const std::map > &acks, - watchable_map_t, empty_value_t> - *connections_map, - std::set *remove_contracts_out, - std::map > *add_contracts_out, - std::map *register_current_branches_out, - std::set *remove_branches_out, - branch_history_t *add_branches_out) { - - ASSERT_FINITE_CORO_WAITING; - - /* Initially, we put every branch into `remove_branches_out`. Then as we process - contracts, we "mark branches live" by removing them from `remove_branches_out`. */ - for (const auto &pair : old_state.branch_history.branches) { - remove_branches_out->insert(pair.first); - } - - std::vector new_contract_region_vector; - std::vector new_contract_vector; - - /* We want to break the key-space into sub-regions small enough that the contract, - table config, and ack versions are all constant across the sub-region. First we - iterate over all contracts: */ - for (const std::pair > &cpair : - old_state.contracts) { - /* Next iterate over all shards of the table config and find the ones that - overlap the contract in question: */ - for (size_t shard_index = 0; shard_index < old_state.config.config.shards.size(); - ++shard_index) { - region_t region = region_intersection( - cpair.second.first, - region_t(old_state.config.shard_scheme.get_shard_range(shard_index))); - if (region_is_empty(region)) { - continue; - } - - /* Find acks for this contract. If there aren't any acks for this contract, - then `acks` might not even have an empty map, so we need to construct an - empty map in that case. */ - const std::map *this_contract_acks; - { - static const std::map empty_ack_map; - auto it = acks.find(cpair.first); - this_contract_acks = (it == acks.end()) ? &empty_ack_map : &it->second; - } - - /* Now collect the acks for this contract into `ack_frags`. `ack_frags` is - homogeneous at first and then it gets fragmented as we iterate over `acks`. - */ - region_map_t > frags_by_server( - region); - for (const auto &pair : *this_contract_acks) { - /* Sanity-check the ack */ - DEBUG_ONLY_CODE(pair.second.sanity_check( - pair.first, cpair.first, old_state)); - - /* There are two situations where we don't compute the common ancestor: - 1. If the server sending the ack is not in `voters` or `temp_voters` - 2. If the contract has the `after_emergency_repair` flag set - In these situations, we don't need the common ancestor, but computing it - might be dangerous because the branch history might be incomplete. */ - bool compute_common_ancestor = - (cpair.second.second.voters.count(pair.first) == 1 || - (static_cast(cpair.second.second.temp_voters) && - cpair.second.second.temp_voters->count(pair.first) == 1)) && - !cpair.second.second.after_emergency_repair; - - region_map_t frags = break_ack_into_fragments( - region, pair.second, old_state.current_branches, - &old_state.branch_history, compute_common_ancestor); - - frags.visit(region, - [&](const region_t ®, const contract_ack_frag_t &frag) { - frags_by_server.visit_mutable(reg, - [&](const region_t &, - std::map *acks_map) { - auto res = acks_map->insert( - std::make_pair(pair.first, frag)); - guarantee(res.second); - }); - }); - } - - frags_by_server.visit(region, - [&](const region_t ®, - const std::map &acks_map) { - /* We've finally collected all the inputs to `calculate_contract()` and - broken the key space into regions across which the inputs are - homogeneous. So now we can actually call it. */ - - const contract_t &old_contract = cpair.second.second; - - contract_t new_contract = calculate_contract( - old_contract, - old_state.config.config.shards[shard_index], - acks_map, - connections_map); - - /* Register a branch if a primary is asking us to */ - optional registered_new_branch; - if (static_cast(old_contract.primary) && - static_cast(new_contract.primary) && - old_contract.primary->server == - new_contract.primary->server && - acks_map.count(old_contract.primary->server) == 1 && - acks_map.at(old_contract.primary->server).state == - contract_ack_t::state_t::primary_need_branch) { - branch_id_t to_register = - *acks_map.at(old_contract.primary->server).branch; - bool already_registered = true; - old_state.current_branches.visit(reg, - [&](const region_t &, const branch_id_t &cur_branch) { - already_registered &= (cur_branch == to_register); - }); - if (!already_registered) { - auto res = register_current_branches_out->insert( - std::make_pair(reg, to_register)); - guarantee(res.second); - /* Due to branch garbage collection on the executor, - the branch history in the contract_ack might be incomplete. - Usually this isn't a problem, because the executor is - only going to garbage collect branches when it is sure - that the current branches are already present in the Raft - state. In that case `copy_branch_history_for_branch()` is - not going to traverse to the GCed branches. - However this assumption no longer holds if the Raft state - has just been overwritten by an emergency repair operation. - Hence we ignore missing branches in the copy operation. */ - bool ignore_missing_branches - = old_contract.after_emergency_repair; - copy_branch_history_for_branch( - to_register, - this_contract_acks->at( - old_contract.primary->server).branch_history, - old_state, - ignore_missing_branches, - add_branches_out); - registered_new_branch = make_optional(to_register); - } - } - - /* Check to what extent we can confirm that the replicas are on - `current_branch`. We'll use this to determine when it's safe to GC and - whether we can switch off the `after_emergency_repair` flag (if it was - on). */ - bool can_gc_branch_history = true, can_end_after_emergency_repair = true; - for (const server_id_t &server : new_contract.replicas) { - auto it = this_contract_acks->find(server); - if (it == this_contract_acks->end() || ( - it->second.state != - contract_ack_t::state_t::primary_ready && - it->second.state != - contract_ack_t::state_t::secondary_streaming)) { - /* At least one replica can't be confirmed to be on - `current_branch`, so we should keep the branch history around in - order to make it easy for that replica to rejoin later. */ - can_gc_branch_history = false; - - if (new_contract.voters.count(server) == 1 || - (static_cast(new_contract.temp_voters) && - new_contract.temp_voters->count(server) == 1)) { - /* If the `after_emergency_repair` flag is set to `true`, we - need to leave it set to `true` until we can confirm that - the branch history is intact. */ - can_end_after_emergency_repair = false; - } - } - } - - /* Branch history GC. The key decision is whether we should only keep - `current_branch`, or whether we need to keep all of its ancestors too. */ - old_state.current_branches.visit(reg, - [&](const region_t &subregion, const branch_id_t ¤t_branch) { - if (!current_branch.is_nil()) { - if (can_gc_branch_history) { - remove_branches_out->erase(current_branch); - } else { - mark_all_ancestors_live(current_branch, subregion, - &old_state.branch_history, remove_branches_out); - } - } - }); - - if (can_end_after_emergency_repair) { - new_contract.after_emergency_repair = false; - } - - new_contract_region_vector.push_back(reg); - new_contract_vector.push_back(new_contract); - }); - } - } - - /* Put the new contracts into a `region_map_t` to coalesce adjacent regions that have - identical contracts */ - region_map_t new_contract_region_map = - region_map_t::from_unordered_fragments( - std::move(new_contract_region_vector), std::move(new_contract_vector)); - - /* Slice the new contracts by CPU shard and by user shard, so that no contract spans - more than one CPU shard or user shard. */ - std::map new_contract_map; - for (size_t cpu = 0; cpu < CPU_SHARDING_FACTOR; ++cpu) { - region_t region = cpu_sharding_subspace(cpu); - for (size_t shard = 0; shard < old_state.config.config.shards.size(); ++shard) { - region.inner = old_state.config.shard_scheme.get_shard_range(shard); - new_contract_region_map.visit(region, - [&](const region_t ®, const contract_t &contract) { - guarantee(reg.beg == region.beg && reg.end == region.end); - new_contract_map.insert(std::make_pair(reg, contract)); - }); - } - } - - /* Diff the new contracts against the old contracts */ - for (const auto &cpair : old_state.contracts) { - auto it = new_contract_map.find(cpair.second.first); - if (it != new_contract_map.end() && it->second == cpair.second.second) { - /* The contract was unchanged. Remove it from `new_contract_map` to signal - that we don't need to assign it a new ID. */ - new_contract_map.erase(it); - } else { - /* The contract was changed. So delete the old one. */ - remove_contracts_out->insert(cpair.first); - } - } - for (const auto &pair : new_contract_map) { - /* The contracts remaining in `new_contract_map` are actually new; whatever - contracts used to cover their region have been deleted. So assign them contract - IDs and export them. */ - add_contracts_out->insert(std::make_pair(generate_uuid(), pair)); - } -} - diff --git a/src/clustering/table_contract/coordinator/calculate_contracts.hpp b/src/clustering/table_contract/coordinator/calculate_contracts.hpp deleted file mode 100644 index f54f64783c7..00000000000 --- a/src/clustering/table_contract/coordinator/calculate_contracts.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_CONTRACT_COORDINATOR_CALCULATE_CONTRACTS_HPP_ -#define CLUSTERING_TABLE_CONTRACT_COORDINATOR_CALCULATE_CONTRACTS_HPP_ - -#include "clustering/table_contract/contract_metadata.hpp" -#include "concurrency/watchable_map.hpp" - -void calculate_all_contracts( - const table_raft_state_t &old_state, - const std::map > &acks, - watchable_map_t, empty_value_t> - *connections_map, - std::set *remove_contracts_out, - std::map > *add_contracts_out, - std::map *register_current_branches_out, - std::set *remove_branches, - branch_history_t *add_branches_out); - -#endif /* CLUSTERING_TABLE_CONTRACT_COORDINATOR_CALCULATE_CONTRACTS_HPP_ */ - diff --git a/src/clustering/table_contract/coordinator/calculate_misc.cc b/src/clustering/table_contract/coordinator/calculate_misc.cc deleted file mode 100644 index ab50dbc776d..00000000000 --- a/src/clustering/table_contract/coordinator/calculate_misc.cc +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_contract/coordinator/calculate_misc.hpp" - -/* `calculate_server_names()` figures out what changes need to be made to the server -names stored in the Raft state. When a contract is added that refers to a new server, it -will copy the server name from the table config; when the last contract that refers to a -server is removed, it will delete hte server name. */ -void calculate_server_names( - const table_raft_state_t &old_state, - const std::set &remove_contracts, - const std::map > &add_contracts, - std::set *remove_server_names_out, - server_name_map_t *add_server_names_out) { - std::set all_replicas; - for (const auto &pair : old_state.contracts) { - if (remove_contracts.count(pair.first) == 0) { - all_replicas.insert( - pair.second.second.replicas.begin(), - pair.second.second.replicas.end()); - } - } - for (const auto &pair : add_contracts) { - all_replicas.insert( - pair.second.second.replicas.begin(), - pair.second.second.replicas.end()); - for (const server_id_t &server : pair.second.second.replicas) { - if (old_state.server_names.names.count(server) == 0) { - add_server_names_out->names.insert(std::make_pair(server, - old_state.config.server_names.names.at(server))); - } - } - } - for (const auto &pair : old_state.server_names.names) { - if (all_replicas.count(pair.first) == 0) { - remove_server_names_out->insert(pair.first); - } - } -} - -/* `calculate_member_ids_and_raft_config()` figures out when servers need to be added to -or removed from the `table_raft_state_t::member_ids` map or the Raft configuration. The -goals are as follows: -- A server ought to be in `member_ids` and the Raft configuration if it appears in the - current `table_config_t` or in a contract. -- If it is a voter in any contract, it should be a voter in the Raft configuration; - otherwise, it should be a non-voting member. - -There is constraint on how we approach those goals: Every server in the Raft -configuration must also be in `member_ids`. So we add new servers to `member_ids` before -adding them to the Raft configuration, and we remove obsolete servers from the Raft -configuration before removing them from `member_ids`. Note that `calculate_member_ids()` -assumes that if it returns changes for both `member_ids` and the Raft configuration, the -`member_ids` changes will be applied first. */ -void calculate_member_ids_and_raft_config( - const raft_member_t::state_and_config_t &sc, - std::set *remove_member_ids_out, - std::map *add_member_ids_out, - raft_config_t *new_config_out) { - /* Assemble a set of all of the servers that ought to be in `member_ids`. */ - std::set members_goal; - for (const table_config_t::shard_t &shard : sc.state.config.config.shards) { - members_goal.insert(shard.all_replicas.begin(), shard.all_replicas.end()); - } - for (const auto &pair : sc.state.contracts) { - members_goal.insert( - pair.second.second.replicas.begin(), pair.second.second.replicas.end()); - } - /* Assemble a set of all of the servers that ought to be Raft voters. */ - std::set voters_goal; - for (const auto &pair : sc.state.contracts) { - voters_goal.insert( - pair.second.second.voters.begin(), - pair.second.second.voters.end()); - } - /* Create entries in `add_member_ids_out` for any servers in `goal` that don't - already have entries in `member_ids` */ - for (const server_id_t &server : members_goal) { - if (sc.state.member_ids.count(server) == 0) { - add_member_ids_out->insert(std::make_pair( - server, raft_member_id_t(generate_uuid()))); - } - } - /* For any servers in the current `member_ids` that aren't in `servers`, add an entry - in `remove_member_ids_out`, unless they are still in the Raft configuration. (If - they're still in the Raft configuration, we'll remove them soon.) */ - for (const auto &existing : sc.state.member_ids) { - if (members_goal.count(existing.first) == 0 && - !sc.config.is_member(existing.second)) { - remove_member_ids_out->insert(existing.first); - } - } - /* Set up `new_config_out`. */ - for (const server_id_t &server : members_goal) { - raft_member_id_t member_id = (sc.state.member_ids.count(server) == 1) - ? sc.state.member_ids.at(server) - : add_member_ids_out->at(server); - if (voters_goal.count(server) == 1) { - new_config_out->voting_members.insert(member_id); - } else { - new_config_out->non_voting_members.insert(member_id); - } - } -} - diff --git a/src/clustering/table_contract/coordinator/calculate_misc.hpp b/src/clustering/table_contract/coordinator/calculate_misc.hpp deleted file mode 100644 index b0b10de1115..00000000000 --- a/src/clustering/table_contract/coordinator/calculate_misc.hpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_CONTRACT_COORDINATOR_CALCULATE_MISC_HPP_ -#define CLUSTERING_TABLE_CONTRACT_COORDINATOR_CALCULATE_MISC_HPP_ - -#include "clustering/table_contract/contract_metadata.hpp" - -void calculate_server_names( - const table_raft_state_t &old_state, - const std::set &remove_contracts, - const std::map > &add_contracts, - std::set *remove_server_names_out, - server_name_map_t *add_server_names_out); - -void calculate_member_ids_and_raft_config( - const raft_member_t::state_and_config_t &sc, - std::set *remove_member_ids_out, - std::map *add_member_ids_out, - raft_config_t *new_config_out); - -#endif /* CLUSTERING_TABLE_CONTRACT_COORDINATOR_CALCULATE_MISC_HPP_ */ - diff --git a/src/clustering/table_contract/coordinator/check_ready.cc b/src/clustering/table_contract/coordinator/check_ready.cc deleted file mode 100644 index 9513732ca38..00000000000 --- a/src/clustering/table_contract/coordinator/check_ready.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_contract/coordinator/check_ready.hpp" - -bool check_all_replicas_ready( - const table_raft_state_t &table_state, - watchable_map_t, contract_ack_t> *acks) { - for (const auto &pair : table_state.contracts) { - - /* Find the config shard corresponding to this contract */ - const table_config_t::shard_t *shard = nullptr; - for (size_t i = 0; i < table_state.config.config.shards.size(); ++i) { - if (table_state.config.shard_scheme.get_shard_range(i) == - pair.second.first.inner) { - shard = &table_state.config.config.shards[i]; - break; - } - } - if (shard == nullptr) { - return false; - } - - /* Check if the config shard matches the contract */ - const contract_t &contract = pair.second.second; - if (contract.replicas != shard->all_replicas || - contract.voters != shard->voting_replicas() || - static_cast(contract.temp_voters) || - !static_cast(contract.primary) || - contract.primary->server != shard->primary_replica || - static_cast(contract.primary->hand_over) || - contract.after_emergency_repair) { - return false; - } - - /* Check if all the replicas have acked the contract */ - for (const server_id_t &server : contract.replicas) { - bool ok; - acks->read_key(std::make_pair(server, pair.first), - [&](const contract_ack_t *ack) { - ok = (ack != nullptr) && ( - ack->state == contract_ack_t::state_t::primary_ready || - ack->state == contract_ack_t::state_t::secondary_streaming); - }); - if (!ok) { - return false; - } - } - } - return true; -} diff --git a/src/clustering/table_contract/coordinator/check_ready.hpp b/src/clustering/table_contract/coordinator/check_ready.hpp deleted file mode 100644 index 1183bb83ec6..00000000000 --- a/src/clustering/table_contract/coordinator/check_ready.hpp +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_CONTRACT_COORDINATOR_HPP_ -#define CLUSTERING_TABLE_CONTRACT_COORDINATOR_HPP_ - -#include "clustering/table_contract/contract_metadata.hpp" - -/* Returns `true` if the contracts are consistent with the config, and the acks indicate -that every server is executing its contracts and finished with all backfills, etc. */ -bool check_all_replicas_ready( - const table_raft_state_t &table_state, - watchable_map_t, contract_ack_t> *acks); - -#endif /* CLUSTERING_TABLE_CONTRACT_COORDINATOR_HPP_ */ - diff --git a/src/clustering/table_contract/coordinator/coordinator.cc b/src/clustering/table_contract/coordinator/coordinator.cc deleted file mode 100644 index 7440c5f8c75..00000000000 --- a/src/clustering/table_contract/coordinator/coordinator.cc +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_contract/coordinator/coordinator.hpp" - -#include "clustering/generic/raft_core.tcc" -#include "clustering/table_contract/branch_history_gc.hpp" -#include "clustering/table_contract/coordinator/calculate_contracts.hpp" -#include "clustering/table_contract/coordinator/calculate_misc.hpp" -#include "clustering/table_contract/coordinator/check_ready.hpp" -#include "logger.hpp" - -contract_coordinator_t::contract_coordinator_t( - raft_member_t *_raft, - watchable_map_t, contract_ack_t> *_acks, - watchable_map_t, empty_value_t> - *_connections_map) : - raft(_raft), - acks(_acks), - connections_map(_connections_map), - config_pumper(std::bind(&contract_coordinator_t::pump_configs, this, ph::_1)), - contract_pumper(std::bind(&contract_coordinator_t::pump_contracts, this, ph::_1)), - ack_subs(acks, - std::bind(&contract_coordinator_t::on_ack_change, this, ph::_1, ph::_2), - initial_call_t::YES), - connections_map_subs(connections_map, - std::bind(&pump_coro_t::notify, &contract_pumper), initial_call_t::NO) -{ - raft->assert_thread(); - /* Do an initial round of pumping, in case there are any changes the previous - coordinator didn't take care of */ - contract_pumper.notify(); - config_pumper.notify(); -} - -optional contract_coordinator_t::change_config( - const std::function &changer, - signal_t *interruptor) { - assert_thread(); - scoped_ptr_t::change_token_t> change_token; - raft_log_index_t log_index; - { - raft_member_t::change_lock_t change_lock(raft, interruptor); - table_raft_state_t::change_t::set_table_config_t change; - bool is_noop; - raft->get_latest_state()->apply_read( - [&](const raft_member_t::state_and_config_t *state) { - change.new_config = state->state.config; - changer(&change.new_config); - log_index = state->log_index; - is_noop = (change.new_config == state->state.config); - }); - if (is_noop) { - return make_optional(log_index); - } - change_token = raft->propose_change( - &change_lock, table_raft_state_t::change_t(change)); - log_index += 1; - } - if (!change_token.has()) { - return r_nullopt; - } - contract_pumper.notify(); - config_pumper.notify(); - wait_interruptible(change_token->get_ready_signal(), interruptor); - if (!change_token->wait()) { - return r_nullopt; - } - return make_optional(log_index); -} - -bool contract_coordinator_t::check_all_replicas_ready(signal_t *interruptor) { - assert_thread(); - /* The bulk of the work is in the `::check_all_replicas_ready()` function defined in - `check_ready.hpp`. But we also make sure to commit the state that `check_ready` saw - before we reply, to ensure that we are actually still the Raft leader. */ - scoped_ptr_t::change_token_t> change_token; - { - raft_member_t::change_lock_t change_lock(raft, interruptor); - bool ok; - raft->get_latest_state()->apply_read( - [&](const raft_member_t::state_and_config_t *state) { - ok = ::check_all_replicas_ready(state->state, acks); - }); - if (!ok) { - return false; - } - change_token = raft->propose_noop(&change_lock); - } - if (!change_token.has()) { - return false; - } - wait_interruptible(change_token->get_ready_signal(), interruptor); - if (!change_token->wait()) { - return false; - } - return true; -} - -bool contract_coordinator_t::check_outdated_all_replicas_ready( - UNUSED signal_t *interruptor) { - assert_thread(); - bool ok; - raft->get_latest_state()->apply_read( - [&](const raft_member_t::state_and_config_t *state) { - ok = ::check_all_replicas_ready(state->state, acks); - }); - return ok; -} - -void contract_coordinator_t::on_ack_change( - const std::pair &key, - const contract_ack_t *ack) { - if (ack != nullptr) { - acks_by_contract[key.second][key.first] = *ack; - } else { - auto it = acks_by_contract.find(key.second); - if (it != acks_by_contract.end()) { - auto jt = it->second.find(key.first); - if (jt != it->second.end()) { - it->second.erase(jt); - if (it->second.empty()) { - acks_by_contract.erase(it); - } - } - } - } - - contract_pumper.notify(); -} - -void contract_coordinator_t::pump_contracts(signal_t *interruptor) { - assert_thread(); - - /* Wait a little while to give changes time to accumulate, because - `calculate_all_contracts()` needs to examine every shard of the table even if - nothing about them has changed, and that might be expensive. */ - nap(200, interruptor); - - /* Now we'll apply changes to Raft. We keep trying in a loop in case it - doesn't work at first. */ - while (true) { - /* Wait until the Raft member is likely to accept changes */ - raft->get_readiness_for_change()->run_until_satisfied( - [](bool is_ready) { return is_ready; }, interruptor); - - raft_member_t::change_lock_t change_lock(raft, interruptor); - - /* Call `include_latest_notifications()` right before we read the Raft state. - This is because any changes to the Raft state that happened up to this point will - be included in this round of pumping, but any changes after this point must be - run in their own round. */ - contract_pumper.include_latest_notifications(); - - /* Calculate the proposed change */ - table_raft_state_t::change_t::new_contracts_t change; - raft->get_latest_state()->apply_read( - [&](const raft_member_t::state_and_config_t *state) { - calculate_all_contracts( - state->state, acks_by_contract, connections_map, - &change.remove_contracts, &change.add_contracts, - &change.register_current_branches, - &change.remove_branches, &change.add_branches); - calculate_server_names( - state->state, change.remove_contracts, change.add_contracts, - &change.remove_server_names, &change.add_server_names); - }); - - /* Apply the change, unless it's a no-op */ - bool change_ok; - if (!change.remove_contracts.empty() || - !change.add_contracts.empty() || - !change.remove_branches.empty() || - !change.add_branches.branches.empty() || - !change.register_current_branches.empty()) { - scoped_ptr_t::change_token_t> - change_token = raft->propose_change( - &change_lock, table_raft_state_t::change_t(change)); - - /* `pump_configs()` sometimes makes changes in reaction to changes in - contracts, so wake it up. */ - config_pumper.notify(); - - change_ok = change_token.has(); - - /* Note that we don't wait on the change token. We know we won't - start a redundant change because we always compute the change by - comparing to `get_latest_state()`. */ - } else { - change_ok = true; - } - - /* If the change failed, go back to the top of the loop and wait on - `get_readiness_for_change()` again. */ - if (change_ok) { - break; - } - } -} - -void contract_coordinator_t::pump_configs(signal_t *interruptor) { - assert_thread(); - - while (true) { - nap(200, interruptor); - - /* Wait until the Raft member is likely to accept config changes. This - isn't actually necessary for changes to `member_ids`, but it's easier to - just handle `member_ids` changes and Raft configuration changes at the - same time. */ - raft->get_readiness_for_config_change()->run_until_satisfied( - [](bool is_ready) { return is_ready; }, - interruptor); - - raft_member_t::change_lock_t change_lock(raft, interruptor); - - /* As in `pump_contracts()`, call `include_latest_notifications()` right before - we read the Raft state */ - config_pumper.include_latest_notifications(); - - /* Calculate changes to `table_raft_state_t::member_ids` */ - table_raft_state_t::change_t::new_member_ids_t member_ids_change; - optional config_change; - raft->get_latest_state()->apply_read( - [&](const raft_member_t::state_and_config_t *sc) { - raft_config_t new_config; - calculate_member_ids_and_raft_config( - *sc, - &member_ids_change.remove_member_ids, - &member_ids_change.add_member_ids, - &new_config); - /* The config almost always won't be a joint consensus, because we - waited until the `raft_member_t` was ready for config changes. But - it's still possible so we have to handle that case somehow. Setting - `config_change` won't change the config in a joint consensus because - `propose_config_change()` will fail, but at least it will force us to - go around the loop again. */ - if ((!sc->config.is_joint_consensus() && - new_config != sc->config.config) || - (sc->config.is_joint_consensus() && - new_config != *sc->config.new_config)) { - config_change = make_optional(new_config); - } - }); - - if (!member_ids_change.remove_member_ids.empty() || - !member_ids_change.add_member_ids.empty()) { - /* Apply the `member_ids` change */ - scoped_ptr_t::change_token_t> - change_token = raft->propose_change( - &change_lock, - table_raft_state_t::change_t(member_ids_change)); - if (!change_token.has()) { - /* Go back to the top of the loop and try again. Note that we don't even - attempt the config change if the member IDs change fails; the config - change would not be valid to apply unless it came after the member IDs - change. */ - continue; - } - } - - if (static_cast(config_change)) { - /* Apply the config change */ - raft->propose_config_change(&change_lock, *config_change); - - /* Go back to the top of the loop whether or not the change succeeded. If it - failed, we want to try again; if it succeeded, we want to wait until it gets - applied and then re-run `calculate_member_ids_and_raft_config()` because - there might be further changes. */ - continue; - } - - /* Both changes succeeded, or there was nothing to change in the first place; so - we break out of the loop. */ - break; - } -} - diff --git a/src/clustering/table_contract/coordinator/coordinator.hpp b/src/clustering/table_contract/coordinator/coordinator.hpp deleted file mode 100644 index 196e1dac251..00000000000 --- a/src/clustering/table_contract/coordinator/coordinator.hpp +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_CONTRACT_COORDINATOR_COORDINATOR_HPP_ -#define CLUSTERING_TABLE_CONTRACT_COORDINATOR_COORDINATOR_HPP_ - -#include "clustering/generic/raft_core.hpp" -#include "clustering/id_types.hpp" -#include "concurrency/pump_coro.hpp" - -class contract_ack_t; -class server_id_t; -class table_config_and_shards_t; -class table_raft_state_t; - -/* There is one `contract_coordinator_t` per table, located on whichever server is -currently the Raft leader. It's the only thing which ever initiates Raft transactions. -Its jobs are as follows: - -1. Applying config changes: The `table_meta_client_t` sends config change requests to the - `multi_table_manager_t`, which calls `contract_coordinator_t::change_config()` to - apply the changes. - -2. Issuing `contract_t`s: The coordinator cross-references the `contract_t`s stored in - the Raft state, the current table configuration stored in the Raft state, and the - `contract_ack_t`s sent by the `contract_executor_t`s to decide if and when to change - the `contract_t`s in the Raft state. It's responsible for ensuring correctness during - complex changes like primary changes, failovers, and replica set changes. - -3. Adding and removing replicas: When a new replica appears in the table config, the - coordinator puts an entry into `table_raft_state_t::member_ids` so the new replica - will join the Raft cluster. When the new member is ready, the coordinator issues a - Raft config change to make the new replica a voting member. When a replica leaves, it - goes through the reverse process. -*/ - -class contract_coordinator_t : public home_thread_mixin_debug_only_t { -public: - contract_coordinator_t( - raft_member_t *raft, - watchable_map_t, contract_ack_t> *acks, - watchable_map_t, empty_value_t> - *connections_map); - - /* `table_meta_client_t` calls `change_config()` (indirectly, over the network) to - change the cluster config. */ - optional change_config( - const std::function &changer, - signal_t *interruptor); - - /* Returns `true` if we can confirm that the contracts match the config and all of - the contracts are fully executed. - `check_outdated_all_replicas_ready` skips the expensive test transaction on - the raft state. */ - bool check_all_replicas_ready(signal_t *interruptor); - bool check_outdated_all_replicas_ready(signal_t *interruptor); - -private: - void on_ack_change( - const std::pair &key, const contract_ack_t *ack); - - /* `pump_contracts()` is what actually issues the new contracts. It eventually gets - run after every change. */ - void pump_contracts(signal_t *interruptor); - - /* `pump_configs()` makes changes to the `member_ids` field of the - `table_raft_state_t` and to the Raft cluster configuration. It's separate from - `pump_contracts()` because the Raft cluster configuration changes are limited by the - Raft cluster's readiness for configuration changes, so it's best if they're not - handled in the same function. */ - void pump_configs(signal_t *interruptor); - - raft_member_t *const raft; - watchable_map_t, contract_ack_t> *const acks; - watchable_map_t, empty_value_t> - *const connections_map; - - /* This is the same as `acks` but indexed by contract. */ - std::map > acks_by_contract; - - /* These `pump_coro_t`s are responsible for calling `pump_contracts()` and - `pump_configs()`. Destructor order matters here. We have to destroy `ack_subs` first, - because it notifies `contract_pumper`. Then we have to destroy `contract_pumper`, - because its callback notifies `config_pumper`. Then we have to destroy - `config_pumper`, because it accesses `raft` and `acks`. */ - pump_coro_t config_pumper; - pump_coro_t contract_pumper; - - watchable_map_t, contract_ack_t>::all_subs_t - ack_subs; - watchable_map_t, empty_value_t>::all_subs_t - connections_map_subs; -}; - -#endif /* CLUSTERING_TABLE_CONTRACT_COORDINATOR_COORDINATOR_HPP_ */ - diff --git a/src/clustering/table_contract/cpu_sharding.cc b/src/clustering/table_contract/cpu_sharding.cc deleted file mode 100644 index cd0faf95bee..00000000000 --- a/src/clustering/table_contract/cpu_sharding.cc +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_contract/cpu_sharding.hpp" - -static const uint64_t CPU_SHARD_WIDTH = HASH_REGION_HASH_SIZE / CPU_SHARDING_FACTOR; - -region_t cpu_sharding_subspace(int subregion_number) { - guarantee(subregion_number >= 0); - guarantee(subregion_number < CPU_SHARDING_FACTOR); - - /* Changing this implementation would break backwards compatibility in the disk - format. */ - - // We have to be careful with the math here, to avoid overflow. - uint64_t beg = CPU_SHARD_WIDTH * subregion_number; - uint64_t end = subregion_number + 1 == CPU_SHARDING_FACTOR - ? HASH_REGION_HASH_SIZE : beg + CPU_SHARD_WIDTH; - - return region_t(beg, end, key_range_t::universe()); -} - -int get_cpu_shard_number(const region_t ®ion) { - int subregion_number = region.beg / CPU_SHARD_WIDTH; - guarantee(region.beg == subregion_number * CPU_SHARD_WIDTH); - guarantee(region.end == ( - subregion_number + 1 == CPU_SHARDING_FACTOR - ? HASH_REGION_HASH_SIZE - : region.beg + CPU_SHARD_WIDTH)); - return subregion_number; -} - -int get_cpu_shard_approx_number(const region_t ®ion) { - return region.beg / CPU_SHARD_WIDTH; -} - diff --git a/src/clustering/table_contract/cpu_sharding.hpp b/src/clustering/table_contract/cpu_sharding.hpp deleted file mode 100644 index d1f33234b7c..00000000000 --- a/src/clustering/table_contract/cpu_sharding.hpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_CONTRACT_CPU_SHARDING_HPP_ -#define CLUSTERING_TABLE_CONTRACT_CPU_SHARDING_HPP_ - -#include "clustering/immediate_consistency/history.hpp" -#include "protocol_api.hpp" -#include "region/region.hpp" - -class store_t; - -/* Changing this number would break backwards compatibility in the disk format. */ -#define CPU_SHARDING_FACTOR 8 - -/* `cpu_sharding_subspace()` returns a `region_t` that contains the full key-range space -but only 1/CPU_SHARDING_FACTOR of the shard space. */ -region_t cpu_sharding_subspace(int subregion_number); - -/* `get_cpu_shard_number()` is the reverse of `cpu_sharding_subspace()`; it returns the -subregion number for `region`'s hash subspace. It ignores `region`'s key boundaries. If -`region`'s hash subspace doesn't exactly correspond to a specific CPU sharding region, it -crashes. */ -int get_cpu_shard_number(const region_t ®ion); - -/* `get_cpu_shard_approx_number()` is like `get_cpu_shard_number()`, except that if the -input doesn't correspond exactly to a CPU shard, it returns an estimate. */ -int get_cpu_shard_approx_number(const region_t ®ion); - -/* `multistore_ptr_t` is a bundle of `store_view_t`s, one for each CPU shard. The rule -is that `get_cpu_sharded_store(i)->get_region() == cpu_sharding_subspace(i)`. The -individual stores' home threads may be different from the `multistore_ptr_t`'s home -thread. */ -class multistore_ptr_t : public home_thread_mixin_t { -public: - virtual ~multistore_ptr_t() { } - - virtual branch_history_manager_t *get_branch_history_manager() = 0; - - virtual store_view_t *get_cpu_sharded_store(size_t i) = 0; - - /* The `sindex_manager_t` uses this interface to get at the underlying `store_t`s so - it can create and destroy sindexes on them. The `table_contract` code should never - use it, and some unit tests will return `nullptr` from here. */ - virtual store_t *get_underlying_store(size_t i) = 0; -}; - -#endif /* CLUSTERING_TABLE_CONTRACT_CPU_SHARDING_HPP_ */ - diff --git a/src/clustering/table_contract/emergency_repair.cc b/src/clustering/table_contract/emergency_repair.cc deleted file mode 100644 index 3b3d683d529..00000000000 --- a/src/clustering/table_contract/emergency_repair.cc +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_contract/emergency_repair.hpp" - -/* Returns `true` if `servers` is a subset of `dead`. */ -bool all_dead( - const std::set &servers, const std::set &dead) { - for (const server_id_t &server : servers) { - if (dead.count(server) == 0) { - return false; - } - } - return true; -} - -/* Returns `true` if half or more of the servers in `servers` are also in `dead`. */ -bool quorum_dead( - const std::set &servers, const std::set &dead) { - size_t alive = 0; - for (const server_id_t &server : servers) { - if (dead.count(server) == 0) { - ++alive; - } - } - return alive * 2 <= servers.size(); -} - -/* Returns `true` if any of the servers in `servers` is also in `dead`. */ -bool any_dead( - const std::set &servers, const std::set &dead) { - for (const server_id_t &server : servers) { - if (dead.count(server) == 1) { - return true; - } - } - return false; -} - -void calculate_emergency_repair( - const table_raft_state_t &old_state, - const std::set &dead_servers, - emergency_repair_mode_t mode, - table_raft_state_t *new_state_out, - bool *rollback_found_out, - bool *erase_found_out) { - *rollback_found_out = false; - *erase_found_out = false; - - /* If we're in `"_debug_recommit"` mode we simply copy the old state and are done */ - if (mode == emergency_repair_mode_t::DEBUG_RECOMMIT) { - *new_state_out = old_state; - return; - } - - /* Pick the server we'll use as a replacement for shards that we end up erasing */ - server_id_t erase_replacement = server_id_t::from_server_uuid(nil_uuid()); - for (const auto &pair : old_state.member_ids) { - if (dead_servers.count(pair.first) == 0) { - erase_replacement = pair.first; - break; - } - } - guarantee(!erase_replacement.get_uuid().is_nil(), - "calculate_emergency_repair() should not be called if all servers are dead"); - - /* Calculate the new contracts. This is the guts of the repair operation. */ - new_state_out->contracts.clear(); - for (const auto &pair : old_state.contracts) { - contract_t contract = pair.second.second; - if (all_dead(contract.replicas, dead_servers)) { - *erase_found_out = true; - if (mode == emergency_repair_mode_t::UNSAFE_ROLLBACK_OR_ERASE) { - /* Discard all previous replicas, and set up a new empty replica on - `erase_replacement`. */ - contract = contract_t(); - contract.replicas.insert(erase_replacement); - contract.voters.insert(erase_replacement); - contract.after_emergency_repair = true; - } - } else if (quorum_dead(contract.voters, dead_servers) || - (static_cast(contract.temp_voters) && - quorum_dead(*contract.temp_voters, dead_servers)) || - (contract.after_emergency_repair && - any_dead(contract.voters, dead_servers))) { - *rollback_found_out = true; - /* Remove the primary (a new one will be elected) */ - contract.primary.reset(); - /* Convert temp voters into voters */ - if (static_cast(contract.temp_voters)) { - contract.voters.insert( - contract.temp_voters->begin(), contract.temp_voters->end()); - contract.temp_voters.reset(); - } - /* Forcibly demote all dead voters */ - for (const server_id_t &server : dead_servers) { - contract.voters.erase(server); - } - /* If all the voters were dead, then promote all living non-voters to voter - */ - if (contract.voters.empty()) { - for (const server_id_t &server : contract.replicas) { - if (dead_servers.count(server) == 0) { - contract.voters.insert(server); - } - } - } - /* Record that we just did an emergency repair */ - contract.after_emergency_repair = true; - } - /* We generate a new contract ID for the new contract, whether or not it's - identical to the old contract. In practice this doesn't matter since contract IDs - from the old epoch will never be compared to contract IDs from the new epoch. */ - new_state_out->contracts.insert(std::make_pair( - generate_uuid(), - std::make_pair(pair.second.first, contract))); - } - - /* Calculate the new config to reflect the new contracts as closely as possible. The - details here aren't critical for correctness purposes. */ - { - /* Copy all the simple fields of the old config onto the new config */ - new_state_out->config.config.basic = old_state.config.config.basic; - new_state_out->config.config.sindexes = old_state.config.config.sindexes; - new_state_out->config.config.write_ack_config = - old_state.config.config.write_ack_config; - new_state_out->config.config.durability = old_state.config.config.durability; - - /* We first calculate all the voting and nonvoting replicas for each range in a - `range_map_t`. */ - range_map_t config( - key_range_t::right_bound_t(store_key_t::min()), - key_range_t::right_bound_t::make_unbounded(), - table_config_t::shard_t()); - for (const auto &pair : new_state_out->contracts) { - config.visit_mutable( - key_range_t::right_bound_t(pair.second.first.inner.left), - pair.second.first.inner.right, - [&](const key_range_t::right_bound_t &, - const key_range_t::right_bound_t &, - table_config_t::shard_t *shard) { - for (const server_id_t &server : pair.second.second.replicas) { - if (pair.second.second.voters.count(server) == 1) { - shard->all_replicas.insert(server); - shard->nonvoting_replicas.erase(server); - } else { - if (shard->all_replicas.count(server) == 0) { - shard->all_replicas.insert(server); - shard->nonvoting_replicas.insert(server); - } - } - } - }); - } - - /* Assign primaries. We keep the old primary if possible, otherwise we choose an - arbitrary server. */ - for (size_t i = 0; i < old_state.config.config.shards.size(); ++i) { - const table_config_t::shard_t &old_shard = old_state.config.config.shards[i]; - key_range_t shard_range = old_state.config.shard_scheme.get_shard_range(i); - config.visit_mutable( - key_range_t::right_bound_t(shard_range.left), - shard_range.right, - [&](const key_range_t::right_bound_t &, - const key_range_t::right_bound_t &, - table_config_t::shard_t *shard) { - /* Check if `old_shard.primary_replica` is eligible to be a primary - under the new config */ - if (shard->voting_replicas().count(old_shard.primary_replica) == 1) { - /* It's eligible, so keep it */ - shard->primary_replica = old_shard.primary_replica; - } else { - /* It's ineligible, so pick the first eligible server */ - shard->primary_replica = *shard->voting_replicas().begin(); - } - }); - } - - /* Convert each section of the `range_map_t` into a new user-level shard. */ - config.visit( - key_range_t::right_bound_t(store_key_t::min()), - key_range_t::right_bound_t::make_unbounded(), - [&](const key_range_t::right_bound_t &, - const key_range_t::right_bound_t &right, - const table_config_t::shard_t &shard) { - new_state_out->config.config.shards.push_back(shard); - if (!right.unbounded) { - new_state_out->config.shard_scheme.split_points - .push_back(right.key()); - } - }); - guarantee(new_state_out->config.config.shards.size() == - new_state_out->config.shard_scheme.num_shards()); - - /* Copy over server names for all servers that are in the new config, into the - server names map for the config. (Note that there are two separate server name - maps, one for the config and one for the contracts.) */ - for (const table_config_t::shard_t &shard : - new_state_out->config.config.shards) { - for (const server_id_t &server : shard.all_replicas) { - /* Since the new config is derived from the new contracts which are - derived from the old contracts, every server present in the new config - must appear in `old_state.server_names`. */ - new_state_out->config.server_names.names.insert( - std::make_pair(server, old_state.server_names.names.at(server))); - } - } - } - - /* Copy over the branch history without modification. In theory we could do some - pruning here, but there's not much that we could prune and so it's easier to just let - the branch history GC do it. */ - new_state_out->branch_history = old_state.branch_history; - - /* Copy over the current branch map as well. Where the new contracts specify - a primary that doesn't have the current branch due to the hard override, the - current branch will be reset for its range once it registers its new branch. */ - new_state_out->current_branches = old_state.current_branches; - - /* Find all the servers that appear in the new contracts, and put entries for those - servers in `member_ids` and `server_names` */ - for (const auto &pair : new_state_out->contracts) { - for (const server_id_t &server : pair.second.second.replicas) { - if (new_state_out->member_ids.count(server) == 0) { - /* We generate a new member ID for every server. This shouldn't matter - because member IDs from the new epoch should never be compared to member - IDs from the old epoch, but it slightly reduces the risk of bugs. */ - new_state_out->member_ids.insert( - std::make_pair(server, raft_member_id_t(generate_uuid()))); - - /* Since the new contracts are derived from the old contracts, every - server present in the new contracts must appear in - `old_state.server_names`. */ - new_state_out->server_names.names.insert( - std::make_pair(server, old_state.server_names.names.at(server))); - } - } - } -} - diff --git a/src/clustering/table_contract/emergency_repair.hpp b/src/clustering/table_contract/emergency_repair.hpp deleted file mode 100644 index e7c88ae3295..00000000000 --- a/src/clustering/table_contract/emergency_repair.hpp +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_CONTRACT_EMERGENCY_REPAIR_HPP_ -#define CLUSTERING_TABLE_CONTRACT_EMERGENCY_REPAIR_HPP_ - -#include "clustering/table_contract/contract_metadata.hpp" - -/* `calculate_emergency_repair()` is the brains behind -`table_meta_client_t::emergency_repair()`. It takes as input the table's current state -and a set of servers that are being abandoned. If it finds anything that can be fixed via -a rollback, it fixes it and sets `*rollback_found_out = true`. If it finds anything that -can only be fixed via an erase, it sets `*erase_found_out = true`, but doesn't actually -fix it unless `allow_erase` is `true`. */ -void calculate_emergency_repair( - const table_raft_state_t &old_state, - const std::set &dead_servers, - emergency_repair_mode_t mode, - table_raft_state_t *new_state_out, - bool *rollback_found_out, - bool *erase_found_out); - -#endif /* CLUSTERING_TABLE_CONTRACT_EMERGENCY_REPAIR_HPP_ */ - diff --git a/src/clustering/table_contract/executor/exec.cc b/src/clustering/table_contract/executor/exec.cc deleted file mode 100644 index 97623a46582..00000000000 --- a/src/clustering/table_contract/executor/exec.cc +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_contract/executor/exec.hpp" - -RDB_IMPL_SERIALIZABLE_3_FOR_CLUSTER(contract_execution_bcard_t, - remote_replicator_server, replica, peer); - diff --git a/src/clustering/table_contract/executor/exec.hpp b/src/clustering/table_contract/executor/exec.hpp deleted file mode 100644 index a13b28c4fb5..00000000000 --- a/src/clustering/table_contract/executor/exec.hpp +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_CONTRACT_EXECUTOR_EXEC_HPP_ -#define CLUSTERING_TABLE_CONTRACT_EXECUTOR_EXEC_HPP_ - -#include "clustering/id_types.hpp" -#include "clustering/immediate_consistency/backfill_metadata.hpp" -#include "clustering/immediate_consistency/remote_replicator_metadata.hpp" -#include "paths.hpp" -#include "store_view.hpp" - -class backfill_progress_tracker_t; -class backfill_throttler_t; -class contract_ack_t; -class io_backender_t; -class table_raft_state_t; -class table_query_bcard_t; -template class watchable_map_t; -template class watchable_map_var_t; - -/* `contract_execution_bcard_t`s are passed around between the `contract_executor_t`s for -the same table on different servers. They allow servers to request backfills from one -another and subscribe to receive queries. */ -class contract_execution_bcard_t { -public: - remote_replicator_server_bcard_t remote_replicator_server; - replica_bcard_t replica; - peer_id_t peer; -}; - -RDB_DECLARE_SERIALIZABLE(contract_execution_bcard_t); - -/* `execution_t` is a base class for `primary_execution_t`, `secondary_execution_t`, and -`erase_execution_t`. */ -class execution_t { -public: - /* There is one `context_t` for each `contract_executor_t`; it holds the global - and per-table objects that the `execution_t`s will need. */ - class context_t { - public: - server_id_t server_id; - mailbox_manager_t *mailbox_manager; - branch_history_manager_t *branch_history_manager; - base_path_t base_path; - io_backender_t *io_backender; - backfill_progress_tracker_t *backfill_progress_tracker; - backfill_throttler_t *backfill_throttler; - watchable_map_t, - contract_execution_bcard_t> *remote_contract_execution_bcards; - watchable_map_var_t, - contract_execution_bcard_t> *local_contract_execution_bcards; - watchable_map_var_t *local_table_query_bcards; - }; - - /* There is one `params` for each `execution_t`; it holds information that's specific - to the particular execution, and also provides callbacks for the execution to send - events back. */ - class params_t { - public: - virtual perfmon_collection_t *get_parent_perfmon_collection() const = 0; - virtual const std::string &get_perfmon_name() const = 0; - virtual store_view_t *get_store() = 0; - - /* Sends the given contract ack for the given contract ID. The contract ID should - be an ID that has been passed to the constructor or - `update_contract_or_raft_state()`. */ - virtual void send_ack(const contract_id_t &, const contract_ack_t &) = 0; - - /* Once the execution has finished its backfill/erase/whatever, so that the - metainfo is on a particular branch and will stay there indefinitely, it calls - `enable_gc()` with that branch. `enable_gc()` must not be called more than once. - */ - virtual void enable_gc(const branch_id_t &) = 0; - - protected: - virtual ~params_t() { } - }; - - execution_t(const context_t *_context, params_t *_params) : - context(_context), params(_params), store(params->get_store()), - region(store->get_region()) { } - - /* Note: Subclass destructors may block. */ - virtual ~execution_t() { } - - virtual void update_contract_or_raft_state( - const contract_id_t &cid, - const table_raft_state_t &raft_state) = 0; - -protected: - context_t const *const context; - params_t *const params; - store_view_t *const store; - region_t const region; -}; - -#endif /* CLUSTERING_TABLE_CONTRACT_EXECUTOR_EXEC_HPP_ */ - diff --git a/src/clustering/table_contract/executor/exec_erase.cc b/src/clustering/table_contract/executor/exec_erase.cc deleted file mode 100644 index 041e489b2eb..00000000000 --- a/src/clustering/table_contract/executor/exec_erase.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_contract/executor/exec_erase.hpp" - -#include "concurrency/cross_thread_signal.hpp" - -erase_execution_t::erase_execution_t( - const execution_t::context_t *_context, - execution_t::params_t *_params, - const contract_id_t &cid, - const table_raft_state_t &raft_state) : - execution_t(_context, _params) -{ - update_contract_or_raft_state(cid, raft_state); - coro_t::spawn_sometime(std::bind(&erase_execution_t::run, this, drainer.lock())); -} - -void erase_execution_t::update_contract_or_raft_state( - DEBUG_VAR const contract_id_t &cid, - DEBUG_VAR const table_raft_state_t &raft_state) { - assert_thread(); - rassert(raft_state.contracts.at(cid).second.replicas.count(context->server_id) == 0); - rassert(raft_state.contracts.at(cid).first == region); -} - -void erase_execution_t::run(auto_drainer_t::lock_t keepalive) { - assert_thread(); - { - cross_thread_signal_t interruptor_store_thread( - keepalive.get_drain_signal(), store->home_thread()); - on_thread_t thread_switcher(store->home_thread()); - try { - store->reset_data( - binary_blob_t(version_t::zero()), - region, - write_durability_t::HARD, - &interruptor_store_thread); - } catch (const interrupted_exc_t &) { - /* do nothing */ - } - } - params->enable_gc(nil_uuid()); -} - - diff --git a/src/clustering/table_contract/executor/exec_erase.hpp b/src/clustering/table_contract/executor/exec_erase.hpp deleted file mode 100644 index fc9c80bb7f5..00000000000 --- a/src/clustering/table_contract/executor/exec_erase.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_CONTRACT_EXECUTOR_EXEC_ERASE_HPP_ -#define CLUSTERING_TABLE_CONTRACT_EXECUTOR_EXEC_ERASE_HPP_ - -#include "clustering/table_contract/contract_metadata.hpp" -#include "clustering/table_contract/executor/exec.hpp" -#include "store_view.hpp" - -class erase_execution_t : public execution_t, public home_thread_mixin_t { -public: - erase_execution_t( - const execution_t::context_t *context, - execution_t::params_t *params, - const contract_id_t &cid, - const table_raft_state_t &raft_state); - - void update_contract_or_raft_state( - const contract_id_t &cid, - const table_raft_state_t &raft_state); - -private: - /* `run()` does the actual work of erasing the data. */ - void run(auto_drainer_t::lock_t); - - /* `drainer` makes sure that `run()` stops before the `erase_execution_t` is - destroyed. */ - auto_drainer_t drainer; -}; - -#endif /* CLUSTERING_TABLE_CONTRACT_EXECUTOR_EXEC_ERASE_HPP_ */ - diff --git a/src/clustering/table_contract/executor/exec_primary.cc b/src/clustering/table_contract/executor/exec_primary.cc deleted file mode 100644 index 9d3c2db6114..00000000000 --- a/src/clustering/table_contract/executor/exec_primary.cc +++ /dev/null @@ -1,671 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_contract/executor/exec_primary.hpp" - -#include "clustering/administration/admin_op_exc.hpp" -#include "clustering/immediate_consistency/local_replicator.hpp" -#include "clustering/immediate_consistency/primary_dispatcher.hpp" -#include "clustering/immediate_consistency/remote_replicator_server.hpp" -#include "clustering/query_routing/direct_query_server.hpp" -#include "clustering/table_contract/contract_metadata.hpp" -#include "concurrency/cross_thread_signal.hpp" -#include "concurrency/promise.hpp" -#include "rpc/mailbox/mailbox.hpp" -#include "store_view.hpp" - -void ack_counter_t::note_ack(const server_id_t &server) { - if (static_cast(contract.primary)) { - primary_ack |= (server == contract.primary->server); - } - voter_acks += contract.voters.count(server); - if (static_cast(contract.temp_voters)) { - temp_voter_acks += contract.temp_voters->count(server); - } -} - -bool ack_counter_t::is_safe() const { - return primary_ack && - voter_acks * 2 > contract.voters.size() && - (!static_cast(contract.temp_voters) || - temp_voter_acks * 2 > contract.temp_voters->size()); -} - -class primary_execution_t::contract_info_t - : public slow_atomic_countable_t { -public: - contract_info_t(const contract_id_t &_contract_id, - const contract_t &_contract, - write_durability_t _default_write_durability, - write_ack_config_t _write_ack_config) : - contract_id(_contract_id), - contract(_contract), - default_write_durability(_default_write_durability), - write_ack_config(_write_ack_config) { - } - bool equivalent(const contract_info_t &other) const { - /* This method is called `equivalent` rather than `operator==` to avoid - confusion, because it doesn't actually compare every member */ - return contract_id == other.contract_id && - default_write_durability == other.default_write_durability && - write_ack_config == other.write_ack_config; - } - contract_id_t contract_id; - contract_t contract; - write_durability_t default_write_durability; - write_ack_config_t write_ack_config; - cond_t obsolete; -}; - -primary_execution_t::primary_execution_t( - const execution_t::context_t *_context, - execution_t::params_t *_params, - const contract_id_t &contract_id, - const table_raft_state_t &raft_state) : - execution_t(_context, _params), our_dispatcher(nullptr) -{ - const contract_t &contract = raft_state.contracts.at(contract_id).second; - guarantee(static_cast(contract.primary)); - guarantee(contract.primary->server == context->server_id); - guarantee(raft_state.contracts.at(contract_id).first == region); - latest_contract_home_thread = make_counted( - contract_id, contract, raft_state.config.config.durability, - raft_state.config.config.write_ack_config); - latest_contract_store_thread = latest_contract_home_thread; - begin_write_mutex_assertion.rethread(store->home_thread()); - coro_t::spawn_sometime(std::bind(&primary_execution_t::run, this, drainer.lock())); -} - -primary_execution_t::~primary_execution_t() { - drainer.drain(); - begin_write_mutex_assertion.rethread(home_thread()); -} - -void primary_execution_t::update_contract_or_raft_state( - const contract_id_t &contract_id, - const table_raft_state_t &raft_state) { - assert_thread(); - ASSERT_NO_CORO_WAITING; - - /* Has our branch ID been registered yet? */ - if (!branch_registered.is_pulsed()) { - bool branch_matches = true; - raft_state.current_branches.visit(region, - [&](const region_t &, const branch_id_t &b) { - if (make_optional(b) != our_branch_id) { - branch_matches = false; - } - }); - if (branch_matches) { - rassert(!branch_registered.is_pulsed()); - /* This raft state update just confirmed our branch ID */ - branch_registered.pulse(); - /* Change `latest_ack` immediately so we don't keep sending the branch - registration request */ - latest_ack = make_scoped( - contract_ack_t::state_t::primary_in_progress); - params->send_ack(contract_id, *latest_ack); - } - } - - const contract_t &contract = raft_state.contracts.at(contract_id).second; - guarantee(static_cast(contract.primary)); - guarantee(contract.primary->server == context->server_id); - guarantee(raft_state.contracts.at(contract_id).first == region); - - counted_t new_contract = make_counted( - contract_id, - contract, - raft_state.config.config.durability, - raft_state.config.config.write_ack_config); - - /* Exit early if there aren't actually any changes. This is for performance reasons. - */ - if (new_contract->equivalent(*latest_contract_home_thread)) { - return; - } - - /* Mark the old contract as obsolete, and record the new one */ - latest_contract_home_thread->obsolete.pulse(); - latest_contract_home_thread = new_contract; - - /* If we were acking `primary_ready`, go back to acking `primary_in_progress` until - we sync with the replicas according to the new contract. */ - if (latest_ack.has() && - latest_ack->state == contract_ack_t::state_t::primary_ready) { - latest_ack = make_scoped( - contract_ack_t::state_t::primary_in_progress); - } - - /* Deliver the new contract to the other thread. If the broadcaster exists, we'll - also do sync with the replicas and ack `primary_in_ready`. */ - coro_t::spawn_sometime(std::bind( - &primary_execution_t::update_contract_on_store_thread, this, - latest_contract_home_thread, - drainer.lock(), - new new_mutex_in_line_t(&update_contract_mutex))); - - /* Send an ack for the new contract */ - if (latest_ack.has()) { - params->send_ack(contract_id, *latest_ack); - } -} - -void primary_execution_t::run(auto_drainer_t::lock_t keepalive) { - assert_thread(); - order_source_t order_source(store->home_thread()); - cross_thread_signal_t interruptor_store_thread( - keepalive.get_drain_signal(), store->home_thread()); - - try { - /* Create the `primary_dispatcher_t`, establishing our branch in the process. */ - on_thread_t thread_switcher_1(store->home_thread()); - - read_token_t token; - store->new_read_token(&token); - region_map_t initial_version = to_version_map(store->get_metainfo( - order_source.check_in("primary_t").with_read_mode(), - &token, region, &interruptor_store_thread)); - - perfmon_collection_t perfmon_collection; - perfmon_membership_t perfmon_membership(params->get_parent_perfmon_collection(), - &perfmon_collection, - params->get_perfmon_name()); - - primary_dispatcher_t primary_dispatcher(&perfmon_collection, initial_version); - - direct_query_server_t direct_query_server( - context->mailbox_manager, - store); - - on_thread_t thread_switcher_2(home_thread()); - - /* Put an entry in the global directory so clients can find us for outdated reads - */ - table_query_bcard_t tq_bcard_direct; - tq_bcard_direct.region = region; - tq_bcard_direct.primary = r_nullopt; - tq_bcard_direct.direct = make_optional(direct_query_server.get_bcard()); - watchable_map_var_t::entry_t directory_entry_direct( - context->local_table_query_bcards, generate_uuid(), tq_bcard_direct); - - /* Send a request for the coordinator to register our branch */ - { - our_branch_id = make_optional(primary_dispatcher.get_branch_id()); - contract_ack_t ack(contract_ack_t::state_t::primary_need_branch); - ack.branch.set(*our_branch_id); - context->branch_history_manager->export_branch_history( - primary_dispatcher.get_branch_birth_certificate().origin, - &ack.branch_history); - ack.branch_history.branches.insert(std::make_pair( - *our_branch_id, - primary_dispatcher.get_branch_birth_certificate())); - latest_ack = make_scoped(ack); - params->send_ack(latest_contract_home_thread->contract_id, ack); - } - - /* Wait until we get our branch registered */ - wait_interruptible(&branch_registered, keepalive.get_drain_signal()); - - /* Acquire the mutex so that instances of `update_contract_on_store_thread()` - will be blocked out during the transitional period when we're setting up the - local replica. At the same time as we acquire the mutex, make a local copy of - `latest_contract_on_home_thread`. `sync_contract_with_replicas()` won't have - happened for this contract because `our_dispatcher` was `nullptr` when - `update_contract_on_store_thread()` ran for it. So we have to manually call - `sync_contract_with_replicas()` for it. */ - counted_t pre_replica_contract = latest_contract_home_thread; - scoped_ptr_t pre_replica_mutex_in_line( - new new_mutex_in_line_t(&update_contract_mutex)); - wait_interruptible( - pre_replica_mutex_in_line->acq_signal(), keepalive.get_drain_signal()); - - /* Set up the `local_replicator_t`, `remote_replicator_server_t`, and - `primary_query_server_t`. */ - on_thread_t thread_switcher_3(store->home_thread()); - - local_replicator_t local_replicator( - context->mailbox_manager, - context->server_id, - &primary_dispatcher, - store, - context->branch_history_manager, - &interruptor_store_thread); - - remote_replicator_server_t remote_replicator_server( - context->mailbox_manager, - &primary_dispatcher); - - auto_drainer_t primary_dispatcher_drainer; - assignment_sentry_t our_dispatcher_drainer_assign( - &our_dispatcher_drainer, &primary_dispatcher_drainer); - assignment_sentry_t our_dispatcher_assign( - &our_dispatcher, &primary_dispatcher); - - primary_query_server_t primary_query_server( - context->mailbox_manager, - region, - this); - - on_thread_t thread_switcher_4(home_thread()); - - /* OK, now we have to make sure that `sync_contract_with_replicas()` gets called - for `pre_broadcaster_contract` by spawning an extra copy of - `update_contract_on_store_thread()` manually. A copy was already spawned for the - contract when it first came in, but that copy saw `our_broadcaster` as `nullptr` - so it didn't call `sync_contract_with_replicas()`. */ - coro_t::spawn_sometime(std::bind( - &primary_execution_t::update_contract_on_store_thread, this, - pre_replica_contract, - keepalive, - pre_replica_mutex_in_line.release())); - - /* The `local_replicator_t` constructor set the metainfo to `*our_branch_id`, so - it's now safe to call `enable_gc()`. */ - params->enable_gc(*our_branch_id); - - /* Put an entry in the minidir so the replicas can find us */ - contract_execution_bcard_t ce_bcard; - ce_bcard.remote_replicator_server = remote_replicator_server.get_bcard(); - ce_bcard.replica = local_replicator.get_replica_bcard(); - ce_bcard.peer = context->mailbox_manager->get_me(); - watchable_map_var_t, - contract_execution_bcard_t>::entry_t minidir_entry( - context->local_contract_execution_bcards, - std::make_pair(context->server_id, *our_branch_id), - ce_bcard); - - /* Put an entry in the global directory so clients can find us for up-to-date - writes and reads */ - table_query_bcard_t tq_bcard_primary; - tq_bcard_primary.region = region; - tq_bcard_primary.primary = - make_optional(primary_query_server.get_bcard()); - tq_bcard_primary.direct = r_nullopt; - watchable_map_var_t::entry_t - directory_entry_primary( - context->local_table_query_bcards, generate_uuid(), tq_bcard_primary); - - /* Wait until we are no longer the primary, or it's time to shut down */ - keepalive.get_drain_signal()->wait_lazily_unordered(); - - } catch (const interrupted_exc_t &) { - /* do nothing */ - } -} - -/* `write_callback_t` waits until the query is safe to ack, then pulses `done`. */ -class primary_execution_t::write_callback_t : public primary_dispatcher_t::write_callback_t { -public: - write_callback_t(write_response_t *_r_out, - write_durability_t _default_write_durability, - write_ack_config_t _write_ack_config, - contract_t *contract) : - ack_counter(*contract), - default_write_durability(_default_write_durability), - write_ack_config(_write_ack_config), - response_out(_r_out) { } - - promise_t result; -private: - write_durability_t get_default_write_durability() { - /* This only applies to writes that don't specify the durability */ - return default_write_durability; - } - void on_ack(const server_id_t &server, write_response_t &&resp) { - if (!result.is_pulsed()) { - switch (write_ack_config) { - case write_ack_config_t::SINGLE: - break; - case write_ack_config_t::MAJORITY: - ack_counter.note_ack(server); - if (!ack_counter.is_safe()) { - // We're not ready to safely ack the query yet. - return; - } - break; - } - *response_out = std::move(resp); - result.pulse(true); - } - } - void on_end() { - if (!result.is_pulsed()) { - result.pulse(false); - } - } - ack_counter_t ack_counter; - write_durability_t default_write_durability; - write_ack_config_t write_ack_config; - write_response_t *response_out; -}; - -bool primary_execution_t::on_write( - const write_t &request, - fifo_enforcer_sink_t::exit_write_t *exiter, - order_token_t order_token, - signal_t *interruptor, - write_response_t *response_out, - admin_err_t *error_out) { - store->assert_thread(); - guarantee(our_dispatcher != nullptr); - - /* `acq` asserts that `update_contract_on_store_thread()` doesn't run between when we - take `contract_snapshot` and when we call `spawn_write()`. This is important because - `update_contract_on_store_thread()` needs to be able to make sure that all writes - that see the old contract are spawned before it calls - `sync_contract_with_replicas()`. */ - mutex_assertion_t::acq_t begin_write_mutex_acq(&begin_write_mutex_assertion); - - /* The reason that the `begin_write_mutex_acq` holds, is because we don't block in - here nor anywhere else where we acquire the mutex assertion. */ - DEBUG_ONLY(scoped_ptr_t finite_coro_waiting( - make_scoped(__FILE__, __LINE__))); - - counted_t contract_snapshot = latest_contract_store_thread; - - if (static_cast(contract_snapshot->contract.primary->hand_over)) { - *error_out = admin_err_t{ - "The primary replica is currently changing from one replica to " - "another. The write was not performed. This error should go away in a " - "couple of seconds.", - query_state_t::FAILED}; - return false; - } - - /* Make sure that we have contact with a majority of replicas. It's bad practice to - accept writes if we can't contact a majority of replicas because those writes might - be lost during a failover. We do this even if the user set the ack threshold to - "single". */ - if (!is_majority_available(contract_snapshot, our_dispatcher)) { - *error_out = admin_err_t{ - "The primary replica isn't connected to a quorum of replicas. " - "The write was not performed.", - query_state_t::FAILED}; - return false; - } - - write_callback_t write_callback(response_out, - contract_snapshot->default_write_durability, - contract_snapshot->write_ack_config, - &contract_snapshot->contract); - our_dispatcher->spawn_write(request, order_token, &write_callback); - - /* Now that we've called `spawn_write()`, our write is in the queue. So it's safe to - release the `begin_write_mutex`; any calls to `update_contract_on_store_thread()` - will enter the queue after us. */ - DEBUG_ONLY(finite_coro_waiting.reset()); - begin_write_mutex_acq.reset(); - - /* This will allow other calls to `on_write()` to happen. */ - exiter->end(); - - wait_interruptible(write_callback.result.get_ready_signal(), interruptor); - - bool res = write_callback.result.assert_get_value(); - if (!res) { - *error_out = admin_err_t{ - "The primary replica lost contact with the secondary " - "replicas. The write may or may not have been performed.", - query_state_t::INDETERMINATE}; - } - return res; -} - -bool primary_execution_t::sync_committed_read(const read_t &read_request, - order_token_t order_token, - signal_t *interruptor, - admin_err_t *error_out) { - write_response_t response; - write_t request = write_t::make_sync(read_request.get_region(), - read_request.profile); - - /* See the comments in `on_write` for an explanation about why we're acquiring - `begin_write_mutex_assertion` here. */ - mutex_assertion_t::acq_t begin_write_mutex_acq(&begin_write_mutex_assertion); - DEBUG_ONLY(scoped_ptr_t finite_coro_waiting( - make_scoped(__FILE__, __LINE__))); - counted_t contract_snapshot = latest_contract_store_thread; - - if (static_cast(contract_snapshot->contract.primary->hand_over)) { - *error_out = admin_err_t{ - "The primary replica is currently changing from one replica to " - "another. The read could not be guaranteed as committed. This error " - "should go away in a couple of seconds.", - query_state_t::FAILED}; - return false; - } - - write_callback_t write_callback(&response, - write_durability_t::HARD, - write_ack_config_t::MAJORITY, - &contract_snapshot->contract); - our_dispatcher->spawn_write(request, order_token, &write_callback); - - DEBUG_ONLY(finite_coro_waiting.reset()); - begin_write_mutex_acq.reset(); - - wait_interruptible(write_callback.result.get_ready_signal(), interruptor); - - bool res = write_callback.result.assert_get_value(); - if (!res) { - *error_out = admin_err_t{ - "The primary replica lost contact with the secondary " - "replicas. The read could not be guaranteed as committed.", - query_state_t::FAILED}; - } - return res; -} - -bool primary_execution_t::on_read( - const read_t &request, - fifo_enforcer_sink_t::exit_read_t *exiter, - order_token_t order_token, - signal_t *interruptor, - read_response_t *response_out, - admin_err_t *error_out) { - store->assert_thread(); - guarantee(our_dispatcher != nullptr); - - counted_t contract_snapshot = latest_contract_store_thread; - - switch (request.read_mode) { - case read_mode_t::SINGLE: - case read_mode_t::MAJORITY: // Fallthrough intentional - /* Make sure that we have contact with a majority of replicas, if we've lost - this we may no longer be able to do up-to-date reads. */ - if (!is_majority_available(contract_snapshot, our_dispatcher)) { - *error_out = admin_err_t{ - "The primary replica isn't connected to a quorum of replicas. " - "The read was not performed, you can do an outdated read using " - "`read_mode=\"outdated\"`.", - query_state_t::FAILED}; - return false; - } - break; - case read_mode_t::OUTDATED: // Fallthrough intentional - case read_mode_t::DEBUG_DIRECT: - default: - // These read modes should not come through the `primary_exection_t`. - unreachable(); - } - - try { - our_dispatcher->read( - request, - exiter, - order_token, - interruptor, - response_out); - - if (request.read_mode == read_mode_t::MAJORITY) { - return sync_committed_read(request, order_token, interruptor, error_out); - } else { - return true; - } - } catch (const cannot_perform_query_exc_t &e) { - *error_out = admin_err_t{e.what(), e.get_query_state()}; - return false; - } -} - -void primary_execution_t::update_contract_on_store_thread( - counted_t contract, - auto_drainer_t::lock_t keepalive, - new_mutex_in_line_t *sync_mutex_in_line_ptr) { - scoped_ptr_t sync_mutex_in_line(sync_mutex_in_line_ptr); - assert_thread(); - - try { - wait_any_t interruptor_home_thread( - &contract->obsolete, keepalive.get_drain_signal()); - - /* Wait until we hold `sync_mutex`, so that we don't reorder contracts if several - new contracts come in quick succession */ - wait_interruptible(sync_mutex_in_line->acq_signal(), &interruptor_home_thread); - - bool should_ack; - { - /* Go to the store's thread */ - cross_thread_signal_t interruptor_store_thread( - &interruptor_home_thread, store->home_thread()); - on_thread_t thread_switcher(store->home_thread()); - - /* We can only send a `primary_ready` response to the contract coordinator - once we're sure that both all incoming writes will be executed under the new - contract, and all previous writes are safe under the conditions of the new - contract. We ensure the former condition by setting - `latest_contract_store_thread`, so that incoming writes will pick up the new - contract. We ensure the latter condition by running sync writes until we get - one to succeed under the new contract. The sync writes must enter the - dispatcher's queue after any writes that were run under the old contract, so - that the sync writes can't succeed under the new contract until the old - writes are also safe under the new contract. */ - - /* Deliver the latest contract */ - { - /* We acquire `begin_write_mutex_assertion` to assert that we're not - interleaving with a call to `on_write()`. This way, we can be sure that - any writes that saw the old contract will enter the primary dispatcher's - queue before we call `sync_contract_with_replicas()`. */ - mutex_assertion_t::acq_t begin_write_mutex_acq( - &begin_write_mutex_assertion); - ASSERT_FINITE_CORO_WAITING; - latest_contract_store_thread = contract; - } - - /* If we have a broadcaster, then try to sync with replicas so we can ack the - contract. */ - if (our_dispatcher != nullptr) { - /* We're going to hold a pointer to the `primary_dispatcher_t` that lives - on the stack in `run()`, so we need to make sure we'll stop before - `run()` stops. We do this by acquiring a lock on the `auto_drainer_t` on - the stack in `run()`. */ - auto_drainer_t::lock_t keepalive2 = our_dispatcher_drainer->lock(); - wait_any_t combiner( - &interruptor_store_thread, keepalive2.get_drain_signal()); - should_ack = true; - sync_contract_with_replicas(contract, &combiner); - } else { - should_ack = false; - } - } - - if (should_ack) { - /* OK, time to ack the contract */ - latest_ack = make_scoped(contract_ack_t::state_t::primary_ready); - params->send_ack(contract->contract_id, *latest_ack); - } - - } catch (const interrupted_exc_t &) { - /* Either the contract is obsolete or we are being destroyed. In either case, - stop trying to ack the contract. */ - } -} - -void primary_execution_t::sync_contract_with_replicas( - counted_t contract, - signal_t *interruptor) { - store->assert_thread(); - guarantee(our_dispatcher != nullptr); - while (true) { - if (interruptor->is_pulsed()) { - throw interrupted_exc_t(); - } - - /* Wait until it looks like the write could go through */ - our_dispatcher->get_ready_dispatchees()->run_until_satisfied( - [&](const std::set &servers) { - return is_contract_ackable(contract, servers); - }, - interruptor); - /* Now try to actually put the write through */ - class safe_write_callback_t : public primary_dispatcher_t::write_callback_t { - public: - write_durability_t get_default_write_durability() { - return write_durability_t::HARD; - } - void on_ack(const server_id_t &server, write_response_t &&) { - servers.insert(server); - if (is_contract_ackable(contract, servers)) { - done.pulse_if_not_already_pulsed(); - } - } - void on_end() { - done.pulse_if_not_already_pulsed(); - } - counted_t contract; - std::set servers; - cond_t done; - } write_callback; - write_callback.contract = contract; - our_dispatcher->spawn_write( - write_t::make_sync(region, profile_bool_t::DONT_PROFILE), - order_token_t::ignore, &write_callback); - wait_interruptible(&write_callback.done, interruptor); - if (is_contract_ackable(contract, write_callback.servers)) { - break; - } - } -} - -bool primary_execution_t::is_contract_ackable( - counted_t contract_info, const std::set &servers) { - /* If it's a regular contract, we can ack it as soon as we send a sync to a quorum of - replicas. If it's a hand-over contract, we also need to ensure that we performed a - sync with the new primary. - - If we return `true` from this function, we are going to send a `primary_ready` ack - to the coordinator. The coordinator relies on this status for two things: - * if the we are in a configuration with `temp_voters`, the coordinator is going to - use the `primary_ready` ack as a confirmation that a majority of `voters` as well as - a majority of `temp_voters` have up-to-date data. Thus it will be able to safely - turn `temp_voters` into `voters` (this is critical for correctness). - * if we currently have a `hand_over` primary set, the coordinator will wait for the - `primary_ready` ack before it shuts down the current primary (us). - Our stricter criteria for `hand_over` configurations is to make sure that when the - time comes to start up a new primary, the `hand_over` primary is up to date and - actually eligible to become a primary. - (the additional criteria is not critical for correctness, but makes sure that the - transition to the new primary goes through smoothly) */ - if (static_cast(contract_info->contract.primary->hand_over) && - servers.count(*contract_info->contract.primary->hand_over) != 1) { - return false; - } - ack_counter_t ack_counter(contract_info->contract); - for (const server_id_t &s : servers) { - ack_counter.note_ack(s); - } - return ack_counter.is_safe(); -} - -bool primary_execution_t::is_majority_available( - counted_t contract_info, - primary_dispatcher_t *dispatcher) { - ack_counter_t counter(contract_info->contract); - dispatcher->get_ready_dispatchees()->apply_read( - [&](const std::set *servers) { - for (const server_id_t &s : *servers) { - counter.note_ack(s); - } - }); - return counter.is_safe(); -} diff --git a/src/clustering/table_contract/executor/exec_primary.hpp b/src/clustering/table_contract/executor/exec_primary.hpp deleted file mode 100644 index dc6d221829f..00000000000 --- a/src/clustering/table_contract/executor/exec_primary.hpp +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_CONTRACT_EXECUTOR_EXEC_PRIMARY_HPP_ -#define CLUSTERING_TABLE_CONTRACT_EXECUTOR_EXEC_PRIMARY_HPP_ - -#include "clustering/query_routing/primary_query_server.hpp" -#include "clustering/table_contract/executor/exec.hpp" -#include "containers/counted.hpp" - -class contract_t; -class io_backender_t; -class primary_dispatcher_t; - -/* `ack_counter_t` computes whether a write is guaranteed to be preserved -even after a failover or other reconfiguration. */ -class ack_counter_t { -public: - explicit ack_counter_t(const contract_t &_contract) : - contract(_contract), primary_ack(false), voter_acks(0), temp_voter_acks(0) { } - void note_ack(const server_id_t &server); - bool is_safe() const; -private: - const contract_t &contract; - bool primary_ack; - size_t voter_acks, temp_voter_acks; - - DISABLE_COPYING(ack_counter_t); -}; - -class primary_execution_t : - public execution_t, - public home_thread_mixin_t, - private primary_query_server_t::query_callback_t { -public: - primary_execution_t( - const execution_t::context_t *context, - execution_t::params_t *params, - const contract_id_t &cid, - const table_raft_state_t &raft_state); - ~primary_execution_t(); - - void update_contract_or_raft_state( - const contract_id_t &cid, - const table_raft_state_t &raft_state); - -private: - class write_callback_t; - /* `contract_info_t` stores a contract, its ack callback, and a condition variable - indicating if it's obsolete. The reason this is in a struct is because we sometimes - need to reason about old contracts, so we may keep multiple versions around. */ - class contract_info_t; - - /* This is started in a coroutine when the `primary_t` is created. It sets up the - broadcaster, listener, etc. */ - void run(auto_drainer_t::lock_t keepalive); - - /* These override virtual methods on `master_t::query_callback_t`. They get called - when we receive queries over the network. Warning: They are run on the store's home - thread, which is not necessarily our home thread. */ - bool on_write( - const write_t &request, - fifo_enforcer_sink_t::exit_write_t *exiter, - order_token_t order_token, - signal_t *interruptor, - write_response_t *response_out, - admin_err_t *error_out); - bool on_read( - const read_t &request, - fifo_enforcer_sink_t::exit_read_t *exiter, - order_token_t order_token, - signal_t *interruptor, - read_response_t *response_out, - admin_err_t *error_out); - - /* `sync_majority()` is used after a read in 'majority' mode, and will perform a - `sync` operation across a majority of replicas to make sure what has just been read - has been committed to disk on a majority of replicas for each shard. */ - bool sync_committed_read(const read_t &read_request, - order_token_t order_token, - signal_t *interruptor, - admin_err_t *error_out); - - /* `update_contract_or_raft_state()` spawns `update_contract_on_store_thread()` - to deliver the new contract to `store->home_thread()`. It has two jobs: - 1. It sets `latest_contract_store_thread` to the new contract - 2. If the broadcaster has been created, it waits until it's safe to ack - `primary_ready`, and then does so. */ - void update_contract_on_store_thread( - counted_t contract, - auto_drainer_t::lock_t keepalive, - new_mutex_in_line_t *mutex_in_line_ptr); - - /* `sync_contract_with_replicas()` blocks until it's safe to ack `primary_ready` for - the given contract. It does this by sending a sync write to all of the replicas and - waiting until enough of them respond; this relies on the principle that if a replica - acknowledges the sync write, it must have also acknowledged every write initiated - before the sync write. If the first sync write fails, it will try repeatedly until it - succeeds or is interrupted. */ - void sync_contract_with_replicas( - counted_t contract, - signal_t *interruptor); - - /* `is_contract_ackable()` is a helper function for `sync_contract_with_replicas()` - that returns `true` if it's safe to ack `primary_ready` for the given contract, given - that all the replicas in `servers` have performed the sync write. */ - static bool is_contract_ackable( - counted_t contract, - const std::set &servers); - - /* `is_majority_available()` is a helper function to determine whether a majority - of the replicas are available to acknowledge a read or write. */ - static bool is_majority_available( - counted_t contract, - primary_dispatcher_t *dispatcher); - - optional our_branch_id; - - /* `latest_contract_*` stores the latest contract we've received, along with its ack - callback. The `home_thread` version should only be accessed on `this->home_thread()`, - and the `store_thread` version should only be accessed on `store->home_thread()`. */ - counted_t latest_contract_home_thread, latest_contract_store_thread; - - /* `latest_ack` stores the latest contract ack we've sent. This is possibly null -- - treated as an optional, with a pointer indirection for the sake of - compile times. */ - scoped_ptr_t latest_ack; - - /* `update_contract_mutex` is used to order calls to - `update_contract_on_store_thread()`, so that we don't overwrite a newer contract with - an older one */ - new_mutex_t update_contract_mutex; - - /* `branch_registered` is pulsed once the coordinator has committed our branch ID to - the Raft state. */ - cond_t branch_registered; - - /* `our_dispatcher` stores the pointer to the `primary_dispatcher_t` we constructed. - It will be `nullptr` until the `primary_dispatcher_t` actually exists and has a valid - replica. */ - primary_dispatcher_t *our_dispatcher; - - /* Anything that might try to use `our_dispatcher` after the `primary_execution_t` - destructor has begin should hold a lock on `*our_dispatcher_drainer`. (Specifically, - this applies to `update_contract_on_store_thread()`.) `our_dispatcher` and - `our_dispatcher_drainer` will always both be null or both be non-null. */ - auto_drainer_t *our_dispatcher_drainer; - - /* `begin_write_mutex_assertion` is used to ensure that we don't ack a contract until - all past and ongoing writes are safe under the contract's conditions. */ - mutex_assertion_t begin_write_mutex_assertion; - - /* `drainer` ensures that `run()` and `update_contract_on_store_thread()` are - stopped before the other member variables are destroyed. */ - auto_drainer_t drainer; -}; - -#endif /* CLUSTERING_TABLE_CONTRACT_EXECUTOR_EXEC_PRIMARY_HPP_ */ - diff --git a/src/clustering/table_contract/executor/exec_secondary.cc b/src/clustering/table_contract/executor/exec_secondary.cc deleted file mode 100644 index 117ee73b400..00000000000 --- a/src/clustering/table_contract/executor/exec_secondary.cc +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_contract/executor/exec_secondary.hpp" - -#include - -#include "clustering/immediate_consistency/remote_replicator_client.hpp" -#include "clustering/query_routing/direct_query_server.hpp" -#include "concurrency/cross_thread_signal.hpp" - -secondary_execution_t::secondary_execution_t( - const execution_t::context_t *_context, - execution_t::params_t *_params, - const contract_id_t &cid, - const table_raft_state_t &raft_state, - const branch_id_t &_branch) : - execution_t(_context, _params) -{ - const contract_t &c = raft_state.contracts.at(cid).second; - guarantee(c.replicas.count(context->server_id) == 1); - guarantee(raft_state.contracts.at(cid).first == region); - - /* If the current contract doesn't have a primary, we won't attempt to connect to the - primary. Also, if we don't have a branch ID yet or the current contract's region - doesn't match the branch's region, we won't attempt to connect. This is because - the only way that can happen is if the table was just resharded, so the primary - is about to stop and restart with a different branch ID, and there's no point in - trying to connect to the existing primary. In fact, we can't connect to the - existing primary, since it's impossible to subscribe to a primary with a different - region. */ - if (static_cast(c.primary) && !_branch.is_nil() && - raft_state.branch_history.branches.at(_branch).get_region() == region) { - connect_to_primary = true; - primary = c.primary->server; - } else { - connect_to_primary = false; - primary = server_id_t::from_server_uuid(nil_uuid()); - } - - branch = _branch; - contract_id = cid; - is_critical_priority = c.is_voter(context->server_id) - ? backfill_throttler_t::priority_t::critical_t::YES - : backfill_throttler_t::priority_t::critical_t::NO; - coro_t::spawn_sometime(std::bind(&secondary_execution_t::run, this, drainer.lock())); -} - -void secondary_execution_t::update_contract_or_raft_state( - const contract_id_t &cid, - const table_raft_state_t &raft_state) { - assert_thread(); - const contract_t &c = raft_state.contracts.at(cid).second; - guarantee(raft_state.contracts.at(cid).first == region); - guarantee(c.replicas.count(context->server_id) == 1); - guarantee(primary.get_uuid().is_nil() || primary == c.primary->server); - if (contract_id != cid && static_cast(last_ack)) { - params->send_ack(cid, *last_ack); - } - contract_id = cid; - is_critical_priority = c.is_voter(context->server_id) - ? backfill_throttler_t::priority_t::critical_t::YES - : backfill_throttler_t::priority_t::critical_t::NO; -} - -void secondary_execution_t::run(auto_drainer_t::lock_t keepalive) { - assert_thread(); - order_source_t order_source(store->home_thread()); - bool enabled_gc = false; - while (!keepalive.get_drain_signal()->is_pulsed()) { - try { - /* Switch to the store thread so we can extract our metainfo and set up the - `direct_query_server_t` to serve queries while we wait for the primary to - be available */ - cross_thread_signal_t interruptor_on_store_thread( - keepalive.get_drain_signal(), store->home_thread()); - on_thread_t thread_switcher_1(store->home_thread()); - - contract_ack_t initial_ack(contract_ack_t::state_t::secondary_need_primary); - read_token_t token; - store->new_read_token(&token); - initial_ack.version.set(to_version_map( - store->get_metainfo( - order_source.check_in("secondary_execution_t").with_read_mode(), - &token, region, &interruptor_on_store_thread))); - - direct_query_server_t direct_query_server(context->mailbox_manager, store); - - /* Switch back to the home thread so we can send the initial ack */ - on_thread_t thread_switcher_2(home_thread()); - - context->branch_history_manager->export_branch_history( - *initial_ack.version, &initial_ack.branch_history); - - send_ack(initial_ack); - - /* Serve outdated reads while we wait for the primary */ - object_buffer_t::entry_t> - directory_entry; - { - table_query_bcard_t tq_bcard; - tq_bcard.region = region; - tq_bcard.direct = make_optional(direct_query_server.get_bcard()); - directory_entry.create( - context->local_table_query_bcards, generate_uuid(), tq_bcard); - } - - if (!connect_to_primary) { - /* Instead of establishing a connection to the primary and doing a - backfill, we'll just wait for a new contract. */ - keepalive.get_drain_signal()->wait_lazily_unordered(); - return; - } - - /* Set up a subscription to look for the primary in the directory and also - detect if we lose contact. Initially, `primary_bcard` and - `primary_no_more_bcard` are both unpulsed, and `primary_disconnect_watcher` - is empty. When we first see the primary, we pulse `primary_bcard` and create - `primary_disconnected`. If the primary later disappears from the directory, - we pulse `primary_no_more_bcard`. */ - promise_t primary_bcard; - object_buffer_t primary_disconnected; - cond_t primary_no_more_bcard; - - object_buffer_t, - contract_execution_bcard_t>::key_subs_t> - primary_watcher; - primary_watcher.create( - context->remote_contract_execution_bcards, - std::make_pair(primary, branch), - [&](const contract_execution_bcard_t *bc) { - if (!primary_bcard.get_ready_signal()->is_pulsed()) { - if (bc != nullptr) { - primary_bcard.pulse(*bc); - primary_disconnected.create( - context->mailbox_manager, bc->peer); - } - } else if (!primary_no_more_bcard.is_pulsed()) { - if (bc == nullptr) { - primary_no_more_bcard.pulse(); - } - } - }, initial_call_t::YES); - - /* Wait until we see a primary */ - wait_interruptible( - primary_bcard.get_ready_signal(), keepalive.get_drain_signal()); - - /* Stop serving outdated reads, because we're going to do a backfill */ - directory_entry.reset(); - - /* Let the coordinator know we found the primary. */ - send_ack(contract_ack_t(contract_ack_t::state_t::secondary_backfilling)); - - /* While we're on the home thread, make a local copy of - `is_critical_priority` */ - backfill_throttler_t::priority_t::critical_t local_is_critical_priority = - is_critical_priority; - - /* Set up a signal that will get pulsed if we lose contact with the primary - or we get interrupted */ - wait_any_t stop_signal( - &primary_no_more_bcard, - primary_disconnected.get(), - keepalive.get_drain_signal()); - - /* We have to construct and destroy the `listener_t` and the `replier_t` on - the store's home thread. So we switcher there and switch back, and when the - stack variables get destructed we do the reverse. */ - cross_thread_signal_t stop_signal_on_store_thread( - &stop_signal, store->home_thread()); - on_thread_t thread_switcher_3(store->home_thread()); - - /* Backfill and start streaming from the primary. */ - remote_replicator_client_t remote_replicator_client( - context->backfill_throttler, - backfill_config_t(), - context->backfill_progress_tracker, - context->mailbox_manager, - context->server_id, - local_is_critical_priority, - branch, - primary_bcard.assert_get_value().remote_replicator_server, - primary_bcard.assert_get_value().replica, - primary, - store, - context->branch_history_manager, - &stop_signal_on_store_thread); - - on_thread_t thread_switcher_4(home_thread()); - - /* Now that we've backfilled, it's safe to call `enable_gc()`. */ - if (!enabled_gc) { - enabled_gc = true; - params->enable_gc(branch); - } - - /* Let the coordinator know we finished backfilling */ - send_ack(contract_ack_t(contract_ack_t::state_t::secondary_streaming)); - - /* Resume serving outdated reads now that the backfill is over */ - { - table_query_bcard_t tq_bcard; - tq_bcard.region = region; - tq_bcard.direct = make_optional(direct_query_server.get_bcard()); - directory_entry.create( - context->local_table_query_bcards, generate_uuid(), tq_bcard); - } - - /* Wait until we lose contact with the primary or we get interrupted */ - stop_signal.wait_lazily_unordered(); - - } catch (const interrupted_exc_t &) { - /* do nothing */ - } - } -} - -void secondary_execution_t::send_ack(const contract_ack_t &ca) { - assert_thread(); - params->send_ack(contract_id, ca); - last_ack = make_optional(ca); -} - diff --git a/src/clustering/table_contract/executor/exec_secondary.hpp b/src/clustering/table_contract/executor/exec_secondary.hpp deleted file mode 100644 index ef2ccbbab3a..00000000000 --- a/src/clustering/table_contract/executor/exec_secondary.hpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_CONTRACT_EXECUTOR_EXEC_SECONDARY_HPP_ -#define CLUSTERING_TABLE_CONTRACT_EXECUTOR_EXEC_SECONDARY_HPP_ - -#include "clustering/immediate_consistency/backfill_throttler.hpp" -#include "clustering/table_contract/contract_metadata.hpp" -#include "clustering/table_contract/executor/exec.hpp" -#include "store_view.hpp" - -class io_backender_t; - -class secondary_execution_t : public execution_t, public home_thread_mixin_t { -public: - secondary_execution_t( - const execution_t::context_t *_context, - execution_t::params_t *_params, - const contract_id_t &cid, - const table_raft_state_t &raft_state, - const branch_id_t &_branch); - - void update_contract_or_raft_state( - const contract_id_t &cid, - const table_raft_state_t &raft_state); - -private: - /* `run()` does the actual work of setting up the `listener_t`, etc. */ - void run(auto_drainer_t::lock_t); - void send_ack(const contract_ack_t &ca); - - /* If we are secondary for a contract for which there is no primary, or if the - contract's region doesn't match the branch's region, we'll set `connect_to_primary` - to `false`. In this case, we'll serve outdated reads but won't attempt to find a - primary in the directory. */ - bool connect_to_primary; - - server_id_t primary; - branch_id_t branch; - - contract_id_t contract_id; - - /* `last_ack` contains the last ack we've sent via `ack_cb`, if any. */ - optional last_ack; - - /* Set to `YES` if we're a voter, `NO` if we're a nonvoter */ - backfill_throttler_t::priority_t::critical_t is_critical_priority; - - /* `drainer` ensures that `run` is stopped before the other member variables are - destroyed. */ - auto_drainer_t drainer; -}; - -#endif /* CLUSTERING_TABLE_CONTRACT_EXECUTOR_EXEC_SECONDARY_HPP_ */ - diff --git a/src/clustering/table_contract/executor/executor.cc b/src/clustering/table_contract/executor/executor.cc deleted file mode 100644 index bc5a0cdcc23..00000000000 --- a/src/clustering/table_contract/executor/executor.cc +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_contract/executor/executor.hpp" - -#include "clustering/table_contract/branch_history_gc.hpp" -#include "clustering/table_contract/executor/exec_erase.hpp" -#include "clustering/table_contract/executor/exec_primary.hpp" -#include "clustering/table_contract/executor/exec_secondary.hpp" -#include "store_subview.hpp" - -class contract_executor_t::execution_wrapper_t : private execution_t::params_t { -public: - execution_wrapper_t( - contract_executor_t *_parent, - const execution_key_t &key, - const branch_id_t &branch, - const contract_id_t &_contract_id, - const table_raft_state_t &state) : - parent(_parent), contract_id(_contract_id), - store_subview( - parent->multistore->get_cpu_sharded_store( - get_cpu_shard_number(key.region)), - key.region), - perfmon_name(strprintf("%s-%d", key.role_name().c_str(), ++parent->perfmon_counter)) - { - switch (key.role) { - case execution_key_t::role_t::primary: - execution.init(new primary_execution_t( - &parent->execution_context, this, contract_id, state)); - break; - case execution_key_t::role_t::secondary: - execution.init(new secondary_execution_t( - &parent->execution_context, this, contract_id, state, branch)); - break; - case execution_key_t::role_t::erase: - execution.init(new erase_execution_t( - &parent->execution_context, this, contract_id, state)); - break; - default: unreachable(); - } - } - ~execution_wrapper_t() { - /* Stop the execution first, so it can't call `send_ack()` after we've deleted - the entry from `ack_map` */ - execution.reset(); - parent->ack_map.delete_key(std::make_pair(parent->server_id, contract_id)); - } - - void update(const contract_id_t &new_contract_id, - const table_raft_state_t &new_state) { - contract_id_t old_contract_id = contract_id; - contract_id = new_contract_id; - /* Note that `update_contract_or_raft_state()` will never block. Also note that - we call it even if the contract ID has not actually changed, because it might - care about the changes to the parts of the Raft state other than the contract. */ - execution->update_contract_or_raft_state(new_contract_id, new_state); - if (old_contract_id != new_contract_id) { - /* Delete the old contract ack, if there was one */ - parent->ack_map.delete_key( - std::make_pair(parent->server_id, old_contract_id)); - } - } - - contract_id_t get_contract_id() const { - return contract_id; - } - - /* If the execution has called its `enable_gc()` callback, this will contain the - value that was provided. Otherwise, this will be empty. */ - optional enable_gc_branch; - -private: - perfmon_collection_t *get_parent_perfmon_collection() const { - return parent->perfmons; - } - - const std::string &get_perfmon_name() const { - return perfmon_name; - } - - store_view_t *get_store() { - return &store_subview; - } - - void send_ack(const contract_id_t &cid, const contract_ack_t &ack) { - parent->assert_thread(); - /* If the contract is out of date, don't send the ack */ - if (cid == contract_id) { - parent->ack_map.set_key_no_equals( - std::make_pair(parent->server_id, contract_id), ack); - } - } - - void enable_gc(const branch_id_t &new_enable_gc_branch) { - parent->assert_thread(); - guarantee(!static_cast(enable_gc_branch)); - enable_gc_branch = make_optional(new_enable_gc_branch); - parent->gc_branch_history_pumper.notify(); - } - - contract_executor_t *const parent; - - /* The contract ID of the contract governing this execution. Note that this may - change over the course of an execution; see the comment about `execution_key_t`. */ - contract_id_t contract_id; - - /* A `store_subview_t` containing only the sub-region of the store that this - execution affects. */ - store_subview_t store_subview; - - /* We create a new perfmon category for each execution. This way the executions - themselves don't have to think about perfmon key collisions. This does not construct - the perfmons itself, but provides the name so they may be added later */ - std::string perfmon_name; - - /* The execution itself */ - scoped_ptr_t execution; -}; - -contract_executor_t::contract_executor_t( - const server_id_t &_server_id, - mailbox_manager_t *_mailbox_manager, - const clone_ptr_t > &_raft_state, - watchable_map_t, contract_execution_bcard_t> - *_remote_contract_execution_bcards, - multistore_ptr_t *_multistore, - const base_path_t &_base_path, - io_backender_t *_io_backender, - backfill_throttler_t *_backfill_throttler, - backfill_progress_tracker_t *_backfill_progress_tracker, - perfmon_collection_t *_perfmons) : - server_id(_server_id), - raft_state(_raft_state), - multistore(_multistore), - perfmons(_perfmons), - perfmon_counter(0), - update_pumper( - std::bind(&contract_executor_t::update_blocking, this, ph::_1)), - gc_branch_history_pumper( - std::bind(&contract_executor_t::gc_branch_history, this, ph::_1)), - raft_state_subs([this]() { - update_pumper.notify(); - gc_branch_history_pumper.notify(); - }) -{ - execution_context.server_id = _server_id; - execution_context.mailbox_manager = _mailbox_manager; - execution_context.branch_history_manager = multistore->get_branch_history_manager(); - execution_context.base_path = _base_path; - execution_context.io_backender = _io_backender; - execution_context.backfill_progress_tracker = _backfill_progress_tracker; - execution_context.backfill_throttler = _backfill_throttler; - execution_context.remote_contract_execution_bcards - = _remote_contract_execution_bcards; - execution_context.local_contract_execution_bcards - = &local_contract_execution_bcards; - execution_context.local_table_query_bcards = &local_table_query_bcards; - - multistore->assert_thread(); - - watchable_t::freeze_t freeze(raft_state); - raft_state_subs.reset(raft_state, &freeze); - update_pumper.notify(); -} - -contract_executor_t::~contract_executor_t() { - /* We have to destroy `raft_state_subs` before the `pump_coro_t`s because its - callback accesses them. */ - raft_state_subs.reset(); - - /* We have to drain `update_pumper` and `gc_branch_history_pumper` before - `executions` because their callbacks access `executions`. */ - update_pumper.drain(); - gc_branch_history_pumper.drain(); - - /* We have to clean out `executions` before destroying `gc_branch_history_pumper` - because the `execution_wrapper_t`s may access `gc_branch_history_pumper`. */ - executions.clear(); -} - -range_map_t -contract_executor_t::get_shard_status() { - range_map_t result( - key_range_t::right_bound_t(store_key_t::min()), - key_range_t::right_bound_t::make_unbounded()); - raft_state->apply_read([&](const table_raft_state_t *state) { - for (const auto &pair : state->contracts) { - if (pair.second.second.replicas.count(server_id) == 0) { - continue; - } - result.visit_mutable( - key_range_t::right_bound_t(pair.second.first.inner.left), - pair.second.first.inner.right, - [&](const key_range_t::right_bound_t &, - const key_range_t::right_bound_t &, - table_shard_status_t *status) { - ack_map.read_key(std::make_pair(server_id, pair.first), - [&](const contract_ack_t *ack) { - if (ack == nullptr) { - status->transitioning = true; - return; - } - switch (ack->state) { - case contract_ack_t::state_t::primary_need_branch: - status->primary = true; - status->need_quorum = true; - break; - case contract_ack_t::state_t::primary_in_progress: // fall thru - case contract_ack_t::state_t::primary_ready: - status->primary = true; - break; - case contract_ack_t::state_t::secondary_need_primary: - status->secondary = true; - status->need_primary = true; - break; - case contract_ack_t::state_t::secondary_backfilling: - status->secondary = true; - status->backfilling = true; - break; - case contract_ack_t::state_t::secondary_streaming: - status->secondary = true; - break; - } - }); - }); - } - }); - return result; -} - -contract_executor_t::execution_key_t contract_executor_t::get_contract_key( - const std::pair &pair, - const branch_id_t &branch) { - execution_key_t key; - key.region = pair.first; - if (static_cast(pair.second.primary) && - pair.second.primary->server == server_id) { - key.role = execution_key_t::role_t::primary; - key.primary = server_id_t::from_server_uuid(nil_uuid()); - key.branch = nil_uuid(); - } else if (pair.second.replicas.count(server_id) == 1) { - key.role = execution_key_t::role_t::secondary; - if (static_cast(pair.second.primary)) { - key.primary = pair.second.primary->server; - } else { - key.primary = server_id_t::from_server_uuid(nil_uuid()); - } - key.branch = branch; - } else { - key.role = execution_key_t::role_t::erase; - key.primary = server_id_t::from_server_uuid(nil_uuid()); - key.branch = nil_uuid(); - } - return key; -} - -void contract_executor_t::update_blocking(signal_t *interruptor) { - new_mutex_acq_t executions_mutex_acq(&executions_mutex, interruptor); - std::set dont_delete; - { - ASSERT_NO_CORO_WAITING; - raft_state->apply_read([&](const table_raft_state_t *new_state) { - for (const auto &new_pair : new_state->contracts) { - /* Extract the current branch ID for the region covered by this contract. - If there are multiple branches for different subregions, we consider the - branch as incoherent and don't set a branch ID. */ - branch_id_t branch = nil_uuid(); - bool branch_mismatch = false; - new_state->current_branches.visit(new_pair.second.first, - [&](const region_t &, const branch_id_t &b) { - if (branch.is_nil()) { - branch = b; - } else if (branch != b) { - branch_mismatch = true; - } - }); - if (branch_mismatch) { - branch = nil_uuid(); - } - - execution_key_t key = get_contract_key(new_pair.second, branch); - dont_delete.insert(key); - auto it = executions.find(key); - if (it != executions.end()) { - it->second->update(new_pair.first, *new_state); - } else { - /* Create a new execution, unless there's already an execution whose - region overlaps ours. In the latter case, the execution will be - deleted soon. */ - bool ok_to_create = true; - for (const auto &old_pair : executions) { - if (region_overlaps(old_pair.first.region, - new_pair.second.first)) { - ok_to_create = false; - break; - } - } - if (ok_to_create) { - executions[key] = make_scoped( - this, key, branch, new_pair.first, *new_state); - } - } - } - }); - } - bool deleted_any = false; - for (auto it = executions.begin(); it != executions.end();) { - if (dont_delete.count(it->first) == 1) { - ++it; - } else { - /* This is the part that can block. */ - executions.erase(it++); - deleted_any = true; - } - } - if (deleted_any) { - /* Now that we've deleted the executions, `update()` is likely to have new - instructions for us, so we should run again. */ - update_pumper.notify(); - } -} - -void contract_executor_t::gc_branch_history(signal_t *interruptor) { - new_mutex_acq_t executions_mutex_acq(&executions_mutex, interruptor); - std::set remove_branches; - execution_context.branch_history_manager->prepare_gc(&remove_branches); - bool ok = true; - raft_state->apply_read([&](const table_raft_state_t *state) { - std::set contract_ids; - for (const auto &pair : state->contracts) { - contract_ids.insert(pair.first); - } - for (const auto &pair : executions) { - contract_ids.erase(pair.second->get_contract_id()); - if (!static_cast(pair.second->enable_gc_branch)) { - ok = false; - break; - } - if (!pair.second->enable_gc_branch->is_nil()) { - mark_ancestors_since_base_live( - *pair.second->enable_gc_branch, - pair.first.region, - execution_context.branch_history_manager, - &state->branch_history, - &remove_branches); - } - } - if (!contract_ids.empty()) { - /* Since we were missing executions for one more more contracts, don't - garbage collect. This is to make sure that if we're in a transitional state - where some regions have no execution, we won't throw away the branches for - those regions. */ - ok = false; - } - }); - if (ok) { - execution_context.branch_history_manager->perform_gc(remove_branches); - } -} - diff --git a/src/clustering/table_contract/executor/executor.hpp b/src/clustering/table_contract/executor/executor.hpp deleted file mode 100644 index 25225de1c20..00000000000 --- a/src/clustering/table_contract/executor/executor.hpp +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_CONTRACT_EXECUTOR_EXECUTOR_HPP_ -#define CLUSTERING_TABLE_CONTRACT_EXECUTOR_EXECUTOR_HPP_ - -#include "clustering/generic/raft_core.hpp" -#include "clustering/immediate_consistency/history.hpp" -#include "clustering/table_contract/cpu_sharding.hpp" -#include "clustering/table_contract/executor/exec.hpp" -#include "concurrency/pump_coro.hpp" -#include "store_subview.hpp" - -class backfill_progress_tracker_t; -class contract_t; -class table_shard_status_t; - -/* The `contract_executor_t` is responsible for executing the instructions contained in -the `contract_t`s in the `table_raft_state_t`. Each server has one `contract_executor_t` -for each table it's a replica of. The `contract_executor_t` constantly monitors the -current Raft state and compares the contracts in the Raft state against its "executions", -which represent activities it is currently performing. As contracts appear and disappear -from the Raft state, it creates, updates, and destroys executions to match. It also takes -care of forwarding the `contract_ack_t`s generated by the executions back to the -`contract_coordinator_t`. */ - -class contract_executor_t : public home_thread_mixin_t { -public: - contract_executor_t( - const server_id_t &server_id, - mailbox_manager_t *const mailbox_manager, - const clone_ptr_t > &raft_state, - watchable_map_t, contract_execution_bcard_t> - *remote_contract_execution_bcards, - multistore_ptr_t *multistore, - const base_path_t &base_path, - io_backender_t *io_backender, - backfill_throttler_t *backfill_throttler, - backfill_progress_tracker_t *backfill_progress_tracker, - perfmon_collection_t *perfmons); - ~contract_executor_t(); - - watchable_map_t, contract_ack_t> *get_acks() { - return &ack_map; - } - - watchable_map_t, contract_execution_bcard_t> - *get_local_contract_execution_bcards() { - return &local_contract_execution_bcards; - } - - watchable_map_t *get_local_table_query_bcards() { - return &local_table_query_bcards; - } - - range_map_t get_shard_status(); - -private: - /* The actual work of executing the contract--accepting queries from the user, - performing backfills, etc.--is carried out by the three `execution_t` subclasses, - `primary_execution_t`, `secondary_execution_t`, and `erase_execution_t`. - `execution_wrapper_t` is just a simple wrapper around an `execution_t` with some - supporting objects. */ - class execution_wrapper_t; - - /* When a contract changes, we sometimes want to create a new execution and we - sometimes want to update an existing one. Specifically, we want to create a new - execution when: - - The region of the contract changes - - This server's role in the contract (primary, secondary, or neither) changes - - This server's role is a secondary but the primary or branch has changed - - We implement this by computing an `execution_key_t` based on each contract - and the `current_branches` field of the Raft state, using the `get_contract_key()` - function. If the old and new contracts have the same `execution_key_t`, then we - update the corresponding execution. But if they differ, then we delete the old - execution and create a new one. */ - class execution_key_t { - public: - enum class role_t { primary, secondary, erase }; - /* This is for generating perfmon keys */ - std::string role_name() const { - switch (role) { - case role_t::primary: return "primary"; - case role_t::secondary: return "secondary"; - case role_t::erase: return "erase"; - default: unreachable(); - } - } - /* This is just so we can use it as a `std::set`/`std::map` key */ - bool operator<(const execution_key_t &k) const { - return std::tie(region, role, primary, branch) < - std::tie(k.region, k.role, k.primary, k.branch); - } - region_t region; - role_t role; - server_id_t primary; - branch_id_t branch; - }; - - execution_key_t get_contract_key( - const std::pair &pair, - const branch_id_t &branch); - - /* In response to Raft state changes, we want to delete existing executions and spawn - new ones. However, deleting executions may block. So `raft_state_subs` notifies - `update_pumper` which spawns `update_blocking()`. `update_blocking()` calls - `apply_read()` on the Raft state watchable and passes the result to `update()`. - `update()` may spawn new executions, but it may not delete them, because that would - block. Instead, it puts their regions in `to_delete_out`, and then - `update_blocking()` deletes them. */ - void update_blocking(signal_t *interruptor); - void update(const table_raft_state_t &new_state, - std::set *to_delete_out); - - /* `gc_branch_history()` runs in a `pump_coro_t` to garbage-collect the branch - history. */ - void gc_branch_history(signal_t *interruptor); - - const server_id_t server_id; - clone_ptr_t > raft_state; - multistore_ptr_t *const multistore; - perfmon_collection_t *const perfmons; - - /* `ack_map` contains the `contract_ack_t`s created by our execution of contracts. - It will be sent over the network to the `contract_coordinator_t` via the minidir. */ - watchable_map_var_t, contract_ack_t> ack_map; - - /* `local_contract_execution_bcards` contains the `contract_execution_bcards` for - our `primary_execution_t`s. It will be sent over the network to the other - `contract_executor_t`s for this table, via the minidir, so that they can request - backfills from us and connect their `listener_t`s to our `broadcaster_t`s. */ - watchable_map_var_t, contract_execution_bcard_t> - local_contract_execution_bcards; - - /* `local_table_query_bcards` contains the `table_query_bcard_t`s for our - `primary_execution_t`s. It will be sent over the network to all the servers in the - cluster, via the directory, so that they can run queries. */ - watchable_map_var_t local_table_query_bcards; - - /* This is just a convenient struct to hold a bunch of objects that the - `execution_t`s need access to. */ - execution_t::context_t execution_context; - - std::map > executions; - - /* This lock prevents `update_blocking()` and `gc_branch_history()` from accessing - `executions` at the same time. */ - new_mutex_t executions_mutex; - - /* Used to generate unique names for perfmons */ - int perfmon_counter; - - /* `update_pumper` calls `update_blocking()`. */ - pump_coro_t update_pumper; - - /* `gc_branch_history_pumper` calls `gc_branch_history()`. */ - pump_coro_t gc_branch_history_pumper; - - /* We subscribe to changes in the Raft committed state so we can find out when a new - contract has been issued. */ - watchable_t::subscription_t raft_state_subs; - - /* Normally destructor order would matter for `executions`, the `*_pumper`s, and - `raft_state_subs`; but in this case we actually manage the destruction explicitly in - `~executor_t()`. */ -}; - -#endif /* CLUSTERING_TABLE_CONTRACT_EXECUTOR_EXECUTOR_HPP_ */ - diff --git a/src/clustering/table_manager/backfill_progress_tracker.cc b/src/clustering/table_manager/backfill_progress_tracker.cc deleted file mode 100644 index 44183cf4aa7..00000000000 --- a/src/clustering/table_manager/backfill_progress_tracker.cc +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. - -#include "clustering/table_manager/backfill_progress_tracker.hpp" - -backfill_progress_tracker_t::progress_tracker_t * -backfill_progress_tracker_t::insert_progress_tracker(const region_t ®ion) { - return &progress_trackers.get()->insert( - std::make_pair(region, backfill_progress_tracker_t::progress_tracker_t()) - ).first->second; -} - -std::map -backfill_progress_tracker_t::get_progress_trackers() { - std::map output; - pmap(get_num_threads(), [&](int thread) { - std::map inner; - { - on_thread_t on_thread((threadnum_t(thread))); - inner = *progress_trackers.get(); - } - for (const auto &pair : inner) { - auto res = output.insert(std::move(pair)); - guarantee(res.second); - } - }); - return output; -} diff --git a/src/clustering/table_manager/backfill_progress_tracker.hpp b/src/clustering/table_manager/backfill_progress_tracker.hpp deleted file mode 100644 index bd01b12819f..00000000000 --- a/src/clustering/table_manager/backfill_progress_tracker.hpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_MANAGER_BACKFILL_PROGRESS_TRACKER_HPP_ -#define CLUSTERING_TABLE_MANAGER_BACKFILL_PROGRESS_TRACKER_HPP_ - -#include - -#include "concurrency/one_per_thread.hpp" -#include "containers/uuid.hpp" -#include "region/region.hpp" -#include "rpc/connectivity/server_id.hpp" -#include "time.hpp" - -class backfill_progress_tracker_t { -public: - class progress_tracker_t { - public: - bool is_ready; - microtime_t start_time; - server_id_t source_server_id; - double progress; - }; - - progress_tracker_t * insert_progress_tracker(const region_t ®ion); - - std::map - get_progress_trackers(); - -private: - one_per_thread_t > progress_trackers; -}; - -#endif /* CLUSTERING_TABLE_MANAGER_BACKFILL_PROGRESS_TRACKER_HPP_ */ - diff --git a/src/clustering/table_manager/multi_table_manager.cc b/src/clustering/table_manager/multi_table_manager.cc deleted file mode 100644 index 6bb8d0d10e3..00000000000 --- a/src/clustering/table_manager/multi_table_manager.cc +++ /dev/null @@ -1,712 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_manager/multi_table_manager.hpp" - -#include "clustering/generic/raft_core.tcc" -#include "clustering/query_routing/metadata.hpp" -#include "clustering/table_manager/table_manager.hpp" -#include "logger.hpp" -#include "rpc/mailbox/mailbox.hpp" - -multi_table_manager_t::multi_table_manager_t( - const server_id_t &_server_id, - mailbox_manager_t *_mailbox_manager, - server_config_client_t *_server_config_client, - watchable_map_t - *_multi_table_manager_directory, - watchable_map_t, table_manager_bcard_t> - *_table_manager_directory, - watchable_map_t, empty_value_t> - *_connections_map, - table_persistence_interface_t *_persistence_interface, - const base_path_t &_base_path, - io_backender_t *_io_backender, - perfmon_collection_repo_t *_perfmon_collection_repo) : - is_proxy_server(false), - server_id(_server_id), - mailbox_manager(_mailbox_manager), - server_config_client(_server_config_client), - multi_table_manager_directory(_multi_table_manager_directory), - table_manager_directory(_table_manager_directory), - connections_map(_connections_map), - persistence_interface(_persistence_interface), - base_path(_base_path), - io_backender(_io_backender), - perfmon_collection_repo(_perfmon_collection_repo) { - - /* Resurrect any tables that were sitting on disk from when we last shut down */ - cond_t non_interruptor; - persistence_interface->read_all_metadata( - [&](const namespace_id_t &table_id, - const table_active_persistent_state_t &state, - raft_storage_interface_t *raft_storage, - metadata::read_txn_t *metadata_read_txn) { - guarantee(tables.count(table_id) == 0); - table_t *table; - tables[table_id].init(table = new table_t); - rwlock_acq_t table_lock_acq(&table->access_rwlock, access_t::write); - perfmon_collection_repo_t::collections_t *perfmon_collections = - perfmon_collection_repo->get_perfmon_collections_for_namespace(table_id); - table->status = table_t::status_t::ACTIVE; - persistence_interface->load_multistore( - table_id, metadata_read_txn, &table->multistore_ptr, &non_interruptor, - &perfmon_collections->serializers_collection); - table->active = make_scoped( - this, table, table_id, state.epoch, state.raft_member_id, raft_storage, - raft_start_election_immediately_t::NO, table->multistore_ptr.get(), - &perfmon_collections->namespace_collection); - }, - [&](const namespace_id_t &table_id, - const table_inactive_persistent_state_t &state, - metadata::read_txn_t *) { - guarantee(tables.count(table_id) == 0); - table_t *table; - tables[table_id].init(table = new table_t); - rwlock_acq_t table_lock_acq(&table->access_rwlock, access_t::write); - table->status = table_t::status_t::INACTIVE; - table->basic_configs_entry.create(&table_basic_configs, table_id, - std::make_pair(state.second_hand_config, state.timestamp)); - }, - &non_interruptor); - - help_construct(); -} - -/* This constructor is used for proxy servers. */ -multi_table_manager_t::multi_table_manager_t( - mailbox_manager_t *_mailbox_manager, - watchable_map_t - *_multi_table_manager_directory, - watchable_map_t, table_manager_bcard_t> - *_table_manager_directory) : - is_proxy_server(true), - server_id(server_id_t::from_server_uuid(nil_uuid())), - mailbox_manager(_mailbox_manager), - server_config_client(nullptr), - multi_table_manager_directory(_multi_table_manager_directory), - table_manager_directory(_table_manager_directory), - connections_map(nullptr), - persistence_interface(nullptr), - base_path(r_nullopt), - io_backender(nullptr), - perfmon_collection_repo(nullptr) -{ - help_construct(); -} - -multi_table_manager_t::~multi_table_manager_t() { - /* First, shut out further mailbox events or watchable callbacks. This ensures that - tables are not created or destroyed, nor are their states changed (active vs. - inactive vs. deleted). */ - get_status_mailbox.reset(); - action_mailbox.reset(); - table_manager_directory_subs.reset(); - multi_table_manager_directory_subs.reset(); - - /* Next, destroy all of the `active_table_t`s. This is important because otherwise - `active_table_t` can call `schedule_sync()`. */ - for (auto &&pair : tables) { - rwlock_acq_t lock_acq(&pair.second->access_rwlock, access_t::write); - pair.second->status = table_t::status_t::SHUTTING_DOWN; - pair.second->active.reset(); - pair.second->multistore_ptr.reset(); - pair.second->basic_configs_entry.reset(); - } - - /* Drain any running coroutines */ - drainer.drain(); -} - -multi_table_manager_t::active_table_t::active_table_t( - multi_table_manager_t *_parent, - table_t *_table, - const namespace_id_t &_table_id, - const multi_table_manager_timestamp_t::epoch_t &epoch, - const raft_member_id_t &member_id, - raft_storage_interface_t *raft_storage, - const raft_start_election_immediately_t start_election_immediately, - multistore_ptr_t *multistore_ptr, - perfmon_collection_t *perfmon_collection_namespace) : - parent(_parent), - table(_table), - table_id(_table_id), - manager(parent->server_id, parent->mailbox_manager, parent->server_config_client, - parent->table_manager_directory, &parent->backfill_throttler, - parent->connections_map, *parent->base_path, parent->io_backender, table_id, - epoch, member_id, raft_storage, start_election_immediately, multistore_ptr, - perfmon_collection_namespace), - table_manager_bcard_copier( - &parent->table_manager_bcards, table_id, manager.get_table_manager_bcard()), - table_query_bcard_source( - &parent->table_query_bcard_combiner, table_id, manager.get_table_query_bcards()), - raft_committed_subs(std::bind(&active_table_t::on_raft_commit, this)) -{ - guarantee(!parent->is_proxy_server, "proxy server shouldn't be hosting data"); - watchable_t::state_and_config_t>::freeze_t - freeze(manager.get_raft()->get_committed_state()); - raft_committed_subs.reset(manager.get_raft()->get_committed_state(), &freeze); - - update_basic_configs_entry(); -} - -void multi_table_manager_t::active_table_t::on_raft_commit() { - /* If the Raft transaction changed the table's name, database, or primary key, push - the changes out to the `table_meta_client_t`. */ - bool basic_config_changed = update_basic_configs_entry(); - - /* Maybe re-sync to other servers in the cluster in reaction to this change */ - parent->multi_table_manager_directory->read_all( - [&](const peer_id_t &peer, const multi_table_manager_bcard_t *multi_bcard) { - if (peer != parent->mailbox_manager->get_me()) { - /* We don't re-sync to every server all the time, for performance - reasons. Instead, we try to determine whether it's actually necessary to - sync or not, and if we're sure it's unnecessary we don't sync. */ - if (basic_config_changed || - should_sync_assuming_no_name_change(peer, multi_bcard->server_id)) { - parent->schedule_sync(manager.table_id, table, peer); - } - } - }); -} - -bool multi_table_manager_t::active_table_t::update_basic_configs_entry() { - bool changed; - manager.get_raft()->get_committed_state()->apply_read( - [&](const raft_member_t::state_and_config_t *sc) { - std::pair p; - p.first = sc->state.config.config.basic; - p.second.epoch = manager.epoch; - p.second.log_index = sc->log_index; - if (table->basic_configs_entry.has()) { - table->basic_configs_entry->change( - [&](std::pair *value - ) -> bool { - changed = !(value->first == p.first); - *value = p; - return true; - }); - } else { - table->basic_configs_entry.create( - &parent->table_basic_configs, table_id, p); - changed = true; - } - }); - return changed; -} - -bool multi_table_manager_t::active_table_t::should_sync_assuming_no_name_change( - const peer_id_t &other_peer_id, const server_id_t &other_server_id) { - guarantee(other_peer_id != parent->mailbox_manager->get_me()); - bool should_sync = false; - manager.get_raft()->get_committed_state()->apply_read( - [&](const raft_member_t::state_and_config_t *sc) { - parent->table_manager_directory->read_key( - std::make_pair(other_peer_id, table_id), - [&](const table_manager_bcard_t *table_bcard) { - if (table_bcard == nullptr) { - /* Sync if the other server is supposed to be involved in - this table. */ - should_sync = (sc->state.member_ids.count(other_server_id) == 1); - } else { - /* Sync if: - - The other server shouldn't be involved in this table - - The other server has the wrong raft member ID - - The other server has an epoch that is too old */ - should_sync = - sc->state.member_ids.count(other_server_id) == 0 || - sc->state.member_ids.at(other_server_id) != - table_bcard->raft_member_id || - manager.epoch.supersedes(table_bcard->timestamp.epoch); - } - }); - }); - return should_sync; -} - -void multi_table_manager_t::help_construct() { - /* Whenever a server connects, we need to sync all of our tables to it. - Additionally: sync all of our existing tables with all servers that are - already connected. */ - multi_table_manager_directory_subs.init( - new watchable_map_t::all_subs_t( - multi_table_manager_directory, - [this](const peer_id_t &peer, const multi_table_manager_bcard_t *bcard) { - if (peer != mailbox_manager->get_me() && bcard != nullptr) { - mutex_assertion_t::acq_t mutex_acq(&mutex); - for (const auto &pair : tables) { - schedule_sync(pair.first, pair.second.get(), peer); - } - } - }, initial_call_t::YES)); - - /* Whenever a server changes its entry for a table in the directory, we need to - re-sync that table to that server. */ - table_manager_directory_subs.init( - new watchable_map_t, table_manager_bcard_t> - ::all_subs_t( - table_manager_directory, - [this](const std::pair &key, - const table_manager_bcard_t *) { - if (key.first != mailbox_manager->get_me()) { - mutex_assertion_t::acq_t mutex_acq(&mutex); - auto it = tables.find(key.second); - if (it != tables.end()) { - schedule_sync(key.second, it->second.get(), key.first); - } - } - }, initial_call_t::NO)); - - action_mailbox.init(new multi_table_manager_bcard_t::action_mailbox_t( - mailbox_manager, - std::bind(&multi_table_manager_t::on_action, this, ph::_1, ph::_2))); - - get_status_mailbox.init(new multi_table_manager_bcard_t::get_status_mailbox_t( - mailbox_manager, - std::bind(&multi_table_manager_t::on_get_status, this, ph::_1, ph::_2))); -} - -void multi_table_manager_t::on_action( - signal_t *interruptor, - const multi_table_manager_bcard_t::action_message_t &msg) { - typedef multi_table_manager_bcard_t::status_t action_status_t; - - const namespace_id_t &table_id = msg.table_id; - const multi_table_manager_timestamp_t ×tamp = msg.timestamp; - multi_table_manager_bcard_t::status_t action_status = msg.status; - const optional &basic_config = msg.basic_config; - const optional &raft_member_id = msg.raft_member_id; - const optional > - &initial_raft_state = msg.initial_raft_state; - const optional - &start_election_immediately = msg.start_election_immediately; - const mailbox_t<>::address_t &ack_addr = msg.ack_addr; - - /* Validate the incoming message */ - guarantee(static_cast(basic_config) == - (action_status == action_status_t::INACTIVE || - action_status == action_status_t::MAYBE_ACTIVE)); - guarantee(static_cast(raft_member_id) == - (action_status == action_status_t::ACTIVE)); - guarantee(static_cast(initial_raft_state) == - (action_status == action_status_t::ACTIVE)); - guarantee(timestamp.is_deletion() == - (action_status == action_status_t::DELETED)); - guarantee(action_status != action_status_t::ACTIVE || - static_cast(start_election_immediately)); - - /* Find or create the table record for this table */ - mutex_assertion_t::acq_t global_mutex_acq(&mutex); - bool is_new = (tables.count(msg.table_id) == 0); - table_t *table; - if (is_new) { - tables[table_id].init(table = new table_t); - } else { - table = tables.at(table_id).get(); - } - rwlock_in_line_t table_lock_in_line(&table->access_rwlock, access_t::write); - global_mutex_acq.reset(); - wait_interruptible(table_lock_in_line.write_signal(), interruptor); - - /* Validate existing record */ - if (!is_new) { - guarantee(table->status != table_t::status_t::ACTIVE || !is_proxy_server); - guarantee(table->multistore_ptr.has() == - (table->status == table_t::status_t::ACTIVE)); - guarantee(table->active.has() == - (table->status == table_t::status_t::ACTIVE)); - guarantee(table->basic_configs_entry.has() == - (table->status != table_t::status_t::DELETED)); - } - - /* Reject outdated or redundant messages */ - if (!is_new) { - multi_table_manager_timestamp_t current_timestamp; - switch (table->status) { - case table_t::status_t::ACTIVE: - current_timestamp.epoch = table->active->manager.epoch; - current_timestamp.log_index = - table->active->get_raft()->get_committed_state()->get().log_index; - break; - case table_t::status_t::INACTIVE: - current_timestamp = table->basic_configs_entry->get_value().second; - break; - case table_t::status_t::DELETED: - current_timestamp = multi_table_manager_timestamp_t::deletion(); - break; - case table_t::status_t::SHUTTING_DOWN: /* fall through */ - default: unreachable(); - } - /* Rejecting old actions is absolutely critical for correctness. - ACTIVE actions contain the Raft member ID. If we accepted an INACTIVE and then - an ACTIVE action in the wrong order, we might swipe away our Raft state due to - the INACTIVE action, and then become active again *with an old Raft ID* that - we had been active with before. This will violate Raft invariants, and can lead - to split-brain configurations and data loss. */ - if (!timestamp.supersedes(current_timestamp)) { - /* If we are inactive and are told to become active, we print a special - message. The `base_table_config_t` in our inactive state might be - a left-over from an emergency repair that none of the currently active - servers has seen. In that case we would have no chance to become active - again for this table until another emergency repair happened - (which might be impossible, if the table is otherwise still available). */ - bool outdated_activation = - table->status == table_t::status_t::INACTIVE && - action_status == action_status_t::ACTIVE && - timestamp.epoch != current_timestamp.epoch; - if (outdated_activation) { - logWRN("Table %s (%s): Not adding a replica on this server because the " - "active configuration conflicts with a more recent inactive " - "configuration. " - "If you see replicas get stuck in the `transitioning` state " - "after reconfiguring this table, you can try recovering by " - "running `.reconfigure({emergencyRepair: '_debug_recommit'})` on " - "it. Please make sure that the cluster is idle when running this " - "operation. RethinkDB does not guarantee consistency during " - "the emergency repair.", - uuid_to_str(table_id).c_str(), - table->basic_configs_entry->get_value().first.name.c_str()); - } - if (!ack_addr.is_nil()) { - send(mailbox_manager, ack_addr); - } - return; - } - } - - guarantee(is_new || table->status != table_t::status_t::DELETED, - "It shouldn't be possible to undelete a table."); - - bool should_resync = false; - - /* Bring record up to date */ - if (action_status == action_status_t::ACTIVE) { - guarantee(!is_proxy_server, "proxy server shouldn't be hosting data"); - - perfmon_collection_repo_t::collections_t *perfmon_collections = - perfmon_collection_repo->get_perfmon_collections_for_namespace(table_id); - - if (is_new || table->status != table_t::status_t::ACTIVE) { - /* The table is being created, or we are joining it */ - table->status = table_t::status_t::ACTIVE; - - /* Write a record to disk for the new table */ - raft_storage_interface_t *raft_storage; - persistence_interface->write_metadata_active( - table_id, - table_active_persistent_state_t { timestamp.epoch, *raft_member_id }, - *initial_raft_state, - &raft_storage); - - /* Open files for the new table. We do this after writing the record, because - this way if we crash we won't leak the file. */ - /* Don't interrupt here. We've already set the `table->status` to ACTIVE, and - it not having a `multistore_ptr` with that status would be illegal. */ - cond_t non_interruptor; - persistence_interface->create_multistore( - table_id, - &table->multistore_ptr, - &non_interruptor, - &perfmon_collections->serializers_collection); - - /* Create the `active_table_t`, which contains the `raft_member_t` and does - all of the important work of actually handing queries */ - table->active = make_scoped( - this, table, table_id, timestamp.epoch, *raft_member_id, - raft_storage, *start_election_immediately, table->multistore_ptr.get(), - &perfmon_collections->namespace_collection); - - logINF("Table %s: Added replica on this server.", - uuid_to_str(table_id).c_str()); - - } else if (table->active->manager.epoch != timestamp.epoch || - table->active->manager.raft_member_id != *raft_member_id) { - /* The table was in an active state before, and it should still be in an - active state; but the epoch or member ID changed. So we have to destroy and - then re-create `table->active`. */ - - table->active.reset(); - - /* Update the record on disk */ - raft_storage_interface_t *raft_storage; - persistence_interface->write_metadata_active( - table_id, - table_active_persistent_state_t { timestamp.epoch, *raft_member_id }, - *initial_raft_state, - &raft_storage); - - table->active = make_scoped( - this, table, table_id, timestamp.epoch, *raft_member_id, - raft_storage, *start_election_immediately, table->multistore_ptr.get(), - &perfmon_collections->namespace_collection); - - logINF("Table %s: Reset replica on this server.", - uuid_to_str(table_id).c_str()); - } - - should_resync = true; - - } else if (action_status == action_status_t::INACTIVE || - (action_status == action_status_t::MAYBE_ACTIVE && - (is_new || table->status != table_t::status_t::ACTIVE))) { - if (!is_new && table->status == table_t::status_t::ACTIVE) { - /* We are being demoted; we used to be hosting this table, but no longer are. - But we still keep a record of the table's name, etc. just like every other - server that's not hosting the table. */ - - table->active.reset(); - - /* Destroy files for the table before we update the metadata record, so that - if we crash we won't leak the file. */ - persistence_interface->destroy_multistore( - table_id, &table->multistore_ptr); - - logINF("Table %s: Removed replica on this server.", - uuid_to_str(table_id).c_str()); - } - - /* We just found out about this table, or we are updating an existing - second-hand information record with newer information */ - table->status = table_t::status_t::INACTIVE; - - if (table->basic_configs_entry.has()) { - table->basic_configs_entry->set(std::make_pair(*basic_config, timestamp)); - } else { - table->basic_configs_entry.create(&table_basic_configs, table_id, - std::make_pair(*basic_config, timestamp)); - } - - /* Update the record on disk */ - if (!is_proxy_server) { - persistence_interface->write_metadata_inactive( - table_id, - table_inactive_persistent_state_t { *basic_config, timestamp }); - } - - } else if (action_status == action_status_t::DELETED) { - if (!is_new) { - /* Clean up the table's current records */ - if (table->status == table_t::status_t::ACTIVE) { - table->active.reset(); - - /* Destroy files for the table before we update the metadata record, so - that if we crash we won't leak the file. */ - persistence_interface->destroy_multistore( - table_id, &table->multistore_ptr); - - logINF("Table %s: Deleted the table.", uuid_to_str(table_id).c_str()); - } - table->basic_configs_entry.reset(); - } - - table->status = table_t::status_t::DELETED; - - /* Remove the record on disk */ - if (!is_proxy_server) { - persistence_interface->delete_metadata(table_id); - } - - should_resync = true; - } - - if (!ack_addr.is_nil()) { - send(mailbox_manager, ack_addr); - } - - /* If we just became active for the table or we just found out that the table was - deleted, resync to every other server. This serves two purposes: - - When a table is first created, the creating server sends the creation action to the - active servers, which then send the table's name and other information to all the - other servers via this resync. - - Resyncing makes us more resilient to non-transitive connectivity. - However, it might become a performance problem at some point. */ - if (should_resync) { - multi_table_manager_directory->read_all( - [&](const peer_id_t &peer, const multi_table_manager_bcard_t *) { - if (peer != mailbox_manager->get_me()) { - schedule_sync(table_id, table, peer); - } - }); - } -} - -void multi_table_manager_t::on_get_status( - signal_t *interruptor, - const multi_table_manager_bcard_t::get_status_message_t &msg) { - - const std::set &tables_of_interest = msg.table_ids; - const table_status_request_t &request = msg.request; - const mailbox_addr_t> &reply_addr - = msg.reply_addr; - - std::map responses; - for (const namespace_id_t &table_id : tables_of_interest) { - /* Fetch information for a specific table. */ - mutex_assertion_t::acq_t global_mutex_acq(&mutex); - auto it = tables.find(table_id); - if (it != tables.end()) { - rwlock_in_line_t table_lock_in_line(&it->second->access_rwlock, access_t::read); - global_mutex_acq.reset(); - wait_interruptible(table_lock_in_line.read_signal(), interruptor); - if (it->second->status == table_t::status_t::ACTIVE) { - it->second->active->manager.get_status( - request, interruptor, &responses[table_id]); - } - } - } - send(mailbox_manager, reply_addr, responses); -} - -void multi_table_manager_t::do_sync( - const namespace_id_t &table_id, - const table_t &table, - const server_id_t &other_server_id, - const optional &table_bcard, - const multi_table_manager_bcard_t &table_manager_bcard) { - typedef multi_table_manager_bcard_t::status_t action_status_t; - - if (table.status == table_t::status_t::ACTIVE) { - multi_table_manager_timestamp_t timestamp; - timestamp.epoch = table.active->manager.epoch; - action_status_t action_status; - optional basic_config; - optional raft_member_id; - optional > - initial_raft_state; - { - cond_t non_interruptor; - raft_member_t::change_lock_t raft_change_lock( - table.active->get_raft(), &non_interruptor); - table.active->get_raft()->get_committed_state()->apply_read( - [&](const raft_member_t::state_and_config_t *st) { - timestamp.log_index = st->log_index; - auto it = st->state.member_ids.find(other_server_id); - if (it != st->state.member_ids.end()) { - action_status = action_status_t::ACTIVE; - raft_member_id = make_optional(it->second); - initial_raft_state = make_optional( - table.active->get_raft()->get_state_for_init( - raft_change_lock)); - } else { - action_status = action_status_t::INACTIVE; - basic_config = - make_optional(st->state.config.config.basic); - } - }); - } - - if (table_bcard.has_value()) { - /* If the peer already has an entry in the directory, we can use that to - avoid sending unnecessary updates to save network traffic. */ - if (action_status == action_status_t::ACTIVE - && !timestamp.epoch.supersedes(table_bcard->timestamp.epoch) - && *raft_member_id == table_bcard->raft_member_id) { - return; - } - } - - send(mailbox_manager, table_manager_bcard.action_mailbox, - {table_id, - timestamp, - action_status, - basic_config, - raft_member_id, - initial_raft_state, - optional(raft_start_election_immediately_t::NO), - mailbox_t<>::address_t()}); - - } else if (table.status == table_t::status_t::INACTIVE) { - if (table_bcard.has_value()) { - /* No point in sending a `MAYBE_ACTIVE` message to a server that's actually - hosting the table already (it would be a noop) */ - return; - } - - send(mailbox_manager, table_manager_bcard.action_mailbox, - {table_id, - table.basic_configs_entry->get_value().second, - action_status_t::MAYBE_ACTIVE, - optional( - table.basic_configs_entry->get_value().first), - optional(), - optional >(), - optional(), - mailbox_t<>::address_t()}); - - } else if (table.status == table_t::status_t::DELETED) { - send(mailbox_manager, table_manager_bcard.action_mailbox, - {table_id, - multi_table_manager_timestamp_t::deletion(), - action_status_t::DELETED, - optional(), - optional(), - optional >(), - optional(), - mailbox_t<>::address_t()}); - - } else { - unreachable(); - } -} - -void multi_table_manager_t::schedule_sync( - const namespace_id_t &table_id, - table_t *table, - const peer_id_t &peer_id) { - ASSERT_FINITE_CORO_WAITING; - table->to_sync_set.insert(peer_id); - if (table->sync_coro_running) { - /* The sync coro will pick up our change when it finishes the current batch */ - return; - } - guarantee(table->to_sync_set.size() == 1, "If there were other changes queued up, " - "the sync coro should have already been running"); - table->sync_coro_running = true; - auto_drainer_t::lock_t keepalive(&drainer); - /* Spawn the sync coroutine. Note that we have at most one instance of this coroutine - per table running at any given time. The naive approach would be to spawn one - instance every time that `schedule_sync()` is called. The problem is that - `schedule_sync()` may be called redundantly many times; this approach means that a - lot of the redundant calls will be collapsed. */ - coro_t::spawn_sometime( - [this, keepalive /* important to capture */, table_id, table]() { - try { - mutex_assertion_t::acq_t global_mutex_acq(&mutex); - while (!table->to_sync_set.empty()) { - std::set to_sync_set; - std::swap(table->to_sync_set, to_sync_set); - rwlock_in_line_t table_lock_in_line(&table->access_rwlock, access_t::write); - global_mutex_acq.reset(); - wait_interruptible(table_lock_in_line.write_signal(), - keepalive.get_drain_signal()); - if (table->status == table_t::status_t::SHUTTING_DOWN) { - return; - } - for (const peer_id_t &peer : to_sync_set) { - guarantee(peer != mailbox_manager->get_me()); - /* Removing `peer` from `table->to_sync_set` isn't strictly - necessary, but it sometimes reduces redundant traffic */ - table->to_sync_set.erase(peer); - optional - multi_table_manager_bcard = - multi_table_manager_directory->get_key(peer); - if (!multi_table_manager_bcard.has_value()) { - /* Peer is not connected. */ - continue; - } - do_sync( - table_id, - *table, - multi_table_manager_bcard->server_id, - table_manager_directory->get_key(std::make_pair(peer, table_id)), - *multi_table_manager_bcard); - } - global_mutex_acq.reset(&mutex); - } - table->sync_coro_running = false; - } catch (const interrupted_exc_t &) { - /* We're shutting down. Don't continue syncing. `sync_coro_running` will be - left in the `true` state, but that doesn't matter. */ - } - }); -} - diff --git a/src/clustering/table_manager/multi_table_manager.hpp b/src/clustering/table_manager/multi_table_manager.hpp deleted file mode 100644 index 36707953a6a..00000000000 --- a/src/clustering/table_manager/multi_table_manager.hpp +++ /dev/null @@ -1,433 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_MANAGER_MULTI_TABLE_MANAGER_HPP_ -#define CLUSTERING_TABLE_MANAGER_MULTI_TABLE_MANAGER_HPP_ - -#include "clustering/administration/perfmon_collection_repo.hpp" -#include "clustering/immediate_consistency/standard_backfill_throttler.hpp" -#include "clustering/table_contract/cpu_sharding.hpp" -#include "clustering/table_manager/table_manager.hpp" -#include "concurrency/rwlock.hpp" -#include "containers/optional.hpp" - -/* There is one `multi_table_manager_t` on each server. For tables hosted on this server, -it handles administrative operations: table creation and deletion, adding and removing -this server from the table, and changing the table configuration. It's also responsible -for constructing the `contract_coordinator_t` and `contract_executor_t`, which carry out -the actual business of handling queries. - -The `multi_table_manager_t` exposes an "action mailbox", a "get config mailbox", and a -"set config mailbox" via the directory. See `table_metadata.hpp` for the type signatures -of these mailboxes. The `table_meta_client_t` is responsible for translating client -actions into messages to the `multi_table_manager_t`'s mailboxes. However, the -`multi_table_manager_t` will also send messages directly to other -`multi_table_manager_t`s' mailboxes sometimes. - -Here are some examples of scenarios involving the `multi_table_manager_t`: - -* When the user creates a table, the `table_meta_client_t` generates a UUID for the new -table; an initial epoch ID; an initial configuration for the table; and a -`raft_member_id_t` for each server in the table's configuration. Then it sends a message -to each server's action mailbox with `table_id` set to the new table's UUID; `timestamp` -set to the newly-generated epoch; `status` set to `ACTIVE`; `basic_config` is empty; -`raft_member_id` set to the new `raft_member_id_t` for that server; and -`initial_raft_state` set to the newly-generated table configuration. - -* When the user drops a table, the `table_meta_client_t` sends a message to the action -mailbox of every visible member of the table's Raft cluster. `table_id` is the table -being deleted; `timestamp` is set to the far future; `status` is `DELETED`; and -`basic_config`, `member_id`, and `initial_state` are empty. - -* When the user changes the table's configuration without adding or removing any servers -(e.g. changing its name), the `table_meta_client_t` will send a message to the set-config -mailbox of the Raft cluster leader. The Raft cluster leader will then initiate a Raft -transaction to change the table's name. - -* When the user adds a server to the table's configuration, the `table_meta_client_t` -will send a message to the set-config mailbox of the Raft cluster leader to update the -table's configuration. Next, the `contract_coordinator_t` will initiate Raft transactions -to add the new server to the Raft configuration and generate a `raft_member_id_t` for it. -After the transaction completes, one of the members in the Raft cluster will see that the -new server is present in the Raft configuration, but not acting as a cluster member. So -that member will send a message to the new server's action mailbox with the new server's -`raft_member_id_t`, the current epoch ID, and the new timestamp. - -* When the user removes a server from the table's configuration, the same process -happens; after the Raft cluster commits the removal transaction, one of the remaining -members sends a message to its action mailbox with `status` set to `INACTIVE`, indicating -that it's no longer a member of the cluster. - -* In the table-creation scenario, suppose that two of the three servers for the table -dropped offline as the table was being created, so they didn't get the messages. When -they come back online, the server that did get the message will forward the message to -the remaining servers' action mailboxes, so the table will finish being created. - -* Similarly, if a server sees that a table was dropped, but it sees another server acting -as though the table still exists, it will forward the drop message to that server's -action mailbox. */ - -class multi_table_manager_t : public home_thread_mixin_t { -public: - multi_table_manager_t( - const server_id_t &_server_id, - mailbox_manager_t *_mailbox_manager, - server_config_client_t *server_config_client, - watchable_map_t - *_multi_table_manager_directory, - watchable_map_t, table_manager_bcard_t> - *_table_manager_directory, - watchable_map_t, empty_value_t> - *_connections_map, - table_persistence_interface_t *_persistence_interface, - const base_path_t &_base_path, - io_backender_t *_io_backender, - perfmon_collection_repo_t *_perfmon_collection_repo); - - /* This constructor is used on proxy servers. */ - multi_table_manager_t( - mailbox_manager_t *_mailbox_manager, - watchable_map_t - *_multi_table_manager_directory, - watchable_map_t, table_manager_bcard_t> - *_table_manager_directory); - - ~multi_table_manager_t(); - - multi_table_manager_bcard_t get_multi_table_manager_bcard() { - multi_table_manager_bcard_t bcard; - bcard.action_mailbox = action_mailbox->get_address(); - bcard.get_status_mailbox = get_status_mailbox->get_address(); - bcard.server_id = server_id; - return bcard; - } - - watchable_map_t * - get_table_manager_bcards() { - return &table_manager_bcards; - } - - watchable_map_t, table_query_bcard_t> * - get_table_query_bcards() { - return &table_query_bcard_combiner; - } - - watchable_map_t > - *get_table_basic_configs() { - return &table_basic_configs; - } - - /* Calls `callable` for each active table, it must have a signature of: - void(const namespace_id_t &table_id, multistore_ptr_t *, table_manager_t *) - `access` must be `access_t::write` if you change the state or configuration of - the table in the callback (but not for running queries). Otherwise - `access_t::read` is sufficient. - */ - template - void visit_tables(signal_t *interruptor, access_t access, const F &callable) { - /* Fetch information for all tables that we know about. First we get in line for - each rwlock, then we release the global mutex assertion, then we wait for each - rwlock acquisition to be ready and copy out its data. */ - mutex_assertion_t::acq_t global_mutex_acq(&mutex); - std::map > - table_lock_in_lines; - for (const auto &pair : tables) { - table_lock_in_lines[pair.first] = - make_scoped(&pair.second->access_rwlock, access); - } - global_mutex_acq.reset(); - for (const auto &pair : table_lock_in_lines) { - switch (access) { - case access_t::read: - wait_interruptible(pair.second->read_signal(), interruptor); - break; - case access_t::write: - wait_interruptible(pair.second->write_signal(), interruptor); - break; - default: - unreachable(); - } - auto it = tables.find(pair.first); - guarantee(it != tables.end()); - if (it->second->status == table_t::status_t::ACTIVE) { - callable(pair.first, - it->second->multistore_ptr.get(), - &(it->second->active->manager)); - } - } - } - - /* Calls `callable` on the specific table, if it's available. `callable`'s signature - must be: - void(multistore_ptr_t *, table_manager_t *) - If the table doesn't exist or is not active on this server, calls `callable` with - null pointers. - `access` must be `access_t::write` if you change the state or configuration of - the table in the callback (but not for running queries). Otherwise - `access_t::read` is sufficient.*/ - template - void visit_table( - const namespace_id_t &table_id, signal_t *interruptor, access_t access, - const F &callable) { - mutex_assertion_t::acq_t global_mutex_acq(&mutex); - auto it = tables.find(table_id); - if (it == tables.end()) { - callable(nullptr, nullptr); - return; - } - table_t *table = it->second.get(); - rwlock_in_line_t lock_in_line(&table->access_rwlock, access); - global_mutex_acq.reset(); - switch (access) { - case access_t::read: - wait_interruptible(lock_in_line.read_signal(), interruptor); - break; - case access_t::write: - wait_interruptible(lock_in_line.write_signal(), interruptor); - break; - default: - unreachable(); - } - if (table->status == table_t::status_t::ACTIVE) { - callable(table->multistore_ptr.get(), &table->active->manager); - } else { - callable(nullptr, nullptr); - } - } - -private: - /* The `multi_table_manager_t` has four possible states with respect to a table: - - 1. We've never heard of the table. In this case, we don't have any records for the - table. - - 2. We've heard of the table, but we don't consider ourselves to be a replica for the - table. In this case, we store a `table_t` in our `tables` map with status - `INACTIVE`. We record the table's name and database in the metadata but we don't - have a data file for the table. - - 3. We are currently a replica for the table, In this case, we store a `table_t` in - our `tables` map with status `ACTIVE`. We store a record for the table in the - on-disk metadata; we also have a data file on disk for the table; and we have an - `active_table_t`, which takes care of actually storing the data for the table. We - also broadcast a business card for the table. - - 4. We believe the table has been deleted. In this case, we store a `table_t` in our - `tables` map with status `DELETED`. We don't record anything on disk for the - table. This means that if we restart, we'll forget that it ever existed and - return to the first state in this list. - */ - - class table_t; - - class active_table_t { - public: - active_table_t( - multi_table_manager_t *parent, - table_t *table, - const namespace_id_t &table_id, - const multi_table_manager_timestamp_t::epoch_t &epoch, - const raft_member_id_t &member_id, - raft_storage_interface_t *raft_storage, - const raft_start_election_immediately_t start_election_immediately, - multistore_ptr_t *multistore_ptr, - perfmon_collection_t *perfmon_collection_namespace); - - raft_member_t *get_raft() { - return manager.get_raft(); - } - - void on_raft_commit(); - - /* Updates `table->basic_configs_entry` to reflect any changes in the table's - basic config. Returns `true` if there was a change and `false` if it's the same. - */ - bool update_basic_configs_entry(); - - /* Checks if there is a discrepancy between the table bcard we're getting from - the other peer and our local knowledge about the table. The reason for the - `_assuming_no_name_change` part of the name is that if the table's name has - changed, we need to sync to every server, but this function will still return - `false`. */ - bool should_sync_assuming_no_name_change( - const peer_id_t &peer_id, const server_id_t &server_id); - - multi_table_manager_t *const parent; - table_t *const table; - namespace_id_t const table_id; - - /* The `table_manager_t` is the guts of the `active_table_t`. The - `active_table_t` is just a thin wrapper that handles forwarding the bcards from - the `table_manager_t` into the `watchable_map_t`s, and also sending updates to - other `multi_table_manager_t`s. */ - table_manager_t manager; - - /* The `watchable_map_entry_copier_t` copies the `table_manager_bcard_t` from - `manager.get_table_manager_bcard()` into the `table_manager_bcards` map. */ - watchable_map_entry_copier_t - table_manager_bcard_copier; - - /* The `table_query_bcard_source` receives `table_query_bcard_t`s from the - `table_manager_t` and sends them on to `table_query_bcard_combiner`. From there - they will be sent to other servers' `namespace_repo_t`s. */ - watchable_map_combiner_t::source_t - table_query_bcard_source; - - watchable_subscription_t::state_and_config_t> - raft_committed_subs; - }; - - /* `table_t`'s main job is to hold the `active_table_t`. If there is no - `active_table_t`, this means that we used to be a replica for this table but no - longer are; in this case, `table_t`'s job is to remember why we are no longer a - replica for this table. */ - class table_t { - public: - enum class status_t { ACTIVE, INACTIVE, DELETED, SHUTTING_DOWN }; - - table_t() : sync_coro_running(false) { } - - /* `to_sync_set` holds the server IDs and peer IDs of all the servers for which - we should call `do_sync()` with this table. `schedule_sync()` adds entries to - `to_sync_set` and sometimes also spawns a coroutine to call `do_sync()`; it uses - `sync_coro_running` to make sure it doesn't spawn redundant coroutines. */ - std::set to_sync_set; - bool sync_coro_running; - - /* You must hold this lock to access the other fields of the `table_t` except - for `to_sync_set` and `sync_coro_running` */ - rwlock_t access_rwlock; - - status_t status; - - /* If `status` is either `ACTIVE` or `INACTIVE`, `basic_configs_entry` propagates - knowledge about the table's basic config to the `table_meta_client_t` via - `table_basic_configs`. When `status` is `ACTIVE`, it's kept up-to-date by the - `active_table_t`. (Note that the `active_table_t` may change the value without - holding `mutex`.) When `status` is `INACTIVE`, it's updated when we receive - messages from other servers. */ - object_buffer_t - >::entry_t> - basic_configs_entry; - - /* If `status` is `ACTIVE`, `multistore_ptr` contains our files on disk for the - table, and `active` contains our Raft instance, etc. Otherwise these are empty. - */ - scoped_ptr_t multistore_ptr; - scoped_ptr_t active; - - /* We must destroy `active` before `multistore_ptr` because `active` uses the - multistore. We must destroy `active` before `basic_configs_entry` because - `active` sometimes manipulates `basic_configs_entry` through a `table_t *`. */ - }; - - /* `help_construct()` initializes `multi_table_manager_directory_subs`, - `table_manager_directory_subs`, `action_mailbox`, and `get_config_mailbox`. */ - void help_construct(); - - /* `on_action()` and `on_get_config()` are mailbox callbacks */ - - void on_action( - signal_t *interruptor, - const multi_table_manager_bcard_t::action_message_t &msg); - - void on_get_status( - signal_t *interruptor, - const multi_table_manager_bcard_t::get_status_message_t &msg); - - /* `do_sync()` checks if it is necessary to send an action message to the given - server regarding the given table, and sends one if so. It is called in the following - situations: - - Whenever another server connects, it is called for that server and every table - - Whenever a server changes its `table_manager_bcard_t` in the directory for a - table, it is called for that server and that table - - Whenever a Raft transaction is committed for a table that we are a member of, it - is called for every server and that table - This results in a lot of redundant calls. This is OK because every action message has - a timestamp, which makes them idempotent. */ - void do_sync( - const namespace_id_t &table_id, - /* `table` is our record for this table. Caller must own `table.mutex`. */ - const table_t &table, - const server_id_t &server_id, - /* This is the other server's directory entry for this table, or an empty - optional if there is no entry. */ - const optional &table_bcard, - /* This is the other server's global directory entry. If the other server is not - connected, don't call `do_sync()`. */ - const multi_table_manager_bcard_t &table_manager_bcard); - - /* `schedule_sync()` arranges for `do_sync()` to eventually be called with the given - table and server. The caller must hold `multi_table_manager_t::mutex`. - `schedule_sync()` does not block. */ - void schedule_sync( - const namespace_id_t &table_id, - table_t *table, - const peer_id_t &peer_id); - - /* If we're a proxy server, then `is_proxy_server` will be `true`; `server_id` will - be `nil_uuid()`; `persistence_interface` will be `nullptr`; `base_path` will be - empty; and `io_backender` will be `nullptr`. */ - - bool is_proxy_server; - server_id_t server_id; - mailbox_manager_t * const mailbox_manager; - server_config_client_t *server_config_client; - watchable_map_t - * const multi_table_manager_directory; - watchable_map_t, table_manager_bcard_t> - * const table_manager_directory; - watchable_map_t, empty_value_t> - * const connections_map; - table_persistence_interface_t *persistence_interface; - - optional base_path; - io_backender_t *io_backender; - - perfmon_collection_repo_t *perfmon_collection_repo; - - standard_backfill_throttler_t backfill_throttler; - - /* This collects the `table_basic_config_t` for every non-deleted table in the - `tables` map, for the benefit of the `table_meta_client_t`. */ - watchable_map_var_t > - table_basic_configs; - - /* This map describes the table business cards that we show to other servers via the - directory. `active_table_t` creates and deletes entries in this map. */ - watchable_map_var_t - table_manager_bcards; - - /* This collects `table_query_bcard_t`s from all of the tables on this server into - a single `watchable_map_t`. */ - watchable_map_combiner_t - table_query_bcard_combiner; - - /* Note: `tables` must be destroyed before `table_manager_bcards` or any of the - earlier member variables. */ - std::map > tables; - - /* You must hold this mutex whenever you: - - Access the `tables` map - - Access `table_t::dirty_set` or `table_t::send_actions_coro_running` - - Get in line for a `table_t::mutex`. (However, once you are in line for the - `table_t::mutex`, you can release this mutex; and once you own `table_t::mutex`, - you can freely access the fields of `table_t` that it protects.) - */ - mutex_assertion_t mutex; - - auto_drainer_t drainer; - - scoped_ptr_t::all_subs_t> - multi_table_manager_directory_subs; - scoped_ptr_t, table_manager_bcard_t>::all_subs_t> - table_manager_directory_subs; - - scoped_ptr_t action_mailbox; - scoped_ptr_t get_status_mailbox; -}; - -#endif /* CLUSTERING_TABLE_MANAGER_MULTI_TABLE_MANAGER_HPP_ */ - diff --git a/src/clustering/table_manager/server_name_cache_updater.cc b/src/clustering/table_manager/server_name_cache_updater.cc deleted file mode 100644 index 5ee4602fe41..00000000000 --- a/src/clustering/table_manager/server_name_cache_updater.cc +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. - -#include "clustering/table_manager/server_name_cache_updater.hpp" - -#include "clustering/generic/raft_core.tcc" -#include "clustering/table_contract/contract_metadata.hpp" - -server_name_cache_updater_t::server_name_cache_updater_t( - raft_member_t *_raft, - server_config_client_t *server_config_client) - : raft(_raft), - update_pumper( - std::bind(&server_name_cache_updater_t::update_blocking, this, ph::_1)), - server_config_map(server_config_client->get_server_config_map()), - server_config_map_subs( - server_config_map, - [&](const server_id_t &server_id, const server_config_versioned_t *) { - dirty_server_ids.insert(server_id); - update_pumper.notify(); - }, - initial_call_t::YES) { -} - -void calculate_new_server_names( - const server_name_map_t &old_server_names, - watchable_map_t *server_config_map, - std::set dirty_server_ids, - server_name_map_t *new_server_names_out) { - for (const auto &server_id : dirty_server_ids) { - server_config_map->read_key(server_id, - [&](const server_config_versioned_t *server_config) { - if (server_config == nullptr) { - /* The server config could not be read, we'll get a new notification - when it reconnects. */ - return; - } - - auto old_name_it = old_server_names.names.find(server_id); - if (old_name_it == old_server_names.names.end() || - old_name_it->second.first >= server_config->version || - old_name_it->second.second == server_config->config.name) { - /* Either the server is not involved in this table, the server name - that's already stored has a never version that the server configuration, - or the server name hasn't changed, thus we skip this server. */ - return; - } - - new_server_names_out->names.insert( - std::make_pair(server_id, - std::make_pair(server_config->version, server_config->config.name))); - }); - } - - return; -} - -void server_name_cache_updater_t::update_blocking(signal_t *interruptor) { - std::set local_dirty_server_ids; - std::swap(dirty_server_ids, local_dirty_server_ids); - if (local_dirty_server_ids.empty()) { - /* Nothing to do. */ - return; - } - - /* Now we'll apply changes to Raft. We keep trying in a loop in case it - doesn't work at first. */ - while (true) { - /* Wait until the Raft member is likely to accept changes */ - raft->get_readiness_for_change()->run_until_satisfied( - [](bool is_ready) { return is_ready; }, interruptor); - - raft_member_t::change_lock_t change_lock(raft, interruptor); - - /* Calculate the proposed change */ - table_raft_state_t::change_t::new_server_names_t change; - raft->get_latest_state()->apply_read( - [&](const raft_member_t::state_and_config_t *state) { - calculate_new_server_names( - state->state.config.server_names, - server_config_map, - local_dirty_server_ids, - &change.config_and_shards); - calculate_new_server_names( - state->state.server_names, - server_config_map, - local_dirty_server_ids, - &change.raft_state); - }); - - /* Apply the change, unless it's a no-op */ - bool change_ok; - if (!change.config_and_shards.names.empty() || - !change.raft_state.names.empty()) { - scoped_ptr_t::change_token_t> - change_token = raft->propose_change( - &change_lock, table_raft_state_t::change_t(change)); - - change_ok = change_token.has(); - - /* Note that we don't wait on the change token. We know we won't - start a redundant change because we always compute the change by - comparing to `get_latest_state()`. */ - } else { - change_ok = true; - } - - /* If the change failed, go back to the top of the loop and wait on - `get_readiness_for_change()` again. */ - if (change_ok) { - break; - } - } -} diff --git a/src/clustering/table_manager/server_name_cache_updater.hpp b/src/clustering/table_manager/server_name_cache_updater.hpp deleted file mode 100644 index 26ba9e8d43f..00000000000 --- a/src/clustering/table_manager/server_name_cache_updater.hpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_MANAGER_SERVER_NAME_CACHE_UPDATER_HPP_ -#define CLUSTERING_TABLE_MANAGER_SERVER_NAME_CACHE_UPDATER_HPP_ - -#include "clustering/administration/servers/config_client.hpp" -#include "concurrency/pump_coro.hpp" - -template class raft_member_t; -class table_raft_state_t; - -class server_name_cache_updater_t { -public: - server_name_cache_updater_t( - raft_member_t *raft, - server_config_client_t *server_config_client); - -private: - void on_config_change( - const server_id_t &server_id, const server_config_versioned_t *config); - - void update_blocking(signal_t *interruptor); - - raft_member_t *raft; - - std::set dirty_server_ids; - pump_coro_t update_pumper; - - watchable_map_t *server_config_map; - watchable_map_t::all_subs_t - server_config_map_subs; -}; - -#endif /* CLUSTERING_TABLE_MANAGER_SERVER_NAME_CACHE_UPDATER_HPP_ */ - diff --git a/src/clustering/table_manager/sindex_manager.cc b/src/clustering/table_manager/sindex_manager.cc deleted file mode 100644 index 089aa024337..00000000000 --- a/src/clustering/table_manager/sindex_manager.cc +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved -#include "clustering/table_manager/sindex_manager.hpp" - -#include "clustering/administration/issues/outdated_index.hpp" - -#include "concurrency/cross_thread_signal.hpp" -#include "concurrency/pmap.hpp" -#include "rdb_protocol/store.hpp" - -sindex_manager_t::sindex_manager_t( - multistore_ptr_t *multistore_, - const clone_ptr_t > &table_config_) : - multistore(multistore_), table_config(table_config_), - update_pumper([this](signal_t *interruptor) { update_blocking(interruptor); }), - table_config_subs([this]() { update_pumper.notify(); }) -{ - watchable_t::freeze_t freeze(table_config); - table_config_subs.reset(table_config, &freeze); - update_pumper.notify(); -} - -std::map > -sindex_manager_t::get_status(signal_t *interruptor) const { - /* First, we make a list of all the sindexes in the config. Then, we iterate over - the actual sindexes in the stores and try to match them to the sindexes in the - config. If we find a match, we accumulate the `sindex_status_t`s. If there's a sindex - in the config with no matching actual sindex, we set `ready = false`. */ - - std::map > res; - table_config->apply_read([&](const table_config_t *config) { - for (const auto &pair : config->sindexes) { - auto it = res.insert( - std::make_pair(pair.first, std::make_pair(pair.second, - sindex_status_t()))).first; - it->second.second.outdated = - (pair.second.func_version != reql_version_t::LATEST) - && !outdated_index_issue_tracker_t::is_acceptable_outdated(pair.second); - } - }); - - pmap(static_cast(0), static_cast(CPU_SHARDING_FACTOR), - [&](int64_t i) { - std::map > store_state; - store_t *store = multistore->get_underlying_store(i); - cross_thread_signal_t ct_interruptor(interruptor, store->home_thread()); - { - on_thread_t thread_switcher(store->home_thread()); - store_state = store->sindex_list(&ct_interruptor); - } - - for (auto &&pair : res) { - auto it = store_state.find(pair.first); - /* Note that we treat an index with the wrong definition like a missing - index. */ - if (it != store_state.end() && it->second.first == pair.second.first) { - pair.second.second.accum(it->second.second); - } else { - pair.second.second.ready = false; - } - } - }); - - return res; -} - -void sindex_manager_t::update_blocking(signal_t *interruptor) { - std::map goal; - table_config->apply_read([&](const table_config_t *config) { - goal = config->sindexes; - }); - - for (size_t i = 0; i < CPU_SHARDING_FACTOR; ++i) { - store_t *store = multistore->get_underlying_store(i); - cross_thread_signal_t ct_interruptor(interruptor, store->home_thread()); - on_thread_t thread_switcher(store->home_thread()); - - std::map current; - for (const auto &pair : store->sindex_list(&ct_interruptor)) { - current.insert(std::make_pair(pair.first, pair.second.first)); - } - - /* First we'll compute what changes to make to sindexes; then we'll make the - changes. The reason to do this in two steps is that we compute the changes in a - different order than we want to perform them. */ - std::set to_drop; - std::map to_rename; - std::map to_create; - - /* If an existing index is missing or has a different name in `goal`, then add it - to `to_drop`. We might remove it later, if we realize it was actually renamed - instead of dropped. */ - for (const auto &cur_pair : current) { - auto goal_it = goal.find(cur_pair.first); - if (goal_it == goal.end() || goal_it->second != cur_pair.second) { - to_drop.insert(cur_pair.first); - } - } - - /* Go through the indexes in `goal`, and add entries to `to_create`, Or, if there - is an existing index with that definition that would otherwise be dropped, then - rename the existing index instead of creating a new one. */ - for (const auto &goal_pair : goal) { - auto cur_it = current.find(goal_pair.first); - if (cur_it != current.end() && cur_it->second == goal_pair.second) { - /* OK, we already have this sindex */ - continue; - } - /* See if there's a sindex in `to_drop` with the desired definition. If so, - we'll rename it instead of dropping and recreating. */ - bool renamed = false; - for (const std::string &other : to_drop) { - if (current.at(other) == goal_pair.second) { - /* OK, we'll rename the index instead of dropping it */ - to_rename.insert(std::make_pair(other, goal_pair.first)); - to_drop.erase(other); - renamed = true; - break; - } - } - /* If we weren't able to find a sindex to rename, we'll have to create a new - one */ - if (!renamed) { - to_create.insert(goal_pair); - } - } - - /* OK, time to actually execute the changes */ - for (const std::string &index : to_drop) { - store->sindex_drop(index, &ct_interruptor); - } - store->sindex_rename_multi(to_rename, &ct_interruptor); - for (const auto &pair : to_create) { - /* Note that this crashes if the index already exists. Luckily we are running - in a `pump_coro_t`, and we should be the only thing that's creating indexes - on the store so this shouldn't be an issue. */ - store->sindex_create(pair.first, pair.second, &ct_interruptor); - } - } -} - diff --git a/src/clustering/table_manager/sindex_manager.hpp b/src/clustering/table_manager/sindex_manager.hpp deleted file mode 100644 index 3ea575932a3..00000000000 --- a/src/clustering/table_manager/sindex_manager.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_MANAGER_SINDEX_MANAGER_HPP_ -#define CLUSTERING_TABLE_MANAGER_SINDEX_MANAGER_HPP_ - -#include "clustering/table_contract/cpu_sharding.hpp" -#include "clustering/administration/tables/table_metadata.hpp" -#include "concurrency/pump_coro.hpp" -#include "concurrency/watchable.hpp" - -sindex_status_t empty_sindex_status(); -void combine_sindex_status(sindex_status_t *accum, const sindex_status_t &status); - -/* The `sindex_manager_t` is responsible for reading the sindex description from the -`table_config_t` and adding, dropping, and renaming sindexes on the `store_t` to match -the description. */ - -class sindex_manager_t { -public: - sindex_manager_t( - multistore_ptr_t *multistore, - const clone_ptr_t > &table_config); - - std::map > - get_status(signal_t *interruptor) const; - -private: - void update_blocking(signal_t *interruptor); - - multistore_ptr_t *const multistore; - clone_ptr_t > const table_config; - - /* Destructor order matters: The `table_config_subs` must be destroyed before the - `update_pumper` because it calls `update_pumper.notify()`. But `update_pumper` must - be destroyed before the other variables because it runs `update_blocking()`, which - accesses the other variables. */ - pump_coro_t update_pumper; - - watchable_t::subscription_t table_config_subs; -}; - -#endif /* CLUSTERING_TABLE_MANAGER_SINDEX_MANAGER_HPP_ */ - diff --git a/src/clustering/table_manager/table_manager.cc b/src/clustering/table_manager/table_manager.cc deleted file mode 100644 index 61a88a6538c..00000000000 --- a/src/clustering/table_manager/table_manager.cc +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_manager/table_manager.hpp" - -#include "clustering/generic/minidir.tcc" -#include "clustering/generic/raft_core.tcc" -#include "clustering/generic/raft_network.tcc" - -table_manager_t::table_manager_t( - const server_id_t &_server_id, - mailbox_manager_t *_mailbox_manager, - server_config_client_t *_server_config_client, - watchable_map_t, table_manager_bcard_t> - *_table_manager_directory, - backfill_throttler_t *_backfill_throttler, - watchable_map_t, empty_value_t> - *_connections_map, - const base_path_t &_base_path, - io_backender_t *_io_backender, - const namespace_id_t &_table_id, - const multi_table_manager_timestamp_t::epoch_t &_epoch, - const raft_member_id_t &_raft_member_id, - raft_storage_interface_t *raft_storage, - const raft_start_election_immediately_t start_election_immediately, - multistore_ptr_t *multistore_ptr, - perfmon_collection_t *perfmon_collection_namespace) : - table_id(_table_id), - epoch(_epoch), - raft_member_id(_raft_member_id), - mailbox_manager(_mailbox_manager), - server_config_client(_server_config_client), - connections_map(_connections_map), - perfmon_membership(perfmon_collection_namespace, &perfmon_collection, "regions"), - raft(raft_member_id, _mailbox_manager, raft_directory.get_values(), raft_storage, - "Table " + uuid_to_str(table_id), start_election_immediately), - table_manager_bcard(table_manager_bcard_t()), /* we'll set this later */ - raft_bcard_copier(&table_manager_bcard_t::raft_business_card, - raft.get_business_card(), &table_manager_bcard), - execution_bcard_read_manager(_mailbox_manager), - contract_executor( - _server_id, mailbox_manager, - raft.get_raft()->get_committed_state()->subview( - [](const raft_member_t::state_and_config_t &sc) - -> table_raft_state_t { - return sc.state; - }), - execution_bcard_read_manager.get_values(), multistore_ptr, _base_path, - _io_backender, _backfill_throttler, &backfill_progress_tracker, - &perfmon_collection), - execution_bcard_write_manager( - mailbox_manager, - contract_executor.get_local_contract_execution_bcards(), - execution_bcard_minidir_directory.get_values()), - contract_ack_write_manager( - mailbox_manager, - contract_executor.get_acks(), - contract_ack_minidir_directory.get_values()), - sindex_manager( - multistore_ptr, - raft.get_raft()->get_committed_state()->subview( - [](const raft_member_t::state_and_config_t &sc) - -> table_config_t { - return sc.state.config.config; - })), - table_directory_subs( - _table_manager_directory, - std::bind(&table_manager_t::on_table_directory_change, this, ph::_1, ph::_2), - initial_call_t::YES), - raft_readiness_subs(std::bind(&table_manager_t::on_raft_readiness_change, this)) -{ - guarantee(!raft_member_id.is_nil()); - guarantee(!epoch.is_unset()); - - /* Set up the initial table bcard */ - { - table_manager_bcard_t bcard; - bcard.timestamp.epoch = epoch; - raft.get_raft()->get_committed_state()->apply_read( - [&](const raft_member_t::state_and_config_t *sc) { - bcard.timestamp.log_index = sc->log_index; - }); - bcard.raft_member_id = raft_member_id; - bcard.raft_business_card = raft.get_business_card()->get(); - bcard.execution_bcard_minidir_bcard = execution_bcard_read_manager.get_bcard(); - bcard.server_id = _server_id; - table_manager_bcard.set_value_no_equals(bcard); - } - - { - watchable_t::freeze_t freeze(raft.get_raft()->get_readiness_for_change()); - raft_readiness_subs.reset(raft.get_raft()->get_readiness_for_change(), &freeze); - } -} - -table_manager_t::~table_manager_t() { - /* This is defined in the `.cc` file so that we can see and instantiate the - destructors for the templated types `raft_networked_member_t` and - `minidir_write_manager_t`. The destructors are defined in `.tcc` files which are - visible here but not in the `.hpp`. */ -} - -void table_manager_t::get_status( - const table_status_request_t &request, - signal_t *interruptor, - table_status_response_t *response) - THROWS_ONLY(interrupted_exc_t) { - if (request.want_config) { - get_raft()->get_committed_state()->apply_read( - [&](const raft_member_t::state_and_config_t *s) { - response->config.set(s->state.config); - }); - } - if (request.want_sindexes) { - response->sindexes = sindex_manager.get_status(interruptor); - } - if (request.want_raft_state) { - get_raft()->get_committed_state()->apply_read( - [&](const raft_member_t::state_and_config_t *s) { - response->raft_state.set(s->state); - multi_table_manager_timestamp_t ts; - ts.epoch = epoch; - ts.log_index = s->log_index; - response->raft_state_timestamp.set(ts); - }); - } - if (request.want_contract_acks) { - contract_executor.get_acks()->read_all( - [&](const std::pair &k, const contract_ack_t *ack) { - response->contract_acks.insert(std::make_pair(k.second, *ack)); - }); - } - if (request.want_shard_status) { - response->shard_status = contract_executor.get_shard_status(); - } - if (request.want_all_replicas_ready) { - switch (request.all_replicas_ready_mode) { - case all_replicas_ready_mode_t::EXCLUDE_RAFT_TEST: { - rwlock_in_line_t leader_in_line(&leader_lock, access_t::read); - // If we cannot acquire the leader_lock immediately, that implies - // that the leader is currently transitioning. Instead of waiting for the - // lock, we simply bail out and report not all replicas ready. - if (leader_in_line.read_signal()->is_pulsed() && static_cast(leader)) { - response->all_replicas_ready = - leader->get_contract_coordinator()-> - check_outdated_all_replicas_ready(interruptor); - } else { - response->all_replicas_ready = false; - } - } break; - case all_replicas_ready_mode_t::INCLUDE_RAFT_TEST: { - rwlock_acq_t leader_acq(&leader_lock, access_t::read, interruptor); - if (static_cast(leader)) { - response->all_replicas_ready = - leader->get_contract_coordinator()-> - check_all_replicas_ready(interruptor); - } else { - response->all_replicas_ready = false; - } - } break; - default: - unreachable(); - } - } -} - -table_manager_t::leader_t::leader_t(table_manager_t *_parent) : - parent(_parent), - contract_ack_read_manager(parent->mailbox_manager), - coordinator(parent->get_raft(), contract_ack_read_manager.get_values(), - parent->connections_map), - server_name_cache_updater(parent->get_raft(), parent->server_config_client), - set_config_mailbox(parent->mailbox_manager, - std::bind(&leader_t::on_set_config, this, ph::_1, ph::_2, ph::_3)) -{ - parent->table_manager_bcard.apply_atomic_op([&](table_manager_bcard_t *bcard) { - table_manager_bcard_t::leader_bcard_t leader_bcard; - leader_bcard.uuid = generate_uuid(); - leader_bcard.set_config_mailbox = set_config_mailbox.get_address(); - leader_bcard.contract_ack_minidir_bcard = contract_ack_read_manager.get_bcard(); - bcard->leader = make_optional(leader_bcard); - return true; - }); -} - -table_manager_t::leader_t::~leader_t() { - parent->table_manager_bcard.apply_atomic_op([&](table_manager_bcard_t *bcard) { - bcard->leader = r_nullopt; - return true; - }); -} - -void table_manager_t::leader_t::on_set_config( - signal_t *interruptor, - const table_config_and_shards_change_t &table_config_and_shards_change, - const mailbox_addr_t, bool> &reply_addr) { - logINF("Table %s: Configuration is changing.", - uuid_to_str(parent->table_id).c_str()); - bool is_change_successful = false; - optional result = coordinator.change_config( - [&](table_config_and_shards_t *config_and_shards) { - is_change_successful = - table_config_and_shards_change.apply_change(config_and_shards); - }, - interruptor); - if (static_cast(result) && is_change_successful) { - multi_table_manager_timestamp_t timestamp; - timestamp.epoch = parent->epoch; - timestamp.log_index = *result; - send(parent->mailbox_manager, reply_addr, - make_optional(timestamp), true); - } else { - /* If `is_change_successful` is false the change was considered a no-op and the - returned log_index is that of the last change, which we safely ignore. */ - send(parent->mailbox_manager, reply_addr, - optional(), is_change_successful); - } -} - -void table_manager_t::on_table_directory_change( - const std::pair &key, - const table_manager_bcard_t *bcard) { - if (key.second == table_id) { - /* Update `raft_directory` */ - if (bcard != nullptr && bcard->timestamp.epoch == epoch) { - raft_directory.set_key( - key.first, - bcard->raft_member_id, - bcard->raft_business_card); - } else { - raft_directory.delete_key(key.first); - } - - /* Update `execution_bcard_minidir_directory` */ - if (bcard != nullptr) { - execution_bcard_minidir_directory.set_key( - key.first, - bcard->server_id, - bcard->execution_bcard_minidir_bcard); - } else { - execution_bcard_minidir_directory.delete_key(key.first); - } - - /* Update `current_ack_minidir_directory` */ - if (bcard != nullptr && static_cast(bcard->leader)) { - contract_ack_minidir_directory.set_key( - key.first, - bcard->leader->uuid, - bcard->leader->contract_ack_minidir_bcard); - } else { - contract_ack_minidir_directory.delete_key(key.first); - } - } -} - -void table_manager_t::on_raft_readiness_change() { - /* Create or destroy `leader` depending on whether we are the Raft cluster's leader. - Since `leader_t`'s constructor and destructor may block, we have to spawn a - coroutine to do it. */ - auto_drainer_t::lock_t keepalive(&drainer); - coro_t::spawn_sometime([this, keepalive /* important to capture */]() { - rwlock_acq_t mutex_acq(&leader_lock, access_t::write); - bool ready = raft.get_raft()->get_readiness_for_change()->get(); - if (ready && !leader.has()) { - leader.init(new leader_t(this)); - } else if (!ready && leader.has()) { - leader.reset(); - } - }); -} - diff --git a/src/clustering/table_manager/table_manager.hpp b/src/clustering/table_manager/table_manager.hpp deleted file mode 100644 index 8fba7dafd9d..00000000000 --- a/src/clustering/table_manager/table_manager.hpp +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_MANAGER_TABLE_MANAGER_HPP_ -#define CLUSTERING_TABLE_MANAGER_TABLE_MANAGER_HPP_ - -#include "clustering/table_contract/coordinator/coordinator.hpp" -#include "clustering/table_contract/executor/executor.hpp" -#include "clustering/table_manager/backfill_progress_tracker.hpp" -#include "clustering/table_manager/server_name_cache_updater.hpp" -#include "clustering/table_manager/sindex_manager.hpp" -#include "clustering/table_manager/table_metadata.hpp" -#include "concurrency/rwlock.hpp" - -/* `table_manager_t` hosts the `raft_member_t` and the `contract_executor_t`. It also -hosts the `contract_coordinator_t` if we are the Raft leader. */ -class table_manager_t { -public: - table_manager_t( - const server_id_t &_server_id, - mailbox_manager_t *_mailbox_manager, - server_config_client_t *server_config_client, - watchable_map_t, table_manager_bcard_t> - *_table_manager_directory, - backfill_throttler_t *_backfill_throttler, - watchable_map_t, empty_value_t> - *_connections_map, - const base_path_t &_base_path, - io_backender_t *_io_backender, - const namespace_id_t &_table_id, - const multi_table_manager_timestamp_t::epoch_t &_epoch, - const raft_member_id_t &raft_member_id, - raft_storage_interface_t *raft_storage, - const raft_start_election_immediately_t start_election_immediately, - multistore_ptr_t *multistore_ptr, - perfmon_collection_t *perfmon_collection_namespace); - - ~table_manager_t(); - - /* These are public so that `multi_table_manager_t` can see them */ - const namespace_id_t table_id; - const multi_table_manager_timestamp_t::epoch_t epoch; - const raft_member_id_t raft_member_id; - - raft_member_t *get_raft() { - return raft.get_raft(); - } - - clone_ptr_t > get_table_manager_bcard() { - return table_manager_bcard.get_watchable(); - } - - watchable_map_t *get_table_query_bcards() { - return contract_executor.get_local_table_query_bcards(); - } - - backfill_progress_tracker_t &get_backfill_progress_tracker() { - return backfill_progress_tracker; - } - - const sindex_manager_t &get_sindex_manager() const { - return sindex_manager; - } - - void get_status( - const table_status_request_t &request, - signal_t *interruptor, - table_status_response_t *response) - THROWS_ONLY(interrupted_exc_t); - -private: - /* `leader_t` hosts the `contract_coordinator_t`. */ - class leader_t { - public: - explicit leader_t(table_manager_t *_parent); - ~leader_t(); - - contract_coordinator_t *get_contract_coordinator() { return &coordinator; } - - private: - void on_set_config( - signal_t *interruptor, - const table_config_and_shards_change_t &table_config_and_shards_change, - const mailbox_addr_t, bool> &reply_addr); - - table_manager_t * const parent; - minidir_read_manager_t, contract_ack_t> - contract_ack_read_manager; - contract_coordinator_t coordinator; - server_name_cache_updater_t server_name_cache_updater; - table_manager_bcard_t::leader_bcard_t::set_config_mailbox_t set_config_mailbox; - }; - - /* This is the callback for `table_directory_subs`. It's responsible for - maintaining `raft_directory`, `execution_bcard_minidir_directory`, and - `contract_ack_minidir_directory`. */ - void on_table_directory_change( - const std::pair &key, - const table_manager_bcard_t *bcard); - - /* This is the callback for `raft_readiness_subs` */ - void on_raft_readiness_change(); - - mailbox_manager_t * const mailbox_manager; - server_config_client_t *server_config_client; - watchable_map_t, empty_value_t> - * const connections_map; - - perfmon_collection_t perfmon_collection; - perfmon_membership_t perfmon_membership; - - /* `table_manager_t` monitors `table_manager_directory` and derives three other - `watchable_map_t`s from it: - - `raft_directory` contains all of the Raft business cards. It is consumed by - `raft`, which uses it for Raft internal RPCs. - - `execution_bcard_minidir_directory` contains business cards for the minidirs - that carry the execution bcards between servers. It is consumed by - `execution_bcard_write_manager`. - - `contract_ack_minidir_directory` contains business cards for the minidirs that - carry the contract acks between servers. It is consumed by - `contract_ack_write_manager`. - */ - watchable_map_keyed_var_t< - peer_id_t, - raft_member_id_t, - raft_business_card_t > - raft_directory; - watchable_map_keyed_var_t< - peer_id_t, - server_id_t, - minidir_bcard_t, - contract_execution_bcard_t> > - execution_bcard_minidir_directory; - watchable_map_keyed_var_t< - peer_id_t, - uuid_u, // The leader UUID - minidir_bcard_t, contract_ack_t> > - contract_ack_minidir_directory; - - raft_networked_member_t raft; - - watchable_variable_t table_manager_bcard; - watchable_field_copier_t< - raft_business_card_t, table_manager_bcard_t - > raft_bcard_copier; - - /* `leader` will be non-empty if we are the Raft leader */ - scoped_ptr_t leader; - - /* `leader_lock` controls access to `leader`. This is important because creating - and destroying the `leader_t` may block. */ - rwlock_t leader_lock; - - /* The `execution_bcard_read_manager` receives `contract_execution_bcard_t`s from - `contract_executor_t`s on other servers and passes them to the - `contract_executor_t` on this server. */ - minidir_read_manager_t, - contract_execution_bcard_t> execution_bcard_read_manager; - - /* The `backfill_progress_tracker` keeps track of backfills their destination - server, start time, and progress. This must be destructed after the - `contract_executor`. */ - backfill_progress_tracker_t backfill_progress_tracker; - - /* The `contract_executor_t` creates and destroys `broadcaster_t`s, - `listener_t`s, etc. to handle queries. */ - contract_executor_t contract_executor; - - /* The `execution_bcard_write_manager` receives `contract_execution_bcard_t`s - from the `contract_executor` on this server and passes them to the - `contract_executor_t`s on other servers. */ - minidir_write_manager_t, - contract_execution_bcard_t> execution_bcard_write_manager; - - /* The `contract_ack_write_manager` receives `contract_ack_t`s from the - `contract_executor` and passes them to the `contract_coordinator_t` on the Raft - leader. */ - minidir_write_manager_t, - contract_ack_t> contract_ack_write_manager; - - /* The `sindex_manager` watches the `table_config_t` and changes the sindexes on - `multistore_ptr` according to what it sees. */ - sindex_manager_t sindex_manager; - - auto_drainer_t drainer; - - watchable_map_t, table_manager_bcard_t> - ::all_subs_t table_directory_subs; - watchable_subscription_t raft_readiness_subs; -}; - -#endif /* CLUSTERING_TABLE_MANAGER_TABLE_MANAGER_HPP_ */ - diff --git a/src/clustering/table_manager/table_meta_client.cc b/src/clustering/table_manager/table_meta_client.cc deleted file mode 100644 index 70cfed9e658..00000000000 --- a/src/clustering/table_manager/table_meta_client.cc +++ /dev/null @@ -1,855 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_manager/table_meta_client.hpp" - -#include "clustering/administration/issues/outdated_index.hpp" -#include "clustering/administration/servers/config_client.hpp" -#include "clustering/generic/raft_core.tcc" -#include "clustering/table_contract/emergency_repair.hpp" -#include "clustering/table_manager/multi_table_manager.hpp" -#include "concurrency/cross_thread_signal.hpp" -#include "rpc/mailbox/disconnect_watcher.hpp" - -table_meta_client_t::table_meta_client_t( - mailbox_manager_t *_mailbox_manager, - multi_table_manager_t *_multi_table_manager, - watchable_map_t - *_multi_table_manager_directory, - watchable_map_t, table_manager_bcard_t> - *_table_manager_directory, - server_config_client_t *_server_config_client) : - mailbox_manager(_mailbox_manager), - multi_table_manager(_multi_table_manager), - multi_table_manager_directory(_multi_table_manager_directory), - table_manager_directory(_table_manager_directory), - server_config_client(_server_config_client), - table_basic_configs(multi_table_manager->get_table_basic_configs()) - { } - -table_meta_client_t::~table_meta_client_t() {} - -void table_meta_client_t::find( - const database_id_t &database, - const name_string_t &name, - namespace_id_t *table_id_out, - std::string *primary_key_out) - THROWS_ONLY(no_such_table_exc_t, ambiguous_table_exc_t) { - size_t count = 0; - table_basic_configs.get_watchable()->read_all( - [&](const namespace_id_t &key, const timestamped_basic_config_t *value) { - if (value->first.database == database && value->first.name == name) { - ++count; - *table_id_out = key; - if (primary_key_out != nullptr) { - *primary_key_out = value->first.primary_key; - } - } - }); - if (count == 0) { - throw no_such_table_exc_t(); - } else if (count >= 2) { - throw ambiguous_table_exc_t(); - } -} - -bool table_meta_client_t::exists(const namespace_id_t &table_id) { - bool found = false; - table_basic_configs.get_watchable()->read_key(table_id, - [&](const timestamped_basic_config_t *value) { - found = (value != nullptr); - }); - return found; -} - -bool table_meta_client_t::exists( - const database_id_t &database, const name_string_t &name) { - bool found = false; - table_basic_configs.get_watchable()->read_all( - [&](const namespace_id_t &, const timestamped_basic_config_t *value) { - if (value->first.database == database && value->first.name == name) { - found = true; - } - }); - return found; -} - -void table_meta_client_t::get_name( - const namespace_id_t &table_id, - table_basic_config_t *basic_config_out) - THROWS_ONLY(no_such_table_exc_t) { - table_basic_configs.get_watchable()->read_key(table_id, - [&](const timestamped_basic_config_t *value) { - if (value == nullptr) { - throw no_such_table_exc_t(); - } - *basic_config_out = value->first; - }); -} - -void table_meta_client_t::list_names( - std::map *names_out) const { - table_basic_configs.get_watchable()->read_all( - [&](const namespace_id_t &table_id, const timestamped_basic_config_t *value) { - (*names_out)[table_id] = value->first; - }); -} - -void table_meta_client_t::get_config( - const namespace_id_t &table_id, - signal_t *interruptor_on_caller, - table_config_and_shards_t *config_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t) { - cross_thread_signal_t interruptor(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - table_status_request_t request; - request.want_config = true; - std::set failures; - get_status( - make_optional(table_id), - request, - server_selector_t::BEST_SERVER_ONLY, - &interruptor, - [&](const server_id_t &, const namespace_id_t &, - const table_status_response_t &response) { - *config_out = *response.config; - }, - &failures); - if (!failures.empty()) { - throw_appropriate_exception(table_id); - } -} - -void table_meta_client_t::list_configs( - signal_t *interruptor_on_caller, - std::map *configs_out, - std::map *disconnected_configs_out) - THROWS_ONLY(interrupted_exc_t) { - cross_thread_signal_t interruptor(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - configs_out->clear(); - table_status_request_t request; - request.want_config = true; - std::set failures; - get_status( - r_nullopt, - request, - server_selector_t::BEST_SERVER_ONLY, - &interruptor, - [&](const server_id_t &, const namespace_id_t &table_id, - const table_status_response_t &response) { - configs_out->insert(std::make_pair(table_id, *response.config)); - }, - &failures); - for (const namespace_id_t &table_id : failures) { - multi_table_manager->get_table_basic_configs()->read_key(table_id, - [&](const timestamped_basic_config_t *value) { - if (value != nullptr) { - disconnected_configs_out->insert(std::make_pair(table_id, value->first)); - } - }); - } -} - -void table_meta_client_t::get_sindex_status( - const namespace_id_t &table_id, - signal_t *interruptor_on_caller, - std::map > - *sindex_statuses_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t) { - cross_thread_signal_t interruptor(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - sindex_statuses_out->clear(); - table_config_and_shards_t config; - get_config(table_id, &interruptor, &config); - for (const auto &pair : config.config.sindexes) { - auto it = sindex_statuses_out->insert( - std::make_pair(pair.first, std::make_pair(pair.second, - sindex_status_t()))).first; - it->second.second.outdated = - (pair.second.func_version != reql_version_t::LATEST) - && !outdated_index_issue_tracker_t::is_acceptable_outdated(pair.second); - } - table_status_request_t request; - request.want_sindexes = true; - std::set failures; - get_status( - make_optional(table_id), - request, - server_selector_t::EVERY_SERVER, - &interruptor, - [&](const server_id_t &, const namespace_id_t &, - const table_status_response_t &response) { - for (auto &&pair : *sindex_statuses_out) { - auto it = response.sindexes.find(pair.first); - /* Note that we treat an index with the wrong definition like a - missing index. */ - if (it != response.sindexes.end() && - it->second.first == pair.second.first) { - pair.second.second.accum(it->second.second); - } else { - pair.second.second.ready = false; - } - } - }, - &failures); - if (!failures.empty()) { - throw_appropriate_exception(table_id); - } -} - -void table_meta_client_t::get_shard_status( - const namespace_id_t &table_id, - all_replicas_ready_mode_t all_replicas_ready_mode, - signal_t *interruptor_on_caller, - std::map > *shard_statuses_out, - bool *all_replicas_ready_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t) { - cross_thread_signal_t interruptor(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - *all_replicas_ready_out = false; - table_status_request_t request; - request.want_shard_status = (shard_statuses_out != nullptr); - request.want_all_replicas_ready = true; - request.all_replicas_ready_mode = all_replicas_ready_mode; - std::set failures; - get_status( - make_optional(table_id), - request, - /* If we only care about `all_replicas_ready`, there's no need to contact any - server other than the primary */ - shard_statuses_out != nullptr - ? server_selector_t::EVERY_SERVER - : server_selector_t::BEST_SERVER_ONLY, - &interruptor, - [&](const server_id_t &server_id, const namespace_id_t &, - const table_status_response_t &response) { - if (shard_statuses_out != nullptr) { - shard_statuses_out->insert( - std::make_pair(server_id, response.shard_status)); - } - *all_replicas_ready_out |= response.all_replicas_ready; - }, - &failures); - if (!failures.empty()) { - throw_appropriate_exception(table_id); - } -} - -void table_meta_client_t::get_raft_leader( - const namespace_id_t &table_id, - signal_t *interruptor_on_caller, - optional *raft_leader_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t) { - cross_thread_signal_t interruptor(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - - table_manager_directory->read_all( - [&](const std::pair &key, - const table_manager_bcard_t *bcard) { - if (key.second == table_id && static_cast(bcard->leader)) { - *raft_leader_out = make_optional(bcard->server_id); - } - }); -} - -void table_meta_client_t::get_debug_status( - const namespace_id_t &table_id, - all_replicas_ready_mode_t all_replicas_ready_mode, - signal_t *interruptor_on_caller, - std::map *responses_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t) { - cross_thread_signal_t interruptor(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - table_status_request_t request; - request.want_config = true; - request.want_sindexes = true; - request.want_raft_state = true; - request.want_contract_acks = true; - request.want_shard_status = true; - request.want_all_replicas_ready = true; - request.all_replicas_ready_mode = all_replicas_ready_mode; - std::set failures; - get_status( - make_optional(table_id), - request, - server_selector_t::EVERY_SERVER, - &interruptor, - [&](const server_id_t &server_id, const namespace_id_t &, - const table_status_response_t &response) { - responses_out->insert(std::make_pair(server_id, response)); - }, - &failures); - if (!failures.empty()) { - throw_appropriate_exception(table_id); - } -} - -void table_meta_client_t::create( - namespace_id_t table_id, - const table_config_and_shards_t &initial_config, - signal_t *interruptor_on_caller) - THROWS_ONLY(interrupted_exc_t, failed_table_op_exc_t, - maybe_failed_table_op_exc_t) { - cross_thread_signal_t interruptor(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - - /* Sanity-check that `table_id` is unique */ - multi_table_manager->get_table_basic_configs()->read_key(table_id, - [&](const timestamped_basic_config_t *value) { - guarantee(value == nullptr); - }); - - create_or_emergency_repair( - table_id, - make_new_table_raft_state(initial_config), - multi_table_manager_timestamp_t::epoch_t::make( - multi_table_manager_timestamp_t::epoch_t::min()), - &interruptor); -} - -void table_meta_client_t::drop( - const namespace_id_t &table_id, - signal_t *interruptor_on_caller) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t) { - cross_thread_signal_t interruptor(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - - if (!exists(table_id)) { - throw no_such_table_exc_t(); - } - - /* Find business cards for all servers, not just the ones that are hosting the table. - This is because sometimes it makes sense to drop a table even if the table is - completely unreachable. */ - std::map bcards = - multi_table_manager_directory->get_all(); - guarantee(!bcards.empty(), "We should be connected to ourself"); - - /* Send a message to each server. */ - size_t num_acked = 0; - pmap(bcards.begin(), bcards.end(), - [&](const std::pair &pair) { - try { - disconnect_watcher_t dw(mailbox_manager, pair.first); - cond_t got_ack; - mailbox_t<> ack_mailbox(mailbox_manager, - [&](signal_t *) { got_ack.pulse(); }); - send(mailbox_manager, pair.second.action_mailbox, - {table_id, - multi_table_manager_timestamp_t::deletion(), - multi_table_manager_bcard_t::status_t::DELETED, - optional(), - optional(), - optional >(), - optional(), - ack_mailbox.get_address()}); - wait_any_t interruptor_combined(&dw, &interruptor); - wait_interruptible(&got_ack, &interruptor_combined); - ++num_acked; - } catch (const interrupted_exc_t &) { - /* do nothing */ - } - }); - if (interruptor.is_pulsed()) { - throw interrupted_exc_t(); - } - guarantee(num_acked != 0, "We should at least have an ack from ourself"); - - /* Wait until the table disappears from the directory. */ - wait_until_change_visible( - table_id, - [](const timestamped_basic_config_t *value) { return value == nullptr; }, - &interruptor); -} - -void table_meta_client_t::set_config( - const namespace_id_t &table_id, - const table_config_and_shards_change_t &table_config_and_shards_change, - signal_t *interruptor_on_caller) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t, - maybe_failed_table_op_exc_t, config_change_exc_t) { - cross_thread_signal_t interruptor(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - - multi_table_manager_timestamp_t timestamp; - retry([&](signal_t *interruptor2) { - /* Find the server (if any) which is acting as leader for the table */ - uuid_u best_leader_uuid; - table_manager_bcard_t::leader_bcard_t::set_config_mailbox_t::address_t - best_mailbox; - multi_table_manager_timestamp_t best_timestamp; - table_manager_directory->read_all( - [&](const std::pair &key, - const table_manager_bcard_t *bcard) { - if (key.second == table_id && static_cast(bcard->leader)) { - if (best_mailbox.is_nil() || - bcard->timestamp.supersedes(best_timestamp)) { - best_leader_uuid = bcard->leader->uuid; - best_mailbox = bcard->leader->set_config_mailbox; - best_timestamp = bcard->timestamp; - } - } - }); - if (best_mailbox.is_nil()) { - throw_appropriate_exception(table_id); - } - - /* There are two reasons why our message might get lost. The first is that the - other server disconnects. The second is that the other server remains connected - but it stops being leader for that table. We detect the first with a - `disconnect_watcher_t`. We detect the second by setting up a `key_subs_t` that - pulses `leader_stopped` if the other server is no longer leader or if its leader - UUID changes. (The `key_subs_t` alone is not enough because it might fail to - detect a transient connection failure.) */ - disconnect_watcher_t leader_disconnected( - mailbox_manager, best_mailbox.get_peer()); - cond_t leader_stopped; - watchable_map_t, table_manager_bcard_t> - ::key_subs_t leader_stopped_subs( - table_manager_directory, - std::make_pair(best_mailbox.get_peer(), table_id), - [&](const table_manager_bcard_t *bcard) { - if (bcard == nullptr || !static_cast(bcard->leader) || - bcard->leader->uuid != best_leader_uuid) { - leader_stopped.pulse_if_not_already_pulsed(); - } - }); - - /* OK, now send the change and wait for a reply, or for something to go wrong */ - promise_t, bool> > promise; - mailbox_t, bool> - ack_mailbox(mailbox_manager, - [&](signal_t *, - const optional &change_timestamp, - bool is_change_successful) { - promise.pulse(std::make_pair(change_timestamp, is_change_successful)); - }); - send(mailbox_manager, - best_mailbox, - table_config_and_shards_change, - ack_mailbox.get_address()); - wait_any_t done_cond(promise.get_ready_signal(), - &leader_disconnected, &leader_stopped); - wait_interruptible(&done_cond, interruptor2); - if (!promise.get_ready_signal()->is_pulsed()) { - throw maybe_failed_table_op_exc_t(); - } - - /* Sometimes the server will reply by indicating that something went wrong */ - std::pair, bool> response = - promise.wait(); - if (response.second == false) { - throw config_change_exc_t(); - } else if (!static_cast(response.first)) { - throw maybe_failed_table_op_exc_t(); - } - timestamp = response.first.get(); - }, &interruptor); - - /* We know for sure that the change has been applied; now we just need to wait until - the change is visible in the directory before returning. The naive thing is to wait - until the table's name and database match whatever we just changed the config to. But - this could go wrong if the table's name and database are changed again in quick - succession. We detect that other case by using the timestamps. */ - wait_until_change_visible( - table_id, - [&](const timestamped_basic_config_t *value) { - return value == nullptr || value->second.supersedes(timestamp) || - table_config_and_shards_change.name_and_database_equal(value->first); - }, - &interruptor); -} - -void table_meta_client_t::emergency_repair( - const namespace_id_t &table_id, - emergency_repair_mode_t mode, - bool dry_run, - signal_t *interruptor_on_caller, - table_config_and_shards_t *new_config_out, - bool *rollback_found_out, - bool *erase_found_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t, - maybe_failed_table_op_exc_t) { - cross_thread_signal_t interruptor(interruptor_on_caller, home_thread()); - on_thread_t thread_switcher(home_thread()); - - table_raft_state_t old_state; - table_status_request_t request; - request.want_raft_state = true; - std::set failures; - get_status( - make_optional(table_id), - request, - server_selector_t::BEST_SERVER_ONLY, - &interruptor, - [&](const server_id_t &, const namespace_id_t &, - const table_status_response_t &response) { - old_state = *response.raft_state; - }, - &failures); - if (!failures.empty()) { - throw_appropriate_exception(table_id); - } - - std::set dead_servers; - for (const auto &pair : old_state.member_ids) { - if (!server_config_client-> - get_server_to_peer_map()->get_key(pair.first).has_value()) { - dead_servers.insert(pair.first); - } - } - - table_raft_state_t new_state; - calculate_emergency_repair( - old_state, - dead_servers, - mode, - &new_state, - rollback_found_out, - erase_found_out); - - *new_config_out = new_state.config; - - if ((*rollback_found_out || *erase_found_out || - mode == emergency_repair_mode_t::DEBUG_RECOMMIT) && !dry_run) { - /* In theory, we don't always have to start a new epoch. Sometimes we run an - emergency repair where we've lost a quorum of one shard, but still have a quorum - of the Raft cluster as a whole. In that case we could run a regular Raft - transaction, which could be made slightly safer. But it's simpler to do - everything through the same code path. */ - - /* Fetch the table's current epoch's timestamp to make sure that the new epoch - has a higher timestamp, even if the server's clock is wrong. */ - multi_table_manager_timestamp_t::epoch_t old_epoch; - multi_table_manager->get_table_basic_configs()->read_key(table_id, - [&](const std::pair *pair) { - if (pair == nullptr) { - throw no_such_table_exc_t(); - } - old_epoch = pair->second.epoch; - }); - - create_or_emergency_repair( - table_id, - new_state, - multi_table_manager_timestamp_t::epoch_t::make(old_epoch), - &interruptor); - } -} - -void table_meta_client_t::create_or_emergency_repair( - const namespace_id_t &table_id, - const table_raft_state_t &raft_state, - const multi_table_manager_timestamp_t::epoch_t &epoch, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, failed_table_op_exc_t, - maybe_failed_table_op_exc_t) { - assert_thread(); - - /* Prepare the message that we'll be sending to each server */ - multi_table_manager_timestamp_t timestamp; - timestamp.epoch = epoch; - timestamp.log_index = 0; - - std::set all_servers, voting_servers; - for (const table_config_t::shard_t &shard : raft_state.config.config.shards) { - all_servers.insert(shard.all_replicas.begin(), shard.all_replicas.end()); - std::set voters = shard.voting_replicas(); - voting_servers.insert(voters.begin(), voters.end()); - } - - raft_config_t raft_config; - server_id_t initial_leader; - for (const server_id_t &server_id : all_servers) { - if (voting_servers.count(server_id) == 1) { - if (raft_config.voting_members.empty()) { - // This is the first voting member we've seen; arbitrarily - // choose it as the initial leader - initial_leader = server_id; - } - raft_config.voting_members.insert(raft_state.member_ids.at(server_id)); - } else { - raft_config.non_voting_members.insert(raft_state.member_ids.at(server_id)); - } - } - - raft_persistent_state_t raft_ps = - raft_persistent_state_t::make_initial( - raft_state, raft_config); - - /* Find the business cards of the servers we'll be sending to */ - std::map bcards; - multi_table_manager_directory->read_all( - [&](const peer_id_t &, const multi_table_manager_bcard_t *bc) { - if (all_servers.count(bc->server_id) == 1) { - bcards[bc->server_id] = *bc; - } - }); - - if (bcards.empty()) { - throw failed_table_op_exc_t(); - } - - size_t num_acked = 0; - pmap(bcards.begin(), bcards.end(), - [&](const std::pair &pair) { - optional start_immediately( - pair.first == initial_leader - ? raft_start_election_immediately_t::YES - : raft_start_election_immediately_t::NO); - try { - /* Send the message for the server and wait for a reply */ - disconnect_watcher_t dw(mailbox_manager, - pair.second.action_mailbox.get_peer()); - cond_t got_ack; - mailbox_t<> ack_mailbox(mailbox_manager, - [&](signal_t *) { got_ack.pulse(); }); - send(mailbox_manager, pair.second.action_mailbox, - {table_id, - timestamp, - multi_table_manager_bcard_t::status_t::ACTIVE, - optional(), - optional(raft_state.member_ids.at(pair.first)), - optional >(raft_ps), - start_immediately, - ack_mailbox.get_address()}); - wait_any_t interruptor_combined(&dw, interruptor); - wait_interruptible(&got_ack, &interruptor_combined); - - ++num_acked; - } catch (const interrupted_exc_t &) { - /* do nothing */ - } - }); - if (interruptor->is_pulsed()) { - throw interrupted_exc_t(); - } - - if (num_acked == 0) { - throw maybe_failed_table_op_exc_t(); - } - - /* Wait until the table appears in the directory. */ - wait_until_change_visible( - table_id, - [&](const timestamped_basic_config_t *value) { - return value != nullptr && - (value->second.epoch == timestamp.epoch || - value->second.epoch.supersedes(timestamp.epoch)); - }, - interruptor); -} - -/* `best_server_rank_t` is used for comparing servers when we're in `BEST_SERVER_ONLY` -mode. */ -class best_server_rank_t { -public: - /* Note that a default-constructed `best_server_rank_t` is superseded by any other - `best_server_rank_t`. */ - best_server_rank_t() : - is_leader(false), timestamp(multi_table_manager_timestamp_t::min()) { } - best_server_rank_t(bool il, const multi_table_manager_timestamp_t ts) : - is_leader(il), timestamp(ts) { } - - bool supersedes(const best_server_rank_t &other) const { - /* Ordered comparison first on `timestamp.epoch`, then `is_leader`, then - `timestamp.log_index`. This is slightly convoluted because `timestamp.epoch` is - compared with `supersedes()` rather than `operator<()`. */ - return timestamp.epoch.supersedes(other.timestamp.epoch) || - (timestamp.epoch == other.timestamp.epoch && - std::tie(is_leader, timestamp.log_index) > - std::tie(other.is_leader, other.timestamp.log_index)); - } - - bool is_leader; - multi_table_manager_timestamp_t timestamp; -}; - -void table_meta_client_t::get_status( - const optional &table, - const table_status_request_t &request, - server_selector_t servers, - signal_t *interruptor, - const std::function &callback, - std::set *failures_out) - THROWS_ONLY(interrupted_exc_t) { - assert_thread(); - interruptor->assert_thread(); - - /* Assemble a set of all the tables we need information for. As we get information - for each table, we'll remove it from the set; we'll use this to track which tables - we need to retry for. */ - std::set tables_todo; - if (static_cast(table)) { - tables_todo.insert(*table); - } else { - table_basic_configs.get_watchable()->read_all( - [&](const namespace_id_t &table_id, const timestamped_basic_config_t *) { - tables_todo.insert(table_id); - }); - } - - /* If we're in `BEST_SERVER_ONLY` mode, there's a risk that the table will move off - the server we selected before we get a chance to run the query, which could cause - spurious failures. So in that mode we try again if the first try fails. */ - int tries_left = servers == server_selector_t::BEST_SERVER_ONLY ? 2 : 1; - - while (!tables_todo.empty() && tries_left > 0) { - --tries_left; - - /* Assemble a list of which servers we need to send messages to, and which tables - we want from each server. */ - std::map > targets; - switch (servers) { - case server_selector_t::EVERY_SERVER: { - table_manager_directory->read_all( - [&](const std::pair &key, - const table_manager_bcard_t *) { - if (tables_todo.count(key.second) == 1) { - targets[key.first].insert(key.second); - } - }); - break; - } - case server_selector_t::BEST_SERVER_ONLY: { - std::map best_servers; - std::map best_ranks; - table_manager_directory->read_all( - [&](const std::pair &key, - const table_manager_bcard_t *bcard) { - if (tables_todo.count(key.second) == 1) { - best_server_rank_t rank( - static_cast(bcard->leader), bcard->timestamp); - /* This relies on the fact that a default-constructed - `best_server_rank_t` is always superseded */ - if (rank.supersedes(best_ranks[key.second])) { - best_ranks[key.second] = rank; - best_servers[key.second] = key.first; - } - } - }); - for (const auto &pair : best_servers) { - targets[pair.second].insert(pair.first); - } - break; - } - } - - /* Dispatch all the messages in parallel. Probably this should really be a - `throttled_pmap` instead. */ - pmap(targets.begin(), targets.end(), - [&](const std::pair > &target) { - optional bcard = - multi_table_manager_directory->get_key(target.first); - if (!bcard.has_value()) { - return; - } - disconnect_watcher_t dw(mailbox_manager, - bcard->get_status_mailbox.get_peer()); - cond_t got_ack; - mailbox_t> - ack_mailbox(mailbox_manager, - [&](signal_t *, const std::map< - namespace_id_t, table_status_response_t> &resp) { - for (const auto &pair : resp) { - callback(bcard->server_id, pair.first, pair.second); - tables_todo.erase(pair.first); - } - got_ack.pulse(); - }); - send(mailbox_manager, bcard->get_status_mailbox, - {target.second, request, ack_mailbox.get_address()}); - wait_any_t waiter(&dw, interruptor, &got_ack); - waiter.wait_lazily_unordered(); - }); - - if (interruptor->is_pulsed()) { - throw interrupted_exc_t(); - } - } - - *failures_out = std::move(tables_todo); -} - -void table_meta_client_t::retry( - const std::function &fun, - signal_t *interruptor) { - /* 8 tries at 300ms initially with 1.5x exponential backoff means the last try will - be about 15 seconds after the first try. Since that's about ten times the Raft - election timeout, we can be reasonably certain that if it fails that many times there - is a real problem and not just a transient failure. */ - static const int max_tries = 8; - static const int initial_wait_ms = 300; - int tries_left = max_tries; - int wait_ms = initial_wait_ms; - bool maybe_succeeded = false; - for (;;) { - try { - fun(interruptor); - return; - } catch (const failed_table_op_exc_t &) { - /* ignore */ - } catch (const maybe_failed_table_op_exc_t &) { - maybe_succeeded = true; - } - --tries_left; - if (tries_left == 0) { - if (maybe_succeeded) { - /* Throw `maybe_failed_table_op_exc_t` if any of the tries threw - `maybe_failed_table_op_exc_t`. */ - throw maybe_failed_table_op_exc_t(); - } else { - throw failed_table_op_exc_t(); - } - } - nap(wait_ms, interruptor); - wait_ms *= 1.5; - } -} - -NORETURN void table_meta_client_t::throw_appropriate_exception( - const namespace_id_t &table_id) - THROWS_ONLY(no_such_table_exc_t, failed_table_op_exc_t) { - multi_table_manager->get_table_basic_configs()->read_key( - table_id, - [&](const timestamped_basic_config_t *value) { - if (value == nullptr) { - throw no_such_table_exc_t(); - } else { - throw failed_table_op_exc_t(); - } - }); - unreachable(); -} - -void table_meta_client_t::wait_until_change_visible( - const namespace_id_t &table_id, - const std::function &cb, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, maybe_failed_table_op_exc_t) -{ - signal_timer_t timeout; - timeout.start(10*1000); - wait_any_t interruptor_combined(interruptor, &timeout); - try { - multi_table_manager->get_table_basic_configs()->run_key_until_satisfied( - table_id, cb, &interruptor_combined); - } catch (const interrupted_exc_t &) { - if (interruptor->is_pulsed()) { - throw; - } else { - /* The timeout ran out. We know the change was applied, but it isn't visible - to us yet, so we error anyway to preserve the guarantee that changes should - be visible after the operation completes. */ - throw maybe_failed_table_op_exc_t(); - } - } - /* Wait until the change is also visible on other threads */ - table_basic_configs.flush(); -} - diff --git a/src/clustering/table_manager/table_meta_client.hpp b/src/clustering/table_manager/table_meta_client.hpp deleted file mode 100644 index 9c0a58889b8..00000000000 --- a/src/clustering/table_manager/table_meta_client.hpp +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_MANAGER_TABLE_META_CLIENT_HPP_ -#define CLUSTERING_TABLE_MANAGER_TABLE_META_CLIENT_HPP_ - -#include "btree/keys.hpp" -#include "concurrency/cross_thread_watchable.hpp" -#include "concurrency/watchable_map.hpp" -#include "containers/uuid.hpp" - -enum class all_replicas_ready_mode_t; -enum class emergency_repair_mode_t; -class mailbox_manager_t; -class multi_table_manager_t; -class multi_table_manager_bcard_t; -class multi_table_manager_timestamp_t; -class multi_table_manager_timestamp_epoch_t; -class name_string_t; -class peer_id_t; -template class range_map_t; -class server_config_client_t; -class server_id_t; -class sindex_config_t; -class sindex_status_t; -class table_basic_config_t; -class table_config_and_shards_t; -class table_config_and_shards_change_t; -class table_raft_state_t; -class table_manager_bcard_t; -class table_shard_status_t; -class table_status_request_t; -class table_status_response_t; - -/* These four exception classes are all thrown by `table_meta_client_t` to describe -different error conditions. There are several reasons why this is better than having -`table_meta_client_t` just throw an `admin_op_exc_t`: - - 1. Sometimes we want to catch `no_such_table_exc_t` instead of reporting it to the user. - For example, if we call `list_names()` and then run some other operation on each - table in the list, we want to ignore any `no_such_table_exc_t`s thrown by that - operation. - - 2. `table_meta_client_t` usually doesn't have access to the name of the table. The - caller will typically catch the exception at a point where the table name is known - and then produce a user-readable error message that includes the table name. - - 3. `table_meta_client_t` often doesn't have enough context to produce an optimal error - message. The caller will typically add more context when it catches the exception and - forwards it to the user. For example, instead of saying "the table's configuration - was not modified" it can say "the secondary index was not renamed". - -Note that the exception descriptions here are mostly just for documentation; unless there -is a bug, they will never be shown to the user. */ - -class no_such_table_exc_t : public std::runtime_error { -public: - no_such_table_exc_t() : - std::runtime_error("There is no table with the given name / UUID.") { } -}; - -class ambiguous_table_exc_t : public std::runtime_error { -public: - ambiguous_table_exc_t() : - std::runtime_error("There are multiple tables with the given name.") { } -}; - -class failed_table_op_exc_t : public std::runtime_error { -public: - failed_table_op_exc_t() : std::runtime_error("The attempt to read or modify the " - "table's configuration failed because none of the servers were accessible. If " - "it was an attempt to modify, the modification did not take place.") { } -}; - -class maybe_failed_table_op_exc_t : public std::runtime_error { -public: - maybe_failed_table_op_exc_t() : std::runtime_error("The attempt to modify the " - "table's configuration failed because we lost contact with the servers after " - "initiating the modification, or the Raft leader lost contact with its " - "followers, or we timed out while waiting for the changes to propagate. The " - "modification may or may not have taken place.") { } -}; - -class config_change_exc_t : public std::runtime_error { -public: - config_change_exc_t() : std::runtime_error("The change could not be applied to the " - "table's configuration.") { } -}; - -/* `table_meta_client_t` is responsible for submitting client requests over the network -to the `multi_table_manager_t`. It doesn't have any real state of its own; it's just a -convenient way of bundling together all of the objects that are necessary for submitting -a client request. */ -class table_meta_client_t : - public home_thread_mixin_t { -public: - table_meta_client_t( - mailbox_manager_t *_mailbox_manager, - multi_table_manager_t *_multi_table_manager, - watchable_map_t - *_multi_table_manager_directory, - watchable_map_t, table_manager_bcard_t> - *_table_manager_directory, - server_config_client_t *_server_config_client); - ~table_meta_client_t(); - - /* All of these functions can be called from any thread. */ - - /* `find()` determines the ID of the table with the given name in the given database. - */ - void find( - const database_id_t &database, const name_string_t &name, - namespace_id_t *table_id_out, std::string *primary_key_out = nullptr) - THROWS_ONLY(no_such_table_exc_t, ambiguous_table_exc_t); - - /* `exists()` returns `true` if a table with the given `table_id` exists. */ - bool exists(const namespace_id_t &table_id); - - /* `exists()` returns `true` if one or more tables exist with the given name in the - given database. */ - bool exists(const database_id_t &database, const name_string_t &name); - - /* `get_name()` determines the name, database, and primary key of the table with the - given ID; it's the reverse of `find()`. It will not block. */ - void get_name( - const namespace_id_t &table_id, - table_basic_config_t *basic_config_out) - THROWS_ONLY(no_such_table_exc_t); - - /* `list_names()` determines the IDs, names, databases, and primary keys of every - table. It will not block. */ - void list_names( - std::map *names_out) const; - - /* `get_config()` fetches the configuration of the table with the given ID. It may - block and it may fail. */ - void get_config( - const namespace_id_t &table_id, - signal_t *interruptor, - table_config_and_shards_t *config_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t); - - /* `list_configs()` fetches the configurations of every table at once. It may block. - If it can't find a config for a certain table, then it puts the table's name and info - into `disconnected_configs_out` instead. */ - void list_configs( - signal_t *interruptor, - std::map *configs_out, - std::map *disconnected_configs_out) - THROWS_ONLY(interrupted_exc_t); - - /* `get_sindex_status()` returns a list of the sindexes on the given table and the - status of each one. */ - void get_sindex_status( - const namespace_id_t &table_id, - signal_t *interruptor, - std::map > - *index_statuses_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t); - - /* `get_shard_status()` returns some of the information necessary to fill in the - `rethinkdb.table_status` system table. If `server_shards_out` is set to `nullptr`, it - that information will not be retrieved, which will improve performance. */ - void get_shard_status( - const namespace_id_t &table_id, - all_replicas_ready_mode_t all_replicas_ready_mode, - signal_t *interruptor, - std::map > *shard_statuses_out, - bool *all_replicas_ready_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t); - - /* `get_raft_leader()` fetches raft leader from the table directory. - This is for displaying raft information in `rethinkdb.table_status`. */ - void get_raft_leader( - const namespace_id_t &table_id, - signal_t *interruptor, - optional *raft_leader_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t); - - /* `get_debug_status()` fetches all status information from all servers. This is for - displaying in `rethinkdb._debug_table_status`. */ - void get_debug_status( - const namespace_id_t &table_id, - all_replicas_ready_mode_t all_replicas_ready_mode, - signal_t *interruptor, - std::map *responses_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t); - - /* `create()` creates a table with the given configuration. It sets `*table_id_out` - to the ID of the newly generated table. It may block. If it returns successfully, the - change will be visible in `find()`, etc. */ - void create( - namespace_id_t new_table_id, - const table_config_and_shards_t &new_config, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, failed_table_op_exc_t, - maybe_failed_table_op_exc_t); - - /* `drop()` drops the table with the given ID. It may block. As long as the table - exists it will always succeed, even if the other servers are not accessible. */ - void drop( - const namespace_id_t &table_id, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t); - - /* `set_config()` changes the configuration of the table with the given ID. It may - block. If it returns successfully, the change will be visible in `find()`, etc. */ - void set_config( - const namespace_id_t &table_id, - const table_config_and_shards_change_t &table_config_and_shards_change, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t, - maybe_failed_table_op_exc_t, config_change_exc_t); - - /* `emergency_repair()` performs an emergency repair operation on the given table, - creating a new table epoch. If all of the replicas for a given shard are missing, it - will leave the shard alone if `allow_erase` is `false`, or replace the shard with - a new empty shard if `allow_erase` is `true`. If `dry_run` is `true` it will - compute the repair operation but not actually apply it. `simple_errors_found_out` - and `data_loss_found_out` will indicate whether the two types of errors were - detected. */ - void emergency_repair( - const namespace_id_t &table_id, - emergency_repair_mode_t mode, - bool dry_run, - signal_t *interruptor, - table_config_and_shards_t *new_config_out, - bool *erase_found_out, - bool *rollback_found_out) - THROWS_ONLY(interrupted_exc_t, no_such_table_exc_t, failed_table_op_exc_t, - maybe_failed_table_op_exc_t); - -private: - typedef std::pair - timestamped_basic_config_t; - - /* `create_or_emergency_repair()` factors out the common parts of `create()` and - `emergency_repair()`. */ - void create_or_emergency_repair( - const namespace_id_t &table_id, - const table_raft_state_t &raft_state, - const multi_table_manager_timestamp_epoch_t &epoch, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, failed_table_op_exc_t, - maybe_failed_table_op_exc_t); - - /* `get_status()` runs a status query. It can run for a specific table or every - table. If `servers` is `EVERY_SERVER`, it runs against every server for the table(s); - if `BEST_SERVER_ONLY`, it only runs against one server for each table, which will be - the most up-to-date server that can be found. If it fails to contact at least one - server for a given table, it calls `failure_callback()` on that table. If you specify - a particular table and that table doesn't exist, it throws `no_such_table_exc_t`. */ - enum class server_selector_t { EVERY_SERVER, BEST_SERVER_ONLY }; - void get_status( - const optional &table, - const table_status_request_t &request, - server_selector_t servers, - signal_t *interruptor, - const std::function &callback, - std::set *failures_out) - THROWS_ONLY(interrupted_exc_t); - - /* `retry()` calls `fun()` repeatedly. If `fun()` fails with a - `failed_table_op_exc_t` or `maybe_failed_table_op_exc_t`, then `retry()` catches the - exception, waits for some time, and calls `fun()` again. After some number of tries - it gives up and allows the exception to bubble up to the caller. */ - void retry(const std::function &fun, signal_t *interruptor); - - NORETURN void throw_appropriate_exception(const namespace_id_t &table_id) - THROWS_ONLY(no_such_table_exc_t, failed_table_op_exc_t); - - void wait_until_change_visible( - const namespace_id_t &table_id, - const std::function &cb, - signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t, maybe_failed_table_op_exc_t); - - mailbox_manager_t *const mailbox_manager; - multi_table_manager_t *const multi_table_manager; - watchable_map_t - *const multi_table_manager_directory; - watchable_map_t, table_manager_bcard_t> - *const table_manager_directory; - server_config_client_t *const server_config_client; - - /* `table_basic_configs` distributes the `table_basic_config_t`s from the - `multi_table_manager_t` to each thread, so that `find()`, `get_name()`, and - `list_names()` can run without blocking. */ - all_thread_watchable_map_var_t - table_basic_configs; -}; - -#endif /* CLUSTERING_TABLE_MANAGER_TABLE_META_CLIENT_HPP_ */ - diff --git a/src/clustering/table_manager/table_metadata.cc b/src/clustering/table_manager/table_metadata.cc deleted file mode 100644 index be808dd67de..00000000000 --- a/src/clustering/table_manager/table_metadata.cc +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "clustering/table_manager/table_metadata.hpp" - -#include "clustering/administration/datum_adapter.hpp" - -RDB_IMPL_SERIALIZABLE_2_SINCE_v2_1( - multi_table_manager_timestamp_t::epoch_t, timestamp, id); -RDB_IMPL_SERIALIZABLE_2_SINCE_v2_1( - multi_table_manager_timestamp_t, epoch, log_index); -RDB_IMPL_SERIALIZABLE_3_FOR_CLUSTER( - table_manager_bcard_t::leader_bcard_t, - uuid, set_config_mailbox, contract_ack_minidir_bcard); -RDB_IMPL_SERIALIZABLE_6_FOR_CLUSTER( - table_manager_bcard_t, - leader, timestamp, raft_member_id, raft_business_card, - execution_bcard_minidir_bcard, server_id); -RDB_IMPL_SERIALIZABLE_7_FOR_CLUSTER(table_status_request_t, - want_config, want_sindexes, want_raft_state, want_contract_acks, want_shard_status, - want_all_replicas_ready, all_replicas_ready_mode); -RDB_IMPL_SERIALIZABLE_7_FOR_CLUSTER(table_status_response_t, - config, sindexes, raft_state, raft_state_timestamp, contract_acks, shard_status, - all_replicas_ready); - -RDB_IMPL_SERIALIZABLE_8_FOR_CLUSTER( - multi_table_manager_bcard_t::action_message_t, - table_id, timestamp, status, basic_config, raft_member_id, initial_raft_state, - start_election_immediately, ack_addr); -RDB_IMPL_SERIALIZABLE_3_FOR_CLUSTER( - multi_table_manager_bcard_t::get_status_message_t, - table_ids, request, reply_addr); -RDB_IMPL_SERIALIZABLE_3_FOR_CLUSTER( - multi_table_manager_bcard_t, - action_mailbox, get_status_mailbox, server_id); - -RDB_IMPL_SERIALIZABLE_2_SINCE_v2_1(table_active_persistent_state_t, - epoch, raft_member_id); -RDB_IMPL_SERIALIZABLE_2_SINCE_v2_1(table_inactive_persistent_state_t, - second_hand_config, timestamp); - -const microtime_t multi_table_manager_timestamp_t::epoch_t::special_timestamp = - static_cast(std::numeric_limits::min()); - -multi_table_manager_timestamp_t::epoch_t -multi_table_manager_timestamp_t::epoch_t::min() { - epoch_t e; - e.id = nil_uuid(); - e.timestamp = 0; - return e; -} - -multi_table_manager_timestamp_t::epoch_t -multi_table_manager_timestamp_t::epoch_t::deletion() { - epoch_t e; - e.id = nil_uuid(); - e.timestamp = std::numeric_limits::max(); - return e; -} - -multi_table_manager_timestamp_t::epoch_t -multi_table_manager_timestamp_t::epoch_t::make(const epoch_t &prev) { - guarantee(prev.timestamp != std::numeric_limits::max()); - microtime_t old_timestamp = - (prev.timestamp == special_timestamp ? 0 : prev.timestamp); - - epoch_t e; - e.id = generate_uuid(); - e.timestamp = std::max(current_microtime(), old_timestamp + 1); - return e; -} - -multi_table_manager_timestamp_t::epoch_t -multi_table_manager_timestamp_t::epoch_t::migrate(time_t ts) { - epoch_t e; - e.id = generate_uuid(); - e.timestamp = static_cast(ts < 0 ? 0 : ts); - return e; -} - -ql::datum_t multi_table_manager_timestamp_t::epoch_t::to_datum() const { - ql::datum_object_builder_t builder; - builder.overwrite( - "timestamp", ql::datum_t(static_cast(timestamp))); - builder.overwrite("id", convert_uuid_to_datum(id)); - return std::move(builder).to_datum(); -} - -bool multi_table_manager_timestamp_t::epoch_t::is_unset() const { - return id.is_unset(); -} - -bool multi_table_manager_timestamp_t::epoch_t::is_deletion() const { - return id.is_nil() && timestamp == std::numeric_limits::max(); -} - -bool multi_table_manager_timestamp_t::epoch_t::operator==(const epoch_t &other) const { - return timestamp == other.timestamp && id == other.id; -} -bool multi_table_manager_timestamp_t::epoch_t::operator!=(const epoch_t &other) const { - return !(*this == other); -} - -bool multi_table_manager_timestamp_t::epoch_t::supersedes(const epoch_t &other) const { - // Workaround for issue #4668 - handle invalid timestamps from migration - microtime_t this_timestamp = - (timestamp == special_timestamp ? 0 : timestamp); - microtime_t other_timestamp = - (other.timestamp == special_timestamp ? 0 : other.timestamp); - - if (this_timestamp > other_timestamp) { - return true; - } else if (this_timestamp < other_timestamp) { - return false; - } else { - return other.id < id; - } -} diff --git a/src/clustering/table_manager/table_metadata.hpp b/src/clustering/table_manager/table_metadata.hpp deleted file mode 100644 index 8ab3d33319d..00000000000 --- a/src/clustering/table_manager/table_metadata.hpp +++ /dev/null @@ -1,370 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CLUSTERING_TABLE_MANAGER_TABLE_METADATA_HPP_ -#define CLUSTERING_TABLE_MANAGER_TABLE_METADATA_HPP_ - -#include "clustering/generic/minidir.hpp" -#include "clustering/generic/raft_core.hpp" -#include "clustering/generic/raft_network.hpp" -#include "clustering/table_contract/contract_metadata.hpp" -#include "clustering/table_contract/cpu_sharding.hpp" -#include "clustering/table_contract/executor/exec.hpp" -#include "containers/optional.hpp" -#include "rpc/mailbox/typed.hpp" - -namespace metadata { -class read_txn_t; -class write_txn_t; -} - -class multi_table_manager_timestamp_epoch_t { - using epoch_t = multi_table_manager_timestamp_epoch_t; -public: - static epoch_t min(); - static epoch_t deletion(); - static epoch_t migrate(time_t ts); - static epoch_t make(const epoch_t &prev); - - ql::datum_t to_datum() const; - - bool is_unset() const; - bool is_deletion() const; - bool operator==(const epoch_t &other) const; - bool operator!=(const epoch_t &other) const; - bool supersedes(const epoch_t &other) const; -private: - // Workaround for issue #4668 - invalid timestamps that were migrated - // These timestamps should never supercede any other timestamps - static const microtime_t special_timestamp; - - /* Every table's lifetime is divided into "epochs". Each epoch corresponds to - one Raft instance. Normally tables only have one epoch; a new epoch is created - only when the user manually overrides the Raft state, which requires creating a - new Raft instance. - - `timestamp` is the wall-clock time when the epoch began. `id` is a unique ID - created for the epoch. An epoch with a later `timestamp` supersedes an epoch - with an earlier `timestamp`. `id` breaks ties. Ties are possible because the - user may manually override the Raft state on both sides of a netsplit, for - example. */ - microtime_t timestamp; - uuid_u id; - - // Keep the class keyword here to satisfy VC++ - RDB_DECLARE_ME_SERIALIZABLE(class multi_table_manager_timestamp_epoch_t); -}; - -/* Every message to the `action_mailbox` has an `multi_table_manager_timestamp_t` -attached. This is used to filter out outdated instructions. */ -class multi_table_manager_timestamp_t { -public: - using epoch_t = multi_table_manager_timestamp_epoch_t; - - static multi_table_manager_timestamp_t min() { - multi_table_manager_timestamp_t ts; - ts.epoch = epoch_t::min(); - ts.log_index = 0; - return ts; - } - - static multi_table_manager_timestamp_t deletion() { - multi_table_manager_timestamp_t ts; - ts.epoch = epoch_t::deletion(); - ts.log_index = std::numeric_limits::max(); - return ts; - } - - bool is_deletion() const { - return epoch.is_deletion(); - } - - bool operator==(const multi_table_manager_timestamp_t &other) const { - return epoch == other.epoch && log_index == other.log_index; - } - bool operator!=(const multi_table_manager_timestamp_t &other) const { - return !(*this == other); - } - - bool supersedes(const multi_table_manager_timestamp_t &other) const { - if (epoch.supersedes(other.epoch)) { - return true; - } else if (other.epoch.supersedes(epoch)) { - return false; - } - return log_index > other.log_index; - } - - // TODO: make the data members private and strictly control how they may be changed - epoch_t epoch; - - /* Within each epoch, Raft log indices provide a monotonically increasing clock. */ - raft_log_index_t log_index; -}; -RDB_DECLARE_SERIALIZABLE(multi_table_manager_timestamp_t); - -/* In VERIFIED mode, the all replicas ready check makes sure that the leader -still has a quorum and can perform Raft transactions. This is relatively expensive -and causes disk writes and network overhead. -OUTDATED_OK skips that step, but might temporarily return an incorrect result. */ -enum class all_replicas_ready_mode_t { INCLUDE_RAFT_TEST = 0, EXCLUDE_RAFT_TEST }; -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE(all_replicas_ready_mode_t, - int8_t, - all_replicas_ready_mode_t::INCLUDE_RAFT_TEST, - all_replicas_ready_mode_t::EXCLUDE_RAFT_TEST); - -class table_status_request_t { -public: - table_status_request_t() : - want_config(false), want_sindexes(false), want_raft_state(false), - want_contract_acks(false), want_shard_status(false), - want_all_replicas_ready(false), - all_replicas_ready_mode(all_replicas_ready_mode_t::INCLUDE_RAFT_TEST) { } - - bool want_config; - bool want_sindexes; - bool want_raft_state; - bool want_contract_acks; - bool want_shard_status; - bool want_all_replicas_ready; - all_replicas_ready_mode_t all_replicas_ready_mode; -}; -RDB_DECLARE_SERIALIZABLE(table_status_request_t); - -class table_status_response_t { -public: - /* The booleans in `table_status_request_t` control whether each field of - `table_status_response_t` will be included or not. This is to avoid making an - expensive computation if the result will not be used. If a field is not requested, - its value is undefined (but typically empty or default-constructed). */ - - /* We must default-initialize boolean fields or they might cause out of range - errors during serialization and/or deserializtion. */ - table_status_response_t() : all_replicas_ready(false) { } - - /* `config` is controlled by `want_config`. */ - optional config; - - /* `sindexes` is controlled by `want_sindexes`. */ - std::map > sindexes; - - /* `raft_state` and `raft_state_timestamp` are controlled by `want_raft_state` */ - optional raft_state; - optional raft_state_timestamp; - - /* `contract_acks` is controlled by `want_contract_acks` */ - std::map contract_acks; - - /* `shard_status` is controlled by `want_shard_status`. */ - range_map_t shard_status; - - /* `all_replicas_ready` is controlled by `want_all_replicas_ready`. It will be set to - `true` if the responding server is leader and can confirm that all backfills are - completed, the status matches the config, etc. Otherwise it will be set to `false`. - */ - bool all_replicas_ready; -}; -RDB_DECLARE_SERIALIZABLE(table_status_response_t); - -class multi_table_manager_bcard_t { -public: - enum class status_t { ACTIVE, INACTIVE, DELETED, MAYBE_ACTIVE }; - - /* `action_mailbox` handles table creation and deletion, adding and removing servers - from the table, and manually overriding the table's configuration. - - The fields `status`, `basic_config`, `member_id`, and `initial_state` describe what - the state of the receiver is supposed to be. `timestamp` is a timestamp as of which - the information is accurate. - - The possibilities are as follows: - - `ACTIVE` means that the receiver is supposed to be hosting the table. `member_id` - and `initial_state` will contain the details. `basic_config` will be empty. - - `INACTIVE` means the receiver is not supposed to be hosting the table. - `basic_config` will be present but `member_id` and `initial_state` will be empty. - - `DELETED` means that the table has been deleted. The other three will be empty. - - `MAYBE_ACTIVE` means the sender doesn't know if the receiver is supposed to be - hosting the table or not. `basic_config` will be present but `member_id` and - `initial_state` will be empty. */ - struct action_message_t { - namespace_id_t table_id; - multi_table_manager_timestamp_t timestamp; - status_t status; - optional basic_config; - optional raft_member_id; - optional > initial_raft_state; - optional start_election_immediately; - mailbox_t<>::address_t ack_addr; - }; - - typedef mailbox_t action_mailbox_t; - action_mailbox_t::address_t action_mailbox; - - /* `get_status_mailbox` retrieves configurations, current statuses, etc. for one or - more tables. Many different types of status queries are combined into one mailbox in - order to allow more code to be re-used. */ - struct get_status_message_t { - std::set table_ids; - table_status_request_t request; - mailbox_t>::address_t reply_addr; - }; - typedef mailbox_t get_status_mailbox_t; - get_status_mailbox_t::address_t get_status_mailbox; - - /* The server ID of the server sending this business card. In theory you could figure - it out from the peer ID, but this is way more convenient. Proxy servers will set this - to `nil_uuid()`. */ - server_id_t server_id; -}; -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE( - multi_table_manager_bcard_t::status_t, - int8_t, - multi_table_manager_bcard_t::status_t::ACTIVE, - multi_table_manager_bcard_t::status_t::MAYBE_ACTIVE); -RDB_DECLARE_SERIALIZABLE(multi_table_manager_bcard_t::action_message_t); -RDB_DECLARE_SERIALIZABLE(multi_table_manager_bcard_t::get_status_message_t); -RDB_DECLARE_SERIALIZABLE(multi_table_manager_bcard_t); - -class table_manager_bcard_t { -public: - /* Whichever server is Raft leader will publish a `leader_bcard_t` in its directory. - This is the destination for config change messages, and also the way that contract - acks find their way to the contract coordinator. */ - class leader_bcard_t { - public: - /* Sometimes a server may stop being leader and then start again in quick - succession. It will have a new set of mailboxes, and so any messages that were in - flight to the old set of mailboxes will be dropped. Message senders need an easy - way to detect when this happens. The solution is the `uuid` field; every set of - mailboxes will get a unique UUID, so if the UUID changes, senders know they need - to re-send their messages. */ - uuid_u uuid; - - /* `set_config_mailbox` handles changes to the `table_config_and_shards_t`. These - changes may or may not involve adding and removing servers; if they do, then the - initial config change message will trigger subsequent action messages to add and - remove the servers. If the change was committed, it returns the action timestamp - for the commit; the client can use this to determine which servers have seen the - commit. If something goes wrong, it returns an empty `optional`, in which - case the change may or may not eventually be committed. */ - typedef mailbox_t< - table_config_and_shards_change_t, - mailbox_addr_t, bool> - > set_config_mailbox_t; - set_config_mailbox_t::address_t set_config_mailbox; - - /* `contract_executor_t`s for this table on other servers send contract acks to - the `contract_coordinator_t` for this table via this bcard. */ - minidir_bcard_t, contract_ack_t> - contract_ack_minidir_bcard; - }; - optional leader; - - /* This timestamp contains a `raft_log_index_t`. It would be expensive to update the - directory every time a Raft commit happened. Therefore, this timestamp is only - guaranteed to be updated when: - - The server has entered a new epoch for the table, or - - The server has entered or left the Raft cluster, or */ - multi_table_manager_timestamp_t timestamp; - - /* The other members of the Raft cluster send Raft RPCs through - `raft_business_card`. */ - raft_member_id_t raft_member_id; - raft_business_card_t raft_business_card; - - /* `contract_executor_t`s for this table on other servers send messages to the - `contract_executor_t` on this server via this minidir bcard. */ - minidir_bcard_t, contract_execution_bcard_t> - execution_bcard_minidir_bcard; - - /* The server ID of the server sending this business card. In theory you could figure - it out from the peer ID, but this is way more convenient. */ - server_id_t server_id; -}; -RDB_DECLARE_SERIALIZABLE(table_manager_bcard_t::leader_bcard_t); -RDB_DECLARE_SERIALIZABLE(table_manager_bcard_t); - -/* If we are an active member for a given table, we'll store a -`table_active_persistent_state_t` plus a `raft_persistent_state_t`; -the latter will be stored as individual components for better efficiency. If we are not -an active member, we'll store a `table_inactive_persistent_state_t`. If the table is -deleted we won't store anything. */ - -class table_active_persistent_state_t { -public: - multi_table_manager_timestamp_t::epoch_t epoch; - raft_member_id_t raft_member_id; -}; - -RDB_DECLARE_SERIALIZABLE(table_active_persistent_state_t); - -class table_inactive_persistent_state_t { -public: - table_basic_config_t second_hand_config; - - /* `timestamp` records a time at which `second_hand_config` is known to have been - correct. */ - multi_table_manager_timestamp_t timestamp; -}; - -RDB_DECLARE_SERIALIZABLE(table_inactive_persistent_state_t); - -class table_persistence_interface_t { -public: - /* Some of these methods return a `raft_storage_interface_t *`. This returned pointer - will remain valid until the next call to `read_all_metadata()`, `write_metadata_*()` - or `delete_metadata()` affecting that table. */ - - /* Finds all tables stored in the metadata and calls the appropriate callback. Note - that this invalidates any existing `raft_storage_interface_t`s! */ - virtual void read_all_metadata( - const std::function *raft_storage, - metadata::read_txn_t *metadata_read_txn)> &active_cb, - const std::function &inactive_cb, - signal_t *interruptor) = 0; - - /* `write_metadata_active()` sets the stored metadata for the table to be the given - `state` and `raft_state`. It then returns a `raft_storage_interface_t *` that can be - used to make incremental updates to the Raft state. Any previously existing - `raft_storage_interface_t *` for this table will be invalidated. */ - virtual void write_metadata_active( - const namespace_id_t &table_id, - const table_active_persistent_state_t &state, - const raft_persistent_state_t &raft_state, - raft_storage_interface_t **raft_storage_out) = 0; - - /* `write_metadata_inactive()` sets the stored metadata for the table to be the given - `state`, which is inactive. */ - virtual void write_metadata_inactive( - const namespace_id_t &table_id, - const table_inactive_persistent_state_t &state) = 0; - - /* `delete_metadata()` deletes all metadata for the table. */ - virtual void delete_metadata( - const namespace_id_t &table_id) = 0; - - virtual void load_multistore( - const namespace_id_t &table_id, - metadata::read_txn_t *metadata_read_txn, - scoped_ptr_t *multistore_ptr_out, - signal_t *interruptor, - perfmon_collection_t *perfmon_collection_serializers) = 0; - virtual void create_multistore( - const namespace_id_t &table_id, - scoped_ptr_t *multistore_ptr_out, - signal_t *interruptor, - perfmon_collection_t *perfmon_collection_serializers) = 0; - virtual void destroy_multistore( - const namespace_id_t &table_id, - scoped_ptr_t *multistore_ptr_in) = 0; - -protected: - virtual ~table_persistence_interface_t() { } -}; - -#endif /* CLUSTERING_TABLE_MANAGER_TABLE_METADATA_HPP_ */ - diff --git a/src/concurrency/access.hpp b/src/concurrency/access.hpp deleted file mode 100644 index e1991233bdb..00000000000 --- a/src/concurrency/access.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef CONCURRENCY_ACCESS_HPP_ -#define CONCURRENCY_ACCESS_HPP_ - -// For specifying read or write access. The only valid values of this type are, and -// always will be, `read` and `write`. -enum class access_t { read, write }; - -// For specifying read access. -enum class read_access_t { read }; - -// For specifying write access. -enum class write_access_t { write }; - -#endif // CONCURRENCY_ACCESS_HPP_ diff --git a/src/concurrency/auto_drainer.cc b/src/concurrency/auto_drainer.cc deleted file mode 100644 index 1fd9f20152f..00000000000 --- a/src/concurrency/auto_drainer.cc +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "concurrency/auto_drainer.hpp" -#include "concurrency/interruptor.hpp" - -#include "arch/runtime/coroutines.hpp" - -auto_drainer_t::auto_drainer_t() : - refcount(0) { } - -auto_drainer_t::~auto_drainer_t() { - if (!draining.is_pulsed()) { - begin_draining(); - } - drained.wait_lazily_unordered(); - guarantee(refcount == 0); -} - -auto_drainer_t::lock_t::lock_t() : parent(nullptr) { -} - -auto_drainer_t::lock_t::lock_t(auto_drainer_t *p, throw_if_draining_t thr) : parent(p) { - guarantee(parent != nullptr); - if (thr == throw_if_draining_t::YES && parent->is_draining()) { - throw interrupted_exc_t(); - } - guarantee(!parent->is_draining(), - "New processes should not acquire a draining `auto_drainer_t`."); - parent->incref(); -} - -auto_drainer_t::lock_t::lock_t(const lock_t &l) : parent(l.parent) { - if (parent) parent->incref(); -} - -auto_drainer_t::lock_t &auto_drainer_t::lock_t::operator=(const lock_t &l) { - if (l.parent) l.parent->incref(); - if (parent) parent->decref(); - parent = l.parent; - return *this; -} - -auto_drainer_t::lock_t::lock_t(lock_t &&l) : parent(l.parent) { - l.parent = nullptr; -} - -auto_drainer_t::lock_t &auto_drainer_t::lock_t::operator=(lock_t &&l) { - lock_t tmp(std::move(l)); - std::swap(parent, tmp.parent); - return *this; -} - -auto_drainer_t::lock_t auto_drainer_t::lock() { - return auto_drainer_t::lock_t(this); -} - -void auto_drainer_t::lock_t::reset() { - if (parent) parent->decref(); - parent = nullptr; -} - -bool auto_drainer_t::lock_t::has_lock() const { - return parent != nullptr; -} - - -signal_t *auto_drainer_t::lock_t::get_drain_signal() const { - guarantee(parent, "calling `get_drain_signal()` on a nil " - "`auto_drainer_t::lock_t`."); - return &parent->draining; -} - -void auto_drainer_t::lock_t::assert_is_holding(auto_drainer_t *p) const { - guarantee(p); - guarantee(parent); - guarantee(p == parent); -} - -auto_drainer_t::lock_t::~lock_t() { - if (parent) parent->decref(); -} - -void auto_drainer_t::begin_draining() { - assert_not_draining(); - draining.pulse(); - if (refcount == 0) { - drained.pulse(); - } -} - -void auto_drainer_t::drain() { - begin_draining(); - drained.wait_lazily_unordered(); - guarantee(refcount == 0); -} - -void auto_drainer_t::incref() { - assert_thread(); - refcount++; -} - -void auto_drainer_t::decref() { - assert_thread(); - refcount--; - if (refcount == 0 && draining.is_pulsed()) { - drained.pulse(); - } -} diff --git a/src/concurrency/auto_drainer.hpp b/src/concurrency/auto_drainer.hpp deleted file mode 100644 index 639391359fe..00000000000 --- a/src/concurrency/auto_drainer.hpp +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_AUTO_DRAINER_HPP_ -#define CONCURRENCY_AUTO_DRAINER_HPP_ - -#include "concurrency/cond_var.hpp" - -/* `auto_drainer_t` is useful when you have a resource and some number of -processes that use that resource, and you need to be sure that all the processes -are done before the resource is destroyed. - -Construct an `auto_drainer_t` for the resource. Have each process construct an -`auto_drainer_t::lock_t` and hold it. When the resource must be destroyed, first -prevent any new processes from starting and then call the `auto_drainer_t`'s -`drain()` method. `drain()` will block until all `auto_drainer_t`s have been -destroyed. - -The signal returned by `lock_t::get_drain_signal()` will be pulsed when the -`auto_drainer_t`'s destructor is called. You can use this to interrupt the -running processes if it makes sense for your situation. */ - -enum class throw_if_draining_t { NO, YES }; - -class auto_drainer_t : public home_thread_mixin_t { -public: - auto_drainer_t(); - - /* If `drain()` was not called already, then the destructor will call it - automatically. However, this behavior is deprecated, for two reasons: - 1) There are legitimate reasons to access an auto_drainer_t while it's - draining, but it's bad form to access an object during its destructor. - 2) This encourages people to make an `auto_drainer_t` a member of their - class and rely on the destruction order of members to make sure it - drains before the other members are destroyed, which is fragile. - So new code should call `drain()` before running `~auto_drainer_t()`. */ - ~auto_drainer_t(); - - class lock_t { - public: - lock_t(); - explicit lock_t(auto_drainer_t *, - throw_if_draining_t thr = throw_if_draining_t::NO); - lock_t(const lock_t &); - lock_t &operator=(const lock_t &); - lock_t(lock_t &&); - lock_t &operator=(lock_t &&); - - void reset(); - bool has_lock() const; - signal_t *get_drain_signal() const; - void assert_is_holding(auto_drainer_t *) const; - ~lock_t(); - private: - auto_drainer_t *parent; - }; - - lock_t lock(); - - void assert_not_draining() { - guarantee(!is_draining()); - } - - void assert_draining() { - guarantee(is_draining()); - } - - bool is_draining() { - return draining.is_pulsed(); - } - - signal_t *get_drain_signal() { - return &draining; - } - - void begin_draining(); - - void drain(); - - void rethread(threadnum_t new_thread) { - guarantee(refcount == 0); - real_home_thread = new_thread; - draining.rethread(new_thread); - } - -private: - void incref(); - void decref(); - - cond_t draining; - int refcount; - cond_t drained; -}; - -/* For example, here's a toy class to handle TCP connections: - - class toy_tcp_server_t { - - public: - toy_tcp_server_t(int port, foo_t cxt) : - context(cxt), - listener(new tcp_listener_t(port, - std::bind(&toy_tcp_server_t::serve, - this, - auto_drainer_t::lock_t(&drainer), - _1 - ) - )) - { } - - ~toy_tcp_server_t() { - listener.reset(); - drainer.drain(); - } - - private: - void serve(auto_drainer_t::lock_t keepalive, scoped_ptr_t &conn) { - signal_t *interruptor = keepalive.get_drain_signal(); - try { - serve_queries_until_interrupted(conn, context, interruptor); - } catch (const interrupted_exc_t &) { - // Ignore - } - } - - // Destructor order is important here; `listener` must be destroyed before - // `drainer` because it acquires `drainer`, but `drainer` must be destroyed - // before `context` because `serve()` accesses `context` - foo_t context; - auto_drainer_t drainer; - scoped_ptr_t listener; - }; - -When a TCP connection comes in, the functor created by `std::bind()` is -called, and it makes a copy of the `auto_drainer_t::lock_t` and passes it to -`serve()`. When the `toy_tcp_server_t` is destroyed, first the `tcp_listener_t` -destructor is run (so no new connections are accepted) and then the -`auto_drainer_t::drain()` is run. `keepalive.get_drain_signal()` is pulsed, -which interrupts `serve_queries_until_interrupted()`, causing `serve()` to -return and destroy `keepalive`. This way, `drain()` blocks until -all the copies of `serve` have returned. - -When an `auto_drainer_t` is used as a member of a class, it usually must be -destroyed in a specific order with respect to the other class members. In -particular, the destructor order is usually as follows: - -1. Destroy any `mailbox_t`s, `watchable_t` subscriptions, - `repeating_timer_t`s, or other member variables that will spontaneously - call callbacks or spawn coroutines that access the class. -2. Destroy the `auto_drainer_t` itself, which will block until any such - coroutines have stopped. -3. Destroy all the other members of the class. - -When writing code that relies on the destructor order, it's good practice to -leave a comment explaining which things must be destroyed before which other -things and why. */ - -#endif /* CONCURRENCY_AUTO_DRAINER_HPP_ */ diff --git a/src/concurrency/cache_line_padded.hpp b/src/concurrency/cache_line_padded.hpp deleted file mode 100644 index 509ab2d37da..00000000000 --- a/src/concurrency/cache_line_padded.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef CONCURRENCY_CACHE_LINE_PADDED_HPP_ -#define CONCURRENCY_CACHE_LINE_PADDED_HPP_ - -#define CACHE_LINE_SIZE 64 - -/* Pad a value to the size of one or multiple cache lines to avoid false sharing. - */ -#define COMPUTE_PADDING_SIZE(value, alignment) \ - (alignment) - ((((value) + (alignment) - 1) % (alignment)) + 1) - -template -struct cache_line_padded_t { - cache_line_padded_t() { } - template - explicit cache_line_padded_t(Args &&... args) : value(std::forward(args)...) { } - value_t value; - char padding[COMPUTE_PADDING_SIZE(sizeof(value_t), CACHE_LINE_SIZE)]; -}; -#undef COMPUTE_PADDING_SIZE - - -#endif // CONCURRENCY_CACHE_LINE_PADDED_HPP_ diff --git a/src/concurrency/cond_var.cc b/src/concurrency/cond_var.cc deleted file mode 100644 index 568fa7ce0e7..00000000000 --- a/src/concurrency/cond_var.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "concurrency/cond_var.hpp" - -#include "arch/runtime/coroutines.hpp" -#include "do_on_thread.hpp" - -void cond_t::pulse_if_not_already_pulsed() { - assert_thread(); - if (!is_pulsed()) { - pulse(); - } -} - -void one_waiter_cond_t::pulse() { - rassert(!pulsed_); - pulsed_ = true; - if (waiter_) { - coro_t *tmp = waiter_; - waiter_ = nullptr; - tmp->notify_later_ordered(); - } -} - -void one_waiter_cond_t::wait_ordered() { - rassert(!waiter_); - if (!pulsed_) { - waiter_ = coro_t::self(); - coro_t::wait(); - rassert(pulsed_); - } -} diff --git a/src/concurrency/cond_var.hpp b/src/concurrency/cond_var.hpp deleted file mode 100644 index acd9f9f3d71..00000000000 --- a/src/concurrency/cond_var.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_COND_VAR_HPP_ -#define CONCURRENCY_COND_VAR_HPP_ - -#include "errors.hpp" -#include "concurrency/signal.hpp" - -/* A cond_t is the simplest form of signal. It just exposes the pulse() method -directly. */ - -class coro_t; - -class cond_t : public signal_t { -public: - cond_t() { } - cond_t(cond_t &&movee) : signal_t(std::move(movee)) { } - void pulse_if_not_already_pulsed(); - - using signal_t::pulse; - using signal_t::reset; - -private: - DISABLE_COPYING(cond_t); -}; - -class one_waiter_cond_t { -public: - one_waiter_cond_t() : pulsed_(false), waiter_(nullptr) { } - ~one_waiter_cond_t() { - rassert(pulsed_); - rassert(!waiter_); - } - - void pulse(); - - void wait_ordered(); - -private: - bool pulsed_; - coro_t *waiter_; - - DISABLE_COPYING(one_waiter_cond_t); -}; - -#endif /* CONCURRENCY_COND_VAR_HPP_ */ diff --git a/src/concurrency/coro_pool.hpp b/src/concurrency/coro_pool.hpp deleted file mode 100644 index 4b2188ae6e5..00000000000 --- a/src/concurrency/coro_pool.hpp +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_CORO_POOL_HPP_ -#define CONCURRENCY_CORO_POOL_HPP_ - -#include - -#include "concurrency/auto_drainer.hpp" -#include "concurrency/interruptor.hpp" -#include "concurrency/queue/passive_producer.hpp" - -/* coro_pool_t maintains a bunch of coroutines; when you give it tasks, it -distributes them among the coroutines. It draws its tasks from a -`passive_producer_t`. */ - -template -class coro_pool_callback_t { -public: - virtual ~coro_pool_callback_t() { } - virtual void coro_pool_callback(T, signal_t *) = 0; -}; - -template -class std_function_callback_t : public coro_pool_callback_t { -public: - explicit std_function_callback_t(std::function _f) - : f(std::move(_f)) - { } - void coro_pool_callback(T t, signal_t *interruptor) { - f(t, interruptor); - } -private: - std::function f; - - DISABLE_COPYING(std_function_callback_t); -}; - -template -class coro_pool_t : private availability_callback_t, public home_thread_mixin_t { -public: - coro_pool_t(size_t _worker_count, passive_producer_t *_source, coro_pool_callback_t *_callback) - : max_worker_count(_worker_count), - active_worker_count(0), - source(_source), - callback(_callback) { - rassert(max_worker_count > 0); - on_source_availability_changed(); // Start process if necessary - source->available->set_callback(this); - } - - ~coro_pool_t() { - assert_thread(); - source->available->unset_callback(); - } - - void rethread(threadnum_t new_thread) { - /* Can't rethread while there are active operations */ - rassert(active_worker_count == 0); - rassert(!source->available->get()); - real_home_thread = new_thread; - coro_drain_semaphore.rethread(new_thread); - } - -private: - void worker_run(T object, auto_drainer_t::lock_t coro_drain_semaphore_lock) THROWS_NOTHING { - assert_thread(); - try { - while (!coro_drain_semaphore_lock.get_drain_signal()->is_pulsed()) { - callback->coro_pool_callback(object, coro_drain_semaphore_lock.get_drain_signal()); - if (source->available->get()) { - object = source->pop(); - } else { - break; - } - coro_t::yield(); - } - } catch (const interrupted_exc_t &) { - rassert(coro_drain_semaphore_lock.get_drain_signal()->is_pulsed()); - } - --active_worker_count; - } - - void on_source_availability_changed() { - assert_thread(); - while (source->available->get() && active_worker_count < max_worker_count) { - ++active_worker_count; - coro_t::spawn_sometime(std::bind( - &coro_pool_t::worker_run, this, - source->pop(), auto_drainer_t::lock_t(&coro_drain_semaphore))); - } - } - - int max_worker_count, active_worker_count; - passive_producer_t *source; - coro_pool_callback_t *callback; - auto_drainer_t coro_drain_semaphore; -}; - -class calling_callback_t : public coro_pool_callback_t > { -public: - void coro_pool_callback(std::function f, UNUSED signal_t *interruptor) { - f(); - } -}; - -#endif /* CONCURRENCY_CORO_POOL_HPP_ */ - diff --git a/src/concurrency/cross_thread_mutex.cc b/src/concurrency/cross_thread_mutex.cc deleted file mode 100644 index 859416b8ec6..00000000000 --- a/src/concurrency/cross_thread_mutex.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "concurrency/cross_thread_mutex.hpp" - -#include "arch/runtime/coroutines.hpp" - - -cross_thread_mutex_t::acq_t::acq_t(cross_thread_mutex_t *l) : lock_(nullptr) { - reset(l); -} - -cross_thread_mutex_t::acq_t::~acq_t() { - reset(); -} - -void cross_thread_mutex_t::acq_t::reset() { - if (lock_) { - lock_->unlock(); - } - lock_ = nullptr; -} - -void cross_thread_mutex_t::acq_t::reset(cross_thread_mutex_t *l) { - reset(); - lock_ = l; - lock_->co_lock(); -} - -void cross_thread_mutex_t::co_lock() { - bool do_wait = false; - { - spinlock_acq_t acq(&spinlock); - if (locked_count > 0 && lock_holder != coro_t::self()) { - waiters.push_back(coro_t::self()); - do_wait = true; - } else { - guarantee(locked_count == 0 || is_recursive, - "Deadlock detected. A coroutine tried to acquire a mutex which it is " - "already holding."); - lock_holder = coro_t::self(); - ++locked_count; - } - } - if (do_wait) { - coro_t::wait(); - } -} - -void cross_thread_mutex_t::unlock() { - spinlock_acq_t acq(&spinlock); - rassert(locked_count > 0); - rassert(lock_holder == coro_t::self()); - --locked_count; - if (locked_count == 0 && !waiters.empty()) { - coro_t *next = waiters.front(); - waiters.pop_front(); - ++locked_count; - lock_holder = next; - next->notify_sometime(); - } -} diff --git a/src/concurrency/cross_thread_mutex.hpp b/src/concurrency/cross_thread_mutex.hpp deleted file mode 100644 index b590a61ba30..00000000000 --- a/src/concurrency/cross_thread_mutex.hpp +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_CROSS_THREAD_MUTEX_HPP_ -#define CONCURRENCY_CROSS_THREAD_MUTEX_HPP_ - -#include -#include - -#include "arch/spinlock.hpp" - -class coro_t; -class cross_thread_mutex_t; - -/* `cross_thread_mutex_t` behaves like `mutex_t`, but can be used across - * multiple threads. - * It also allows recursive lock acquisition from the same coroutine if desired. - * It is more efficient than doing - * `{ on_thread_t t(mutex_home_thread); co_lock_mutex(&m); }` - * because the thread switch is avoided. - * In single-threaded use cases, it comes with more overhead than a regular - * `mutex_t` though. */ -class cross_thread_mutex_t { -public: - class acq_t { - public: - acq_t() : lock_(nullptr) { } - explicit acq_t(cross_thread_mutex_t *l); - acq_t(acq_t &&movee) - : lock_(nullptr) { - swap(movee); - } - ~acq_t(); - void reset(); - void reset(cross_thread_mutex_t *l); - void assert_is_holding(DEBUG_VAR cross_thread_mutex_t *m) const { - rassert(lock_ == m); - } - void swap(cross_thread_mutex_t::acq_t &other) { - std::swap(lock_, other.lock_); - } - private: - friend void swap(acq_t &, acq_t &); - cross_thread_mutex_t *lock_; - DISABLE_COPYING(acq_t); - }; - - explicit cross_thread_mutex_t(bool recursive = false) : - is_recursive(recursive), locked_count(0), lock_holder(nullptr) { } - ~cross_thread_mutex_t() { -#ifndef NDEBUG - spinlock_acq_t acq(&spinlock); - rassert(locked_count == 0); -#endif - } - - bool is_locked() { - spinlock_acq_t acq(&spinlock); - return locked_count > 0; - } - -private: - // These are private. Use `acq_t` to lock and unlock the mutex. - void co_lock(); - void unlock(); - - spinlock_t spinlock; - bool is_recursive; - int locked_count; - coro_t *lock_holder; - std::deque waiters; - - DISABLE_COPYING(cross_thread_mutex_t); -}; - -#endif /* CONCURRENCY_CROSS_THREAD_MUTEX_HPP_ */ diff --git a/src/concurrency/cross_thread_semaphore.hpp b/src/concurrency/cross_thread_semaphore.hpp deleted file mode 100644 index c7342632523..00000000000 --- a/src/concurrency/cross_thread_semaphore.hpp +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_CROSS_THREAD_SEMAPHORE_HPP_ -#define CONCURRENCY_CROSS_THREAD_SEMAPHORE_HPP_ - -#include "containers/scoped.hpp" -#include "containers/intrusive_list.hpp" -#include "concurrency/interruptor.hpp" -#include "concurrency/promise.hpp" -#include "arch/io/concurrency.hpp" -#include "arch/runtime/runtime.hpp" -#include "arch/runtime/coroutines.hpp" - -// This class implements a semaphore that can be used across threads -// Each element acquired from the semaphore is an item of type value_t, which may -// be changed before being returned to the semaphore with `unlock()` -template -class cross_thread_semaphore_t { -public: - // Constructs max_count elements with the given arguments - template - explicit cross_thread_semaphore_t(size_t max_count, Args&&... args) : - available_value_index(0), values(max_count) - { - for (size_t i = 0; i < values.size(); ++i) { - values[i] = new value_t(std::forward(args)...); - } - } - - // Acquires all outstanding elements before destruction completes - ~cross_thread_semaphore_t(); - - class lock_t { - public: - explicit lock_t(cross_thread_semaphore_t *_parent, signal_t *interruptor) : - parent(_parent), value(parent->lock(interruptor)) { } - - ~lock_t() { - parent->unlock(value); - } - - value_t *get_value() { return value; } - - private: - cross_thread_semaphore_t *parent; - value_t *value; - DISABLE_COPYING(lock_t); - }; - -private: - // Class used to queue up requests for items - class request_node_t : public intrusive_list_node_t { - public: - explicit request_node_t(promise_t *_promise_out) : - thread_id(get_thread_id()), - promise_out(_promise_out) { - guarantee(promise_out != nullptr); - } - - bool is_abandoned() const { return promise_out == nullptr; } - threadnum_t get_thread() const { return thread_id; } - void abandon() { promise_out = nullptr; } - - void fulfill_promise(value_t *value) { - guarantee(!is_abandoned()); - guarantee(get_thread_id() == thread_id); - promise_out->pulse(value); - } - - private: - threadnum_t thread_id; - promise_t *promise_out; - DISABLE_COPYING(request_node_t); - }; - - // Class that allows us to cleanly get a value when allocated on the stack - class request_t { - public: - explicit request_t(cross_thread_semaphore_t *_parent); - ~request_t(); - - value_t *wait_and_get(signal_t *interruptor); - - private: - cross_thread_semaphore_t *parent; - - // Track whether we have given the value to anyone. If not, we have to re-release it - bool value_returned; - promise_t value_promise; - - // The request in the request_queue. We allocate this, but ownership is passed to - // the parent when we put it in the queue - request_node_t *request; - }; - - value_t *lock(signal_t *interruptor); - void unlock(value_t *value); - - // Mutex to control access, since a lock may be constructed from any thread - // The mutex is guaranteed to be released within constant time - system_mutex_t mutex; - - // Coroutine to switch to the target thread and hand off the acquired item - void pass_value_coroutine(request_node_t *request, - value_t *value); - - size_t available_value_index; - scoped_array_t values; - intrusive_list_t request_queue; - - DISABLE_COPYING(cross_thread_semaphore_t); -}; - -// Acquires all outstanding elements before destruction completes -template -cross_thread_semaphore_t::~cross_thread_semaphore_t() { - for (size_t i = 0; i < values.size(); ++i) { - delete lock(nullptr); - } -} - -template -void cross_thread_semaphore_t::pass_value_coroutine(request_node_t *request, - value_t *value) { - on_thread_t rethreader(request->get_thread()); - - if (request->is_abandoned()) { - // No one is waiting anymore, re-release the item - delete request; - unlock(value); - } else { - request->fulfill_promise(value); - delete request; - } -} - -template -value_t *cross_thread_semaphore_t::lock(signal_t *interruptor) { - system_mutex_t::lock_t _lock(&mutex); - value_t *result = nullptr; - - if (available_value_index == values.size()) { - request_t request(this); - _lock.unlock(); - result = request.wait_and_get(interruptor); - } else { - result = values[available_value_index]; - values[available_value_index] = nullptr; - ++available_value_index; - } - - guarantee(result != nullptr); - return result; -} - -template -void cross_thread_semaphore_t::unlock(value_t *value) { - system_mutex_t::lock_t _lock(&mutex); - - if (request_queue.size() > 0) { - request_node_t *request = nullptr; - while (request_queue.size() > 0) { - request = request_queue.head(); - request_queue.remove(request); - - if (!request->is_abandoned()) { - break; - } - - delete request; - request = nullptr; - } - - if (request != nullptr) { - coro_t::spawn_sometime(std::bind(&cross_thread_semaphore_t::pass_value_coroutine, - this, request, value)); - return; - } - } - - // If we get here, there were no valid requests in the queue - guarantee(available_value_index > 0); - guarantee(values[available_value_index - 1] == nullptr); - values[available_value_index - 1] = value; - --available_value_index; -} - -template -cross_thread_semaphore_t::request_t::request_t(cross_thread_semaphore_t *_parent) : - parent(_parent), - value_returned(false), - request(new request_node_t(&value_promise)) -{ - parent->request_queue.push_back(request); -} - -template -cross_thread_semaphore_t::request_t::~request_t() { - if (!value_promise.is_pulsed()) { - // No item was given to us, just abandon the request - request->abandon(); - } else if (!value_returned) { - // We were given an item, but no one took it, release it - value_t *value; - bool gotten = value_promise.try_get_value(&value); - guarantee(gotten); - parent->unlock(value); - } -} - -template -value_t *cross_thread_semaphore_t::request_t::wait_and_get(signal_t *interruptor) { - value_t *result = nullptr; - - if (interruptor == nullptr) { - value_promise.wait(); - } else { - wait_interruptible(value_promise.get_ready_signal(), interruptor); - } - - bool gotten = value_promise.try_get_value(&result); - guarantee(gotten); - - value_returned = true; - return result; -} - -#endif /* CONCURRENCY_CROSS_THREAD_SEMAPHORE_HPP_ */ diff --git a/src/concurrency/cross_thread_signal.cc b/src/concurrency/cross_thread_signal.cc deleted file mode 100644 index ea89184b975..00000000000 --- a/src/concurrency/cross_thread_signal.cc +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "concurrency/cross_thread_signal.hpp" - -#include "arch/runtime/coroutines.hpp" - -void cross_thread_signal_subscription_t::run() { - parent_->on_signal_pulsed(keepalive_); -} - -cross_thread_signal_t::cross_thread_signal_t(signal_t *source, threadnum_t dest) : - source_thread(get_thread_id()), dest_thread(dest), - rethreader(static_cast(this), dest_thread), - subs(this, auto_drainer_t::lock_t(&drainer)) { - rassert(source->home_thread() == source_thread); - subs.reset(source); -} - -void cross_thread_signal_t::on_signal_pulsed(auto_drainer_t::lock_t keepalive) { - /* We can't do anything that blocks when we're in a signal callback, so we - have to spawn a new coroutine to do the thread switching. */ - coro_t::spawn_sometime(std::bind(&cross_thread_signal_t::deliver, this, keepalive)); -} - -void cross_thread_signal_t::deliver(UNUSED auto_drainer_t::lock_t keepalive) { - on_thread_t thread_switcher(dest_thread); - signal_t::pulse(); -} diff --git a/src/concurrency/cross_thread_signal.hpp b/src/concurrency/cross_thread_signal.hpp deleted file mode 100644 index b136ae70440..00000000000 --- a/src/concurrency/cross_thread_signal.hpp +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_CROSS_THREAD_SIGNAL_HPP_ -#define CONCURRENCY_CROSS_THREAD_SIGNAL_HPP_ - -#include "concurrency/signal.hpp" -#include "concurrency/auto_drainer.hpp" - -class cross_thread_signal_t; - -class cross_thread_signal_subscription_t : public signal_t::subscription_t { -public: - cross_thread_signal_subscription_t(cross_thread_signal_t *parent, - auto_drainer_t::lock_t keepalive) - : parent_(parent), keepalive_(keepalive) { } - - virtual void run(); - -private: - cross_thread_signal_t *parent_; - auto_drainer_t::lock_t keepalive_; - DISABLE_COPYING(cross_thread_signal_subscription_t); -}; - -/* `cross_thread_signal_t` is useful when you have a `signal_t` on some -thread, but you wish you had the same `signal_t` on a different thread. -Construct a `cross_thread_signal_t` on the original thread, and pass the new -thread to its constructor. It will deliver a notification to the specified -thread when the original signal is pulsed. */ - -class cross_thread_signal_t : - public signal_t -{ -public: - cross_thread_signal_t(signal_t *source, threadnum_t dest_thread); - -private: - friend class cross_thread_signal_subscription_t; - void on_signal_pulsed(auto_drainer_t::lock_t); - void deliver(auto_drainer_t::lock_t); - - threadnum_t source_thread; - threadnum_t dest_thread; - - /* This object's constructor rethreads our internal `signal_t` to our other - thread, and then reverses it in the destructor. It must be a separate object - instead of logic in the constructor/destructor because its destructor must - be run after `drainer`'s destructor. */ - class rethreader_t { - public: - rethreader_t(signal_t *s, threadnum_t dest) : - signal(s), original(signal->home_thread()) - { - signal->rethread(dest); - } - ~rethreader_t() { - signal->rethread(original); - } - private: - signal_t *const signal; - const threadnum_t original; - } rethreader; - - /* `drainer` makes sure we don't shut down with a signal still in flight */ - auto_drainer_t drainer; - - /* The destructor for `subs` must be run before the destructor for `drainer` - because `drainer`'s destructor will block until all the - `auto_drainer_t::lock_t` objects are gone, and `subs`'s callback holds an - `auto_drainer_t::lock_t`. */ - cross_thread_signal_subscription_t subs; -}; - -#endif /* CONCURRENCY_CROSS_THREAD_SIGNAL_HPP_ */ diff --git a/src/concurrency/cross_thread_watchable.hpp b/src/concurrency/cross_thread_watchable.hpp deleted file mode 100644 index a46ec36d2de..00000000000 --- a/src/concurrency/cross_thread_watchable.hpp +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_CROSS_THREAD_WATCHABLE_HPP_ -#define CONCURRENCY_CROSS_THREAD_WATCHABLE_HPP_ - -#include "arch/runtime/runtime.hpp" -#include "concurrency/watchable.hpp" -#include "concurrency/watchable_map.hpp" -#include "concurrency/auto_drainer.hpp" -#include "concurrency/pump_coro.hpp" -#include "containers/optional.hpp" - -/* `cross_thread_watchable_variable_t` is used to "proxy" a `watchable_t` from -one thread to another. Create the `cross_thread_watchable_variable_t` on the -source thread; then call `get_watchable()`, and you will get a watchable that is -usable on the `_dest_thread` that you passed to the constructor. - -`cross_thread_watchable_map_var_t` is similar but for `watchable_map_t`. - -See also: `cross_thread_signal_t`, which is the same thing for `signal_t`. */ - -template -class cross_thread_watchable_variable_t -{ -public: - cross_thread_watchable_variable_t( - const clone_ptr_t > &watchable, - threadnum_t _dest_thread); - - clone_ptr_t > get_watchable() { - return clone_ptr_t >(watchable.clone()); - } - - threadnum_t home_thread() { return watchable_thread; } - - template - void apply_read(Callable &&read) { - ASSERT_NO_CORO_WAITING; - const value_t *const_value = &value; - read(const_value); - } - -private: - friend class cross_thread_watcher_subscription_t; - void deliver(signal_t *interruptor); - - class w_t : public watchable_t { - public: - explicit w_t(cross_thread_watchable_variable_t *p) : parent(p) { } - - w_t *clone() const { - return new w_t(parent); - } - value_t get() { - return parent->value; - } - void apply_read(const std::function &read) { - return parent->apply_read(read); - } - publisher_t > *get_publisher() { - return parent->publisher_controller.get_publisher(); - } - rwi_lock_assertion_t *get_rwi_lock_assertion() { - return &parent->rwi_lock_assertion; - } - void rethread(threadnum_t thread) { - home_thread_mixin_t::real_home_thread = thread; - } - cross_thread_watchable_variable_t *parent; - }; - - clone_ptr_t > original; - publisher_controller_t > publisher_controller; - rwi_lock_assertion_t rwi_lock_assertion; - value_t value; - w_t watchable; - - threadnum_t watchable_thread; - threadnum_t dest_thread; - - /* This object's constructor rethreads our internal components to our other - thread, and then reverses it in the destructor. It must be a separate object - instead of logic in the constructor/destructor because its destructor must - be run after `deliver_pumper`'s destructor. */ - class rethreader_t { - public: - explicit rethreader_t(cross_thread_watchable_variable_t *p) : - parent(p) - { - parent->watchable.rethread(parent->dest_thread); - parent->rwi_lock_assertion.rethread(parent->dest_thread); - parent->publisher_controller.rethread(parent->dest_thread); - } - ~rethreader_t() { - parent->watchable.rethread(parent->watchable_thread); - parent->rwi_lock_assertion.rethread(parent->watchable_thread); - parent->publisher_controller.rethread(parent->watchable_thread); - } - private: - cross_thread_watchable_variable_t *parent; - } rethreader; - - pump_coro_t deliver_pumper; - - /* The destructor for `subs` must be run before the destructor for `deliver_pumper` - because `subs` calls `deliver_pumper.notify()` */ - typename watchable_t::subscription_t subs; - - DISABLE_COPYING(cross_thread_watchable_variable_t); -}; - -/* `all_thread_watchable_variable_t` is like a `cross_thread_watchable_variable_t` except -that `get_watchable()` works on every thread, not just a specified thread. Internally it -constructs one `cross_thread_watchable_variable_t` for each thread, so it's a pretty -heavy-weight object. */ - -template -class all_thread_watchable_variable_t { -public: - all_thread_watchable_variable_t( - const clone_ptr_t > &input); - clone_ptr_t > get_watchable() const { - return vars[get_thread_id().threadnum]->get_watchable(); - } -private: - std::vector > > vars; -}; - -template -class cross_thread_watchable_map_var_t { -public: - cross_thread_watchable_map_var_t( - watchable_map_t *input, - threadnum_t output_thread); - - watchable_map_t *get_watchable() { - return &output_var; - } - - /* Block until changes visible on this end at the moment `flush()` is called are - visible on the other end. Caller is responsible for making sure that the - `cross_thread_watchable_map_var_t` is not destroyed until after `flush()` returns. */ - void flush() { - cond_t non_interruptor; - deliver_pumper.flush(&non_interruptor); - } - -private: - void on_change(const key_t &key, const value_t *value); - void deliver(signal_t *interruptor); - watchable_map_var_t output_var; - threadnum_t input_thread, output_thread; - std::map > queued_changes; - - /* This object's constructor rethreads our internal components to our other - thread, and then reverses it in the destructor. It must be a separate object - instead of logic in the constructor/destructor because its destructor must - be run after `drainer`'s destructor. */ - class rethreader_t { - public: - explicit rethreader_t(cross_thread_watchable_map_var_t *p) : parent(p) { - parent->output_var.rethread(parent->output_thread); - } - ~rethreader_t() { - parent->output_var.rethread(parent->input_thread); - } - private: - cross_thread_watchable_map_var_t *parent; - } rethreader; - - pump_coro_t deliver_pumper; - - typename watchable_map_t::all_subs_t subs; -}; - -/* `all_thread_watchable_map_var_t` is like a `cross_thread_watchable_map_var_t` except -that `get_watchable()` works on every thread, not just a specified thread. Internally it -constructs one `cross_thread_watchable_map_var_t` for each thread, so it's a pretty -heavy-weight object. */ - -template -class all_thread_watchable_map_var_t { -public: - all_thread_watchable_map_var_t( - watchable_map_t *input); - watchable_map_t *get_watchable() const { - return vars[get_thread_id().threadnum]->get_watchable(); - } - void flush(); -private: - std::vector > > vars; -}; - -#include "concurrency/cross_thread_watchable.tcc" - -#endif // CONCURRENCY_CROSS_THREAD_WATCHABLE_HPP_ diff --git a/src/concurrency/cross_thread_watchable.tcc b/src/concurrency/cross_thread_watchable.tcc deleted file mode 100644 index 817ed3c6d62..00000000000 --- a/src/concurrency/cross_thread_watchable.tcc +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include - -template -cross_thread_watchable_variable_t::cross_thread_watchable_variable_t( - const clone_ptr_t > &w, - threadnum_t _dest_thread) : - original(w), - watchable(this), - watchable_thread(get_thread_id()), - dest_thread(_dest_thread), - rethreader(this), - deliver_pumper([this](signal_t *interruptor) { this->deliver(interruptor); }), - subs([this]() { this->deliver_pumper.notify(); }) -{ - rassert(original->get_rwi_lock_assertion()->home_thread() == watchable_thread); - typename watchable_t::freeze_t freeze(original); - value = original->get(); - subs.reset(original, &freeze); -} - -template -void cross_thread_watchable_variable_t::deliver( - UNUSED signal_t *interruptor) { - value_t temp = original->get(); - on_thread_t thread_switcher(dest_thread); - value = temp; - publisher_controller.publish([](const std::function &f) { f(); }); -} - -template -all_thread_watchable_variable_t::all_thread_watchable_variable_t( - const clone_ptr_t > &input) { - for (int i = 0; i < get_num_threads(); ++i) { - vars.emplace_back(make_scoped >( - input, threadnum_t(i))); - } -} - -template -cross_thread_watchable_map_var_t::cross_thread_watchable_map_var_t( - watchable_map_t *input, - threadnum_t _output_thread) : - output_var(input->get_all()), - input_thread(get_thread_id()), - output_thread(_output_thread), - rethreader(this), - deliver_pumper([this](signal_t *interruptor) { this->deliver(interruptor); }), - subs(input, - [this](const key_t &key, const value_t *new_value) { - this->on_change(key, new_value); - }, initial_call_t::NO) - { } - -template -void cross_thread_watchable_map_var_t::on_change( - const key_t &key, const value_t *value) { - if (value != nullptr) { - queued_changes[key] = optional(*value); - } else { - queued_changes[key] = optional(); - } - deliver_pumper.notify(); -} - -template -void cross_thread_watchable_map_var_t::deliver( - UNUSED signal_t *interruptor) { - guarantee(get_thread_id() == input_thread); - std::map > local_changes; - std::swap(local_changes, queued_changes); - on_thread_t thread_switcher(output_thread); - for (const auto &pair : local_changes) { - if (static_cast(pair.second)) { - output_var.set_key_no_equals(pair.first, *pair.second); - } else { - output_var.delete_key(pair.first); - } - } -} - -template -all_thread_watchable_map_var_t::all_thread_watchable_map_var_t( - watchable_map_t *input) { - for (int i = 0; i < get_num_threads(); ++i) { - vars.emplace_back(make_scoped >( - input, threadnum_t(i))); - } -} - -template -void all_thread_watchable_map_var_t::flush() { - for (int i = 0; i < get_num_threads(); ++i) { - vars[i]->flush(); - } -} diff --git a/src/concurrency/exponential_backoff.hpp b/src/concurrency/exponential_backoff.hpp deleted file mode 100644 index 86a60ea9be3..00000000000 --- a/src/concurrency/exponential_backoff.hpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_EXPONENTIAL_BACKOFF_HPP_ -#define CONCURRENCY_EXPONENTIAL_BACKOFF_HPP_ - -#include "arch/timing.hpp" - -class exponential_backoff_t { -public: - exponential_backoff_t( - uint64_t _mnbm, uint64_t _mxbm, double _ff = 1.5, double _sf = 0.0) : - min_backoff_ms(_mnbm), max_backoff_ms(_mxbm), fail_factor(_ff), - success_factor(_sf), backoff_ms(0) - { } - void failure(signal_t *interruptor) { - if (backoff_ms == 0) { - coro_t::yield(); - backoff_ms = min_backoff_ms; - } else { - nap(backoff_ms, interruptor); - guarantee(static_cast(backoff_ms * fail_factor) > backoff_ms, - "rounding screwed it up"); - backoff_ms *= fail_factor; - if (backoff_ms > max_backoff_ms) { - backoff_ms = max_backoff_ms; - } - } - } - void success() { - guarantee(static_cast(backoff_ms * success_factor) < backoff_ms - || backoff_ms == 0, "rounding screwed it up"); - backoff_ms *= success_factor; - if (backoff_ms < min_backoff_ms) { - backoff_ms = 0; - } - } -private: - const uint64_t min_backoff_ms, max_backoff_ms; - const double fail_factor, success_factor; - uint64_t backoff_ms; -}; - -#endif /* CONCURRENCY_EXPONENTIAL_BACKOFF_HPP_ */ - diff --git a/src/concurrency/fifo_checker.cc b/src/concurrency/fifo_checker.cc deleted file mode 100644 index 63552c69d9c..00000000000 --- a/src/concurrency/fifo_checker.cc +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "concurrency/fifo_checker.hpp" - -#include - -#include "arch/runtime/runtime.hpp" -#include "config/args.hpp" -#include "containers/archive/stl_types.hpp" - -#ifndef NDEBUG - -#define ORDER_INVALID (-2) -#define ORDER_IGNORE (-1) - -const order_token_t order_token_t::ignore(order_bucket_t::invalid(), ORDER_IGNORE, false, "order_token_t::ignore"); -#else -const order_token_t order_token_t::ignore; -#endif // ifndef NDEBUG - - - -#ifndef NDEBUG - -bool operator==(const order_bucket_t &a, const order_bucket_t &b) { - return a.uuid_ == b.uuid_; -} - -bool operator!=(const order_bucket_t &a, const order_bucket_t &b) { - return !(a == b); -} - -bool operator<(const order_bucket_t &a, const order_bucket_t &b) { - return a.uuid_ < b.uuid_; -} - -bool order_bucket_t::valid() const { - return !uuid_.is_nil(); -} - - - -order_token_t::order_token_t() : bucket_(order_bucket_t::invalid()), value_(ORDER_INVALID) { } - -order_token_t::order_token_t(order_bucket_t bucket, - int64_t x, - bool read_mode, - const std::string &_tag) - : bucket_(bucket), read_mode_(read_mode), value_(x), tag_(_tag) { } - -order_token_t order_token_t::with_read_mode() const { - return order_token_t(bucket_, value_, true, tag_); -} - -void order_token_t::assert_read_mode() const { - rassert(is_ignore() || read_mode_, "Expected an order token in read mode"); -} - -void order_token_t::assert_write_mode() const { - rassert(is_ignore() || !read_mode_, "Expected an order token in write mode"); -} - -const std::string &order_token_t::tag() const { return tag_; } - -bool order_token_t::is_invalid() const { return !bucket_.valid() && value_ == ORDER_INVALID; } -bool order_token_t::is_ignore() const { return !bucket_.valid() && value_ == ORDER_IGNORE; } - -#ifndef NDEBUG -RDB_IMPL_SERIALIZABLE_4_SINCE_v1_13(order_token_t, bucket_, read_mode_, value_, tag_); -#endif - - -order_source_t::order_source_t() : bucket_(order_bucket_t::create()), counter_(0) { } -order_source_t::order_source_t(threadnum_t specified_home_thread) - : home_thread_mixin_debug_only_t(specified_home_thread), - bucket_(order_bucket_t::create()), counter_(0) { } - -order_source_t::~order_source_t() { } - -order_token_t order_source_t::check_in(const std::string &tag) { - assert_thread(); - ++counter_; - return order_token_t(bucket_, counter_, false, tag); -} - - - - -order_sink_t::order_sink_t() { } - -void order_sink_t::check_out(order_token_t token) { - assert_thread(); - if (!token.is_ignore()) { - rassert(!token.is_invalid()); - /* If we haven't seen this bucket before, then this will - make a new entry in the map and fill it with a pair of (0, 0). - Either way, `last_seen` will be a pointer to the `std::pair` - that ends up in the map. */ - last_seens_map_t::iterator it = last_seens_.insert(last_seens_map_t::value_type(token.bucket_, std::pair(tagged_seen_t(0, "0"), tagged_seen_t(0, "0")))).first; - std::pair *last_seen = &it->second; - verify_token_value_and_update(token, last_seen); - } -} - -void order_sink_t::verify_token_value_and_update(order_token_t token, std::pair *ls_pair) { - // We tolerate equality in this comparison because it can be used - // to ensure that multiple actions don't get interrupted. And - // resending the same action isn't normally a problem. - if (token.read_mode_) { - rassert(token.value_ >= ls_pair->first.value, "read_mode expected (0x%" PRIx64 " >= 0x%" PRIx64 "), (%s >= %s), bucket = (%s)", token.value_, ls_pair->first.value, token.tag_.c_str(), ls_pair->first.tag.c_str(), uuid_to_str(token.bucket_.uuid_).c_str()); - if (ls_pair->second.value < token.value_) { - ls_pair->second = tagged_seen_t(token.value_, token.tag_); - } - } else { - rassert(token.value_ >= ls_pair->second.value, "write_mode expected (0x%" PRIx64 " >= 0x%" PRIx64 "), (%s >= %s), bucket = (%s)", token.value_, ls_pair->second.value, token.tag_.c_str(), ls_pair->second.tag.c_str(), uuid_to_str(token.bucket_.uuid_).c_str()); - ls_pair->first = ls_pair->second = tagged_seen_t(token.value_, token.tag_); - } -} - - -plain_sink_t::plain_sink_t() : ls_pair_(tagged_seen_t(0, "0"), tagged_seen_t(0, "0")), have_bucket_(false), bucket_(order_bucket_t::invalid()) { } - -void plain_sink_t::check_out(order_token_t token) { - assert_thread(); - if (!token.is_ignore()) { - rassert(!token.is_invalid()); - if (!have_bucket_) { - bucket_ = token.bucket_; - have_bucket_ = true; - } else { - rassert(token.bucket_ == bucket_, "plain_sink_t can only be used with one order_source_t. for %s (last seens %s, %s)", token.tag_.c_str(), ls_pair_.first.tag.c_str(), ls_pair_.second.tag.c_str()); - } - - order_sink_t::verify_token_value_and_update(token, &ls_pair_); - } -} - -void order_checkpoint_t::set_tagappend(const std::string &tagappend) { - rassert(tagappend_.empty()); - tagappend_ = "+" + tagappend; -} - -order_token_t order_checkpoint_t::check_through(order_token_t tok) { - assert_thread(); - if (!tok.is_ignore()) { - sink_.check_out(tok); - order_token_t tok2 = source_.check_in(tok.tag() + tagappend_); - if (tok.read_mode_) tok2 = tok2.with_read_mode(); - return tok2; - } else { - return order_token_t::ignore; - } -} - -order_token_t order_checkpoint_t::checkpoint_raw_check_in() { - return source_.check_in("(nothing)" + tagappend_); -} - -#endif // ifndef NDEBUG diff --git a/src/concurrency/fifo_checker.hpp b/src/concurrency/fifo_checker.hpp deleted file mode 100644 index 8b91c0a3823..00000000000 --- a/src/concurrency/fifo_checker.hpp +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_FIFO_CHECKER_HPP_ -#define CONCURRENCY_FIFO_CHECKER_HPP_ - -#include -#include -#include - -#include "containers/archive/archive.hpp" -#include "containers/uuid.hpp" -#include "rpc/serialize_macros.hpp" -#include "threading.hpp" - - - -struct order_bucket_t { - uuid_u uuid_; - - static order_bucket_t invalid() { return order_bucket_t(nil_uuid()); } - static order_bucket_t create() { return order_bucket_t(generate_uuid()); } - - bool valid() const; -private: - explicit order_bucket_t(uuid_u uuid) : uuid_(uuid) { } -}; - -RDB_MAKE_SERIALIZABLE_1(order_bucket_t, uuid_); - - - -bool operator==(const order_bucket_t &a, const order_bucket_t &b); -bool operator!=(const order_bucket_t &a, const order_bucket_t &b); -bool operator<(const order_bucket_t &a, const order_bucket_t &b); - - -/* Order tokens of the same bucket need to arrive at order sinks in a - certain order. Pretending that read_mode is always false, they - need to arrive by ascending order of their value -- the same order - that they were created in. This is because write operations cannot - be reordered. However, read operations may be shuffled around, and - so may order_tokens with read_mode set to true. - */ -class order_token_t { -public: - static const order_token_t ignore; - -#ifndef NDEBUG - // By default we construct a totally invalid order token, not - // equal to ignore, that must be initialized. - order_token_t(); - order_token_t with_read_mode() const; - void assert_read_mode() const; - void assert_write_mode() const; - const std::string &tag() const; -#else - order_token_t() { } - order_token_t with_read_mode() const { return order_token_t(); } - void assert_read_mode() const { } - void assert_write_mode() const { } - std::string tag() const { return ""; } -#endif // ifndef NDEBUG - -private: -#ifndef NDEBUG - order_token_t(order_bucket_t bucket, int64_t x, bool read_mode, const std::string &tag); - - bool is_invalid() const; - bool is_ignore() const; - - // TODO: Make these fields const. - order_bucket_t bucket_; - bool read_mode_; - int64_t value_; - // This tag would be inefficient on VC++ or some other non-GNU - // std::string implementation, since we copy by value. - std::string tag_; -#endif // ifndef NDEBUG - - friend class order_source_t; - friend class order_sink_t; - friend class order_checkpoint_t; - friend class plain_sink_t; - -#ifndef NDEBUG - RDB_DECLARE_ME_SERIALIZABLE(order_token_t); -#else - RDB_MAKE_ME_SERIALIZABLE_0(order_token_t); -#endif -}; - -/* Order sources create order tokens with increasing values for a - specific bucket. */ -class order_source_t : public home_thread_mixin_debug_only_t { -public: -#ifndef NDEBUG - order_source_t(); - explicit order_source_t(threadnum_t specified_home_thread); - ~order_source_t(); - - // Makes a write-mode order token. - order_token_t check_in(const std::string &tag); -#else - order_source_t() { } - explicit order_source_t(threadnum_t specified_home_thread) : home_thread_mixin_debug_only_t(specified_home_thread) { } - ~order_source_t() { } - - order_token_t check_in(const std::string&) { return order_token_t(); } -#endif // ndef NDEBUG - - void rethread(DEBUG_VAR threadnum_t new_thread) { - DEBUG_ONLY_CODE(home_thread_mixin_debug_only_t::real_home_thread = new_thread); - } - -private: -#ifndef NDEBUG - order_bucket_t bucket_; - int64_t counter_; -#endif // ifndef NDEBUG - - DISABLE_COPYING(order_source_t); -}; - -struct tagged_seen_t { - int64_t value; - std::string tag; - - tagged_seen_t(int64_t _value, const std::string &_tag) : value(_value), tag(_tag) { } -}; - -/* Eventually order tokens get to an order sink, and those of the same - bucket had better arrive in the right order. */ -class order_sink_t : public home_thread_mixin_debug_only_t { -public: -#ifndef NDEBUG - order_sink_t(); - - void check_out(order_token_t token); -#else - order_sink_t() { } - - void check_out(UNUSED order_token_t token) { } - -#endif // ifndef NDEBUG - - void rethread(DEBUG_VAR threadnum_t new_thread) { - DEBUG_ONLY_CODE(home_thread_mixin_debug_only_t::real_home_thread = new_thread); - } - -private: - -#ifndef NDEBUG - friend class plain_sink_t; - static void verify_token_value_and_update(order_token_t token, std::pair *ls_pair); - - // We keep two last seen values because reads can be reordered. - // .first = last seen write, .second = max(last seen read, last seen write) - typedef std::map > last_seens_map_t; - last_seens_map_t last_seens_; -#endif // ifndef NDEBUG - - DISABLE_COPYING(order_sink_t); -}; - - -// An order sink with less overhead, for situations where there is -// only one source (like the top of a btree slice) and many sinks. If -// there's one bucket there's no point in instantiating a `std::map`. -// TODO: Is a `std::map` of one item really expensive enough to justify -// having a separate type? -class plain_sink_t : public home_thread_mixin_debug_only_t { -public: -#ifndef NDEBUG - plain_sink_t(); - - void check_out(order_token_t token); -#else - plain_sink_t() { } - - void check_out(UNUSED order_token_t token) { } -#endif // NDEBUG - -private: -#ifndef NDEBUG - // The pair of last seen values. - std::pair ls_pair_; - - /* If `have_bucket_`, then `bucket_` is the bucket that we are associated with. - If we get an order token from a different bucket we crap out. */ - bool have_bucket_; - order_bucket_t bucket_; -#endif // NDEBUG -}; - - -// `order_checkpoint_t` is an `order_sink_t` plus an `order_source_t`. -class order_checkpoint_t : public home_thread_mixin_debug_only_t { -public: - order_checkpoint_t() { } -#ifndef NDEBUG - explicit order_checkpoint_t(const std::string &tagappend) : tagappend_(tagappend) { } - void set_tagappend(const std::string &tagappend); - order_token_t check_through(order_token_t token); - order_token_t checkpoint_raw_check_in(); -#else - explicit order_checkpoint_t(UNUSED const std::string &tagappend) { } - void set_tagappend(UNUSED const std::string &tagappend) { } - order_token_t check_through(UNUSED order_token_t token) { return order_token_t(); } - order_token_t checkpoint_raw_check_in() { return order_token_t(); } -#endif // ndef NDEBUG - -private: -#ifndef NDEBUG - order_sink_t sink_; - order_source_t source_; - std::string tagappend_; -#endif -}; - - -#endif // CONCURRENCY_FIFO_CHECKER_HPP_ diff --git a/src/concurrency/fifo_enforcer.cc b/src/concurrency/fifo_enforcer.cc deleted file mode 100644 index dbed784b8c4..00000000000 --- a/src/concurrency/fifo_enforcer.cc +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "concurrency/fifo_enforcer.hpp" - -#include "arch/runtime/coroutines.hpp" -#include "concurrency/cond_var.hpp" -#include "concurrency/wait_any.hpp" - -void fifo_enforcer_state_t::advance_by_read(DEBUG_VAR fifo_enforcer_read_token_t token) THROWS_NOTHING { - rassert(last_timestamp == token.timestamp); - num_reads++; -} - -void fifo_enforcer_state_t::advance_by_write(fifo_enforcer_write_token_t token) THROWS_NOTHING { - rassert(last_timestamp.next() == token.timestamp); - rassert(num_reads == token.num_preceding_reads); - last_timestamp = token.timestamp; - num_reads = 0; -} - -fifo_enforcer_read_token_t fifo_enforcer_source_t::enter_read() THROWS_NOTHING { - assert_thread(); - mutex_assertion_t::acq_t freeze(&lock); - state.num_reads++; - return fifo_enforcer_read_token_t(state.last_timestamp); -} - -fifo_enforcer_write_token_t fifo_enforcer_source_t::enter_write() THROWS_NOTHING { - assert_thread(); - mutex_assertion_t::acq_t freeze(&lock); - state.last_timestamp = state.last_timestamp.next(); - fifo_enforcer_write_token_t token(state.last_timestamp, state.num_reads); - state.num_reads = 0; - return token; -} - -fifo_enforcer_sink_t::exit_read_t::exit_read_t() THROWS_NOTHING : - parent(nullptr), ended(false) { } - -fifo_enforcer_sink_t::exit_read_t::exit_read_t(fifo_enforcer_sink_t *p, fifo_enforcer_read_token_t t) THROWS_NOTHING : - parent(nullptr), ended(false) -{ - begin(p, t); -} - -void fifo_enforcer_sink_t::exit_read_t::begin(fifo_enforcer_sink_t *p, fifo_enforcer_read_token_t t) THROWS_NOTHING { - rassert(parent == nullptr); - rassert(p != nullptr); - parent = p; - token = t; - - parent->assert_thread(); - mutex_assertion_t::acq_t acq(&parent->internal_lock); - parent->internal_read_queue.push(this); - parent->internal_pump(); -} - -void fifo_enforcer_sink_t::exit_read_t::end() THROWS_NOTHING { - rassert(parent && !ended); - parent->assert_thread(); - mutex_assertion_t::acq_t acq(&parent->internal_lock); - if (is_pulsed()) { - parent->internal_finish_a_reader(token); - } else { - /* Swap us out for a dummy. The dummy is heap-allocated and it will - delete itself when it's done. */ - class dummy_exit_read_t : public internal_exit_read_t { - public: - dummy_exit_read_t(fifo_enforcer_read_token_t t, fifo_enforcer_sink_t *s) : - token(t), sink(s) { } - fifo_enforcer_read_token_t get_token() const { - return token; - } - void on_reached_head_of_queue() { - sink->internal_read_queue.remove(this); - sink->internal_finish_a_reader(token); - delete this; - } - void on_early_shutdown() { - delete this; - } - fifo_enforcer_read_token_t token; - fifo_enforcer_sink_t *sink; - }; - parent->internal_read_queue.swap_in_place(this, - new dummy_exit_read_t(token, parent)); - } - ended = true; -} - -fifo_enforcer_sink_t::exit_read_t::~exit_read_t() THROWS_NOTHING { - if (parent && !ended) { - end(); - } -} - -fifo_enforcer_sink_t::exit_write_t::exit_write_t() THROWS_NOTHING : - parent(nullptr), ended(false) { } - -fifo_enforcer_sink_t::exit_write_t::exit_write_t(fifo_enforcer_sink_t *p, fifo_enforcer_write_token_t t) THROWS_NOTHING : - parent(nullptr), ended(false) -{ - begin(p, t); -} - -void fifo_enforcer_sink_t::exit_write_t::begin(fifo_enforcer_sink_t *p, fifo_enforcer_write_token_t t) THROWS_NOTHING { - ASSERT_FINITE_CORO_WAITING; - - rassert(parent == nullptr); - rassert(p != nullptr); - parent = p; - token = t; - - parent->assert_thread(); - mutex_assertion_t::acq_t acq(&parent->internal_lock); - parent->internal_write_queue.push(this); - parent->internal_pump(); -} - -void fifo_enforcer_sink_t::exit_write_t::end() THROWS_NOTHING { - rassert(parent && !ended); - mutex_assertion_t::acq_t acq(&parent->internal_lock); - if (is_pulsed()) { - parent->internal_finish_a_writer(token); - } else { - /* Swap us out for a dummy. */ - class dummy_exit_write_t : public internal_exit_write_t { - public: - dummy_exit_write_t(fifo_enforcer_write_token_t t, fifo_enforcer_sink_t *s) : - token(t), sink(s) { } - fifo_enforcer_write_token_t get_token() const { - return token; - } - void on_reached_head_of_queue() { - sink->internal_write_queue.remove(this); - sink->internal_finish_a_writer(token); - delete this; - } - void on_early_shutdown() { - delete this; - } - fifo_enforcer_write_token_t token; - fifo_enforcer_sink_t *sink; - }; - parent->internal_write_queue.swap_in_place(this, - new dummy_exit_write_t(token, parent)); - } - ended = true; -} - -fifo_enforcer_sink_t::exit_write_t::~exit_write_t() THROWS_NOTHING { - if (parent && !ended) { - end(); - } -} - -fifo_enforcer_sink_t::~fifo_enforcer_sink_t() THROWS_NOTHING { - while (!internal_read_queue.empty()) { - internal_exit_read_t *read = internal_read_queue.pop(); - read->on_early_shutdown(); - } - while (!internal_write_queue.empty()) { - internal_exit_write_t *write = internal_write_queue.pop(); - write->on_early_shutdown(); - } -} - -void fifo_enforcer_sink_t::internal_pump() THROWS_NOTHING { - ASSERT_FINITE_CORO_WAITING; - if (in_pump) { - pump_should_keep_going = true; - } else { - in_pump = true; - do { - pump_should_keep_going = false; - - while (!internal_read_queue.empty() && - internal_read_queue.peek()->get_token().timestamp == finished_state.last_timestamp) { - internal_exit_read_t *read = internal_read_queue.peek(); - popped_state.advance_by_read(read->get_token()); - read->on_reached_head_of_queue(); - } - - if (!internal_write_queue.empty() && - internal_write_queue.peek()->get_token().timestamp == finished_state.last_timestamp.next() && - internal_write_queue.peek()->get_token().num_preceding_reads == finished_state.num_reads) { - internal_exit_write_t *write = internal_write_queue.peek(); - popped_state.advance_by_write(write->get_token()); - write->on_reached_head_of_queue(); - } - } while (pump_should_keep_going); - in_pump = false; - } -} - -void fifo_enforcer_sink_t::internal_finish_a_reader(fifo_enforcer_read_token_t token) THROWS_NOTHING { - rassert(popped_state.last_timestamp == token.timestamp); - rassert(finished_state.num_reads <= popped_state.num_reads); - finished_state.advance_by_read(token); - internal_pump(); -} - -void fifo_enforcer_sink_t::internal_finish_a_writer(fifo_enforcer_write_token_t token) THROWS_NOTHING { - rassert(popped_state.last_timestamp == token.timestamp); - rassert(popped_state.num_reads == 0); - finished_state.advance_by_write(token); - internal_pump(); -} diff --git a/src/concurrency/fifo_enforcer.hpp b/src/concurrency/fifo_enforcer.hpp deleted file mode 100644 index 331f735dfc2..00000000000 --- a/src/concurrency/fifo_enforcer.hpp +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_FIFO_ENFORCER_HPP_ -#define CONCURRENCY_FIFO_ENFORCER_HPP_ - -#include -#include - -#include "concurrency/mutex_assertion.hpp" -#include "concurrency/signal.hpp" -#include "containers/intrusive_priority_queue.hpp" -#include "rpc/serialize_macros.hpp" -#include "timestamps.hpp" - -class cond_t; -class signal_t; - -namespace unittest { -void run_queue_equivalence_test(); -} - -/* `fifo_enforcer.hpp` contains facilities for enforcing that objects pass -through a checkpoint in the same order that they passed through a previous -checkpoint. The objects in transit between the checkpoints are identified by -"tokens", of the types `fifo_enforcer_read_token_t` and -`fifo_enforcer_write_token_t`. Read tokens are allowed to be reordered relative -to each other but not relative to write tokens. */ - -/* A clinic can be used as metaphor for how `fifo_enforcer_{source,sink}_t` are - * used. First you get an appointment - * (`fifo_enforcer_source_t::enter_{read,write}()`), then you come to a waiting - * room (construct `fifo_enforcer_sink_t::exit_{read,write}_t`), where you are - * notified when the doctor is ready to see you (the `exit_{read,write}_t` - * object gets pulsed). When you finish seeing the doctor (destroy the - * `exit_{read,write}_t`), the person who has the next appointment is notified. - * If you leave the waiting room (destroy the `exit_{read,write}_t` before it is - * pulsed), you forfeit your appointment and place in line. - * - * The metaphor breaks down in that, if you don't show up for your appointment, all the - * later appointments are delayed indefinitely. That means that if you never construct a - * `exit_{read,write}_t` for a token, users of all later tokens will never get to go. - */ -class fifo_enforcer_read_token_t { -public: - fifo_enforcer_read_token_t() THROWS_NOTHING { } - explicit fifo_enforcer_read_token_t(state_timestamp_t t) THROWS_NOTHING : - timestamp(t) { } - state_timestamp_t timestamp; -}; - -RDB_MAKE_SERIALIZABLE_1(fifo_enforcer_read_token_t, timestamp); - -class fifo_enforcer_write_token_t { -public: - fifo_enforcer_write_token_t() THROWS_NOTHING : timestamp(), num_preceding_reads(-1) { } - fifo_enforcer_write_token_t(state_timestamp_t t, int npr) THROWS_NOTHING : - timestamp(t), num_preceding_reads(npr) { } - state_timestamp_t timestamp; - int64_t num_preceding_reads; -}; - -RDB_MAKE_SERIALIZABLE_2(fifo_enforcer_write_token_t, timestamp, num_preceding_reads); - -class fifo_enforcer_state_t { -public: - fifo_enforcer_state_t() THROWS_NOTHING : - last_timestamp(valgrind_undefined(state_timestamp_t::zero())), - num_reads(valgrind_undefined(0)) { } - fifo_enforcer_state_t(state_timestamp_t ts, int64_t nr) THROWS_NOTHING : - last_timestamp(ts), num_reads(nr) { } - - void advance_by_read(fifo_enforcer_read_token_t tok) THROWS_NOTHING; - void advance_by_write(fifo_enforcer_write_token_t tok) THROWS_NOTHING; - - state_timestamp_t last_timestamp; - int64_t num_reads; -}; - -RDB_MAKE_SERIALIZABLE_2(fifo_enforcer_state_t, last_timestamp, num_reads); - -class fifo_enforcer_source_t : public home_thread_mixin_debug_only_t { -public: - fifo_enforcer_source_t() THROWS_NOTHING : - state(state_timestamp_t::zero(), 0) { } - - void rethread(threadnum_t new_thread) { - DEBUG_ONLY_CODE(home_thread_mixin_debug_only_t::real_home_thread = new_thread); - lock.rethread(new_thread); - } - - /* Enters the FIFO for read. Does not block. */ - fifo_enforcer_read_token_t enter_read() THROWS_NOTHING; - - /* Enters the FIFO for write. Does not block. */ - fifo_enforcer_write_token_t enter_write() THROWS_NOTHING; - - fifo_enforcer_state_t get_state() THROWS_NOTHING { - return state; - } - -private: - mutex_assertion_t lock; - fifo_enforcer_state_t state; - DISABLE_COPYING(fifo_enforcer_source_t); -}; - -class fifo_enforcer_sink_t : public home_thread_mixin_debug_only_t { -public: - /* `internal_exit_{read,write}_t` represents any thing that waits in line to - go through the FIFO enforcer. It's responsible for adding itself to the FIFO - enforcer queue and for removing itself. It's public so that people can - create more subclasses to do interesting things. You should probably use one - of its subclasses. */ - - class internal_exit_read_t - : public intrusive_priority_queue_node_t { - protected: - virtual ~internal_exit_read_t() { } - - private: - friend class fifo_enforcer_sink_t; - friend bool left_is_higher_priority( - const fifo_enforcer_sink_t::internal_exit_read_t *left, - const fifo_enforcer_sink_t::internal_exit_read_t *right); - - /* Returns the read token for this operation. It will be used to sort - the operation in the queue. */ - virtual fifo_enforcer_read_token_t get_token() const = 0; - - /* Called when the operation has reached the head of the queue. It - should remove the operation from the queue. */ - virtual void on_reached_head_of_queue() = 0; - - /* Called when the FIFO enforcer is being destroyed. The operation will - already have been removed from the queue. */ - virtual void on_early_shutdown() = 0; - }; - - class internal_exit_write_t - : public intrusive_priority_queue_node_t { - protected: - virtual ~internal_exit_write_t() { } - - private: - friend class fifo_enforcer_sink_t; - friend bool left_is_higher_priority( - const fifo_enforcer_sink_t::internal_exit_write_t *left, - const fifo_enforcer_sink_t::internal_exit_write_t *right); - - /* Returns the write token for this operation. It will be used to sort - the operation in the queue. */ - virtual fifo_enforcer_write_token_t get_token() const = 0; - - /* Called when the operation has reached the head of the queue. It - should remove the operation from the queue. (Or change its token, which - is used to efficiently implement an object that stands for more than one - token.) */ - virtual void on_reached_head_of_queue() = 0; - - /* Called when the FIFO enforcer is being destroyed. The operation will - already have been removed from the queue. */ - virtual void on_early_shutdown() = 0; - }; - - /* `exit_{read,write}_t` notes that the given FIFO enforcer token has - entered the queue, and pulses itself when the token has reached the head of - the queue. When the `exit_{read,write}_t` is destroyed, it allows future - operations to proceed. If it hadn't reached the head of the queue by the - time it was destroyed, operations that come later are still allowed to - proceed, even though the destructor doesn't block. It accomplishes this by - putting a dummy entry into the queue. */ - - class exit_read_t : public signal_t, public internal_exit_read_t { - public: - exit_read_t() THROWS_NOTHING; - - /* Calls `begin()` */ - exit_read_t(fifo_enforcer_sink_t *, fifo_enforcer_read_token_t) THROWS_NOTHING; - - /* Calls `end()` if appropriate */ - ~exit_read_t() THROWS_NOTHING; - - void begin(fifo_enforcer_sink_t *, fifo_enforcer_read_token_t) THROWS_NOTHING; - void end() THROWS_NOTHING; - - private: - fifo_enforcer_read_token_t get_token() const { - return token; - } - void on_reached_head_of_queue() { - parent->internal_read_queue.remove(this); - pulse(); - } - void on_early_shutdown() { - crash("illegal to destroy fifo_enforcer_sink_t while outstanding " - "exit_read_t objects exist"); - } - - /* `parent` is `NULL` before `begin()` is called. After `end()` is - called, `parent` remains non-`NULL` but `ended` is set to `true` */ - fifo_enforcer_sink_t *parent; - bool ended; - - fifo_enforcer_read_token_t token; - }; - - class exit_write_t : public signal_t, public internal_exit_write_t { - public: - exit_write_t() THROWS_NOTHING; - - /* Calls `begin()` */ - exit_write_t(fifo_enforcer_sink_t *, fifo_enforcer_write_token_t) THROWS_NOTHING; - - /* Calls `end()` if appropriate */ - ~exit_write_t() THROWS_NOTHING; - - void begin(fifo_enforcer_sink_t *, fifo_enforcer_write_token_t) THROWS_NOTHING; - void end() THROWS_NOTHING; - - private: - fifo_enforcer_write_token_t get_token() const { - return token; - } - void on_reached_head_of_queue() { - parent->internal_write_queue.remove(this); - pulse(); - } - void on_early_shutdown() { - crash("illegal to destroy fifo_enforcer_sink_t while outstanding " - "exit_write_t objects exist"); - } - - /* `parent` is `NULL` before `begin()` is called. After `end()` is - called, `parent` remains non-`NULL` but `ended` is set to `true` */ - fifo_enforcer_sink_t *parent; - bool ended; - - fifo_enforcer_write_token_t token; - }; - - fifo_enforcer_sink_t() THROWS_NOTHING : - popped_state(state_timestamp_t::zero(), 0), - finished_state(state_timestamp_t::zero(), 0), - in_pump(false) - { } - - explicit fifo_enforcer_sink_t(fifo_enforcer_state_t init) THROWS_NOTHING : - popped_state(init), - finished_state(init), - in_pump(false) - { } - - ~fifo_enforcer_sink_t() THROWS_NOTHING; - - void rethread(threadnum_t new_thread) { - DEBUG_ONLY_CODE(home_thread_mixin_debug_only_t::real_home_thread = new_thread); - internal_lock.rethread(new_thread); - } - - /* Don't access this stuff directly unless you know what you're doing. It's - public so that new subclasses of `internal_exit_{read,write}_t` can access - it. */ - - void internal_pump() THROWS_NOTHING; - void internal_finish_a_reader(fifo_enforcer_read_token_t token) THROWS_NOTHING; - void internal_finish_a_writer(fifo_enforcer_write_token_t token) THROWS_NOTHING; - - mutex_assertion_t internal_lock; - intrusive_priority_queue_t internal_read_queue; - intrusive_priority_queue_t internal_write_queue; - -private: - /* The difference between `popped_state` and `finished_state` is the - operations that are currently running. They have been popped off the queue, - but they have not finished. */ - fifo_enforcer_state_t popped_state, finished_state; - - /* If `internal_pump()` is called recursively, the inner call will set - `pump_should_keep_going` to `true` rather than doing its normal work. That - way, we use a loop rather than arbitrarily deep recursion. */ - bool in_pump, pump_should_keep_going; - - DISABLE_COPYING(fifo_enforcer_sink_t); -}; - - -inline bool left_is_higher_priority(const fifo_enforcer_sink_t::internal_exit_read_t *left, - const fifo_enforcer_sink_t::internal_exit_read_t *right) { - return left->get_token().timestamp < right->get_token().timestamp; -} - -inline bool left_is_higher_priority(const fifo_enforcer_sink_t::internal_exit_write_t *left, - const fifo_enforcer_sink_t::internal_exit_write_t *right) { - return left->get_token().timestamp < right->get_token().timestamp; -} - - -#endif /* CONCURRENCY_FIFO_ENFORCER_HPP_ */ diff --git a/src/concurrency/fifo_enforcer_queue.hpp b/src/concurrency/fifo_enforcer_queue.hpp deleted file mode 100644 index 38992fb618a..00000000000 --- a/src/concurrency/fifo_enforcer_queue.hpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_FIFO_ENFORCER_QUEUE_HPP_ -#define CONCURRENCY_FIFO_ENFORCER_QUEUE_HPP_ - -#include -#include - -#include "concurrency/fifo_enforcer.hpp" -#include "concurrency/queue/passive_producer.hpp" - -class perfmon_counter_t; - -namespace unittest { -void run_FIFOEnforcer_QueueEquivalence(); -} - -/* `fifo_enforcer_queue_t` consumes FIFO read and write tokens just like -`fifo_enforcer_sink_t` does, but it uses them to order items in a queue instead -of using them to block up coroutines. */ - -template -class fifo_enforcer_queue_t : public passive_producer_t, public home_thread_mixin_debug_only_t { -public: - fifo_enforcer_queue_t(); - fifo_enforcer_queue_t(perfmon_counter_t *_read_counter, perfmon_counter_t *_write_counter); - ~fifo_enforcer_queue_t(); - - void push(fifo_enforcer_read_token_t token, const T &t); - void finish_read(fifo_enforcer_read_token_t read_token); - void push(fifo_enforcer_write_token_t token, const T &t); - void finish_write(fifo_enforcer_write_token_t write_token); - -private: - typedef std::multimap read_queue_t; - read_queue_t read_queue; - - typedef std::map > write_queue_t; - write_queue_t write_queue; - - mutex_assertion_t lock; - - fifo_enforcer_state_t state; - - perfmon_counter_t *read_counter, *write_counter; - -private: - friend void unittest::run_FIFOEnforcer_QueueEquivalence(); - //passive produce api - T produce_next_value(); - - availability_control_t control; - - void consider_changing_available(); - - DISABLE_COPYING(fifo_enforcer_queue_t); -}; - -#include "concurrency/fifo_enforcer_queue.tcc" - -#endif /* CONCURRENCY_FIFO_ENFORCER_QUEUE_HPP_ */ diff --git a/src/concurrency/fifo_enforcer_queue.tcc b/src/concurrency/fifo_enforcer_queue.tcc deleted file mode 100644 index bf04ca0f284..00000000000 --- a/src/concurrency/fifo_enforcer_queue.tcc +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef __CONCURRENCY_FIFO_ENFORCER_QUEUE_TCC__ -#define __CONCURRENCY_FIFO_ENFORCER_QUEUE_TCC__ - -#include "perfmon/perfmon.hpp" -#include "stl_utils.hpp" - -template -fifo_enforcer_queue_t::fifo_enforcer_queue_t() - : passive_producer_t(&control), state(state_timestamp_t::zero(), 0), - read_counter(NULL), write_counter(NULL) -{ } - -template -fifo_enforcer_queue_t::fifo_enforcer_queue_t(perfmon_counter_t *_read_counter, perfmon_counter_t *_write_counter) - : passive_producer_t(&control), state(state_timestamp_t::zero(), 0), - read_counter(_read_counter), write_counter(_write_counter) -{ } - -template -fifo_enforcer_queue_t::~fifo_enforcer_queue_t() { } - -template -void fifo_enforcer_queue_t::push(fifo_enforcer_read_token_t token, const T &t) { - if (read_counter) { ++(*read_counter); } - - assert_thread(); - DEBUG_VAR mutex_assertion_t::acq_t acq(&lock); - - read_queue.insert(std::make_pair(token.timestamp, t)); - consider_changing_available(); -} - -template -void fifo_enforcer_queue_t::finish_read(DEBUG_VAR fifo_enforcer_read_token_t read_token) { - if (read_counter) { --(*read_counter); } - - assert_thread(); - - rassert(state.last_timestamp == read_token.timestamp); - - state.num_reads++; - consider_changing_available(); -} - -template -void fifo_enforcer_queue_t::push(fifo_enforcer_write_token_t token, const T &t) { - if (write_counter) { ++(*write_counter); } - - assert_thread(); - DEBUG_VAR mutex_assertion_t::acq_t acq(&lock); - - write_queue.insert(std::make_pair(token.timestamp, std::make_pair(token.num_preceding_reads, t))); - consider_changing_available(); -} - -template -void fifo_enforcer_queue_t::finish_write(fifo_enforcer_write_token_t write_token) { - if (write_counter) { --(*write_counter); } - - assert_thread(); - - rassert(state.last_timestamp.next() == write_token.timestamp); - rassert(state.num_reads == write_token.num_preceding_reads); - - state.last_timestamp = write_token.timestamp; - state.num_reads = 0; - consider_changing_available(); -} - -template -T fifo_enforcer_queue_t::produce_next_value() { - typename read_queue_t::iterator rit = read_queue.find(state.last_timestamp); - if (rit != read_queue.end()) { - T res = rit->second; - read_queue.erase(rit); - consider_changing_available(); - return res; - } - - typename write_queue_t::iterator wit = - write_queue.find(state.last_timestamp.next()); - if (wit != write_queue.end()) { - int64_t expected_num_reads = wit->second.first; - rassert(state.num_reads <= expected_num_reads); - - if (state.num_reads == expected_num_reads) { - T res = wit->second.second; - write_queue.erase(wit); - consider_changing_available(); - return res; - } - } - //We're in this function when we don't actually have a value to return - rassert(!control.get(), "Control return true but we don't actually have a value to return. This class is incorrectly implemented."); - crash("Unreachable\n"); - return T(); -} - -template -void fifo_enforcer_queue_t::consider_changing_available() { - if (std_contains(read_queue, state.last_timestamp)) { - control.set_available(true); - } else if (std_contains(write_queue, state.last_timestamp.next()) && - write_queue.find(state.last_timestamp.next())->second.first == state.num_reads) { - control.set_available(true); - } else { - control.set_available(false); - } -} - -#endif diff --git a/src/concurrency/interruptor.cc b/src/concurrency/interruptor.cc deleted file mode 100644 index c7c6edc9b75..00000000000 --- a/src/concurrency/interruptor.cc +++ /dev/null @@ -1,12 +0,0 @@ -#include "concurrency/interruptor.hpp" - -#include "concurrency/wait_any.hpp" - -void wait_interruptible(const signal_t *signal, const signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t) { - wait_any_t waiter(signal, interruptor); - waiter.wait_lazily_unordered(); - if (interruptor->is_pulsed()) { - throw interrupted_exc_t(); - } -} diff --git a/src/concurrency/interruptor.hpp b/src/concurrency/interruptor.hpp deleted file mode 100644 index 6e16c9ce55d..00000000000 --- a/src/concurrency/interruptor.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef CONCURRENCY_INTERRUPTOR_HPP_ -#define CONCURRENCY_INTERRUPTOR_HPP_ - -#include - -#include "errors.hpp" - -class signal_t; - -/* General exception to be thrown when some process is interrupted. It's in -`utils.hpp` because I can't think where else to put it */ -class interrupted_exc_t : public std::exception { -public: - const char *what() const throw () { - return "interrupted"; - } -}; - -/* Waits for the first signal to become pulsed. If the second signal becomes -pulsed, stops waiting and throws `interrupted_exc_t`. */ -void wait_interruptible(const signal_t *signal, const signal_t *interruptor) - THROWS_ONLY(interrupted_exc_t); - -#endif // CONCURRENCY_INTERRUPTOR_HPP_ diff --git a/src/concurrency/mutex.cc b/src/concurrency/mutex.cc deleted file mode 100644 index ed80b80eea6..00000000000 --- a/src/concurrency/mutex.cc +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "concurrency/mutex.hpp" - -#include "arch/runtime/coroutines.hpp" - -mutex_t::acq_t::acq_t(mutex_t *l, bool eager) : lock_(nullptr), eager_(false) { - reset(l, eager); -} - -mutex_t::acq_t::~acq_t() { - reset(); -} - -void mutex_t::acq_t::reset() { - if (lock_) { - unlock_mutex(lock_, eager_); - } - lock_ = nullptr; -} - -void mutex_t::acq_t::reset(mutex_t *l, bool eager) { - reset(); - lock_ = l; - eager_ = eager; - co_lock_mutex(l); -} - -void co_lock_mutex(mutex_t *mutex) { - if (mutex->locked) { - mutex->waiters.push_back(coro_t::self()); - coro_t::wait(); - } else { - mutex->locked = true; - } -} - -void unlock_mutex(mutex_t *mutex, bool eager) { - rassert(mutex->locked); - if (mutex->waiters.empty()) { - mutex->locked = false; - } else { - coro_t *next = mutex->waiters.front(); - mutex->waiters.pop_front(); - if (eager) { - next->notify_now_deprecated(); - } else { - next->notify_sometime(); - } - } -} diff --git a/src/concurrency/mutex.hpp b/src/concurrency/mutex.hpp deleted file mode 100644 index 7f6b6532912..00000000000 --- a/src/concurrency/mutex.hpp +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_MUTEX_HPP_ -#define CONCURRENCY_MUTEX_HPP_ - -#include -#include - -#include "errors.hpp" - -class coro_t; -class mutex_t; - -void co_lock_mutex(mutex_t *mutex); -void unlock_mutex(mutex_t *mutex, bool eager = false); - -// Deprecated. You should use new_mutex_t in new_mutex.hpp instead. -class mutex_t { -public: - class acq_t { - public: - acq_t() : lock_(nullptr), eager_(false) { } - explicit acq_t(acq_t &&other) : lock_(nullptr), eager_(false) { - swap(*this, other); - } - explicit acq_t(mutex_t *l, bool eager = false); - ~acq_t(); - void reset(); - void reset(mutex_t *l, bool eager = false); - void assert_is_holding(DEBUG_VAR mutex_t *m) const { - rassert(lock_ == m); - } - private: - friend void swap(acq_t &, acq_t &); - mutex_t *lock_; - bool eager_; - DISABLE_COPYING(acq_t); - }; - - mutex_t() : locked(false) { } - ~mutex_t() { rassert(!locked); } - - bool is_locked() { - return locked; - } - - friend void co_lock_mutex(mutex_t *mutex); - friend void unlock_mutex(mutex_t *mutex, bool eager); - -private: - bool locked; - std::deque waiters; - - DISABLE_COPYING(mutex_t); -}; - -inline void swap(mutex_t::acq_t &a, mutex_t::acq_t &b) { - std::swap(a.lock_, b.lock_); - std::swap(a.eager_, b.eager_); -} - -#endif /* CONCURRENCY_MUTEX_HPP_ */ - diff --git a/src/concurrency/mutex_assertion.hpp b/src/concurrency/mutex_assertion.hpp deleted file mode 100644 index 64dfed5713c..00000000000 --- a/src/concurrency/mutex_assertion.hpp +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_MUTEX_ASSERTION_HPP_ -#define CONCURRENCY_MUTEX_ASSERTION_HPP_ - -#include - -#include "threading.hpp" - -/* `mutex_assertion_t` is like a mutex, except that it raises an assertion if -there is contention. */ - -#ifndef NDEBUG - -struct mutex_assertion_t : public home_thread_mixin_t { - struct acq_t { - acq_t() : mutex(NULL) { } - explicit acq_t(mutex_assertion_t *m) : mutex(NULL) { - reset(m); - } - ~acq_t() { - reset(NULL); - } - void reset(mutex_assertion_t *m = NULL) { - if (mutex) { - mutex->assert_thread(); - rassert(mutex->locked); - mutex->locked = false; - } - mutex = m; - if (mutex) { - mutex->assert_thread(); - rassert(!mutex->locked); - mutex->locked = true; - } - } - void assert_is_holding(mutex_assertion_t *m) { - rassert(mutex == m); - } - private: - friend void swap(acq_t &, acq_t &); - mutex_assertion_t *mutex; - DISABLE_COPYING(acq_t); - }; - - explicit mutex_assertion_t(threadnum_t explicit_home_thread) : - home_thread_mixin_t(explicit_home_thread), locked(false) { - } - - mutex_assertion_t() : locked(false) { - } - - mutex_assertion_t(mutex_assertion_t &&movee) : locked(movee.locked) { - rassert(!locked); - } - - // Unimplemented because nobody uses this yet. - void operator=(mutex_assertion_t &&movee) = delete; - - ~mutex_assertion_t() { reset(); } - void reset() { rassert(!locked); } - - void rethread(threadnum_t new_thread) { - rassert(!locked); - real_home_thread = new_thread; - } -private: - friend struct acq_t; - bool locked; - DISABLE_COPYING(mutex_assertion_t); -}; - -struct rwi_lock_assertion_t : public home_thread_mixin_t { - struct read_acq_t { - read_acq_t() : lock(NULL) { } - explicit read_acq_t(rwi_lock_assertion_t *l) : lock(NULL) { - reset(l); - } - ~read_acq_t() { - reset(NULL); - } - void reset(rwi_lock_assertion_t *l = NULL) { - if (lock) { - lock->assert_thread(); - rassert(lock->state > 0); - lock->state--; - } - lock = l; - if (lock) { - lock->assert_thread(); - rassert(lock->state != rwi_lock_assertion_t::write_locked); - lock->state++; - } - } - void assert_is_holding(rwi_lock_assertion_t *l) { - rassert(lock == l); - } - private: - rwi_lock_assertion_t *lock; - DISABLE_COPYING(read_acq_t); - }; - struct write_acq_t { - write_acq_t() : lock(NULL) { } - explicit write_acq_t(rwi_lock_assertion_t *l) : lock(NULL) { - reset(l); - } - ~write_acq_t() { - reset(NULL); - } - void reset(rwi_lock_assertion_t *l = NULL) { - if (lock) { - lock->assert_thread(); - rassert(lock->state == rwi_lock_assertion_t::write_locked); - lock->state = 0; - } - lock = l; - if (lock) { - lock->assert_thread(); - rassert(lock->state == 0); - lock->state = rwi_lock_assertion_t::write_locked; - } - } - void assert_is_holding(rwi_lock_assertion_t *l) { - rassert(lock == l); - } - private: - rwi_lock_assertion_t *lock; - DISABLE_COPYING(write_acq_t); - }; - explicit rwi_lock_assertion_t(threadnum_t explicit_home_thread) : - home_thread_mixin_t(explicit_home_thread), state(0) { } - rwi_lock_assertion_t() : state(0) { } - ~rwi_lock_assertion_t() { - rassert(state == 0); - } - void rethread(threadnum_t new_thread) { - rassert(state == 0); - real_home_thread = new_thread; - } -private: - friend struct read_acq_t; - friend struct write_acq_t; - static const int write_locked = -1; - /* If unlocked, `state` will be 0. If read-locked, `state` will be the - number of readers. If write-locked, `state` will be `write_locked`. */ - int state; - DISABLE_COPYING(rwi_lock_assertion_t); -}; - -struct semaphore_assertion_t : public home_thread_mixin_debug_only_t { - struct acq_t { - acq_t() : parent(NULL) { } - explicit acq_t(semaphore_assertion_t *p) : parent(p) { - parent->assert_thread(); - rassert(parent->capacity > 0); - parent->capacity--; - } - void reset() { - parent->assert_thread(); - parent->capacity++; - parent = NULL; - } - ~acq_t() { - if (parent) { - reset(); - } - } - private: - semaphore_assertion_t *parent; - DISABLE_COPYING(acq_t); - }; - explicit semaphore_assertion_t(int cap) : capacity(cap) { } -private: - int capacity; - DISABLE_COPYING(semaphore_assertion_t); -}; - -#else /* NDEBUG */ - -struct mutex_assertion_t { - struct acq_t { - acq_t() { } - explicit acq_t(mutex_assertion_t *) { } - /* This is necessary to avoid compiler complaints about unused variables in - release mode. */ - ~acq_t() { (void)this; } - void reset(mutex_assertion_t * = nullptr) { } - void assert_is_holding(mutex_assertion_t *) { } - private: - DISABLE_COPYING(acq_t); - }; - explicit mutex_assertion_t(int) { } - mutex_assertion_t() { } - mutex_assertion_t(mutex_assertion_t &&) { } - void reset() { } - void rethread(threadnum_t) { } -private: - DISABLE_COPYING(mutex_assertion_t); -}; - -inline void swap(mutex_assertion_t::acq_t &, mutex_assertion_t::acq_t &) { -} - -struct rwi_lock_assertion_t { - struct read_acq_t { - read_acq_t() { } - explicit read_acq_t(rwi_lock_assertion_t *) { } - ~read_acq_t() { (void)this; } - void reset(rwi_lock_assertion_t * = nullptr) { } - void assert_is_holding(rwi_lock_assertion_t *) { } - private: - DISABLE_COPYING(read_acq_t); - }; - struct write_acq_t { - write_acq_t() { } - explicit write_acq_t(rwi_lock_assertion_t *) { } - ~write_acq_t() { (void)this; } - void reset(rwi_lock_assertion_t * = nullptr) { } - void assert_is_holding(rwi_lock_assertion_t *) { } - private: - DISABLE_COPYING(write_acq_t); - }; - explicit rwi_lock_assertion_t(int) { } - rwi_lock_assertion_t() { } - void rethread(threadnum_t) { } -private: - friend struct read_acq_t; - friend struct write_acq_t; - DISABLE_COPYING(rwi_lock_assertion_t); -}; - -struct semaphore_assertion_t { - struct acq_t { - acq_t() { } - explicit acq_t(semaphore_assertion_t *) { } - void reset() { } - ~acq_t() { (void)this; } - private: - DISABLE_COPYING(acq_t); - }; - explicit semaphore_assertion_t(int) { } -private: - DISABLE_COPYING(semaphore_assertion_t); -}; - -#endif /* NDEBUG */ - -#endif /* CONCURRENCY_MUTEX_ASSERTION_HPP_ */ diff --git a/src/concurrency/new_mutex.hpp b/src/concurrency/new_mutex.hpp deleted file mode 100644 index 262bf0eb1a8..00000000000 --- a/src/concurrency/new_mutex.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef CONCURRENCY_NEW_MUTEX_HPP_ -#define CONCURRENCY_NEW_MUTEX_HPP_ - -#include "concurrency/interruptor.hpp" -#include "concurrency/rwlock.hpp" - -// This is a mutex, following the usual semantics (seen in rwlock_t, -// fifo_enforcer_sink_t, buf_lock_t) where construction puts you in the queue, a -// signal is pulsed when you have exclusive access, and acquirers gain exclusive -// access in the same order as construction. You should favor this instead of -// mutex_t. - -class new_mutex_t { -public: - new_mutex_t() { } - ~new_mutex_t() { } - -private: - friend class new_mutex_in_line_t; - - rwlock_t rwlock_; - - DISABLE_COPYING(new_mutex_t); -}; - -class new_mutex_in_line_t { -public: - new_mutex_in_line_t() { } - explicit new_mutex_in_line_t(new_mutex_t *new_mutex) - : rwlock_in_line_(&new_mutex->rwlock_, access_t::write) { } - - new_mutex_in_line_t(new_mutex_in_line_t &&) = default; - - const signal_t *acq_signal() const { return rwlock_in_line_.write_signal(); } - void reset() { rwlock_in_line_.reset(); } - void guarantee_is_for_lock(const new_mutex_t *mutex) const { - rwlock_in_line_.guarantee_is_for_lock(&mutex->rwlock_); - } -private: - rwlock_in_line_t rwlock_in_line_; - - DISABLE_COPYING(new_mutex_in_line_t); -}; - -class new_mutex_acq_t { -public: - // Acquires the lock. The constructor blocks the coroutine, it doesn't return - // until the lock is acquired. - explicit new_mutex_acq_t(new_mutex_t *lock) : in_line(lock) { - in_line.acq_signal()->wait(); - } - - // Acquires the lock. The constructor blocks the coroutine until the lock - // is acquired or the interruptor is pulsed. - new_mutex_acq_t(new_mutex_t *lock, signal_t *interruptor) : in_line(lock) { - wait_interruptible(in_line.acq_signal(), interruptor); - - } - - void guarantee_is_holding(const new_mutex_t *new_mutex) const { - in_line.guarantee_is_for_lock(new_mutex); - } - -private: - new_mutex_in_line_t in_line; - DISABLE_COPYING(new_mutex_acq_t); -}; - -#endif // CONCURRENCY_NEW_MUTEX_HPP_ diff --git a/src/concurrency/new_semaphore.cc b/src/concurrency/new_semaphore.cc deleted file mode 100644 index a98106a3694..00000000000 --- a/src/concurrency/new_semaphore.cc +++ /dev/null @@ -1,121 +0,0 @@ -#include "concurrency/new_semaphore.hpp" - -new_semaphore_t::new_semaphore_t(int64_t _capacity) - : capacity_(_capacity), current_(0) -{ - guarantee(_capacity > 0); -} - -new_semaphore_t::~new_semaphore_t() { - guarantee(current_ == 0); - guarantee(waiters_.empty()); -} - -void new_semaphore_t::set_capacity(int64_t new_capacity) { - assert_thread(); - guarantee(new_capacity > 0); - capacity_ = new_capacity; - pulse_waiters(); -} - -void new_semaphore_t::add_acquirer(new_semaphore_in_line_t *acq) { - assert_thread(); - waiters_.push_back(acq); - pulse_waiters(); -} - -void new_semaphore_t::remove_acquirer(new_semaphore_in_line_t *acq) { - assert_thread(); - rassert(acq->semaphore_ == this); - if (acq->cond_.is_pulsed()) { - current_ -= acq->count_; - } else { - waiters_.remove(acq); - } - pulse_waiters(); -} - -void new_semaphore_t::pulse_waiters() { - assert_thread(); - while (new_semaphore_in_line_t *acq = waiters_.head()) { - if (acq->count_ <= capacity_ - current_ || current_ == 0) { - current_ += acq->count_; - waiters_.remove(acq); - acq->cond_.pulse(); - } else { - break; - } - } -} - -new_semaphore_in_line_t::~new_semaphore_in_line_t() { - reset(); -} - -void new_semaphore_in_line_t::reset() { - if (semaphore_ != nullptr) { - semaphore_->remove_acquirer(this); - semaphore_ = nullptr; - count_ = 0; - cond_.reset(); - } -} - -new_semaphore_in_line_t::new_semaphore_in_line_t() - : semaphore_(nullptr), count_(0) { } - -new_semaphore_in_line_t::new_semaphore_in_line_t(new_semaphore_t *semaphore, int64_t _count) - : semaphore_(nullptr), count_(0) { - init(semaphore, _count); -} - -void new_semaphore_in_line_t::init(new_semaphore_t *semaphore, int64_t _count) { - guarantee(semaphore_ == nullptr); - rassert(count_ == 0); - guarantee(_count >= 0); - semaphore_ = semaphore; - count_ = _count; - semaphore_->add_acquirer(this); -} - -new_semaphore_in_line_t::new_semaphore_in_line_t(new_semaphore_in_line_t &&movee) - : intrusive_list_node_t(std::move(movee)), - semaphore_(movee.semaphore_), - count_(movee.count_), - cond_(std::move(movee.cond_)) { - movee.semaphore_ = nullptr; - movee.count_ = 0; - movee.cond_.reset(); -} - -int64_t new_semaphore_in_line_t::count() const { - return count_; -} - -void new_semaphore_in_line_t::change_count(int64_t new_count) { - guarantee(semaphore_ != nullptr); - guarantee(new_count >= 0); - - if (cond_.is_pulsed()) { - // We've already acquired the semaphore, which means semaphore_->current_ - // already accounts for our existing count_ amount of acquisition, now - // add/subtract the difference. - semaphore_->current_ += (new_count - count_); - } - - count_ = new_count; - - // Things have changed, tell the semaphore to try pulsing waiters (ourself, if we - // haven't acquired it yet, or other waiters, if we have reduced current_). - semaphore_->pulse_waiters(); -} - -void new_semaphore_in_line_t::transfer_in(new_semaphore_in_line_t &&other) { - guarantee(semaphore_ != nullptr); - guarantee(other.semaphore_ == semaphore_); - guarantee(cond_.is_pulsed()); - guarantee(other.cond_.is_pulsed()); - change_count(count_ + other.count_); - other.reset(); -} - diff --git a/src/concurrency/new_semaphore.hpp b/src/concurrency/new_semaphore.hpp deleted file mode 100644 index 4e69ba26df6..00000000000 --- a/src/concurrency/new_semaphore.hpp +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_NEW_SEMAPHORE_HPP_ -#define CONCURRENCY_NEW_SEMAPHORE_HPP_ - -#include "concurrency/cond_var.hpp" -#include "containers/intrusive_list.hpp" - -// This semaphore obeys first-in-line/first-acquisition semantics. The -// new_semaphore_in_line_t's will receive access to the semaphore in the same order that -// such access was requested. Also, there aren't problems with starvation. Also, it -// doesn't have naked lock and unlock functions, you have to use new_semaphore_in_line_t. - -class new_semaphore_in_line_t; - -class new_semaphore_t : public home_thread_mixin_debug_only_t { -public: - explicit new_semaphore_t(int64_t capacity); - ~new_semaphore_t(); - - int64_t capacity() const { return capacity_; } - int64_t current() const { return current_; } - - void set_capacity(int64_t new_capacity); - -private: - friend class new_semaphore_in_line_t; - void add_acquirer(new_semaphore_in_line_t *acq); - void remove_acquirer(new_semaphore_in_line_t *acq); - - void pulse_waiters(); - - // Normally, current_ <= capacity_, and capacity_ doesn't change. current_ can - // exceed capacity_ for three reasons. - // 1. A call to change_acquired_count could force it to overflow. - // 2. An acquirer will never be blocked while current_ is 0. - // 3. An act of God could change capacity_ (currently unimplemented; hail Satan). - int64_t capacity_; - int64_t current_; - - intrusive_list_t waiters_; - - DISABLE_COPYING(new_semaphore_t); -}; - -class new_semaphore_in_line_t : public intrusive_list_node_t { -public: - // Construction is non-blocking, it gets you in line for the semaphore. You need - // to call acquisition_signal()->wait() in order to wait for your acquisition of the - // semaphore. Acquirers receive the semaphore in the same order that they've - // acquired it. - ~new_semaphore_in_line_t(); - new_semaphore_in_line_t(); - new_semaphore_in_line_t(new_semaphore_t *semaphore, int64_t count); - new_semaphore_in_line_t(new_semaphore_in_line_t &&movee); - - // Returns "how much" of the semaphore this acq has acquired or would acquire. - int64_t count() const; - - // Changes "how much" of the semaphore this acq has acquired or would acquire. - // If it's already acquired the semaphore, and new_count is bigger than the - // current value of acquired_count(), it's possible that you'll make the - // semaphore "overfull", meaning that current_ > capacity_. That's not ideal, - // but it's O.K. - void change_count(int64_t new_count); - - // Transfers the "semaphore credits" that `other` owns to `this` and resets `other`. - // `other` and `this` must both already own the semaphore. - // - // For example, if `this->count() == 1` and `other->count() == 2` before the call, - // then after the call `this->count() == 3` and `other` is invalid. - void transfer_in(new_semaphore_in_line_t &&other); - - // Initializes the object. - void init(new_semaphore_t *semaphore, int64_t count); - - void reset(); - - // Returns a signal that gets pulsed when this has successfully acquired the - // semaphore. - const signal_t *acquisition_signal() const { return &cond_; } - -private: - friend class new_semaphore_t; - - // The semaphore this acquires (or NULL if this hasn't acquired a semaphore yet). - new_semaphore_t *semaphore_; - - // The count of "how much" of the semaphore we've acquired (if semaphore_ is - // non-NULL). - int64_t count_; - - // Gets pulsed when we have successfully acquired the semaphore. - cond_t cond_; - DISABLE_COPYING(new_semaphore_in_line_t); -}; - - - -#endif // CONCURRENCY_NEW_SEMAPHORE_HPP_ diff --git a/src/concurrency/one_per_thread.hpp b/src/concurrency/one_per_thread.hpp deleted file mode 100644 index c2c8f57b08b..00000000000 --- a/src/concurrency/one_per_thread.hpp +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_ONE_PER_THREAD_HPP_ -#define CONCURRENCY_ONE_PER_THREAD_HPP_ - -#include "arch/runtime/runtime.hpp" -#include "concurrency/pmap.hpp" -#include "containers/scoped.hpp" -#include "containers/object_buffer.hpp" - -template -class one_per_thread_t { -public: - struct construct_0_t { - one_per_thread_t *parent_; - explicit construct_0_t(one_per_thread_t *p) : parent_(p) { } - void operator()(int thread) const { - on_thread_t th((threadnum_t(thread))); - parent_->array[thread].create(); - } - }; - - one_per_thread_t() : array(get_num_threads()) { - pmap(get_num_threads(), construct_0_t(this)); - } - - template - struct construct_1_t { - one_per_thread_t *parent_; - const arg1_t &arg1_; - construct_1_t(one_per_thread_t *p, const arg1_t &arg1) : parent_(p), arg1_(arg1) { } - - void operator()(int thread) const { - on_thread_t th((threadnum_t(thread))); - parent_->array[thread].create(arg1_); - } - }; - - - template - explicit one_per_thread_t(const arg1_t &arg1) : array(get_num_threads()) { - pmap(get_num_threads(), construct_1_t(this, arg1)); - } - - template - struct construct_2_t { - one_per_thread_t *parent_; - const arg1_t &arg1_; - const arg2_t &arg2_; - construct_2_t(one_per_thread_t *p, const arg1_t &arg1, const arg2_t &arg2) : parent_(p), arg1_(arg1), arg2_(arg2) { } - - void operator()(int thread) const { - on_thread_t th((threadnum_t(thread))); - parent_->array[thread].create(arg1_, arg2_); - } - }; - - template - one_per_thread_t(const arg1_t &arg1, const arg2_t &arg2) : array(get_num_threads()) { - pmap(get_num_threads(), construct_2_t(this, arg1, arg2)); - } - - - struct destruct_t { - one_per_thread_t *parent_; - explicit destruct_t(one_per_thread_t *p) : parent_(p) { } - void operator()(int thread) const { - on_thread_t th((threadnum_t(thread))); - parent_->array[thread].reset(); - } - }; - - ~one_per_thread_t() { - pmap(get_num_threads(), destruct_t(this)); - } - - inner_t *get() { - return array[get_thread_id().threadnum].get(); - } - -private: - // An array of buffers of inner_t of size get_num_threads(), the - // array is allocated and then the buffers are initialized (on - // their respective threads). The array and buffers are owned by - // this object. - scoped_array_t > array; - - DISABLE_COPYING(one_per_thread_t); -}; - -#endif /* CONCURRENCY_ONE_PER_THREAD_HPP_ */ diff --git a/src/concurrency/pmap.hpp b/src/concurrency/pmap.hpp deleted file mode 100644 index b4cc4f4954f..00000000000 --- a/src/concurrency/pmap.hpp +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_PMAP_HPP_ -#define CONCURRENCY_PMAP_HPP_ - -#include "arch/runtime/coroutines.hpp" -#include "concurrency/cond_var.hpp" -#include "concurrency/new_semaphore.hpp" - -template -struct pmap_runner_one_arg_t { - value_t i; - const callable_t *c; - int64_t *outstanding; - cond_t *to_signal; - pmap_runner_one_arg_t(value_t _i, const callable_t *_c, int64_t *_outstanding, - cond_t *_to_signal) - : i(_i), c(_c), outstanding(_outstanding), to_signal(_to_signal) { } - - void operator()() { - (*c)(i); - (*outstanding)--; - if (*outstanding == 0) { - to_signal->pulse(); - } - } -}; - -template -void spawn_pmap_runner_one_arg(value_t i, const callable_t *c, int64_t *outstanding, cond_t *to_signal) { - coro_t::spawn_now_dangerously(pmap_runner_one_arg_t(i, c, outstanding, to_signal)); -} - -template -void pmap(int64_t begin, int64_t end, const callable_t &c) { - guarantee(begin >= 0); // We don't want `end - begin` to overflow, do we? - guarantee(begin <= end); - if (begin == end) { - return; - } - - cond_t cond; - int64_t outstanding = (end - begin); - for (int64_t i = begin; i < end - 1; ++i) { - coro_t::spawn_now_dangerously(pmap_runner_one_arg_t(i, &c, &outstanding, &cond)); - } - pmap_runner_one_arg_t runner(end - 1, &c, &outstanding, &cond); - runner(); - cond.wait(); -} - -template -void pmap(int64_t count, const callable_t &c) { - pmap(0, count, c); -} - -template -void pmap(iterator_t start, iterator_t end, const callable_t &c) { - cond_t cond; - int64_t outstanding = 1; - while (start != end) { - ++outstanding; - spawn_pmap_runner_one_arg(*start, &c, &outstanding, &cond); - ++start; - } - --outstanding; - if (outstanding) { - cond.wait(); - } -} - -template -struct throttled_pmap_runner_t { - value_t i; - const callable_t *c; - int64_t *outstanding; - cond_t *to_signal; - new_semaphore_in_line_t semaphore_acq; - throttled_pmap_runner_t(value_t _i, const callable_t *_c, int64_t *_outstanding, - cond_t *_to_signal, new_semaphore_in_line_t &&acq) - : i(_i), c(_c), outstanding(_outstanding), to_signal(_to_signal), - semaphore_acq(std::move(acq)) { } - - void operator()() { - (*c)(i); - (*outstanding)--; - semaphore_acq.reset(); - if (*outstanding == 0) { - to_signal->pulse(); - } - } -}; - -template -void throttled_pmap(int64_t begin, int64_t end, const callable_t &c, int64_t capacity) { - guarantee(capacity > 0); - guarantee(begin >= 0); // We don't want `end - begin` to overflow, do we? - guarantee(begin <= end); - if (begin == end) { - return; - } - - new_semaphore_t semaphore(capacity); - cond_t cond; - int64_t outstanding = (end - begin); - for (int64_t i = begin; i < end - 1; ++i) { - new_semaphore_in_line_t acq(&semaphore, 1); - acq.acquisition_signal()->wait(); - coro_t::spawn_now_dangerously( - throttled_pmap_runner_t(i, &c, &outstanding, &cond, - std::move(acq))); - } - - { - new_semaphore_in_line_t acq(&semaphore, 1); - acq.acquisition_signal()->wait(); - throttled_pmap_runner_t runner(end - 1, &c, &outstanding, - &cond, std::move(acq)); - runner(); - } - cond.wait(); -} - -template -void throttled_pmap(int64_t count, const callable_t &c, int64_t capacity) { - throttled_pmap(0, count, c, capacity); -} - -#endif /* CONCURRENCY_PMAP_HPP_ */ diff --git a/src/concurrency/promise.hpp b/src/concurrency/promise.hpp deleted file mode 100644 index c2978e85e2a..00000000000 --- a/src/concurrency/promise.hpp +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_PROMISE_HPP_ -#define CONCURRENCY_PROMISE_HPP_ - -#include "concurrency/cond_var.hpp" - -#include "containers/object_buffer.hpp" - -/* A promise_t is a condition variable combined with a "return value", of sorts, that -is transmitted to the thing waiting on the condition variable. */ - -template -class promise_t : public home_thread_mixin_debug_only_t { -public: - promise_t() { } - - void pulse(const value_type &v) { - assert_thread(); - value.create(v); - cond.pulse(); - } - - void pulse_if_not_already_pulsed(const value_type &v) { - if (!is_pulsed()) { - pulse(v); - } - } - - const value_type &wait() const { - assert_thread(); - cond.wait_lazily_unordered(); - return *value.get(); - } - - const signal_t *get_ready_signal() const { - return &cond; - } - - /* Note that `assert_get_value()` can be called on any thread. */ - value_type assert_get_value() const { - cond.guarantee_pulsed(); - return *value.get(); - } - - MUST_USE bool try_get_value(value_type *out) const { - if (is_pulsed()) { - *out = *value.get(); - return true; - } else { - return false; - } - } - - bool is_pulsed() const { - return cond.is_pulsed(); - } - -private: - cond_t cond; - object_buffer_t value; - - DISABLE_COPYING(promise_t); -}; - -#endif /* CONCURRENCY_PROMISE_HPP_ */ diff --git a/src/concurrency/pubsub.hpp b/src/concurrency/pubsub.hpp deleted file mode 100644 index 89072db1fbc..00000000000 --- a/src/concurrency/pubsub.hpp +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_PUBSUB_HPP_ -#define CONCURRENCY_PUBSUB_HPP_ - -#include "arch/runtime/runtime_utils.hpp" -#include "concurrency/mutex_assertion.hpp" -#include "containers/intrusive_list.hpp" -#include "threading.hpp" - -/* Forward declaration */ - -template -class publisher_controller_t; - -/* `publisher_t` is used to implement small-scale pub/sub architectures. It's -useful whenever you need to broadcast notifications to some number of listeners. - -Each publisher has zero or more subscriptions. Each subscription is associated -with a subscriber. A subscription is usually associated with one publisher, but -it is possible to unassociate a subscription or to move it from one publisher to -another. - -The publisher handles concurrency issues for you. In particular, it won't let -you add or remove subscribers or destroy the publisher while in the middle of -delivering a notification. */ - -template -class publisher_t { -public: - class subscription_t : public intrusive_list_node_t { - public: - /* Construct a `subscription_t` that is not subscribed to any publisher. - */ - explicit subscription_t(subscriber_t sub) : subscriber(sub), publisher(nullptr) { } - - /* Construct a `subscription_t` and subscribe to the given publisher. */ - subscription_t(subscriber_t sub, publisher_t *pub) : subscriber(sub), publisher(NULL) { - reset(pub); - } - - subscription_t(subscription_t &&movee) - : intrusive_list_node_t(std::move(movee)), - subscriber(std::move(movee.subscriber)), - publisher(movee.publisher) { - movee.publisher = nullptr; - } - - /* Cause us to be subscribed to the given publisher (if any) and not to - any other publisher. */ - void reset(publisher_t *pub = nullptr) { - if (publisher) { - DEBUG_VAR mutex_assertion_t::acq_t acq(&publisher->mutex); - publisher->subscriptions.remove(this); - } - publisher = pub; - if (publisher) { - DEBUG_VAR mutex_assertion_t::acq_t acq(&publisher->mutex); - publisher->subscriptions.push_back(this); - } - } - - /* Unsubscribe from the publisher you are subscribed to, if any. */ - ~subscription_t() { - reset(); - } - - subscriber_t subscriber; - - private: - friend class publisher_controller_t; - friend class publisher_t; - - publisher_t *publisher; - - DISABLE_COPYING(subscription_t); - }; - - void rethread(threadnum_t new_thread) { - rassert(subscriptions.empty(), - "Cannot rethread a `publisher_t` that has subscribers."); - mutex.rethread(new_thread); - } - -private: - friend class subscription_t; - friend class publisher_controller_t; - - publisher_t() { } - ~publisher_t() { reset(); } - - void reset() { - rassert(subscriptions.empty()); - } - - publisher_t(publisher_t &&movee) - : subscriptions(std::move(movee.subscriptions)), - mutex(std::move(movee.mutex)) { - for (subscription_t *p = subscriptions.head(); p != nullptr; - p = subscriptions.next(p)) { - rassert(p->publisher == &movee); - p->publisher = this; - } - } - - intrusive_list_t subscriptions; - mutex_assertion_t mutex; - - DISABLE_COPYING(publisher_t); -}; - -/* To create a publisher, create a `publisher_controller_t` and call its -`get_publisher()` method. `publisher_t` is split from `publisher_controller_t` -so that you can pass a `publisher_t *` to things; they will be able to listen -for events, but not publish events themselves. - -To publish things, call `publish()` on the `publisher_controller_t` you -created. `publish()` takes a function, which will be called once for each -subscription. The function must not block. */ - -template -class publisher_controller_t { -public: - publisher_controller_t() { } - publisher_controller_t(publisher_controller_t &&movee) - : publisher(std::move(movee.publisher)) { } - - publisher_t *get_publisher() { - return &publisher; - } - - template - void publish(const callable_t &callable) { - DEBUG_VAR mutex_assertion_t::acq_t acq(&publisher.mutex); - for (auto sub = publisher.subscriptions.head(); - sub != nullptr; - sub = publisher.subscriptions.next(sub)) { - /* `callable()` should not block */ - ASSERT_FINITE_CORO_WAITING; - callable(sub->subscriber); - } - } - - void rethread(threadnum_t new_thread) { - rassert(publisher.subscriptions.empty(), - "Cannot rethread a `publisher_t` that has subscribers."); - publisher.rethread(new_thread); - } - - void reset() { - publisher.reset(); - } - -private: - publisher_t publisher; - DISABLE_COPYING(publisher_controller_t); -}; - -#endif /* CONCURRENCY_PUBSUB_HPP_ */ diff --git a/src/concurrency/pump_coro.cc b/src/concurrency/pump_coro.cc deleted file mode 100644 index 7281277cf71..00000000000 --- a/src/concurrency/pump_coro.cc +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "concurrency/pump_coro.hpp" - -#include "assignment_sentry.hpp" -#include "arch/runtime/coroutines.hpp" -#include "concurrency/interruptor.hpp" -#include "containers/map_sentries.hpp" -#include "utils.hpp" - -pump_coro_t::pump_coro_t( - const std::function &_callback, - size_t _max_callbacks) : - callback(_callback), - max_callbacks(_max_callbacks), - running(0), starting(false), queued(false), - timestamp(0), drained(false), running_timestamp(nullptr) -{ - guarantee(max_callbacks >= 1); -} - -void pump_coro_t::notify() { - assert_thread(); - mutex_assertion_t::acq_t acq(&mutex); - if (drained) { - return; - } - ++timestamp; - /* If `starting` is `true`, then there's already a coroutine that's going to call the - callback soon, so we don't need to start another one */ - if (!starting) { - if (running < max_callbacks) { - starting = true; - coro_t::spawn_sometime(std::bind(&pump_coro_t::run, this, drainer.lock())); - } else { - queued = true; - } - } -} - -void pump_coro_t::include_latest_notifications() { - assert_thread(); - mutex_assertion_t::acq_t acq(&mutex); - guarantee(max_callbacks == 1, "Don't use `include_latest_notifications()` with " - "max_callbacks > 1"); - guarantee(running_timestamp != nullptr, "`include_latest_notifications()` should " - "only be called from within the callback."); - *running_timestamp = timestamp; - queued = false; -} - -void pump_coro_t::flush(signal_t *interruptor) { - assert_thread(); - mutex_assertion_t::acq_t acq(&mutex); - guarantee(!drained, "flush() might never succeed if we're draining"); - if (running == 0 && !starting) { - guarantee(!queued, "queued can't be true if running != max_callbacks"); - return; - } - cond_t cond; - multimap_insertion_sentry_t - sentry(&flush_waiters, timestamp, &cond); - acq.reset(); - wait_interruptible(&cond, interruptor); -} - -void pump_coro_t::drain() { - assert_thread(); - { - mutex_assertion_t::acq_t acq(&mutex); - drained = true; - } - drainer.drain(); -} - -void pump_coro_t::run(auto_drainer_t::lock_t keepalive) { - mutex_assertion_t::acq_t acq(&mutex); - guarantee(starting); - guarantee(!queued); - guarantee(running < max_callbacks); - starting = false; - ++running; - - while (!keepalive.get_drain_signal()->is_pulsed()) { - uint64_t local_timestamp = timestamp; - assignment_sentry_t timestamp_sentry; - if (max_callbacks == 1) { - timestamp_sentry.reset(&running_timestamp, &local_timestamp); - } - acq.reset(); - - try { - callback(keepalive.get_drain_signal()); - } catch (const interrupted_exc_t &) { - return; - } - coro_t::yield(); - - acq.reset(&mutex); - for (auto it = flush_waiters.begin(); - it != flush_waiters.upper_bound(local_timestamp); - ++it) { - it->second->pulse_if_not_already_pulsed(); - } - if (queued) { - queued = false; - } else { - break; - } - } - - --running; -} - diff --git a/src/concurrency/pump_coro.hpp b/src/concurrency/pump_coro.hpp deleted file mode 100644 index 4c66fc082e2..00000000000 --- a/src/concurrency/pump_coro.hpp +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_PUMP_CORO_HPP_ -#define CONCURRENCY_PUMP_CORO_HPP_ - -#include -#include -#include - -#include "concurrency/auto_drainer.hpp" -#include "concurrency/mutex_assertion.hpp" -#include "containers/counted.hpp" - -/* `pump_coro_t` is a class for managing a blocking function that must eventually run -after a certain event occurs; but if the event occurs multiple times in quick succession, -it's OK for the function to only run once. For example, we might want to flush a value to -disk every time it changes. - Note that this is not tested very well for `max_callbacks` > 1, and you should be very -careful about your assumptions on how flushing works when providing `max_callbacks` > 1. -*/ - -class pump_coro_t : public home_thread_mixin_debug_only_t{ -public: - /* The constructor specifies which callback should be called. The callback may block. - If the `pump_coro_t` is destroyed its interruptor will be pulsed and it may throw - `interrupted_exc_t`. The callback is guaranteed to stop before the `pump_coro_t` - destructor returns. `max_callbacks` is the maximum number of instances of the - callback that can be running at once. */ - pump_coro_t( - const std::function &callback, - size_t max_callbacks = 1); - - /* `notify()` schedules the callback to be run, unless it's already scheduled. */ - void notify(); - - /* Normally, if `notify()` is called while the callback is running, then the callback - will be called again after it returns. But if the callback calls - `include_latest_notifications()`, all notifications up to that point will be - considered to be handled by this instance of the callback. This is only legal if - `max_callbacks` is 1, because otherwise there's no way for the `pump_coro_t` to know - which callback is calling it. */ - void include_latest_notifications(); - - /* `flush()` blocks until `callback` has run completely at least once since the last - call to `notify()`. (Or if `callback` started before the call to `notify()` but - called `include_latest_notification()` after the call to `notify()`.) */ - void flush(signal_t *interruptor); - - /* `drain()` interrupts any running instances of the callback and waits until they - finish. No further callbacks will be spawned once `drain()` is called, even if - `notify()` is called again. */ - void drain(); - -private: - void run(auto_drainer_t::lock_t keepalive); - - std::function const callback; - size_t const max_callbacks; - - /* `mutex` is used to protect any access to the variables below, to prevent - unexpected blocking. */ - mutex_assertion_t mutex; - - /* `running` is the number of callbacks that are currently running, not counting - coroutines that are starting. `starting` is `true` if there is a coroutine starting. - `queued` is `true` if we hit the `max_callbacks` limit and need to run the callback - again as soon as one of the running instances finishes. */ - size_t running; - bool starting, queued; - - /* `notify()` increments `timestamp` every time it is called. This is used to track - the waiters in `flush_waiters`. */ - uint64_t timestamp; - - /* `flush()` inserts into `flush_waiters` under the current value of `timestamp`. */ - std::multimap flush_waiters; - - /* `drained` is `true` if `drain()` has been called. */ - bool drained; - - /* `run()` will make a copy of `timestamp` when it starts. If `max_callbacks` is 1, - then it will set `running_timestamp` to a pointer to its local copy, so that - `include_latest_notifications()` can adjust it. If `run()` is not active or if - `max_callbacks` is greater than 1, `running_timestamp` will always be null. */ - uint64_t *running_timestamp; - - auto_drainer_t drainer; -}; - -#endif /* CONCURRENCY_PUMP_CORO_HPP_ */ - diff --git a/src/concurrency/queue/accounting.hpp b/src/concurrency/queue/accounting.hpp deleted file mode 100644 index 09d218fa8e2..00000000000 --- a/src/concurrency/queue/accounting.hpp +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_QUEUE_ACCOUNTING_HPP_ -#define CONCURRENCY_QUEUE_ACCOUNTING_HPP_ - -#include "concurrency/queue/passive_producer.hpp" -#include "containers/intrusive_list.hpp" - -/* `accounting_queue_t` is useful when you have some number of actors competing -for a shared resource, and you want them to be granted access to the resource in -a fixed ratio. The `accounting_queue_t` is a `passive_producer_t` that draws -from a number of other `passive_producer_t`s. To associate the -`accounting_queue_t` with a `passive_producer_t`, construct an -`accounting_queue_t::account_t` object. The `account_t` constructor takes a -`share` parameter; the relative ratio of the `share` parameters of the different -`account_t`s determines which `passive_producer_t`s the `accounting_queue_t` -will `pop()` from when its own `pop()` method is called. When one of the sub- -`passive_producer_t`s is not available, then it is ignored until it becomes -available. */ - -template -class accounting_queue_t : - public passive_producer_t, - public home_thread_mixin_debug_only_t -{ -public: - explicit accounting_queue_t(int _batch_factor) : - passive_producer_t(&available_control), - total_shares(0), - selector(0), - batch_factor(_batch_factor) { - - rassert(batch_factor > 0); - } - - ~accounting_queue_t() { - rassert(active_accounts.empty()); - rassert(inactive_accounts.empty()); - } - - class account_t : private availability_callback_t, public intrusive_list_node_t { - public: - account_t(accounting_queue_t *p, passive_producer_t *s, int _shares) - : parent(p), source(s), shares(_shares), active(false) { - parent->assert_thread(); - rassert(shares > 0); - if (source->available->get()) { - activate(); - } else { - parent->inactive_accounts.push_back(this); - } - source->available->set_callback(this); - parent->available_control.set_available(!parent->active_accounts.empty()); - } - ~account_t() { - parent->assert_thread(); - source->available->unset_callback(); - if (active) { - deactivate(); - } else { - parent->inactive_accounts.remove(this); - } - parent->available_control.set_available(!parent->active_accounts.empty()); - } - - private: - friend class accounting_queue_t; - - void on_source_availability_changed() { - parent->assert_thread(); - if (source->available->get() && !active) { - parent->inactive_accounts.remove(this); - activate(); - } else if (!source->available->get() && active) { - deactivate(); - parent->inactive_accounts.push_back(this); - } - parent->available_control.set_available(!parent->active_accounts.empty()); - } - - void activate() { - active = true; - parent->active_accounts.push_back(this); - parent->total_shares += shares; - } - void deactivate() { - active = false; - parent->active_accounts.remove(this); - parent->total_shares -= shares; - } - - accounting_queue_t *parent; - passive_producer_t *source; - int shares; - bool active; - }; - -private: - friend class account_t; - - intrusive_list_t active_accounts, inactive_accounts; - - int total_shares, selector, batch_factor; - - availability_control_t available_control; - value_t produce_next_value() { - assert_thread(); - - selector %= total_shares * batch_factor; - // TODO: Maybe that line should be like this instead? - // It would be very fair, but there might be some issues with that (like - // less sequential access patterns (<-- definitely a problem on rotational drives!), - // maybe other problems) - //selector = randint(total_shares); - - int batch_selector = selector / batch_factor; - - account_t *acct = active_accounts.head(); - int count = 0; - for (;;) { - count += acct->shares; - if (count > batch_selector) { - break; - } - acct = active_accounts.next(acct); - } - selector++; - return acct->source->pop(); - } -}; - -#endif /* CONCURRENCY_QUEUE_ACCOUNTING_HPP_ */ diff --git a/src/concurrency/queue/disk_backed_queue_wrapper.hpp b/src/concurrency/queue/disk_backed_queue_wrapper.hpp deleted file mode 100644 index 9e0acf6a815..00000000000 --- a/src/concurrency/queue/disk_backed_queue_wrapper.hpp +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_QUEUE_DISK_BACKED_QUEUE_WRAPPER_HPP_ -#define CONCURRENCY_QUEUE_DISK_BACKED_QUEUE_WRAPPER_HPP_ - -#include -#include - -#include "assignment_sentry.hpp" -#include "concurrency/auto_drainer.hpp" -#include "concurrency/interruptor.hpp" -#include "concurrency/queue/passive_producer.hpp" -#include "concurrency/wait_any.hpp" -#include "containers/archive/archive.hpp" -#include "containers/archive/vector_stream.hpp" -#include "containers/disk_backed_queue.hpp" -#include "paths.hpp" - -/* `disk_backed_queue_t` can't be used directly as a `passive_producer_t` -because its `pop()` method can sometimes block, and `passive_producer_t`'s -`produce_next_value()` method mustn't. To get around this we have -`disk_backed_queue_wrapper_t`, which wraps a `disk_backed_queue_t` but is also -a `passive_producer_t`. - -Note that it may sometimes indicate that no data is available even when the -queue is not empty. This happens when we cannot read data from disk fast enough -to keep up with the consumer. In this case, we will become available again when -we have loaded the data into memory. */ - -template -class disk_backed_queue_wrapper_t : public passive_producer_t { -public: - disk_backed_queue_wrapper_t(io_backender_t *_io_backender, - const serializer_filepath_t &_filename, perfmon_collection_t *_stats_parent, - int64_t memory_queue_bytes) : - passive_producer_t(&available_control), - memory_queue_free_space(memory_queue_bytes), - notify_when_room_in_memory_queue(nullptr), - items_in_queue(0), - io_backender(_io_backender), - filename(_filename), - stats_parent(_stats_parent), - restart_copy_coro(false) - { } - - void push(const T &value) { - write_message_t wm; - // Despite that we are serializing this *to disk*, disk backed queues are not - // intended to persist across restarts, so using - // `cluster_version_t::LATEST_OVERALL` is safe. - serialize(&wm, value); - - mutex_t::acq_t acq(&push_mutex); - items_in_queue++; - if (disk_queue.has()) { - disk_queue->push(wm); - - // Check if we need to restart the copy coroutine that may have exited while we pushed - if (restart_copy_coro) { - restart_copy_coro = false; - coro_t::spawn_sometime(std::bind( - &disk_backed_queue_wrapper_t::copy_from_disk_queue_to_memory_queue, - this, auto_drainer_t::lock_t(&drainer))); - } - } else { - if (memory_queue_free_space <= 0) { - disk_queue.init(new internal_disk_backed_queue_t( - io_backender, filename, stats_parent)); - disk_queue->push(wm); - coro_t::spawn_sometime(std::bind( - &disk_backed_queue_wrapper_t::copy_from_disk_queue_to_memory_queue, - this, auto_drainer_t::lock_t(&drainer))); - } else { - memory_queue.emplace_back(std::move(wm)); - available_control.set_available(true); - } - } - } - - /* Warning: the `passive_producer_t` may be unavailable even when `size()` - returns nonzero. */ - size_t size() { - return items_in_queue; - } - -private: - T produce_next_value() { - guarantee(!memory_queue.empty()); - write_message_t wm(std::move(memory_queue.front())); - memory_queue.pop_front(); - memory_queue_free_space += wm.size(); - items_in_queue--; - if (memory_queue.empty()) { - available_control.set_available(false); - } - if (notify_when_room_in_memory_queue != nullptr) { - notify_when_room_in_memory_queue->pulse_if_not_already_pulsed(); - } - // TODO: This does some unnecessary copying. - vector_stream_t stream; - stream.reserve(wm.size()); - int res = send_write_message(&stream, &wm); - guarantee(res == 0); - std::vector data; - stream.swap(&data); - vector_read_stream_t rstream(std::move(data)); - T value; - archive_result_t dres = - deserialize(&rstream, &value); - guarantee_deserialization(dres, "disk backed queue wrapper"); - return value; - } - - void copy_from_disk_queue_to_memory_queue(auto_drainer_t::lock_t keepalive) { - try { - while (!keepalive.get_drain_signal()->is_pulsed()) { - if (disk_queue->empty()) { - if (items_in_queue != memory_queue.size()) { - // There is a push in progress, there's no good way to wait on - // it, so we'll start a new coroutine later - restart_copy_coro = true; - } else { - disk_queue.reset(); - } - break; - } - write_message_t wm; - copying_viewer_t viewer(&wm); - disk_queue->pop(&viewer); - if (memory_queue_free_space <= 0) { - guarantee(notify_when_room_in_memory_queue == nullptr); - cond_t cond; - assignment_sentry_t assignment_sentry( - ¬ify_when_room_in_memory_queue, &cond); - wait_interruptible(&cond, keepalive.get_drain_signal()); - } - memory_queue_free_space -= wm.size(); - memory_queue.emplace_back(std::move(wm)); - available_control.set_available(true); - } - } catch (const interrupted_exc_t &) { - /* ignore */ - } - } - - availability_control_t available_control; - mutex_t push_mutex; - scoped_ptr_t disk_queue; - std::list memory_queue; - // Note that `memory_queue_free_space` can sometimes be negative - int64_t memory_queue_free_space; - cond_t *notify_when_room_in_memory_queue; - size_t items_in_queue; - auto_drainer_t drainer; - - io_backender_t *io_backender; - const serializer_filepath_t filename; - perfmon_collection_t *stats_parent; - - // This is used to tell a push operation to restart the copy_from_disk_queue_to_memory_queue - // coroutine since the coroutine exited instead of waiting for the push operation to finish - bool restart_copy_coro; -}; - -#endif /* CONCURRENCY_QUEUE_DISK_BACKED_QUEUE_WRAPPER_HPP_ */ diff --git a/src/concurrency/queue/limited_fifo.hpp b/src/concurrency/queue/limited_fifo.hpp deleted file mode 100644 index 3fd360d11c8..00000000000 --- a/src/concurrency/queue/limited_fifo.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_QUEUE_LIMITED_FIFO_HPP_ -#define CONCURRENCY_QUEUE_LIMITED_FIFO_HPP_ - -#include - -#include "concurrency/queue/passive_producer.hpp" -#include "concurrency/semaphore.hpp" -#include "perfmon/types.hpp" - -/* `limited_fifo_queue_t` is a first-in, first-out queue that has a limited -depth. If the consumer is not reading off the queue as fast as the producer is -pushing things onto the queue, then `push()` will start to block. - -The `capacity` and `trickle_fraction` arguments, and the `set_capacity()` -method, work as in `adjustable_semaphore_t`. */ - -template > -struct limited_fifo_queue_t : - public home_thread_mixin_debug_only_t, - public passive_producer_t { - - limited_fifo_queue_t( - int capacity, double trickle_fraction = 0.0, perfmon_counter_t *_counter = nullptr) - : passive_producer_t(&available_control), - semaphore(capacity, trickle_fraction), - counter(_counter) { } - - void push(const value_t &value) { - assert_thread(); - if (counter) ++(*counter); - semaphore.co_lock(); - queue.push_back(value); - available_control.set_available(!queue.empty()); - } - - void set_capacity(int capacity) { - assert_thread(); - semaphore.set_capacity(capacity); - } - -private: - adjustable_semaphore_t semaphore; - availability_control_t available_control; - - perfmon_counter_t *counter; - - value_t produce_next_value() { - assert_thread(); - value_t v = queue.front(); - queue.pop_front(); - semaphore.unlock(); - if (counter) --(*counter); - available_control.set_available(!queue.empty()); - return v; - } - - queue_t queue; - - DISABLE_COPYING(limited_fifo_queue_t); -}; - -#endif /* CONCURRENCY_QUEUE_LIMITED_FIFO_HPP_ */ diff --git a/src/concurrency/queue/passive_producer.hpp b/src/concurrency/queue/passive_producer.hpp deleted file mode 100644 index 47a2fad6ba8..00000000000 --- a/src/concurrency/queue/passive_producer.hpp +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_QUEUE_PASSIVE_PRODUCER_HPP_ -#define CONCURRENCY_QUEUE_PASSIVE_PRODUCER_HPP_ - -#include "errors.hpp" -#include "arch/runtime/coroutines.hpp" - -/* A passive producer is an object (often a queue) that a consumer can read from -by calling a method. It's called "passive" because the consumer calls a method -on the producer to ask for data, rather than the producer calling a method on -the consumer to give it data. - -In general you must create the passive producer before you create the consumer, -and destroy the consumer before you destroy the passive producer. - -`passive_producer_t` is not thread-safe. - -`availability_t` is used to communicate to the consumer whether or not data is -available on the `passive_producer_t`. `availability_t` is separate from -`passive_producer_t` because often there are several `passive_producer_t`s in a -chain, where each one's availability is equal to the availability of the one -before it, and when `availability_t` is a separate object they can all share the -same `availability_t`. `availability_control_t` is a concrete subclass of -`availability_t` that can actually be instantiated. */ - -class availability_callback_t { -public: - availability_callback_t() { } - virtual void on_source_availability_changed() = 0; -protected: - virtual ~availability_callback_t() { } - DISABLE_COPYING(availability_callback_t); -}; - -class availability_t { -public: - availability_t() : available(false), callback(nullptr) { } - bool get() { return available; } - void set_callback(availability_callback_t *cb) { - rassert(!callback); - rassert(cb); - callback = cb; - } - void unset_callback() { - rassert(callback); - callback = nullptr; - } -private: - friend class availability_control_t; - bool available; - availability_callback_t *callback; - - DISABLE_COPYING(availability_t); -}; - -class availability_control_t : public availability_t { -public: - availability_control_t() { } - void set_available(bool a) { - if (available != a) { - available = a; - if (callback) { - callback->on_source_availability_changed(); - } - } - } - -private: - DISABLE_COPYING(availability_control_t); -}; - -template -struct passive_producer_t { - - /* true if any data is available. */ - availability_t *const available; - - /* `pop()` removes a value from the producer and returns it. It is - an error to call `pop()` when `available->get()` is `false`. */ - value_t pop() { - ASSERT_NO_CORO_WAITING; - rassert(available->get()); - return produce_next_value(); - } - -protected: - /* To implement a passive producer: Pass an `availability_t*` to the - constructor. You can get it either by creating an `availability_control_t`, - or by passing the `availability_t*` from some other queue. Override - `produce_next_value()`; `produce_next_value()` will only be called when the - `watchable_value_t` is true, but it must never fail or block. */ - - explicit passive_producer_t(availability_t *a) : available(a) { } - virtual value_t produce_next_value() = 0; - virtual ~passive_producer_t() { } - -private: - DISABLE_COPYING(passive_producer_t); -}; - -#endif /* CONCURRENCY_QUEUE_PASSIVE_PRODUCER_HPP_ */ diff --git a/src/concurrency/queue/unlimited_fifo.hpp b/src/concurrency/queue/unlimited_fifo.hpp deleted file mode 100644 index e8054ad7524..00000000000 --- a/src/concurrency/queue/unlimited_fifo.hpp +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_QUEUE_UNLIMITED_FIFO_HPP_ -#define CONCURRENCY_QUEUE_UNLIMITED_FIFO_HPP_ - -#include - -#include "concurrency/queue/passive_producer.hpp" -#include "perfmon/perfmon.hpp" - -/* `unlimited_fifo_queue_t` is one of the simplest possible implementations of -`passive_producer_t`. It's a first-in, first-out queue. It's called "unlimited" -to emphasize that it can grow to an arbitrary size, which could be dangerous. - -It's templated on an underlying data structure so that you can use an -`intrusive_list_t` or something like that if you prefer. */ - -namespace unlimited_fifo_queue { -template -T get_front_of_list(const std::list& list) { - return list.front(); -} - -template -T *get_front_of_list(const intrusive_list_t& list) { - return list.head(); -} - -} // namespace unlimited_fifo_queue - -template > -struct unlimited_fifo_queue_t : public passive_producer_t { - unlimited_fifo_queue_t() - : passive_producer_t(&available_control), - counter(nullptr) - { } - - explicit unlimited_fifo_queue_t(perfmon_counter_t *_counter) - : passive_producer_t(&available_control), - counter(_counter) - { } - - void push(const value_t& value) { - if (counter) { - ++(*counter); - } - - queue.push_back(value); - available_control.set_available(!queue.empty()); - } - - int size() { - return queue.size(); - } - -private: - availability_control_t available_control; - value_t produce_next_value() { - if (counter) { - --(*counter); - } - - value_t v = unlimited_fifo_queue::get_front_of_list(queue); - queue.pop_front(); - available_control.set_available(!queue.empty()); - return v; - } - queue_t queue; - perfmon_counter_t *counter; - DISABLE_COPYING(unlimited_fifo_queue_t); -}; - -#endif /* CONCURRENCY_QUEUE_UNLIMITED_FIFO_HPP_ */ diff --git a/src/concurrency/rwlock.cc b/src/concurrency/rwlock.cc deleted file mode 100644 index 460cc0f2bc7..00000000000 --- a/src/concurrency/rwlock.cc +++ /dev/null @@ -1,116 +0,0 @@ -#include "concurrency/rwlock.hpp" - -#include "concurrency/interruptor.hpp" -#include "valgrind.hpp" - -rwlock_t::rwlock_t() { } - -rwlock_t::~rwlock_t() { - guarantee(acqs_.empty()); -} - -void rwlock_t::add_acq(rwlock_in_line_t *acq) { - acqs_.push_back(acq); - pulse_pulsables(acq); -} - -void rwlock_t::remove_acq(rwlock_in_line_t *acq) { - rwlock_in_line_t *subsequent = acqs_.next(acq); - acqs_.remove(acq); - pulse_pulsables(subsequent); -} - -// p is a node whose situation might have changed -- a node whose previous entry is -// different. -void rwlock_t::pulse_pulsables(rwlock_in_line_t *p) { - // We might not have to do any pulsing at all. - if (p == nullptr) { - return; - } else if (p->read_cond_.is_pulsed()) { - // p is already pulsed for read. We don't want to re-pulse the same chain of - // nodes after p. The only question is whether we should pulse p for write. - // (This is typical: When we remove a read-acquirer that has been pulsed for - // read, the subsequent chain of nodes will already have been pulsed for - // read.) - if (p->access_ == access_t::write && acqs_.prev(p) == nullptr) { - p->write_cond_.pulse_if_not_already_pulsed(); - } - return; - } else { - rwlock_in_line_t *prev = acqs_.prev(p); - do { - // p is not null. Should we pulse p for read and continue? - if (prev != nullptr && - !(prev->access_ == access_t::read && prev->read_cond_.is_pulsed())) { - // We're done, because the previous node is present and isn't a - // pulsed read-acquirer. - return; - } - p->read_cond_.pulse(); - - // Should we also pulse p for write (and exit, of course)? - if (p->access_ == access_t::write) { - if (prev == nullptr) { - p->write_cond_.pulse(); - } - return; - } - prev = p; - p = acqs_.next(p); - } while (p != nullptr); - return; - } -} - -rwlock_in_line_t::rwlock_in_line_t() - : lock_(nullptr), access_(valgrind_undefined(access_t::read)) { } - -rwlock_in_line_t::rwlock_in_line_t(rwlock_t *lock, access_t access) - : lock_(lock), access_(access) { - lock_->add_acq(this); -} - -rwlock_in_line_t::rwlock_in_line_t(rwlock_in_line_t &&other) - : lock_(other.lock_), - access_(other.access_), - read_cond_(std::move(other.read_cond_)), - write_cond_(std::move(other.write_cond_)) { - other.lock_ = nullptr; - other.access_ = valgrind_undefined(access_t::read); -} - - -rwlock_in_line_t::~rwlock_in_line_t() { - reset(); -} - -void rwlock_in_line_t::reset() { - if (lock_ != nullptr) { - lock_->remove_acq(this); - lock_ = nullptr; - access_ = valgrind_undefined(access_t::read); - read_cond_.reset(); - write_cond_.reset(); - } -} - - -rwlock_acq_t::rwlock_acq_t(rwlock_t *lock, access_t access) - : rwlock_in_line_t(lock, access) { - if (access == access_t::read) { - read_signal()->wait(); - } else { - write_signal()->wait(); - } -} - -rwlock_acq_t::rwlock_acq_t(rwlock_t *lock, access_t access, signal_t *interruptor) - : rwlock_in_line_t(lock, access) { - if (access == access_t::read) { - wait_interruptible(read_signal(), interruptor); - } else { - wait_interruptible(write_signal(), interruptor); - } -} - -rwlock_acq_t::~rwlock_acq_t() { } diff --git a/src/concurrency/rwlock.hpp b/src/concurrency/rwlock.hpp deleted file mode 100644 index 94bb90ebe00..00000000000 --- a/src/concurrency/rwlock.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef CONCURRENCY_RWLOCK_HPP_ -#define CONCURRENCY_RWLOCK_HPP_ - -#include "concurrency/access.hpp" -#include "concurrency/cond_var.hpp" -#include "containers/intrusive_list.hpp" - -class rwlock_in_line_t; - -class rwlock_t { -public: - rwlock_t(); - ~rwlock_t(); - -private: - friend class rwlock_in_line_t; - void add_acq(rwlock_in_line_t *acq); - void remove_acq(rwlock_in_line_t *acq); - - void pulse_pulsables(rwlock_in_line_t *p); - - // Acquirers, in order by acquisition, with the head containing one of the - // current acquirer, the tail possibly containing a node that does not yet hold - // the lock. - intrusive_list_t acqs_; - DISABLE_COPYING(rwlock_t); -}; - -// Construct one of these to get in line for the read/write lock. Use read_signal() -// and write_signal() to wait for your turn. (Write accessors might get their read -// signal pulsed before their write signal.) -class rwlock_in_line_t : public intrusive_list_node_t { -public: - rwlock_in_line_t(); - - // The constructor does not block. Use read_signal() or write_signal() to wait - // for acquisition. - rwlock_in_line_t(rwlock_t *lock, access_t access); - - // This can be called whenever: You may destroy the lock, getting "out of line", - // before the lock has been acquired. - ~rwlock_in_line_t(); - - rwlock_in_line_t(rwlock_in_line_t &&other); - - const signal_t *read_signal() const { return &read_cond_; } - const signal_t *write_signal() const { - guarantee(access_ == access_t::write); - return &write_cond_; - } - - void reset(); - - void guarantee_is_for_lock(const rwlock_t *lock) const { - guarantee(lock_ == lock); - } - -private: - friend class rwlock_t; - rwlock_t *lock_; - access_t access_; - cond_t read_cond_; - cond_t write_cond_; - - DISABLE_COPYING(rwlock_in_line_t); -}; - -class rwlock_acq_t : private rwlock_in_line_t { -public: - // Acquires the lock. The constructor blocks the coroutine, it doesn't return - // until the lock is acquired. - rwlock_acq_t(rwlock_t *lock, access_t access); - rwlock_acq_t(rwlock_t *lock, access_t access, signal_t *interruptor); - ~rwlock_acq_t(); - - void reset() { - rwlock_in_line_t::reset(); - } - - void guarantee_is_holding(rwlock_t *lock) const { - rwlock_in_line_t::guarantee_is_for_lock(lock); - } - -private: - DISABLE_COPYING(rwlock_acq_t); -}; - -#endif // CONCURRENCY_RWLOCK_HPP_ diff --git a/src/concurrency/semaphore.cc b/src/concurrency/semaphore.cc deleted file mode 100644 index 94e898c2cd0..00000000000 --- a/src/concurrency/semaphore.cc +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "concurrency/semaphore.hpp" - -#include "arch/runtime/coroutines.hpp" -#include "concurrency/cond_var.hpp" -#include "concurrency/interruptor.hpp" - -void static_semaphore_t::lock(semaphore_available_callback_t *cb, int64_t count) { - rassert(!in_callback); - rassert(count <= capacity || capacity == SEMAPHORE_NO_LIMIT); - if (current + count <= capacity || capacity == SEMAPHORE_NO_LIMIT) { - current += count; - DEBUG_ONLY_CODE(in_callback = true); - cb->on_semaphore_available(); - rassert(in_callback); - DEBUG_ONLY_CODE(in_callback = false); - } else { - lock_request_t *r = new lock_request_t; - r->count = count; - r->cb = cb; - waiters.push_back(r); - } -} - -void static_semaphore_t::co_lock(int64_t count) { - rassert(!in_callback); - struct : public semaphore_available_callback_t, public one_waiter_cond_t { - void on_semaphore_available() { pulse(); } - } cb; - lock(&cb, count); - cb.wait_ordered(); - // TODO: Remove the need for in_callback checks. -} - -void static_semaphore_t::co_lock_interruptible(signal_t *interruptor, int64_t count) { - rassert(!in_callback); - struct : public semaphore_available_callback_t, public cond_t { - void on_semaphore_available() { pulse(); } - } cb; - lock(&cb, count); - - try { - wait_interruptible(&cb, interruptor); - } catch (const interrupted_exc_t &ex) { - // Remove our lock request from the queue - for (lock_request_t *request = waiters.head(); request != nullptr; request = waiters.next(request)) { - if (request->cb == &cb) { - waiters.remove(request); - delete request; - break; - } - } - throw; - } -} - -void static_semaphore_t::unlock(int64_t count) { - rassert(!in_callback); - rassert(current >= count); - current -= count; - while (lock_request_t *h = waiters.head()) { - if (current + h->count <= capacity) { - waiters.remove(h); - current += h->count; - rassert(!in_callback); - DEBUG_ONLY_CODE(in_callback = true); - h->on_available(); - rassert(in_callback); - DEBUG_ONLY_CODE(in_callback = false); - } else { - break; - } - } -} - -void static_semaphore_t::lock_now(int64_t count) { - rassert(!in_callback); - rassert(current + count <= capacity || capacity == SEMAPHORE_NO_LIMIT); - current += count; -} - -void static_semaphore_t::force_lock(int64_t count) { - current += count; -} - -void adjustable_semaphore_t::lock(semaphore_available_callback_t *cb, int64_t count) { - rassert(!in_callback); - rassert(count <= capacity || capacity == SEMAPHORE_NO_LIMIT); - if (try_lock(count)) { - rassert(!in_callback); - DEBUG_ONLY_CODE(in_callback = true); - cb->on_semaphore_available(); - rassert(in_callback); - DEBUG_ONLY_CODE(in_callback = false); - } else { - lock_request_t *r = new lock_request_t; - r->count = count; - r->cb = cb; - waiters.push_back(r); - } -} - -void adjustable_semaphore_t::co_lock(int64_t count) { - rassert(!in_callback); - struct : public semaphore_available_callback_t, public one_waiter_cond_t { - void on_semaphore_available() { pulse(); } - } cb; - lock(&cb, count); - cb.wait_ordered(); - // TODO: remove need for in_callback checks -} - -void adjustable_semaphore_t::co_lock_interruptible(signal_t *interruptor, int64_t count) { - rassert(!in_callback); - struct : public semaphore_available_callback_t, public cond_t { - void on_semaphore_available() { pulse(); } - } cb; - lock(&cb, count); - - try { - wait_interruptible(&cb, interruptor); - } catch (const interrupted_exc_t& ex) { - // Remove our lock request from the queue - for (lock_request_t *request = waiters.head(); request != nullptr; request = waiters.next(request)) { - if (request->cb == &cb) { - waiters.remove(request); - delete request; - break; - } - } - throw; - } -} - -void adjustable_semaphore_t::unlock(int64_t count) { - rassert(!in_callback); - rassert(current >= count); - current -= count; - if (current > capacity && capacity != SEMAPHORE_NO_LIMIT) { - trickle_points += trickle_fraction * count; - } - pump(); -} - -void adjustable_semaphore_t::lock_now(int64_t count) { - rassert(!in_callback); - if (!try_lock(count)) { - crash("lock_now() can't lock now.\n"); - } -} - -void adjustable_semaphore_t::force_lock(int64_t count) { - current += count; -} - -void adjustable_semaphore_t::set_capacity(int64_t new_capacity) { - rassert(!in_callback); - capacity = new_capacity; - rassert(capacity >= 0 || capacity == SEMAPHORE_NO_LIMIT); - pump(); -} - -bool adjustable_semaphore_t::try_lock(int64_t count) { - if (current + count > capacity && capacity != SEMAPHORE_NO_LIMIT) { - if (trickle_points >= count) { - trickle_points -= count; - } else { - return false; - } - } - current += count; - return true; -} - -void adjustable_semaphore_t::pump() { - rassert(!in_callback); - lock_request_t *h; - while ((h = waiters.head()) && try_lock(h->count)) { - waiters.remove(h); - rassert(!in_callback); - DEBUG_ONLY_CODE(in_callback = true); - h->on_available(); - rassert(in_callback); - DEBUG_ONLY_CODE(in_callback = false); - } -} diff --git a/src/concurrency/semaphore.hpp b/src/concurrency/semaphore.hpp deleted file mode 100644 index 9196e9bbdcf..00000000000 --- a/src/concurrency/semaphore.hpp +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_SEMAPHORE_HPP_ -#define CONCURRENCY_SEMAPHORE_HPP_ - -#include "concurrency/signal.hpp" -#include "concurrency/wait_any.hpp" -#include "containers/intrusive_list.hpp" - -class semaphore_available_callback_t { -public: - virtual ~semaphore_available_callback_t() {} - virtual void on_semaphore_available() = 0; -}; - -#define SEMAPHORE_NO_LIMIT (-1) - -// DEPRECATED. -class co_semaphore_t { -public: - virtual ~co_semaphore_t() { } - - virtual void lock(semaphore_available_callback_t *cb, int64_t count = 1) = 0; - virtual void co_lock(int64_t count = 1) = 0; - virtual void co_lock_interruptible(signal_t *interruptor, int64_t count = 1) = 0; - virtual void unlock(int64_t count = 1) = 0; - virtual void lock_now(int64_t count = 1) = 0; - virtual void force_lock(int64_t count = 1) = 0; -}; - - -/* `static_semaphore_t` is a `co_semaphore_t` with a fixed capacity that has - to be provided at the time of construction. */ - -// DEPRECATED. Why not use new_semaphore_t? It doesn't have starvation issues. It -// doesn't have starvation issues, obeys first-in/first-out semantics. You wouldn't -// want starvation issues. -class static_semaphore_t : public co_semaphore_t { - struct lock_request_t : public intrusive_list_node_t { - semaphore_available_callback_t *cb; - int64_t count; - void on_available() { - cb->on_semaphore_available(); - delete this; - } - }; - - int64_t capacity; - int64_t current; - intrusive_list_t waiters; - -#ifndef NDEBUG - bool in_callback; -#endif - -public: - explicit static_semaphore_t(int64_t cap) : capacity(cap), current(0) -#ifndef NDEBUG - , in_callback(false) -#endif - { - rassert(capacity >= 0 || capacity == SEMAPHORE_NO_LIMIT); - } - ~static_semaphore_t() { - rassert(waiters.empty()); - } - - void lock(semaphore_available_callback_t *cb, int64_t count = 1); - - void co_lock(int64_t count = 1); - - void co_lock_interruptible(signal_t *interruptor, int64_t count = 1); - - void unlock(int64_t count = 1); - void lock_now(int64_t count = 1); - void force_lock(int64_t count = 1); -}; - -/* `adjustable_semaphore_t` is a `co_semaphore_t` where you can change the -capacity at runtime. If you call `set_capacity()` and the new capacity is less -than the current number of objects that hold the semaphore, then new objects -will be allowed to enter at `trickle_fraction` of the rate that the objects are -leaving until the number of objects drops to the desired capacity. */ - -// DEPRECATED. Why not use new_semaphore_t? It doesn't have starvation issues, -// obeys first-in/first-out semantics. -class adjustable_semaphore_t : public co_semaphore_t { - struct lock_request_t : public intrusive_list_node_t { - semaphore_available_callback_t *cb; - int64_t count; - void on_available() { - cb->on_semaphore_available(); - delete this; - } - }; - - int64_t capacity; - int64_t current; - double trickle_fraction, trickle_points; - intrusive_list_t waiters; - -#ifndef NDEBUG - bool in_callback; -#endif - -public: - explicit adjustable_semaphore_t(int64_t cap, double tf = 0.0) : - capacity(cap), current(0), trickle_fraction(tf), trickle_points(0) -#ifndef NDEBUG - , in_callback(false) -#endif - { - rassert(trickle_fraction <= 1.0 && trickle_fraction >= 0.0); - rassert(capacity >= 0 || capacity == SEMAPHORE_NO_LIMIT); - } - ~adjustable_semaphore_t() { - rassert(waiters.empty()); - } - - void lock(semaphore_available_callback_t *cb, int64_t count = 1); - - void co_lock(int64_t count = 1); - void co_lock_interruptible(signal_t *interruptor, int64_t count = 1); - - void unlock(int64_t count = 1); - void lock_now(int64_t count = 1); - void force_lock(int64_t count = 1); - - void set_capacity(int64_t new_capacity); - - int64_t get_capacity() const { return capacity; } - -private: - bool try_lock(int64_t count); - - void pump(); -}; - - -class semaphore_acq_t { -public: - explicit semaphore_acq_t(co_semaphore_t *_acquiree, int64_t _count = 1, bool _force = false) : - acquiree(_acquiree), - count(_count) { - - if (_force) { - acquiree->force_lock(count); - } else { - acquiree->co_lock(count); - } - } - - semaphore_acq_t(semaphore_acq_t &&movee) : - acquiree(movee.acquiree), - count(movee.count) { - - movee.acquiree = nullptr; - } - - ~semaphore_acq_t() { - if (acquiree) { - acquiree->unlock(count); - } - } - -private: - co_semaphore_t *acquiree; - int64_t count; - DISABLE_COPYING(semaphore_acq_t); -}; - -#endif /* CONCURRENCY_SEMAPHORE_HPP_ */ diff --git a/src/concurrency/signal.cc b/src/concurrency/signal.cc deleted file mode 100644 index f0749242910..00000000000 --- a/src/concurrency/signal.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "concurrency/signal.hpp" - -#include "arch/runtime/coroutines.hpp" - -class notify_later_ordered_subscription_t : public signal_t::subscription_t { -public: - notify_later_ordered_subscription_t() : coro_(coro_t::self()) { } - virtual void run() { - coro_->notify_later_ordered(); - } -private: - coro_t *coro_; - DISABLE_COPYING(notify_later_ordered_subscription_t); -}; - -void signal_t::wait_lazily_ordered() const { - if (!is_pulsed()) { - notify_later_ordered_subscription_t subs; - subs.reset(const_cast(this)); - coro_t::wait(); - } -} - -class notify_sometime_subscription_t : public signal_t::subscription_t { -public: - notify_sometime_subscription_t() : coro_(coro_t::self()) { } - virtual void run() { - coro_->notify_sometime(); - } - -private: - coro_t *coro_; - DISABLE_COPYING(notify_sometime_subscription_t); -}; - -void signal_t::wait_lazily_unordered() const { - if (!is_pulsed()) { - notify_sometime_subscription_t subs; - subs.reset(const_cast(this)); - coro_t::wait(); - } -} - - -signal_t::signal_t(signal_t &&movee) - : home_thread_mixin_t(std::move(movee)), - pulsed(movee.pulsed), - publisher_controller(std::move(movee.publisher_controller)), - lock(std::move(movee.lock)) { - movee.pulsed = false; -} - -// The same thing that happens when a signal_t is destructed happens: We crash if -// there are any current waiters. -void signal_t::reset() { - lock.reset(); - publisher_controller.reset(); - pulsed = false; -} diff --git a/src/concurrency/signal.hpp b/src/concurrency/signal.hpp deleted file mode 100644 index 9c6d1276733..00000000000 --- a/src/concurrency/signal.hpp +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_SIGNAL_HPP_ -#define CONCURRENCY_SIGNAL_HPP_ - -#include "concurrency/pubsub.hpp" -#include "errors.hpp" - -/* A `signal_t` is a boolean variable, combined with a way to be notified if -that boolean variable becomes true. Typically you will construct a concrete -subclass of `signal_t`, then pass a pointer to the underlying `signal_t` to -another object which will read from or listen to it. - -To check if a `signal_t` has already been pulsed, call `is_pulsed()` -on it. To be notified when it gets pulsed, construct a -`signal_t::subscription_t` subclass and reset(...) it with the signal to -watch. The callback will be called when the signal is pulsed. If the -signal is already pulsed at the time you construct the -`signal_t::subscription_t`, then the callback will be called -immediately. - -`signal_t` is not thread-safe. - -Although you may be tempted to, please do not add a method that "unpulses" a -`signal_t`. Part of the definition of a `signal_t` is that it does not return to -the unpulsed state after being pulsed, and some things may depend on that -property. If you want something like that, maybe you should look at something -other than `signal_t`. */ - -class signal_t : public home_thread_mixin_t { -public: - /* True if somebody has called `pulse()`. */ - bool is_pulsed() const { - assert_thread(); - return pulsed; - } - - /* Crashes if the signal is not pulsed. Note that this works on any thread. This is - OK because it should only be called if the signal is already pulsed, and if the - signal is already pulsed, then `pulsed` should not change. */ - void guarantee_pulsed() const { - guarantee(pulsed); - } - - /* Wrapper around a `publisher_t::subscription_t` - */ - - class subscription_t { - public: - subscription_t() : subs(this) { } - subscription_t(subscription_t &&movee) - : subs(std::move(movee.subs)) { } - - virtual ~subscription_t() { } - - void reset(signal_t *s = nullptr) { - if (s) { - mutex_assertion_t::acq_t acq(&s->lock); - if (s->is_pulsed()) { - subs.subscriber->run(); - } else { - subs.reset(s->publisher_controller.get_publisher()); - } - } else { - subs.reset(nullptr); - } - } - - virtual void run() = 0; - private: - publisher_t::subscription_t subs; - DISABLE_COPYING(subscription_t); - }; - - /* The coro that calls `wait_lazily_ordered()` will be pushed onto the event - queue when the signal is pulsed, but will not wake up immediately. Unless - you really need the ordering guarantee, you should call - `wait_lazily_unordered()`. */ - void wait_lazily_ordered() const; - - /* The coro that calls `wait_lazily_unordered()` will be notified soon after - the signal has been pulsed, but not immediately. */ - void wait_lazily_unordered() const; - - /* `wait()` is a deprecated synonym for `wait_lazily_ordered()`. */ - void wait() const { - wait_lazily_ordered(); - } - - void rethread(threadnum_t new_thread) { - real_home_thread = new_thread; - publisher_controller.rethread(new_thread); - lock.rethread(new_thread); - } - -protected: - signal_t() : pulsed(false) { } - signal_t(signal_t &&movee); - ~signal_t() { reset(); } - - void pulse() THROWS_NOTHING { - assert_thread(); - mutex_assertion_t::acq_t acq(&lock); - rassert(!is_pulsed()); - pulsed = true; - publisher_controller.publish(&signal_t::call); - } - - void reset(); - -private: - static void call(subscription_t *subscription) THROWS_NOTHING { - subscription->run(); - } - - bool pulsed; - publisher_controller_t publisher_controller; - mutex_assertion_t lock; - DISABLE_COPYING(signal_t); -}; - -#endif /* CONCURRENCY_SIGNAL_HPP_ */ diff --git a/src/concurrency/timestamp_enforcer.cc b/src/concurrency/timestamp_enforcer.cc deleted file mode 100644 index d1a072780cd..00000000000 --- a/src/concurrency/timestamp_enforcer.cc +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "concurrency/timestamp_enforcer.hpp" - -#include "concurrency/interruptor.hpp" -#include "containers/map_sentries.hpp" - -void timestamp_enforcer_t::wait_all_before( - state_timestamp_t goal, signal_t *interruptor) { - if (goal > timestamp) { - cond_t waiter; - multimap_insertion_sentry_t sentry( - &waiters, goal, &waiter); - wait_interruptible(&waiter, interruptor); - guarantee(timestamp >= goal); - } -} - -void timestamp_enforcer_t::complete(state_timestamp_t completed) { - guarantee(completed > timestamp); - guarantee(future_completed.count(completed) == 0); - if (completed == timestamp.next()) { - timestamp = completed; - while (!future_completed.empty() && - *future_completed.begin() == timestamp.next()) { - timestamp = timestamp.next(); - future_completed.erase(future_completed.begin()); - } - for (auto it = waiters.begin(); it != waiters.upper_bound(timestamp); ++it) { - it->second->pulse_if_not_already_pulsed(); - } - } else { - future_completed.insert(completed); - } -} diff --git a/src/concurrency/timestamp_enforcer.hpp b/src/concurrency/timestamp_enforcer.hpp deleted file mode 100644 index e90a07a37c7..00000000000 --- a/src/concurrency/timestamp_enforcer.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_TIMESTAMP_ENFORCER_HPP_ -#define CONCURRENCY_TIMESTAMP_ENFORCER_HPP_ - -#include -#include - -#include "concurrency/cond_var.hpp" -#include "timestamps.hpp" - -class timestamp_enforcer_t { -public: - explicit timestamp_enforcer_t(state_timestamp_t initial) : timestamp(initial) { } - - /* Blocks until all timestamps less than or equal to `goal` have been completed. */ - void wait_all_before(state_timestamp_t goal, signal_t *interruptor); - - /* Returns the latest timestamp such that all timestamps less than or equal to it - have been completed. */ - state_timestamp_t get_latest_all_before_completed() const { - return timestamp; - } - - /* Marks the given timestamp as completed. */ - void complete(state_timestamp_t completed); - -private: - /* `timestamp` is the latest timestamp such that all timestamps less than or equal to - it have been completed. */ - state_timestamp_t timestamp; - - /* `future_completed` is a collection of timestamps which have been completed. They - are always greater than `timestamp + 1`. */ - std::set future_completed; - - /* `waiters` is a collection of condition variables that should be pulsed when - `timestamp` reaches their value. */ - std::multimap waiters; -}; - -#endif /* CONCURRENCY_TIMESTAMP_ENFORCER_HPP_ */ - diff --git a/src/concurrency/wait_any.cc b/src/concurrency/wait_any.cc deleted file mode 100644 index 8337b12e3d4..00000000000 --- a/src/concurrency/wait_any.cc +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "concurrency/wait_any.hpp" - -void wait_any_t::wait_any_subscription_t::run() { - parent->pulse_if_not_already_pulsed(); -} - -wait_any_t::~wait_any_t() { - while (!subs.empty()) { - wait_any_subscription_t *p = subs.head(); - subs.remove(p); - - if (p->on_heap()) { - delete p; - } - } -} - -void wait_any_t::add(const signal_t *s) { - rassert(s); - wait_any_subscription_t *sub; - - // Use preallocated subscriptions, if possible, to save on dynamic memory usage - if (subs.size() < default_preallocated_subs) { - sub = sub_storage[subs.size()].create(this, false); - } else { - sub = new wait_any_subscription_t(this, true); - } - sub->reset(const_cast(s)); - subs.push_back(sub); -} - -void wait_any_t::pulse_if_not_already_pulsed() { - if (!is_pulsed()) pulse(); -} diff --git a/src/concurrency/wait_any.hpp b/src/concurrency/wait_any.hpp deleted file mode 100644 index adf50a9f56b..00000000000 --- a/src/concurrency/wait_any.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_WAIT_ANY_HPP_ -#define CONCURRENCY_WAIT_ANY_HPP_ - -#include "concurrency/signal.hpp" -#include "containers/intrusive_list.hpp" -#include "containers/object_buffer.hpp" - -/* Monitors multiple signals; becomes pulsed if any individual signal becomes -pulsed. */ - -class wait_any_subscription_t; - -class wait_any_t : public signal_t { -public: - template - explicit wait_any_t(Args... args) { - UNUSED int arr[] = { 0, (add(args), 1)... }; - } - - ~wait_any_t(); - - void add(const signal_t *s); - -private: - class wait_any_subscription_t : public signal_t::subscription_t, public intrusive_list_node_t { - public: - wait_any_subscription_t(wait_any_t *_parent, bool _alloc_on_heap) : parent(_parent), alloc_on_heap(_alloc_on_heap) { } - virtual void run(); - bool on_heap() const { return alloc_on_heap; } - private: - wait_any_t *parent; - bool alloc_on_heap; - DISABLE_COPYING(wait_any_subscription_t); - }; - - static const size_t default_preallocated_subs = 3; - - void pulse_if_not_already_pulsed(); - - intrusive_list_t subs; - - object_buffer_t sub_storage[default_preallocated_subs]; - - DISABLE_COPYING(wait_any_t); -}; - -#endif /* CONCURRENCY_WAIT_ANY_HPP_ */ diff --git a/src/concurrency/watchable.hpp b/src/concurrency/watchable.hpp deleted file mode 100644 index 8ddf29dd082..00000000000 --- a/src/concurrency/watchable.hpp +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_WATCHABLE_HPP_ -#define CONCURRENCY_WATCHABLE_HPP_ - -#include - -#include "concurrency/interruptor.hpp" -#include "concurrency/mutex_assertion.hpp" -#include "concurrency/pubsub.hpp" -#include "concurrency/signal.hpp" -#include "containers/clone_ptr.hpp" -#include "utils.hpp" - -/* `watchable_t` represents a variable that you can get the value of and also -subscribe to further changes to the value. To get the value of a `watchable_t`, -just call `get()`. To subscribe to further changes to the value, construct a -`watchable_t::freeze_t` and then construct a `watchable_t::subscription_t`, -passing it the `watchable_t` and the `freeze_t`. - -To create a `watchable_t`, construct a `watchable_variable_t` and then call its -`get_watchable()` method. You can change the value of the `watchable_t` by -calling `watchable_variable_t::set_value()`. - -`watchable_t` has a home thread; it can only be accessed from that thread. If -you need to access it from another thread, consider using -`cross_thread_watchable_variable_t` to create a proxy-watchable with a different -home thread. */ - -template class watchable_t; -template class watchable_freeze_t; - -/* `initial_call_t` is used to indicate whether a subscription should call its callback -immediately upon being created. */ -enum class initial_call_t { YES, NO }; - -template -class watchable_subscription_t { -public: - /* The `std::function` passed to the constructor is a callback - that will be called whenever the value changes. The callback must not - block, and it must not create or destroy `subscription_t` objects. */ - - explicit watchable_subscription_t(const std::function &f) - : subscription(f) - { } - - watchable_subscription_t(const std::function &f, const clone_ptr_t > &watchable, - watchable_freeze_t *freeze) - : subscription(f, watchable->get_publisher()) { - watchable->assert_thread(); - freeze->rwi_lock_acquisition.assert_is_holding(watchable->get_rwi_lock_assertion()); - } - - watchable_subscription_t( - const std::function &f, - const clone_ptr_t > &watchable, - initial_call_t initial_call) : - subscription(f) - { - rwi_lock_assertion_t::read_acq_t rwi_lock_acquisition( - watchable->get_rwi_lock_assertion()); - subscription.reset(watchable->get_publisher()); - if (initial_call == initial_call_t::YES) { - f(); - } - } - - void reset() { - subscription.reset(); - } - - void reset(const clone_ptr_t > &watchable, watchable_freeze_t *freeze) { - watchable->assert_thread(); - freeze->rwi_lock_acquisition.assert_is_holding(watchable->get_rwi_lock_assertion()); - subscription.reset(watchable->get_publisher()); - } - -private: - typename publisher_t >::subscription_t subscription; - - DISABLE_COPYING(watchable_subscription_t); -}; - - -/* The purpose of the `freeze_t` is to assert that the value of the - `watchable_t` doesn't change for as long as it exists. You should not block - while the `freeze_t` exists. It's kind of like `mutex_assertion_t`. - - A common pattern is to construct a `freeze_t`, then call `get()` to - initialize some object or objects that mirrors the `watchable_t`'s value, - then construct a `subscription_t` to continually keep in sync with the - `watchable_t`, then destroy the `freeze_t`. If it weren't for the - `freeze_t`, a change might "slip through the cracks" if it happened after - `get()` but before constructing the `subscription_t`. If you constructed the - `subscription_t` before calling `get()`, then the callback might get run - before the objects were first initialized. */ - -template -class watchable_freeze_t { -public: - explicit watchable_freeze_t(const clone_ptr_t > &watchable) - : rwi_lock_acquisition(watchable->get_rwi_lock_assertion()) - { - watchable->assert_thread(); - } - - void assert_is_holding(const clone_ptr_t > &watchable) { - rwi_lock_acquisition.assert_is_holding(watchable->get_rwi_lock_assertion()); - } - -private: - friend class watchable_subscription_t; - rwi_lock_assertion_t::read_acq_t rwi_lock_acquisition; - - DISABLE_COPYING(watchable_freeze_t); -}; - -template -class watchable_t : public home_thread_mixin_t { -public: - typedef watchable_freeze_t freeze_t; - typedef watchable_subscription_t subscription_t; - - virtual ~watchable_t() { } - virtual watchable_t *clone() const = 0; - - virtual value_t get() = 0; - - /* Applies `read` to the current value of the watchable. `read` must not block - and cannot change the value. */ - virtual void apply_read(const std::function &read) = 0; - - /* These are internal; the reason they're public is so that `subview()` and - similar things can be implemented. */ - virtual publisher_t > *get_publisher() = 0; - virtual rwi_lock_assertion_t *get_rwi_lock_assertion() = 0; - - /* `subview()` returns another `watchable_t` whose value is derived from - this one by calling `lens` on it. */ - template - clone_ptr_t::type> > subview(const callable_type &lens); - /* `incremental_subview()` is like `subview()`. However it takes an incremental lens. - An incremental lens has the signature - `bool(const input_type &, result_type *current_out)`. - In contrast to a regular (non-incremental) lens it therefore has access to - the current value of the output value and can modify it in-place. - It should return true if `current_out` has been modified, and false otherwise. - We have two variants of this. The first one is a short-cut for incremental lenses - which contain a `result_type` typedef. In this case, result_type does not have - to be specified explicitly when calling `incremental_subview()`. Otherwise - the second variant must be used and `result_type` be specified as a template - parameter. */ - template - clone_ptr_t > incremental_subview(const callable_type &lens); - template - clone_ptr_t > incremental_subview(const callable_type &lens); - - /* `run_until_satisfied()` repeatedly calls `fun` on the current value of - `this` until either `fun` returns `true` or `interruptor` is pulsed. It's - efficient because it only retries `fun` when the value changes. */ - template - void run_until_satisfied(const callable_type &fun, signal_t *interruptor, - int64_t nap_before_retry_ms = 0) THROWS_ONLY(interrupted_exc_t); - -protected: - watchable_t() { } - -private: - DISABLE_COPYING(watchable_t); -}; - -/* `run_until_satisfied_2()` repeatedly calls `fun(a->get(), b->get())` until -`fun` returns `true` or `interruptor` is pulsed. It's efficient because it only -retries `fun` when the value of `a` or `b` changes. */ -template -void run_until_satisfied_2( - const clone_ptr_t > &a, - const clone_ptr_t > &b, - const callable_type &fun, - signal_t *interruptor, - int64_t nap_before_retry_ms = 0) THROWS_ONLY(interrupted_exc_t); - -inline void call_function(const std::function &f) { - f(); -} - -template -class watchable_variable_t { -public: - explicit watchable_variable_t(const value_t &_value) - : value(_value), watchable(this) - { } - - clone_ptr_t > get_watchable() { - return clone_ptr_t >(watchable.clone()); - } - - void set_value(const value_t &_value) { - DEBUG_VAR rwi_lock_assertion_t::write_acq_t acquisition(&rwi_lock_assertion); - /* Sometimes we have `==` but not `!=` for whatever reason */ - if (!(value == _value)) { - value = _value; - publisher_controller.publish(&call_function); - } - } - - void set_value_no_equals(const value_t &_value) { - DEBUG_VAR rwi_lock_assertion_t::write_acq_t acquisition(&rwi_lock_assertion); - value = _value; - publisher_controller.publish(&call_function); - } - - // Applies an atomic modification to the value. - // `op` must return true if the value was modified, - // and should return false otherwise. - void apply_atomic_op(const std::function &op) { // NOLINT(readability/casting) - DEBUG_VAR rwi_lock_assertion_t::write_acq_t acquisition(&rwi_lock_assertion); - bool was_modified; - { - ASSERT_NO_CORO_WAITING; - was_modified = op(&value); - } - if (was_modified) { - publisher_controller.publish(&call_function); - } - } - - // This is similar to apply_atomic_op, except that the operation - // cannot modify the value. - void apply_read(const std::function &read) { - ASSERT_NO_CORO_WAITING; - read(&value); - } - - /* This returns a const reference to the current value. The caller is responsible for - ensuring that nothing calls `set_value()` or `apply_atomic_op()` while this reference - exists. */ - const value_t &get_ref() { - return value; - } - - value_t get() { - return value; - } - -private: - class w_t : public watchable_t { - public: - explicit w_t(watchable_variable_t *p) : parent(p) { } - w_t *clone() const { - return new w_t(parent); - } - value_t get() { - return parent->value; - } - publisher_t > *get_publisher() { - return parent->publisher_controller.get_publisher(); - } - rwi_lock_assertion_t *get_rwi_lock_assertion() { - return &parent->rwi_lock_assertion; - } - void apply_read(const std::function &read) { - parent->apply_read(read); - } - watchable_variable_t *parent; - }; - - publisher_controller_t > publisher_controller; - value_t value; - rwi_lock_assertion_t rwi_lock_assertion; - - w_t watchable; - - DISABLE_COPYING(watchable_variable_t); -}; - -#include "concurrency/watchable.tcc" - -#endif /* CONCURRENCY_WATCHABLE_HPP_ */ diff --git a/src/concurrency/watchable.tcc b/src/concurrency/watchable.tcc deleted file mode 100644 index 38a95a76394..00000000000 --- a/src/concurrency/watchable.tcc +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "concurrency/watchable.hpp" - -#include -#include - -#include "arch/timing.hpp" -#include "concurrency/cond_var.hpp" -#include "concurrency/wait_any.hpp" - -template -class subview_watchable_t : public watchable_t { -public: - subview_watchable_t(const callable_type &l, watchable_t *p) : - cache(new lensed_value_cache_t(l, p)) { } - - subview_watchable_t *clone() const { - return new subview_watchable_t(cache); - } - - result_type get() { - return *cache->get(); - } - - virtual void apply_read(const std::function &read) { - ASSERT_NO_CORO_WAITING; - read(cache->get()); - } - - publisher_t > *get_publisher() { - return cache->get_publisher(); - } - - rwi_lock_assertion_t *get_rwi_lock_assertion() { - return cache->parent->get_rwi_lock_assertion(); - } - -private: - // lensed_value_cache_t is a cache for the result of a lens applied to - // a given parent watchable. It is implemented for incremental lenses only. - // (`non_incremental_lens_wrapper_t` can be used to apply it to non-incremental - // lenses as well). - // The cached value is recomputed only after the parent has published a new - // value. - class lensed_value_cache_t : public home_thread_mixin_t { - public: - lensed_value_cache_t(const callable_type &l, watchable_t *p) : - parent(p->clone()), - lens(l), - parent_subscription(std::bind( - &subview_watchable_t::lensed_value_cache_t::on_parent_changed, - this)) { - typename watchable_t::freeze_t freeze(parent); - ASSERT_FINITE_CORO_WAITING; - compute_value(); - parent_subscription.reset(parent, &freeze); - } - - ~lensed_value_cache_t() { - assert_thread(); - } - - const result_type *get() { - assert_thread(); - return &cached_value; - } - - publisher_t > *get_publisher() { - assert_thread(); - return publisher_controller.get_publisher(); - } - - clone_ptr_t > parent; - - private: - bool compute_value() { - // The closure is to avoid copying the whole value from the parent. - - bool value_changed = false; - struct op_closure_t { - static void apply(callable_type *_lens, - bool *_value_changed, - result_type *_cached_value, - const outer_type *val) { - *_value_changed = (*_lens)(*val, _cached_value); - } - }; - - parent->apply_read(std::bind(&op_closure_t::apply, - &lens, - &value_changed, - &cached_value, - std::placeholders::_1)); - - return value_changed; - } - - void on_parent_changed() { - assert_thread(); - const bool value_changed = compute_value(); - if (value_changed) { - publisher_controller.publish(&call_function); - } - } - - callable_type lens; - result_type cached_value; - publisher_controller_t > publisher_controller; - typename watchable_t::subscription_t parent_subscription; - }; - - explicit subview_watchable_t(const std::shared_ptr &_cache) : - cache(_cache) { } - - // If you clone a subview_watchable_t, all clones share the same cache. - // As a consequence, the lens is only applied once for the whole family - // of clones, not once per instance of subview_watchable_t. This should save - // some CPU and memory as well. - std::shared_ptr cache; -}; - -/* Given a non-incremental lens with the type signature - * `result_type(input_type)`, `non_incremental_lens_wrapper_t` converts - * it into an incremental lens with type signature - * `bool(const input_type &, result_type *)` and otherwise equivalent semantics - * (as much as possible). */ -template -class non_incremental_lens_wrapper_t { -public: - typedef typename std::result_of::type result_type; - - explicit non_incremental_lens_wrapper_t(const callable_type &_inner) : - inner(_inner) { - } - - bool operator()(const outer_type &input, result_type *current_out) { - guarantee(current_out != nullptr); - result_type old_value = *current_out; - *current_out = inner(input); - /* We can't use `!=` here because sometimes lazy programmers will implement `==` - but not `!=`. */ - return !(old_value == *current_out); - } -private: - callable_type inner; -}; - -template -template -clone_ptr_t > watchable_t::incremental_subview(const callable_type &lens) { - assert_thread(); - return clone_ptr_t >( - new subview_watchable_t(lens, this)); -} - -template -template -clone_ptr_t > watchable_t::incremental_subview(const callable_type &lens) { - assert_thread(); - return clone_ptr_t >( - new subview_watchable_t(lens, this)); -} - -template -template -clone_ptr_t::type> > watchable_t::subview(const callable_type &lens) { - assert_thread(); - typedef non_incremental_lens_wrapper_t wrapped_callable_type; - wrapped_callable_type wrapped_lens(lens); - return clone_ptr_t >( - new subview_watchable_t(wrapped_lens, this)); -} - -// These are helper functions for `run_until_satisfied()` and `run_until_satisfied_2()` -// that can be passed into a watchable's `apply_read()`. They simply apply the -// provided function `_fun` to the current value of the watchable(s) and pass out -// its boolean return value. -template -void run_until_satisfied_apply(const callable_type &_fun, const value_type *val, - bool *is_done_out) { - *is_done_out = _fun(*val); -} - -template -template -void watchable_t::run_until_satisfied(const callable_type &fun, - signal_t *interruptor, int64_t nap_before_retry_ms) THROWS_ONLY(interrupted_exc_t) { - assert_thread(); - - bool is_done = false; - auto op = std::bind(&run_until_satisfied_apply, - fun, - std::placeholders::_1, - &is_done); - - clone_ptr_t > clone_this(this->clone()); - while (true) { - cond_t changed; - typename watchable_t::subscription_t subs(std::bind(&cond_t::pulse_if_not_already_pulsed, &changed)); - { - typename watchable_t::freeze_t freeze(clone_this); - ASSERT_FINITE_CORO_WAITING; - clone_this->apply_read(op); - if (is_done) { - return; - } - subs.reset(clone_this, &freeze); - } - // Nap a little so changes to the watchables can accumulate. - // This is purely a performance optimization to save CPU cycles, - // in case that applying `fun` is expensive. - if (nap_before_retry_ms > 0) { - nap(nap_before_retry_ms, interruptor); - } - wait_interruptible(&changed, interruptor); - } -} - -template -void run_until_satisfied_2( - const clone_ptr_t > &a, - const clone_ptr_t > &b, - const callable_type &fun, - signal_t *interruptor, - int64_t nap_before_retry_ms) THROWS_ONLY(interrupted_exc_t) { - a->assert_thread(); - b->assert_thread(); - - while (true) { - cond_t changed; - typename watchable_t::subscription_t a_subs(std::bind(&cond_t::pulse_if_not_already_pulsed, &changed)); - typename watchable_t::subscription_t b_subs(std::bind(&cond_t::pulse_if_not_already_pulsed, &changed)); - { - typename watchable_t::freeze_t a_freeze(a); - typename watchable_t::freeze_t b_freeze(b); - ASSERT_FINITE_CORO_WAITING; - bool is_done; - a->apply_read([&](const a_type *a_value) { - b->apply_read([&](const b_type *b_value) { - is_done = fun(*a_value, *b_value); - }); - }); - if (is_done) { - return; - } - a_subs.reset(a, &a_freeze); - b_subs.reset(b, &b_freeze); - } - // Nap a little so changes to the watchables can accumulate. - // This is purely a performance optimization to save CPU cycles, - // in case that applying `fun` is expensive. - if (nap_before_retry_ms > 0) { - nap(nap_before_retry_ms, interruptor); - } - wait_interruptible(&changed, interruptor); - } -} diff --git a/src/concurrency/watchable_map.hpp b/src/concurrency/watchable_map.hpp deleted file mode 100644 index 6cfaf6923dd..00000000000 --- a/src/concurrency/watchable_map.hpp +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_WATCHABLE_MAP_HPP_ -#define CONCURRENCY_WATCHABLE_MAP_HPP_ - -#include -#include - -#include "concurrency/signal.hpp" -#include "concurrency/pubsub.hpp" -#include "concurrency/watchable.hpp" -#include "containers/map_sentries.hpp" -#include "containers/optional.hpp" -#include "utils.hpp" - -/* `watchable_map_t` is like `watchable_t` except specialized for holding a map in which -the keys usually update independently. If the map contains N keys, it takes O(log(N)) -time to propagate an update for that key, as opposed to a `watchable_t`, which -would take O(N) time. - -Note that `watchable_map_t` doesn't make any guarantees about update ordering. The values -visible in the `watchable_map_t` are guaranteed to quickly converge to the correct value, -but they could do so in any order. In addition, if a value is changed and then quickly -changed back, it's possible that no notification will be sent. (The current -implementation will always deliver notifications in this case, but there's no guarantee.) -*/ - -template -class watchable_map_t : public home_thread_mixin_t { -public: - /* `all_subs_t` registers for notifications whenever any key in the map is inserted, - deleted, or changed. */ - class all_subs_t { - public: - /* Starts watching for changes to the given map. When a change occurs to a key, - `cb` will be called with the key and a pointer to the new value, or `nullptr` if - the key was deleted. If `initial_call` is `YES`, then `cb` will be called once - for each key-value pair in the map at the time the `all_subs_t` is created. */ - all_subs_t( - watchable_map_t *map, - const std::function &cb, - initial_call_t initial_call = initial_call_t::YES); - - private: - typename publisher_t > - ::subscription_t subscription; - DISABLE_COPYING(all_subs_t); - }; - - /* `key_subs_t` registers for notifications whenever a specific key in the map - is inserted, deleted, or changed. */ - class key_subs_t { - public: - /* Starts watching for changes to the given key in the given map. When `key` is - inserted, changed, or deleted in `map`, `cb` will be called with a pointer to the - new value, or `nullptr` if `key` was deleted. If `initial_call` is `YES`, then - the constructor will call `cb` once with the initial value of `key`. */ - key_subs_t( - watchable_map_t *map, - const key_t &key, - const std::function &cb, - initial_call_t initial_call = initial_call_t::YES); - - private: - multimap_insertion_sentry_t > sentry; - DISABLE_COPYING(key_subs_t); - }; - - /* Returns all of the key-value pairs in the map. This needs to copy all of the keys - and values in the map, so it's likely to be slow. */ - virtual std::map get_all() = 0; - - /* Looks up a single key-value pair in the map. Returns the value if found, or an - empty `optional` if not. This needs to copy the value, so it's likely to be - slow. */ - virtual optional get_key(const key_t &key) = 0; - - /* `read_all()` and `read_key()` are like `get_all()` and `get_key()`, except that - they avoid copying. */ - virtual void read_all( - /* This function will be called once for each key-value pair in the map. The - `const value_t *` will always be non-NULL; it's a pointer for consistency with - other functions in this class. */ - const std::function &) = 0; - virtual void read_key( - const key_t &key, - const std::function &) = 0; - - /* `run_key_until_satisfied()` repeatedly calls `fun()` on a `const value_t *` - representing the current value of `key` until `fun` returns `true` or `interruptor` - is pulsed. It's efficient because it only retries `fun` when the value changes. */ - template - void run_key_until_satisfied( - const key_t &key, - const callable_t &fun, - signal_t *interruptor); - - /* `run_all_until_satisfied()` repeatedly calls `fun()` with `this` until `fun` - returns `true` or `interruptor` is pulsed. It's efficient because it only retries - `fun` when a key has changed. */ - template - void run_all_until_satisfied( - const callable_t &fun, - signal_t *interruptor); - -protected: - watchable_map_t() { } - virtual ~watchable_map_t() { } - - /* Subclasses should call `notify_change()` any time that they alter values. */ - void notify_change( - const key_t &key, - const value_t *new_value, - rwi_lock_assertion_t::write_acq_t *acq); - - void rethread(threadnum_t new_thread) { - home_thread_mixin_t::real_home_thread = new_thread; - all_subs_publisher.rethread(new_thread); - } - -private: - virtual rwi_lock_assertion_t *get_rwi_lock() = 0; - - publisher_controller_t > - all_subs_publisher; - std::multimap > key_subs_map; - - DISABLE_COPYING(watchable_map_t); -}; - -template -class watchable_map_var_t : public watchable_map_t { -public: - /* `entry_t` creates a map entry in its constructor and removes it in the destructor. - It is an alternative to `set_key()` and `delete_key()`. It crashes if an entry with - that key already exists. Don't call `delete_key()` on the entry that this creates, - and don't call `set_all()` while this exists. */ - class entry_t { - public: - entry_t() : parent(nullptr) { } - entry_t(watchable_map_var_t *parent, const key_t &key, const value_t &value); - entry_t(const entry_t &) = delete; - entry_t(entry_t &&); - ~entry_t(); - entry_t &operator=(const entry_t &) = delete; - entry_t &operator=(entry_t &&); - - key_t get_key() const; - value_t get_value() const; - void set(const value_t &new_value); - void set_no_equals(const value_t &new_value); - void change(const std::function &callback); - - private: - watchable_map_var_t *parent; - typename std::map::iterator iterator; - }; - - watchable_map_var_t() { } - explicit watchable_map_var_t(std::map &&source); - - std::map get_all(); - optional get_key(const key_t &key); - void read_all(const std::function &); - void read_key(const key_t &key, const std::function &); - - /* `set_all()` removes all of the key-value pairs from the map and replaces them with - values from `new_value`. */ - void set_all(const std::map &new_value); - - /* `set_key()` sets the value of `key` to `new_value`. If `key` was not in the - map before, it will be inserted. As an optimization, `set_key()` compares the - previous value of `key` to `new_value`; if they compare equal, it will not notify - subscriptions. If `value` does not support equality comparisons, use - `set_key_no_equals()`. */ - void set_key(const key_t &key, const value_t &new_value); - void set_key_no_equals(const key_t &key, const value_t &new_value); - - /* `delete_key()` removes `key` from the map if it was present before. */ - void delete_key(const key_t &key); - - /* `change_key()` atomically modifies the value of `key`. It calls the callback - exactly once, with two parameters `exists` and `value`. If the key is present, - `*exists` will be `true` and `*value` will be its value; if the key is absent, then - `*exists` will be `false` and `*value` will be a valid buffer but with undefined - contents. The callback can modify `*exists` and/or `*value`. It must return `true` if - it makes any changes. these changes will be reflected in the `watchable_map_t`. The - callback must not block or call any other methods of the `watchable_map_var_t`. */ - void change_key( - const key_t &key, - const std::function &callback); - - void rethread(threadnum_t new_thread) { - watchable_map_t::rethread(new_thread); - rwi_lock.rethread(new_thread); - } - -private: - rwi_lock_assertion_t *get_rwi_lock() { - return &rwi_lock; - } - - std::map map; - rwi_lock_assertion_t rwi_lock; -}; - -#include "concurrency/watchable_map.tcc" - -#endif /* CONCURRENCY_WATCHABLE_MAP_HPP_ */ - diff --git a/src/concurrency/watchable_map.tcc b/src/concurrency/watchable_map.tcc deleted file mode 100644 index 4f80a04f9ca..00000000000 --- a/src/concurrency/watchable_map.tcc +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "concurrency/watchable_map.hpp" - -#include "assignment_sentry.hpp" -#include "concurrency/cond_var.hpp" -#include "concurrency/interruptor.hpp" - -template -watchable_map_t::all_subs_t::all_subs_t( - watchable_map_t *map, - const std::function &cb, - initial_call_t initial_call) : - subscription(cb) { - rwi_lock_assertion_t::read_acq_t acq(map->get_rwi_lock()); - subscription.reset(map->all_subs_publisher.get_publisher()); - if (initial_call == initial_call_t::YES) { - map->read_all(cb); - } -} - -template -watchable_map_t::key_subs_t::key_subs_t( - watchable_map_t *map, - const key_t &key, - const std::function &cb, - initial_call_t initial_call) : - sentry(&map->key_subs_map, key, cb) { - if (initial_call == initial_call_t::YES) { - map->read_key(key, cb); - } -} - -template -template -void watchable_map_t::run_key_until_satisfied( - const key_t &key, - const callable_t &fun, - signal_t *interruptor) { - assert_thread(); - cond_t ok; - key_subs_t subs(this, key, - [&](const value_t *new_value) { - if (fun(new_value)) { - ok.pulse_if_not_already_pulsed(); - } - }, initial_call_t::YES); - wait_interruptible(&ok, interruptor); -} - -template -template -void watchable_map_t::run_all_until_satisfied( - const callable_t &fun, - signal_t *interruptor) { - assert_thread(); - cond_t *notify = nullptr; - all_subs_t subs(this, - [¬ify](const key_t &, const value_t *) { - if (notify != nullptr) { - notify->pulse_if_not_already_pulsed(); - } - }, - initial_call_t::NO); - while (true) { - cond_t cond; - assignment_sentry_t sentry(¬ify, &cond); - if (fun(this)) { - return; - } - wait_interruptible(&cond, interruptor); - } -} - -template -void watchable_map_t::notify_change( - const key_t &key, - const value_t *new_value, - rwi_lock_assertion_t::write_acq_t *acq) { - ASSERT_FINITE_CORO_WAITING; - acq->assert_is_holding(get_rwi_lock()); - all_subs_publisher.publish( - [&](const std::function &callback) { - callback(key, new_value); - }); - for (auto it = key_subs_map.lower_bound(key); - it != key_subs_map.upper_bound(key); - ++it) { - it->second(new_value); - } -} - -template -watchable_map_var_t::entry_t::entry_t( - watchable_map_var_t *p, const key_t &key, const value_t &value) : - parent(p) -{ - rwi_lock_assertion_t::write_acq_t write_acq(&parent->rwi_lock); - auto pair = parent->map.insert(std::make_pair(key, value)); - guarantee(pair.second, "key for entry_t already exists"); - iterator = pair.first; - parent->notify_change(iterator->first, &iterator->second, &write_acq); -} - -template -watchable_map_var_t::entry_t::entry_t(entry_t &&other) : - parent(other.parent), iterator(other.iterator) -{ - other.parent = nullptr; -} - -template -watchable_map_var_t::entry_t::~entry_t() { - if (parent != nullptr) { - *this = entry_t(); - } -} - -template -typename watchable_map_var_t::entry_t & -watchable_map_var_t::entry_t::operator=(entry_t &&other) { - if (this != &other) { - if (parent != nullptr) { - rwi_lock_assertion_t::write_acq_t write_acq(&parent->rwi_lock); - key_t key = iterator->first; - parent->map.erase(iterator); - parent->notify_change(key, nullptr, &write_acq); - } - parent = other.parent; - iterator = other.iterator; - other.parent = nullptr; - } - return *this; -} - -template -key_t watchable_map_var_t::entry_t::get_key() const { - guarantee(parent != nullptr); - return iterator->first; -} - -template -value_t watchable_map_var_t::entry_t::get_value() const { - guarantee(parent != nullptr); - return iterator->second; -} - -template -void watchable_map_var_t::entry_t::set(const value_t &new_value) { - guarantee(parent != nullptr); - rwi_lock_assertion_t::write_acq_t write_acq(&parent->rwi_lock); - if (iterator->second != new_value) { - iterator->second = new_value; - parent->notify_change(iterator->first, &iterator->second, &write_acq); - } -} - -template -void watchable_map_var_t::entry_t::set_no_equals( - const value_t &new_value) { - guarantee(parent != nullptr); - rwi_lock_assertion_t::write_acq_t write_acq(&parent->rwi_lock); - iterator->second = new_value; - parent->notify_change(iterator->first, &iterator->second, &write_acq); -} - -template -void watchable_map_var_t::entry_t::change( - const std::function &callback) { - guarantee(parent != nullptr); - ASSERT_NO_CORO_WAITING; - rwi_lock_assertion_t::write_acq_t write_acq(&parent->rwi_lock); - bool changed = callback(&iterator->second); - if (changed) { - parent->notify_change(iterator->first, &iterator->second, &write_acq); - } -} - -template -watchable_map_var_t::watchable_map_var_t( - std::map &&source) : - map(std::move(source)) { } - -template -std::map watchable_map_var_t::get_all() { - return map; -} - -template -optional watchable_map_var_t::get_key(const key_t &key) { - auto it = map.find(key); - if (it == map.end()) { - return optional(); - } else { - return optional(it->second); - } -} - -template -void watchable_map_var_t::read_all( - const std::function &fun) { - ASSERT_FINITE_CORO_WAITING; - for (const auto &pair : map) { - fun(pair.first, &pair.second); - } -} - -template -void watchable_map_var_t::read_key( - const key_t &key, - const std::function &fun) { - ASSERT_FINITE_CORO_WAITING; - auto it = map.find(key); - if (it == map.end()) { - fun(nullptr); - } else { - fun(&it->second); - } -} - -template -void watchable_map_var_t::set_all( - const std::map &new_value) { - for (auto it = map.begin(); it != map.end();) { - auto jt = it; - ++it; - if (new_value.count(jt->first) == 0) { - delete_key(jt->first); - } - } - for (auto it = new_value.begin(); it != new_value.end(); ++it) { - set_key_no_equals(it->first, it->second); - } -} - -template -void watchable_map_var_t::set_key( - const key_t &key, const value_t &new_value) { - rwi_lock_assertion_t::write_acq_t write_acq(&rwi_lock); - auto it = map.find(key); - if (it == map.end()) { - map.insert(std::make_pair(key, new_value)); - watchable_map_t::notify_change(key, &new_value, &write_acq); - } else { - if (it->second == new_value) { - return; - } - it->second = new_value; - watchable_map_t::notify_change(key, &new_value, &write_acq); - } -} - -template -void watchable_map_var_t::set_key_no_equals( - const key_t &key, const value_t &new_value) { - rwi_lock_assertion_t::write_acq_t write_acq(&rwi_lock); - map[key] = new_value; - watchable_map_t::notify_change(key, &new_value, &write_acq); -} - -template -void watchable_map_var_t::delete_key(const key_t &key) { - rwi_lock_assertion_t::write_acq_t write_acq(&rwi_lock); - size_t res = map.erase(key); - if (res == 1) { - watchable_map_t::notify_change(key, nullptr, &write_acq); - } -} - -template -void watchable_map_var_t::change_key( - const key_t &key, - const std::function &callback) { - ASSERT_NO_CORO_WAITING; - rwi_lock_assertion_t::write_acq_t write_acq(&rwi_lock); - auto it = map.find(key); - if (it != map.end()) { - bool exists = true; - if (!callback(&exists, &it->second)) { - guarantee(exists); - return; - } - if (exists) { - watchable_map_t::notify_change( - key, &it->second, &write_acq); - } else { - map.erase(it); - watchable_map_t::notify_change(key, nullptr, &write_acq); - } - } else { - bool exists = false; - value_t value_buffer; - if (!callback(&exists, &value_buffer)) { - guarantee(!exists); - return; - } - if (exists) { - auto pair = map.insert(std::make_pair(key, std::move(value_buffer))); - watchable_map_t::notify_change( - key, &pair.first->second, &write_acq); - } - } -} - diff --git a/src/concurrency/watchable_transform.hpp b/src/concurrency/watchable_transform.hpp deleted file mode 100644 index 689c32f54f5..00000000000 --- a/src/concurrency/watchable_transform.hpp +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_WATCHABLE_TRANSFORM_HPP_ -#define CONCURRENCY_WATCHABLE_TRANSFORM_HPP_ - -#include "concurrency/auto_drainer.hpp" -#include "concurrency/watchable.hpp" -#include "concurrency/watchable_map.hpp" -#include "containers/optional.hpp" - -/* This file contains ways of converting between `watchable_t`s and `watchable_map_t`s of -different types. These sorts of conversions may be slow, so view these types with -suspicion. */ - -/* `watchable_map_transform_t` presents a `watchable_map_t` whose content is some -function of the content of another `watchable_map_t`. */ -template -class watchable_map_transform_t : public watchable_map_t { -public: - explicit watchable_map_transform_t(watchable_map_t *inner); - - /* `key_1_to_2` converts a key in the input watchable into the corresponding key for - the output watchable. It can return `false` to indicate that the key should be - excluded from the output watchable. It must be deterministic. */ - virtual bool key_1_to_2( - const key1_t &key1, - key2_t *key2_out) = 0; - - /* `value_1_to_2` converts a value in the input watchable into the corresponding - value for the output watchable. The output value must be a sub-field of the input - value; i.e. `*value2_out` should point to memory owned by `*value1`. It must be - deterministic. It's also OK to set `*value2_out` to `nullptr`, in which case it will - not appear in the output map. */ - virtual void value_1_to_2( - const value1_t *value1, - const value2_t **value2_out) = 0; - - /* `key_2_to_1` is the reverse of `key_1_to_2`. It can return `false` if there is no - key that `key_1_to_2` would map to the given key. */ - virtual bool key_2_to_1( - const key2_t &key2, - key1_t *key1_out) = 0; - - /* `get_all()` must copy every key and value, so beware of performance issues. - `get_key()` will also copy the value. `read_all()` and `read_key()` don't make any - copies of values, although they still may copy keys. */ - std::map get_all(); - optional get_key(const key2_t &key); - void read_all(const std::function &); - void read_key(const key2_t &key, const std::function &); - -private: - /* This method exists to work around a bug in GCC. See GCC issue 61148. */ - void do_notify_change(const key2_t &key, const value2_t *value, - rwi_lock_assertion_t::write_acq_t *acq) { - watchable_map_t::notify_change(key, value, acq); - } - - rwi_lock_assertion_t *get_rwi_lock(); - watchable_map_t *inner; - typename watchable_map_t::all_subs_t all_subs; - rwi_lock_assertion_t rwi_lock; -}; - -/* `watchable_map_value_transform_t` is a specialized form of `watchable_map_transform_t` -that doesn't alter the keys, just the values. This can save some typing. */ -template -class watchable_map_value_transform_t : - public watchable_map_transform_t { -public: - watchable_map_value_transform_t( - watchable_map_t *_input, - const std::function &_fun) : - watchable_map_transform_t(_input), - fun(_fun) { } -private: - bool key_1_to_2(const key_t &key1, key_t *key2_out) { - *key2_out = key1; - return true; - } - bool key_2_to_1(const key_t &key2, key_t *key1_out) { - *key1_out = key2; - return true; - } - void value_1_to_2(const value1_t *value1, const value2_t **value2_out) { - *value2_out = fun(value1); - } - std::function fun; -}; - -/* `get_watchable_for_key()` returns a `watchable_t` that represents the value of a -specific key of a `watchable_map_t`. It must copy the value every time it changes, so -beware of performance issues. */ -template -clone_ptr_t > > get_watchable_for_key( - watchable_map_t *, - const key_t &key); - -/* `watchable_map_entry_copier_t` is the opposite of `get_watchable_for_key()`. It -creates an entry in the given `watchable_map_var_t` for the given key using values from -the given `watchable_t`, and updates the entry every time the `watchable_t` changes. It -must copy the value every time it changes, so beware of performance issues. */ -template -class watchable_map_entry_copier_t { -public: - watchable_map_entry_copier_t( - watchable_map_var_t *map, - const key_t &key, - clone_ptr_t > value); - ~watchable_map_entry_copier_t(); - -private: - watchable_map_var_t *map; - key_t key; - clone_ptr_t > value; - typename watchable_t::subscription_t subs; -}; - -/* `watchable_field_copier_t` keeps some field of a `watchable_variable_t` in sync with -the value of some other `watchable_t`. */ -template -class watchable_field_copier_t { -public: - watchable_field_copier_t( - inner_t outer_t::*_field, - const clone_ptr_t > &_inner, - watchable_variable_t *_outer) : - field(_field), inner(_inner), outer(_outer), - subscription( - [this]() { - outer->apply_atomic_op([this](outer_t *value) { - value->*field = inner->get(); - return true; - }); - }, - inner, - initial_call_t::YES) - { } - -private: - inner_t outer_t::*field; - clone_ptr_t > inner; - watchable_variable_t *outer; - typename watchable_t::subscription_t subscription; - - DISABLE_COPYING(watchable_field_copier_t); -}; - -/* `watchable_map_keyed_var_t` is like a `watchable_map_var_t`, except that the key-value -pairs are indexed by a second set of keys, different from the key-value pairs that appear -in the map. */ -template -class watchable_map_keyed_var_t { -public: - watchable_map_t *get_values() { - return &values; - } - - void set_key(const key1_t &key1, const key2_t &key2, const value_t &value) { - auto key1_iter = entries.find(key1); - - bool key2_exists = false; - values.read_key(key2, [&](const value_t *v) { - key2_exists = (v != nullptr); - }); - - if (key1_iter != entries.end()) { - // A `key1` entry already exists .. - if (key1_iter->second.get_key() == key2) { - // .. and has the right `key2`, change its value, and we're done. - key1_iter->second.change([&](value_t *v) { - *v = value; - return true; - }); - return; - } else { - // .. but does not have the right key, remove it. - entries.erase(key1_iter); - } - } - - if (key2_exists) { - // A `key2` entry already exists, find the matching `key1` which we know - // isn't the one given, and delete it. - delete_secondary_key(key2); - } - - // Insert the new `key1`, `key2`, `value` entry. - auto result = entries.insert(std::make_pair( - key1, typename watchable_map_var_t::entry_t( - &values, key2, value))); - guarantee(result.second); - } - - void delete_key(const key1_t &key1) { - entries.erase(key1); - } - -private: - void delete_secondary_key(const key2_t &key2) { - // Iterate through all the `entries` in search of one with a key of `key2`, - // deleting it will automatically remove it from `values` as well. - for (auto iter = entries.begin(); iter != entries.end(); ++iter) { - if (iter->second.get_key() == key2) { - entries.erase(iter); - break; - } - } - } - - watchable_map_var_t values; - std::map::entry_t> entries; -}; - -/* `watchable_map_combiner_t` combines several `watchable_map_t`s into one, attaching a -tag to each one. */ -template -class watchable_map_combiner_t : - public watchable_map_t, value_t> { -public: - class source_t { - public: - source_t( - watchable_map_combiner_t *parent, - const tag_t &tag, - watchable_map_t *inner); - ~source_t(); - private: - void on_change(const key_t &key, const value_t *value); - watchable_map_combiner_t *parent; - watchable_map_t *inner; - map_insertion_sentry_t *> sentry; - typename watchable_map_t::all_subs_t subs; - }; - - std::map, value_t> get_all(); - optional get_key(const std::pair &key); - void read_all( - const std::function &, const value_t *)> &); - void read_key( - const std::pair &key, - const std::function &); - -private: - rwi_lock_assertion_t *get_rwi_lock() { - return &rwi_lock; - } - rwi_lock_assertion_t rwi_lock; - std::map *> map; -}; - -#include "concurrency/watchable_transform.tcc" - -#endif /* CONCURRENCY_WATCHABLE_TRANSFORM_HPP_ */ - diff --git a/src/concurrency/watchable_transform.tcc b/src/concurrency/watchable_transform.tcc deleted file mode 100644 index 397785dffdf..00000000000 --- a/src/concurrency/watchable_transform.tcc +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "concurrency/watchable_transform.hpp" - -#include "arch/runtime/coroutines.hpp" -#include "stl_utils.hpp" - -template -watchable_map_transform_t::watchable_map_transform_t( - watchable_map_t *_inner) : - inner(_inner), - all_subs(inner, - [this](const key1_t &key1, const value1_t *value1) { - rwi_lock_assertion_t::write_acq_t write_acq(&(this->rwi_lock)); - key2_t key2; - if (this->key_1_to_2(key1, &key2)) { - if (value1 != nullptr) { - const value2_t *value2; - this->value_1_to_2(value1, &value2); - this->do_notify_change(key2, value2, &write_acq); - } else { - this->do_notify_change(key2, nullptr, &write_acq); - } - } - }, initial_call_t::NO) - { } - -template -std::map -watchable_map_transform_t::get_all() { - std::map map2; - read_all([&](const key2_t &key2, const value2_t *value2) { - auto pair = map2.insert(std::make_pair(key2, *value2)); - guarantee(pair.second, "key_1_to_2 created collision"); - }); - return map2; -} - -template -optional -watchable_map_transform_t::get_key( - const key2_t &key2) { - key1_t key1; - if (!key_2_to_1(key2, &key1)) { - return optional(); - } - optional res; - inner->read_key(key1, [&](const value1_t *value1) { - if (value1 != nullptr) { - const value2_t *value2; - this->value_1_to_2(value1, &value2); - if (value2 != nullptr) { - res = optional(*value2); - } - } - }); - return res; -} - -template -void watchable_map_transform_t::read_all( - const std::function &cb) { - inner->read_all([&](const key1_t &key1, const value1_t *value1) { - key2_t key2; - if (this->key_1_to_2(key1, &key2)) { - const value2_t *value2; - this->value_1_to_2(value1, &value2); - if (value2 != nullptr) { - cb(key2, value2); - } - } - }); -} - -template -void watchable_map_transform_t::read_key( - const key2_t &key2, const std::function &cb) { - key1_t key1; - if (!key_2_to_1(key2, &key1)) { - cb(nullptr); - return; - } - inner->read_key(key1, [&](const value1_t *value1) { - if (value1 != nullptr) { - const value2_t *value2; - this->value_1_to_2(value1, &value2); - cb(value2); - } else { - cb(nullptr); - } - }); -} - -template -rwi_lock_assertion_t * -watchable_map_transform_t::get_rwi_lock() { - return &rwi_lock; -} - -template -clone_ptr_t > > get_watchable_for_key( - watchable_map_t *map, - const key_t &key) { - class w_t : public watchable_t > { - public: - w_t(watchable_map_t *_map, const key_t &_key) : - map(_map), key(_key), - subs(map, key, - [this](const value_t *) { - rwi_lock_assertion_t::write_acq_t acq(&(this->rwi_lock)); - publisher.publish([](const std::function &f) { f(); }); - }, - initial_call_t::NO) - { } - w_t *clone() const { - return new w_t(map, key); - } - optional get() { - return map->get_key(key); - } - void apply_read( - const std::function *)> &read) { - optional value = get(); - read(&value); - } - publisher_t > *get_publisher() { - return publisher.get_publisher(); - } - rwi_lock_assertion_t *get_rwi_lock_assertion() { - return &rwi_lock; - } - private: - publisher_controller_t > publisher; - rwi_lock_assertion_t rwi_lock; - watchable_map_t *map; - key_t key; - typename watchable_map_t::key_subs_t subs; - }; - return clone_ptr_t > >(new w_t(map, key)); -} - -template -watchable_map_entry_copier_t::watchable_map_entry_copier_t( - watchable_map_var_t *_map, - const key_t &_key, - clone_ptr_t > _value) : - map(_map), key(_key), value(_value), - subs([this]() { - map->set_key_no_equals(key, value->get()); - }) -{ - typename watchable_t::freeze_t freeze(value); - map->set_key_no_equals(key, value->get()); - subs.reset(value, &freeze); -} - -template -watchable_map_entry_copier_t::~watchable_map_entry_copier_t() { - map->delete_key(key); -} - -template -watchable_map_combiner_t::source_t::source_t( - watchable_map_combiner_t *_parent, - const tag_t &tag, - watchable_map_t *_inner) : - parent(_parent), - inner(_inner), - sentry(&parent->map, tag, inner), - subs(inner, - std::bind(&source_t::on_change, this, ph::_1, ph::_2), - initial_call_t::YES) - { } - -template -watchable_map_combiner_t::source_t::~source_t() { - tag_t tag = sentry.get_key(); - sentry.reset(); - rwi_lock_assertion_t::write_acq_t write_acq(&parent->rwi_lock); - inner->read_all([&](const key_t &key, const value_t *) { - parent->notify_change(std::make_pair(tag, key), nullptr, &write_acq); - }); -} - -template -void watchable_map_combiner_t::source_t::on_change( - const key_t &key, const value_t *value) { - rwi_lock_assertion_t::write_acq_t write_acq(&parent->rwi_lock); - parent->notify_change(std::make_pair(sentry.get_key(), key), value, &write_acq); -} - -template -std::map, value_t> -watchable_map_combiner_t::get_all() { - std::map, value_t> res; - read_all([&](const std::pair &key, const value_t *value) { - res.insert(std::make_pair(key, *value)); - }); - return res; -} - -template -optional watchable_map_combiner_t::get_key( - const std::pair &key) { - auto it = map.find(key.first); - if (it == map.end()) { - return r_nullopt; - } - return it->second->get_key(key.second); -} - -template -void watchable_map_combiner_t::read_all( - const std::function &, const value_t *)> &callback) { - for (const auto &pair : map) { - pair.second->read_all([&](const key_t &key, const value_t *value) { - callback(std::make_pair(pair.first, key), value); - }); - } -} - -template -void watchable_map_combiner_t::read_key( - const std::pair &key, - const std::function &callback) { - auto it = map.find(key.first); - if (it == map.end()) { - callback(nullptr); - } - it->second->read_key(key.second, callback); -} - diff --git a/src/concurrency/watchdog_timer.cc b/src/concurrency/watchdog_timer.cc deleted file mode 100644 index 7b2a758e48f..00000000000 --- a/src/concurrency/watchdog_timer.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "concurrency/watchdog_timer.hpp" - -#include "arch/runtime/coroutines.hpp" -#include "arch/timing.hpp" -#include "random.hpp" -#include "utils.hpp" - -watchdog_timer_t::blocker_t::blocker_t(watchdog_timer_t *p) : parent(p) { - parent->assert_thread(); - ++parent->num_blockers; - parent->state = watchdog_timer_t::state_t::NOT_TRIGGERED; -} - -watchdog_timer_t::blocker_t::~blocker_t() { - parent->assert_thread(); - guarantee(parent->num_blockers > 0); - --parent->num_blockers; - if (parent->num_blockers == 0) { - parent->notify(); - } -} - -watchdog_timer_t::watchdog_timer_t( - int _min, int _max, const std::function &_callback, - state_t initial_state) : - min_timeout_ms(_min), max_timeout_ms(_max), callback(_callback), - num_blockers(0), state(initial_state) -{ - if (initial_state == state_t::TRIGGERED && static_cast(callback)) { - callback(); - } - set_next_threshold(); - coro_t::spawn_sometime(std::bind(&watchdog_timer_t::run, this, drainer.lock())); -} - -watchdog_timer_t::~watchdog_timer_t() { - guarantee(num_blockers == 0); -} - -void watchdog_timer_t::notify() { - assert_thread(); - state = state_t::NOT_TRIGGERED; - set_next_threshold(); -} - -void watchdog_timer_t::run(auto_drainer_t::lock_t keepalive) { - try { - for (;;) { - microtime_t now = current_microtime(); - if (now > next_threshold) { - ASSERT_NO_CORO_WAITING; - if (num_blockers == 0) { - state = state_t::TRIGGERED; - if (static_cast(callback)) { - callback(); - } - } - /* Wait a bit before calling `callback()` again */ - set_next_threshold(); - } - if (next_threshold > now + max_timeout_ms * 1000) { - /* This can only happen if the system clock goes backwards. Rather than - wait until the system clock catches up to its old value, we reset - `next_threshold` to only `max_timeout_ms` in the future. */ - next_threshold = now + max_timeout_ms * 1000; - } - nap((next_threshold - now) / 1000, keepalive.get_drain_signal()); - } - } catch (const interrupted_exc_t &) { - /* `watchdog_timer_t` is being destroyed */ - } -} - -void watchdog_timer_t::set_next_threshold() { - int timeout_ms = min_timeout_ms + randint(max_timeout_ms - min_timeout_ms + 1); - next_threshold = current_microtime() + timeout_ms * 1000; -} - diff --git a/src/concurrency/watchdog_timer.hpp b/src/concurrency/watchdog_timer.hpp deleted file mode 100644 index 51bef3caac1..00000000000 --- a/src/concurrency/watchdog_timer.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CONCURRENCY_WATCHDOG_TIMER_HPP_ -#define CONCURRENCY_WATCHDOG_TIMER_HPP_ - -#include - -#include "concurrency/auto_drainer.hpp" -#include "concurrency/interruptor.hpp" -#include "threading.hpp" -#include "time.hpp" - -/* `watchdog_timer_t` keeps track of how long it's been since the `notify()` method was -last called. If ever `notify()` is not called for a sufficiently long interval (randomly -chosen between `min_timeout_ms` and `max_timeout_ms`), the watchdog becomes "triggered": -it periodically calls its `callback` until it is notified or destroyed. */ - -class watchdog_timer_t : public home_thread_mixin_debug_only_t { -public: - enum state_t { TRIGGERED, NOT_TRIGGERED }; - - /* Creating a `blocker_t` is equivalent to continuously calling `notify()` until the - `blocker_t` is destroyed. */ - class blocker_t { - public: - explicit blocker_t(watchdog_timer_t *parent); - ~blocker_t(); - private: - watchdog_timer_t *parent; - }; - - watchdog_timer_t( - int min_timeout_ms, - int max_timeout_ms, - const std::function &callback, - state_t initial_state = state_t::NOT_TRIGGERED); - ~watchdog_timer_t(); - - void notify(); - - state_t get_state() const { - return state; - } - -private: - void run(auto_drainer_t::lock_t); - void set_next_threshold(); - int min_timeout_ms, max_timeout_ms; - std::function callback; - microtime_t next_threshold; - int num_blockers; - state_t state; - - auto_drainer_t drainer; -}; - -#endif /* CONCURRENCY_WATCHDOG_TIMER_HPP_ */ - diff --git a/src/config/args.hpp b/src/config/args.hpp deleted file mode 100644 index 918b896e781..00000000000 --- a/src/config/args.hpp +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONFIG_ARGS_HPP_ -#define CONFIG_ARGS_HPP_ - -#define KILOBYTE 1024LL -#define MEGABYTE (KILOBYTE * 1024LL) -#define GIGABYTE (MEGABYTE * 1024LL) -#define TERABYTE (GIGABYTE * 1024LL) - -#define THOUSAND 1000LL -#define MILLION (THOUSAND * THOUSAND) -#define BILLION (THOUSAND * MILLION) - -/*! - * Name strings - */ - -#define SOFTWARE_NAME_STRING "RethinkDB" - -/** - * Basic configuration parameters. - */ - -// The number of hash-based CPU shards per table. -// This "must" be hard-coded because a cluster cannot run with -// differing cpu sharding factors. -#define CPU_SHARDING_FACTOR 8 - -// Defines the maximum size of the batch of IO events to process on -// each loop iteration. A larger number will increase throughput but -// decrease concurrency -#define MAX_IO_EVENT_PROCESSING_BATCH_SIZE 50 - -// The io batch factor ensures a minimum number of i/o operations -// which are picked from any specific i/o account consecutively. -// A higher value might be advantageous for throughput if seek times -// matter on the underlying i/o system. A low value improves latency. -// The advantage only holds as long as each account has a tendentially -// sequential set of i/o operations though. If access patterns are random -// for all accounts, a low io batch factor does just as well (as bad) when it comes -// to the number of random seeks, but might still provide a lower latency -// than a high batch factor. Our serializer now groups data writes into -// a single large write operation by itself, making a high value here less -// useful. -#define DEFAULT_IO_BATCH_FACTOR 1 - -// I/O priority of index writes in the log serializer -#define INDEX_WRITE_IO_PRIORITY 128 - -// Each cache uses its own I/O account for reads. -// Writes use the default I/O account, which in practice is going to be overridden -// by a common account in the merger_serializer_t. -// -// By adjusting the priority of the read account, relative to -// MERGER_BLOCK_WRITE_IO_PRIORITY below, reads can be prioritized over writes -// or the other way around. -// -// In practice we want to prioritize reads, so reads can pass ahead of -// flushes. The rationale behind this is that reads are almost always blocking -// operations. Writes, on the other hand, can be non-blocking (from the user's -// perspective) if they are soft-durability or noreply writes. -#define CACHE_READS_IO_PRIORITY (512 / CPU_SHARDING_FACTOR) - -// The cache priority to use for secondary index post construction -// 100 = same priority as all other read operations in the cache together. -// 0 = minimal priority -#define SINDEX_POST_CONSTRUCTION_CACHE_PRIORITY 5 - -// Size of the buffer used to perform IO operations (in bytes). -#define IO_BUFFER_SIZE (4 * KILOBYTE) - -// Size of the device block size (in bytes) -#define DEVICE_BLOCK_SIZE 512 - -// Size of the metablock (in bytes) -#define METABLOCK_SIZE (4 * KILOBYTE) - -// Size of each btree node (in bytes) on disk -#define DEFAULT_BTREE_BLOCK_SIZE (4 * KILOBYTE) - -// Size of each extent (in bytes) -// This should not be too small, or garbage collection will become -// inefficient (especially on rotational drives). -#define DEFAULT_EXTENT_SIZE (2 * MEGABYTE) - -// Ratio of free ram to use for the cache by default -#define DEFAULT_MAX_CACHE_RATIO 2 - -// The maximum number of concurrently active -// index writes per merger serializer. -// The smaller the number, the more effective -// the merger serializer is in merging index writes -// together. This is favorable especially on rotational drives. -// There is a theoretic chance of increased latencies on SSDs for -// small values of this variable. -#define MERGER_SERIALIZER_MAX_ACTIVE_WRITES 1 - -// I/O priority of block writes in the merger_serializer_t -#define MERGER_BLOCK_WRITE_IO_PRIORITY 64 - -// Maximum number of threads we support -// TODO: make this dynamic where possible -#define MAX_THREADS 260 - -// Ticks (in milliseconds) the internal timed tasks are performed at -#define TIMER_TICKS_IN_MS 5 - -// How many times the page replacement algorithm tries to find an eligible page before giving up. -// Note that (MAX_UNSAVED_DATA_LIMIT_FRACTION ** PAGE_REPL_NUM_TRIES) is the probability that the -// page replacement algorithm will succeed on a given try, and if that probability is less than 1/2 -// then the page replacement algorithm will on average be unable to evict pages from the cache. -#define PAGE_REPL_NUM_TRIES 10 - -// How large can the key be, in bytes? This value needs to fit in a byte. -#define MAX_KEY_SIZE 250 - -// Special block IDs. These don't really belong here because they're -// more magic constants than tunable parameters. - -// The btree superblock, which has a reference to the root node block -// id. -#define SUPERBLOCK_ID 0 - -// If the size of the LBA on a given disk exceeds LBA_MIN_SIZE_FOR_GC, then the fraction of the -// entries that are live and not garbage should be at least LBA_MIN_UNGARBAGE_FRACTION. -#define LBA_MIN_SIZE_FOR_GC (MEGABYTE * 1) -#define LBA_MIN_UNGARBAGE_FRACTION 0.5 - -// I/O priority for LBA garbage collection -#define LBA_GC_IO_PRIORITY 8 - -// How many block ids should the LBA garbage collector rewrite before yielding? -#define LBA_GC_BATCH_SIZE (1024 * 8) - -// How many LBA structures to have for each file -#define LBA_SHARD_FACTOR 4 - -// How much space to reserve in the metablock to store inline LBA entries -// Make sure that it fits into METABLOCK_SIZE, including all other meta data -// TODO (daniel): Tune -#define LBA_INLINE_SIZE (METABLOCK_SIZE - 512) - -// How many bytes of buffering space we can use per disk when reading the LBA. If it's set -// too high, then RethinkDB will eat a lot of memory at startup. This is bad because tcmalloc -// doesn't return memory to the OS. If it's set too low, startup will take a longer time. -#define LBA_READ_BUFFER_SIZE (128 * MEGABYTE) - -// After the LBA has been read, we reconstruct the in-memory LBA index. -// For huge tables, this can take some considerable CPU time. We break the reconstruction -// up into smaller batches, each batch reconstructing up to `LBA_RECONSTRUCTION_BATCH_SIZE` -// block infos. -#define LBA_RECONSTRUCTION_BATCH_SIZE 1024 - -#if defined (__powerpc64__) -// getifaddrs() calls alloca() and it tries to allocate 64KB of memory -// in stack frame. To avoid stack overflow, increasing the stack size -// to accomodate both alloca() and regsiter save area during context switch. -#define COROUTINE_STACK_SIZE 262144 -#else -#define COROUTINE_STACK_SIZE 131072 -#endif - - -/** - * Message scheduler configuration - */ - -// Each message on the message hup can have a priority between -// MESSAGE_SCHEDULER_MIN_PRIORITY and MESSAGE_SCHEDULER_MAX_PRIORITY (both inclusive). -#define MESSAGE_SCHEDULER_MIN_PRIORITY (-2) -#define MESSAGE_SCHEDULER_MAX_PRIORITY 2 - -// If no priority is specified, messages will get MESSAGE_SCHEDULER_DEFAULT_PRIORITY. -#define MESSAGE_SCHEDULER_DEFAULT_PRIORITY 0 - -// Ordered messages cannot currently have different priorities, because that would mean -// that ordered messages of a high priority could bypass those of a lower priority. -// (our current implementation does not support re-ordering messages within a given -// priority) -// MESSAGE_SCHEDULER_ORDERED_PRIORITY is the effective priority at which ordered -// messages are scheduled. -#define MESSAGE_SCHEDULER_ORDERED_PRIORITY 0 - -// MESSAGE_SCHEDULER_GRANULARITY specifies how many messages (of -// MESSAGE_SCHEDULER_MAX_PRIORITY) the message scheduler processes before it -// can take in new incoming messages. A smaller value means that high-priority -// messages can bypass lower-priority ones faster, but decreases the efficiency -// of the message hub. -// MESSAGE_SCHEDULER_GRANULARITY should be least -// 2^(MESSAGE_SCHEDULER_MAX_PRIORITY - MESSAGE_SCHEDULER_MIN_PRIORITY + 1) -#define MESSAGE_SCHEDULER_GRANULARITY 32 - -// Priorities for specific tasks -#define CORO_PRIORITY_SINDEX_CONSTRUCTION (-2) -#define CORO_PRIORITY_BACKFILL_SENDER (-2) -#define CORO_PRIORITY_BACKFILL_RECEIVER (-2) -#define CORO_PRIORITY_RESET_DATA (-2) -#define CORO_PRIORITY_DIRECTORY_CHANGES (-2) -#define CORO_PRIORITY_LBA_GC (-2) - -#endif // CONFIG_ARGS_HPP_ - diff --git a/src/containers/archive/archive.cc b/src/containers/archive/archive.cc deleted file mode 100644 index 8dea954187d..00000000000 --- a/src/containers/archive/archive.cc +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "containers/archive/archive.hpp" - -#include - -#ifdef _WIN32 -#include -#else -#include -#endif - -#include - -#include "containers/archive/versioned.hpp" -#include "containers/uuid.hpp" -#include "rpc/serialize_macros.hpp" - -const char *archive_result_as_str(archive_result_t archive_result) { - switch (archive_result) { - case archive_result_t::SUCCESS: - return "archive_result_t::SUCCESS"; - break; - case archive_result_t::SOCK_ERROR: - return "archive_result_t::SOCK_ERROR"; - break; - case archive_result_t::SOCK_EOF: - return "archive_result_t::SOCK_EOF"; - break; - case archive_result_t::RANGE_ERROR: - return "archive_result_t::RANGE_ERROR"; - break; - default: - unreachable(); - } -} - -int64_t force_read(read_stream_t *s, void *p, int64_t n) { - rassert(n >= 0); - - char *chp = static_cast(p); - int64_t written_so_far = 0; - while (n > 0) { - int64_t res = s->read(chp, n); - if (res == 0) { - return written_so_far; - } - if (res == -1) { - // We don't communicate what data has been read so far. - // I'm guessing callers to force_read don't care. - return -1; - } - rassert(res <= n); - - written_so_far += res; - chp += res; - n -= res; - } - return written_so_far; -} - -write_message_t::~write_message_t() { - while (write_buffer_t *buffer = buffers_.head()) { - buffers_.remove(buffer); - delete buffer; - } -} - -void write_message_t::append(const void *p, int64_t n) { - while (n > 0) { - if (buffers_.empty() || buffers_.tail()->size == write_buffer_t::DATA_SIZE) { - buffers_.push_back(new write_buffer_t); - } - - write_buffer_t *b = buffers_.tail(); - int64_t k = std::min(n, write_buffer_t::DATA_SIZE - b->size); - - memcpy(b->data + b->size, p, k); - b->size += k; - p = static_cast(p) + k; - n = n - k; - } -} - -size_t write_message_t::size() const { - size_t ret = 0; - for (write_buffer_t *h = buffers_.head(); h != nullptr; h = buffers_.next(h)) { - ret += h->size; - } - return ret; -} - -int send_write_message(write_stream_t *s, const write_message_t *wm) { - intrusive_list_t *list = const_cast(wm)->unsafe_expose_buffers(); - for (write_buffer_t *p = list->head(); p; p = list->next(p)) { - int64_t res = s->write(p->data, p->size); - if (res == -1) { - return -1; - } - rassert(res == p->size); - } - return 0; -} - -// You MUST NOT change the behavior of serialize_universal and deserialize_universal -// functions! (You could find a way to remove their callers and remove them though.) -void serialize_universal(write_message_t *wm, const uuid_u &uuid) { - rassert(!uuid.is_unset()); - wm->append(uuid.data(), uuid_u::static_size()); -} -MUST_USE archive_result_t deserialize_universal(read_stream_t *s, uuid_u *uuid) { - int64_t sz = uuid_u::static_size(); - int64_t res = force_read(s, uuid->data(), sz); - - if (res == -1) { return archive_result_t::SOCK_ERROR; } - if (res < sz) { return archive_result_t::SOCK_EOF; } - rassert(res == sz); - return archive_result_t::SUCCESS; -} - -template -void serialize(write_message_t *wm, const uuid_u &uuid) { - serialize_universal(wm, uuid); -} - -template -MUST_USE archive_result_t deserialize(read_stream_t *s, uuid_u *uuid) { - return deserialize_universal(s, uuid); -} - -INSTANTIATE_SERIALIZABLE_SINCE_v1_13(uuid_u); - -void serialize_universal(write_message_t *wm, bool b) { - // We depend on the versioned implementation for booleans not changing. - serialize(wm, b); -} -MUST_USE archive_result_t deserialize_universal(read_stream_t *s, bool *b) { - return deserialize(s, b); -} - - -template -void serialize(write_message_t *wm, const in6_addr &addr) { - wm->append(&addr.s6_addr, sizeof(addr.s6_addr)); -} - -template -MUST_USE archive_result_t deserialize(read_stream_t *s, in6_addr *addr) { - int64_t sz = sizeof(addr->s6_addr); - int64_t res = force_read(s, &addr->s6_addr, sz); - - if (res == -1) { return archive_result_t::SOCK_ERROR; } - if (res < sz) { return archive_result_t::SOCK_EOF; } - rassert(res == sz); - return archive_result_t::SUCCESS; -} - -INSTANTIATE_SERIALIZABLE_SINCE_v1_13(in6_addr); - -// Keep the struct keyword here to satisfy VC++ -RDB_IMPL_SERIALIZABLE_1_SINCE_v1_13(struct in_addr, s_addr); diff --git a/src/containers/archive/archive.hpp b/src/containers/archive/archive.hpp deleted file mode 100644 index 944b0fa4c10..00000000000 --- a/src/containers/archive/archive.hpp +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONTAINERS_ARCHIVE_ARCHIVE_HPP_ -#define CONTAINERS_ARCHIVE_ARCHIVE_HPP_ - -#include - -#include -#include - -#include "containers/intrusive_list.hpp" -#include "containers/printf_buffer.hpp" -#include "version.hpp" -#include "valgrind.hpp" - -class uuid_u; - -struct fake_archive_exc_t { - const char *what() const throw() { - return "Writing to a tcp stream failed."; - } -}; - -class read_stream_t { -public: - read_stream_t() { } - // Returns number of bytes read or 0 upon EOF, -1 upon error. - virtual MUST_USE int64_t read(void *p, int64_t n) = 0; -protected: - virtual ~read_stream_t() { } -private: - DISABLE_COPYING(read_stream_t); -}; - -// The return value of deserialization functions. -enum class archive_result_t { - // Success. - SUCCESS, - // An error on the socket happened. - SOCK_ERROR, - // An EOF on the socket happened. - SOCK_EOF, - // The value deserialized was out of range. - RANGE_ERROR, -}; - -inline bool bad(archive_result_t res) { - return res != archive_result_t::SUCCESS; -} - -const char *archive_result_as_str(archive_result_t archive_result); - -#define guarantee_deserialization(result, ...) do { \ - guarantee(result == archive_result_t::SUCCESS, \ - "Deserialization of %s failed with error %s.", \ - strprintf(__VA_ARGS__).c_str(), \ - archive_result_as_str(result)); \ - } while (0) - -class archive_exc_t : public std::exception { -public: - explicit archive_exc_t(std::string _s) : s(std::move(_s)) { } - ~archive_exc_t() throw () { } - const char *what() const throw() { - return s.c_str(); - } -private: - std::string s; -}; - -#define throw_if_bad_deserialization(result, ...) do { \ - if (result != archive_result_t::SUCCESS) { \ - throw archive_exc_t( \ - strprintf("Deserialization of %s failed with error %s.", \ - strprintf(__VA_ARGS__).c_str(), \ - archive_result_as_str(result))); \ - } \ - } while (0) - -// Returns the number of bytes written, or -1. Returns a -// non-negative value less than n upon EOF. -MUST_USE int64_t force_read(read_stream_t *s, void *p, int64_t n); - -class write_stream_t { -public: - write_stream_t() { } - // Returns n, or -1 upon error. Blocks until all bytes are written. - virtual MUST_USE int64_t write(const void *p, int64_t n) = 0; -protected: - virtual ~write_stream_t() { } -private: - DISABLE_COPYING(write_stream_t); -}; - -class write_buffer_t : public intrusive_list_node_t { -public: - write_buffer_t() : size(0) { } - - static const int DATA_SIZE = 4096; - int size; - char data[DATA_SIZE]; - -private: - DISABLE_COPYING(write_buffer_t); -}; - -// A set of buffers in which an atomic message to be sent on a stream -// gets built up. (This way we don't flush after the first four bytes -// sent to a stream, or buffer things and then forget to manually -// flush. This type can be extended to support holding references to -// large buffers, to save copying.) Generally speaking, you serialize -// to a write_message_t, and then flush that to a write_stream_t. -class write_message_t { -public: - write_message_t() { } - explicit write_message_t(write_message_t &&) = default; - ~write_message_t(); - - void append(const void *p, int64_t n); - - size_t size() const; - - intrusive_list_t *unsafe_expose_buffers() { return &buffers_; } - -private: - friend int send_write_message(write_stream_t *s, const write_message_t *wm); - - intrusive_list_t buffers_; - - DISABLE_COPYING(write_message_t); -}; - -// Returns 0 upon success, -1 upon failure. -MUST_USE int send_write_message(write_stream_t *s, const write_message_t *wm); - -template -T *deserialize_deref(T &val) { // NOLINT(runtime/references) - return &val; -} - -template -struct serialized_size_t; -template -struct serialize_universal_size_t; - -// Makes typ1 serializable, sending a typ2 over the wire. Has range -// checking on the closed interval [lo, hi] when deserializing. -#define ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE(typ1, typ2, lo, hi) \ - template \ - void serialize(write_message_t *wm, typ1 x) { \ - union { \ - typ2 v; \ - char buf[sizeof(typ2)]; \ - } u; \ - u.v = static_cast(x); \ - rassert(u.v >= typ2(lo) && u.v <= typ2(hi)); \ - wm->append(u.buf, sizeof(typ2)); \ - } \ - \ - template \ - inline MUST_USE archive_result_t deserialize(read_stream_t *s, typ1 *x) { \ - union { \ - typ2 v; \ - char buf[sizeof(typ2)]; \ - } u; \ - int64_t res = force_read(s, u.buf, sizeof(typ2)); \ - if (res == -1) { \ - return archive_result_t::SOCK_ERROR; \ - } \ - if (res < int64_t(sizeof(typ2))) { \ - return archive_result_t::SOCK_EOF; \ - } \ - if (u.v < typ2(lo) || u.v > typ2(hi)) { \ - return archive_result_t::RANGE_ERROR; \ - } \ - *x = typ1(u.v); \ - return archive_result_t::SUCCESS; \ - } - -// Designed for 's u?int[0-9]+_t types, which are just sent -// raw over the wire. -// -// serialize_universal and deserialize_universal are functions whose behavior must -// never change: if you want to serialize values differently, make a different -// function. -#define ARCHIVE_PRIM_MAKE_RAW_SERIALIZABLE(typ) \ - inline void serialize_universal(write_message_t *wm, typ x) { \ - union { \ - typ v; \ - char buf[sizeof(typ)]; \ - } u; \ - u.v = x; \ - wm->append(u.buf, sizeof(typ)); \ - } \ - template \ - void serialize(write_message_t *wm, typ x) { \ - serialize_universal(wm, x); \ - } \ - \ - inline MUST_USE archive_result_t \ - deserialize_universal(read_stream_t *s, typ *x) { \ - union { \ - typ v; \ - char buf[sizeof(typ)]; \ - } u; \ - int64_t res = force_read(s, u.buf, sizeof(typ)); \ - if (res == -1) { \ - *x = valgrind_undefined(0); \ - return archive_result_t::SOCK_ERROR; \ - } \ - if (res < int64_t(sizeof(typ))) { \ - *x = valgrind_undefined(0); \ - return archive_result_t::SOCK_EOF; \ - } \ - *x = u.v; \ - return archive_result_t::SUCCESS; \ - } \ - \ - template \ - MUST_USE archive_result_t deserialize(read_stream_t *s, typ *x) { \ - return deserialize_universal(s, x); \ - } \ - \ - template <> \ - struct serialized_size_t \ - : public std::integral_constant { }; /* NOLINT(readability/braces) */ \ - template <> \ - struct serialize_universal_size_t \ - : public std::integral_constant { } - - -ARCHIVE_PRIM_MAKE_RAW_SERIALIZABLE(unsigned char); // NOLINT(runtime/int) -ARCHIVE_PRIM_MAKE_RAW_SERIALIZABLE(char); // NOLINT(runtime/int) -ARCHIVE_PRIM_MAKE_RAW_SERIALIZABLE(signed char); // NOLINT(runtime/int) -ARCHIVE_PRIM_MAKE_RAW_SERIALIZABLE(unsigned short); // NOLINT(runtime/int) -ARCHIVE_PRIM_MAKE_RAW_SERIALIZABLE(signed short); // NOLINT(runtime/int) -ARCHIVE_PRIM_MAKE_RAW_SERIALIZABLE(unsigned int); -ARCHIVE_PRIM_MAKE_RAW_SERIALIZABLE(signed int); -ARCHIVE_PRIM_MAKE_RAW_SERIALIZABLE(unsigned long); // NOLINT(runtime/int) -ARCHIVE_PRIM_MAKE_RAW_SERIALIZABLE(signed long); // NOLINT(runtime/int) -ARCHIVE_PRIM_MAKE_RAW_SERIALIZABLE(unsigned long long); // NOLINT(runtime/int) -ARCHIVE_PRIM_MAKE_RAW_SERIALIZABLE(signed long long); // NOLINT(runtime/int) -ARCHIVE_PRIM_MAKE_RAW_SERIALIZABLE(double); - -// Single-precision float is omitted on purpose, don't add it. Go -// change your code to use doubles. - -// Note: serialize_universal depends on this not changing. -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE(bool, int8_t, 0, 1); -template <> -struct serialized_size_t : public serialized_size_t { }; -template <> -struct serialize_universal_size_t : public serialize_universal_size_t { }; - -void serialize_universal(write_message_t *wm, bool b); -MUST_USE archive_result_t deserialize_universal(read_stream_t *s, bool *b); - -void serialize_universal(write_message_t *wm, const uuid_u &uuid); -MUST_USE archive_result_t deserialize_universal(read_stream_t *s, uuid_u *uuid); - -template -void serialize(write_message_t *wm, const uuid_u &uuid); -template -MUST_USE archive_result_t deserialize(read_stream_t *s, uuid_u *uuid); - -struct in_addr; -struct in6_addr; - -template -void serialize(write_message_t *wm, const in_addr &addr); -template -MUST_USE archive_result_t deserialize(read_stream_t *s, in_addr *addr); - -template -void serialize(write_message_t *wm, const in6_addr &addr); -template -MUST_USE archive_result_t deserialize(read_stream_t *s, in6_addr *addr); - -#endif // CONTAINERS_ARCHIVE_ARCHIVE_HPP_ diff --git a/src/containers/archive/boost_types.hpp b/src/containers/archive/boost_types.hpp deleted file mode 100644 index b716cb6dd55..00000000000 --- a/src/containers/archive/boost_types.hpp +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONTAINERS_ARCHIVE_BOOST_TYPES_HPP_ -#define CONTAINERS_ARCHIVE_BOOST_TYPES_HPP_ - -#include "errors.hpp" -#include - -#include "containers/archive/archive.hpp" -#include "containers/archive/varint.hpp" - -template -NORETURN void serialize(UNUSED write_message_t *wm, - UNUSED const boost::detail::variant::void_ &v) { - unreachable("You cannot do serialize(write_message_t *, boost::detail::variant::void_ &)."); -} - -template -MUST_USE archive_result_t deserialize(UNUSED read_stream_t *s, - UNUSED boost::detail::variant::void_ *v) { - unreachable("You cannot do deserialize(read_stream_t *, boost::detail::variant::void_ *)."); -} - -template -struct variant_serializer_t; - -template -struct variant_serializer_t : public variant_serializer_t { - - explicit variant_serializer_t(write_message_t *wm) - : variant_serializer_t(wm) { } - - using variant_serializer_t::operator(); -}; - -template -struct variant_serializer_t : public variant_serializer_t { - - explicit variant_serializer_t(write_message_t *wm) - : variant_serializer_t(wm) { } - - using variant_serializer_t::operator(); - using variant_serializer_t::wm_; - - typedef void result_type; - - void operator()(const T &x) { - uint8_t n = N; - serialize(wm_, n); - serialize(wm_, x); - } -}; - -template -struct variant_serializer_t { - struct end_of_variant { }; - - explicit variant_serializer_t(write_message_t *wm) : wm_(wm) { } - - void operator()(const end_of_variant&){ - unreachable("variant_serializer_t: no more types"); - } - - static const uint8_t size = N; - write_message_t *wm_; - -}; - -template -void serialize(write_message_t *wm, const boost::variant &x) { - variant_serializer_t visitor(wm); - rassert(sizeof(visitor) == sizeof(write_message_t *)); - - boost::apply_visitor(visitor, x); -} - -template -struct variant_deserializer; - -template -struct variant_deserializer { - static MUST_USE archive_result_t deserialize_variant(int, read_stream_t *, Variant *) { - return archive_result_t::RANGE_ERROR; - } -}; - -template -struct variant_deserializer { - static MUST_USE archive_result_t deserialize_variant(int n, read_stream_t *s, Variant *x) { - if (n == N) { - *x = T(); - archive_result_t res = deserialize(s, boost::get(x)); - if (bad(res)) { return res; } - - return archive_result_t::SUCCESS; - } else { - return variant_deserializer::deserialize_variant(n, s, x); - } - } -}; - -template -struct variant_deserializer { - static MUST_USE archive_result_t deserialize_variant(int, read_stream_t *, Variant *) { - return archive_result_t::RANGE_ERROR; - } -}; - -template -MUST_USE archive_result_t deserialize(read_stream_t *s, boost::variant *x) { - uint8_t n; - archive_result_t res = deserialize(s, &n); - if (bad(res)) { return res; } - - return variant_deserializer, BOOST_VARIANT_ENUM_PARAMS(T)>::deserialize_variant(n, s, x); -} - -#endif // CONTAINERS_ARCHIVE_BOOST_TYPES_HPP_ diff --git a/src/containers/archive/buffer_group_stream.cc b/src/containers/archive/buffer_group_stream.cc deleted file mode 100644 index 556ae259d76..00000000000 --- a/src/containers/archive/buffer_group_stream.cc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "containers/buffer_group.hpp" - -#include - -#include "containers/archive/buffer_group_stream.hpp" - -buffer_group_read_stream_t::buffer_group_read_stream_t(const const_buffer_group_t *group) - : group_(group), bufnum_(0), bufpos_(0) { } - -buffer_group_read_stream_t::~buffer_group_read_stream_t() { } - -int64_t buffer_group_read_stream_t::read(void *p, int64_t n) { - char *v = static_cast(p); - const int64_t original_n = n; - - while (bufnum_ < group_->num_buffers() && n > 0) { - const_buffer_group_t::buffer_t buf = group_->get_buffer(bufnum_); - int64_t bytes_to_copy = std::min(buf.size - bufpos_, n); - memcpy(v, static_cast(buf.data) + bufpos_, bytes_to_copy); - n -= bytes_to_copy; - v += bytes_to_copy; - bufpos_ += bytes_to_copy; - - if (bufpos_ == buf.size) { - ++bufnum_; - bufpos_ = 0; - } - } - - return original_n - n; -} - -bool buffer_group_read_stream_t::entire_stream_consumed() const { - return bufnum_ == group_->num_buffers(); -} - -buffer_group_write_stream_t::buffer_group_write_stream_t(const buffer_group_t *group) - : group_(group), bufnum_(0), bufpos_(0) { } - -buffer_group_write_stream_t::~buffer_group_write_stream_t() { } - -int64_t buffer_group_write_stream_t::write(const void *p, int64_t n) { - const char *v = static_cast(p); - const int64_t original_n = n; - - while (bufnum_ < group_->num_buffers() && n > 0) { - buffer_group_t::buffer_t buf = group_->get_buffer(bufnum_); - int64_t bytes_to_copy = std::min(buf.size - bufpos_, n); - memcpy(static_cast(buf.data) + bufpos_, v, bytes_to_copy); - n -= bytes_to_copy; - v += bytes_to_copy; - bufpos_ += bytes_to_copy; - - if (bufpos_ == buf.size) { - ++bufnum_; - bufpos_ = 0; - } - } - - return n == 0 ? original_n : -1; -} - -bool buffer_group_write_stream_t::entire_stream_filled() const { - return bufnum_ == group_->num_buffers(); -} diff --git a/src/containers/archive/buffer_group_stream.hpp b/src/containers/archive/buffer_group_stream.hpp deleted file mode 100644 index a1337fe2aa2..00000000000 --- a/src/containers/archive/buffer_group_stream.hpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONTAINERS_ARCHIVE_BUFFER_GROUP_STREAM_HPP_ -#define CONTAINERS_ARCHIVE_BUFFER_GROUP_STREAM_HPP_ - -#include "version.hpp" -#include "containers/archive/archive.hpp" -#include "utils.hpp" - -class const_buffer_group_t; -class buffer_group_t; - -class buffer_group_read_stream_t : public read_stream_t { -public: - explicit buffer_group_read_stream_t(const const_buffer_group_t *group); - virtual ~buffer_group_read_stream_t(); - - virtual MUST_USE int64_t read(void *p, int64_t n); - - bool entire_stream_consumed() const; - -private: - const const_buffer_group_t *group_; - size_t bufnum_; - int64_t bufpos_; - - DISABLE_COPYING(buffer_group_read_stream_t); -}; - -class buffer_group_write_stream_t : public write_stream_t { -public: - explicit buffer_group_write_stream_t(const buffer_group_t *group); - virtual ~buffer_group_write_stream_t(); - - virtual MUST_USE int64_t write(const void *p, int64_t n); - - bool entire_stream_filled() const; - -private: - const buffer_group_t *group_; - size_t bufnum_; - int64_t bufpos_; - - DISABLE_COPYING(buffer_group_write_stream_t); -}; - -template -void deserialize_from_group(const const_buffer_group_t *group, T *value_out) { - buffer_group_read_stream_t stream(group); - archive_result_t res = deserialize(&stream, value_out); - guarantee_deserialization(res, "T (from a buffer group)"); - guarantee(stream.entire_stream_consumed(), - "Corrupted value in storage (deserialization terminated early)."); -} - -template -void deserialize_for_version_from_group(cluster_version_t cluster_version, - const const_buffer_group_t *group, - T *value_out) { - buffer_group_read_stream_t stream(group); - archive_result_t res = deserialize_for_version(cluster_version, &stream, - value_out); - guarantee_deserialization(res, "T (from a buffer group)"); - guarantee(stream.entire_stream_consumed(), - "Corrupted value in storage (deserialization terminated early)."); -} - - -#endif // CONTAINERS_ARCHIVE_BUFFER_GROUP_STREAM_HPP_ diff --git a/src/containers/archive/buffer_stream.hpp b/src/containers/archive/buffer_stream.hpp deleted file mode 100644 index 18166f939f7..00000000000 --- a/src/containers/archive/buffer_stream.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONTAINERS_ARCHIVE_BUFFER_STREAM_HPP_ -#define CONTAINERS_ARCHIVE_BUFFER_STREAM_HPP_ - -#include - -#include "containers/archive/archive.hpp" - -// Reads from a buffer without taking ownership over it -class buffer_read_stream_t : public read_stream_t { -public: - // Note: These are implemented in the header file because inlining them in - // combination with `deserialize_varint_uint64()` yields significant performance - // gains due to its frequent use in datum_string_t and other datum - // deserialization functions. (measured on GCC 4.8) - // If we end up compiling with whole-program link-time optimization some day, - // we can probably move this back to a .cc file. - - explicit buffer_read_stream_t(const char *buf, size_t size, int64_t offset = 0) - : pos_(offset), buf_(buf), size_(size) { - guarantee(pos_ >= 0); - guarantee(static_cast(pos_) <= size_); - } - virtual ~buffer_read_stream_t() { } - - virtual MUST_USE int64_t read(void *p, int64_t n) { - int64_t num_left = size_ - pos_; - int64_t num_to_read = n < num_left ? n : num_left; - - memcpy(p, buf_ + pos_, num_to_read); - - pos_ += num_to_read; - - return num_to_read; - } - - int64_t tell() const { return pos_; } - -private: - int64_t pos_; - const char *buf_; - size_t size_; - - DISABLE_COPYING(buffer_read_stream_t); -}; - - -#endif // CONTAINERS_ARCHIVE_BUFFER_STREAM_HPP_ diff --git a/src/containers/archive/file_stream.cc b/src/containers/archive/file_stream.cc deleted file mode 100644 index 6551b3103ef..00000000000 --- a/src/containers/archive/file_stream.cc +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. - -#include "containers/archive/file_stream.hpp" - -#include -#include -#include -#include - -blocking_read_file_stream_t::blocking_read_file_stream_t() { } - -blocking_read_file_stream_t::~blocking_read_file_stream_t() { } - -bool blocking_read_file_stream_t::init(const char *path, int *errsv_out) { - guarantee(fd_.get() == INVALID_FD); - -#ifdef _WIN32 - HANDLE h = CreateFile(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); - if (h == INVALID_HANDLE_VALUE) { - logWRN("CreateFile failed: %s", winerr_string(GetLastError()).c_str()); - *errsv_out = EIO; - return false; - } else { - *errsv_out = 0; - fd_.reset(h); - return true; - } -#else - int res; - do { - res = open(path, O_RDONLY); - } while (res == -1 && get_errno() == EINTR); - - if (res == -1) { - *errsv_out = get_errno(); - return false; - } else { - fd_.reset(res); - *errsv_out = 0; - return true; - } -#endif -} - - -bool blocking_read_file_stream_t::init(const char *path) { - // This version of init simply doesn't provide the errno value to - // the caller. - int errsv; - return init(path, &errsv); -} - -int64_t blocking_read_file_stream_t::read(void *p, int64_t n) { - guarantee(fd_.get() != INVALID_FD); - -#ifdef _WIN32 - DWORD read_bytes; - BOOL res = ReadFile(fd_.get(), p, n, &read_bytes, NULL); - if (!res) { - logERR("ReadFile failed: %s", winerr_string(GetLastError()).c_str()); - set_errno(EIO); - return -1; - } else { - return read_bytes; - } -#else - ssize_t res; - do { - res = ::read(fd_.get(), p, n); - } while (res == -1 && get_errno() == EINTR); - - return res; -#endif -} - diff --git a/src/containers/archive/file_stream.hpp b/src/containers/archive/file_stream.hpp deleted file mode 100644 index 309ef605b19..00000000000 --- a/src/containers/archive/file_stream.hpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONTAINERS_ARCHIVE_FILE_STREAM_HPP_ -#define CONTAINERS_ARCHIVE_FILE_STREAM_HPP_ - -#include - -#include "arch/io/io_utils.hpp" -#include "containers/archive/archive.hpp" - -// This is a completely worthless type with BLOCKING I/O used only in -// http/file_app, json_import and for seeding randint() -class blocking_read_file_stream_t : public read_stream_t { -public: - blocking_read_file_stream_t(); - - // Returns true upon success. - MUST_USE bool init(const char *path); - - // Returns true upon success, and more usefully, writes an errno - // value to *errsv_out when returning false. - MUST_USE bool init(const char *path, int *errsv_out); - - - virtual ~blocking_read_file_stream_t(); - - virtual MUST_USE int64_t read(void *p, int64_t n); - -private: - scoped_fd_t fd_; - - DISABLE_COPYING(blocking_read_file_stream_t); -}; - - -#endif // CONTAINERS_ARCHIVE_FILE_STREAM_HPP_ diff --git a/src/containers/archive/optional.hpp b/src/containers/archive/optional.hpp deleted file mode 100644 index 4bcb1083cb7..00000000000 --- a/src/containers/archive/optional.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef CONTAINERS_ARCHIVE_OPTIONAL_HPP_ -#define CONTAINERS_ARCHIVE_OPTIONAL_HPP_ - -#include "containers/archive/archive.hpp" -#include "containers/optional.hpp" - -template -void serialize(write_message_t *wm, const optional &x) { - bool exists = x.has_value(); - serialize(wm, exists); - if (exists) { - serialize(wm, x.get()); - } -} - -template -MUST_USE archive_result_t deserialize(read_stream_t *s, optional *x) { - bool exists; - - archive_result_t res = deserialize(s, &exists); - if (bad(res)) { return res; } - if (exists) { - x->set(T()); - res = deserialize(s, &x->get()); - return res; - } else { - x->reset(); - return archive_result_t::SUCCESS; - } -} - -#endif // CONTAINERS_ARCHIVE_OPTIONAL_HPP_ diff --git a/src/containers/archive/socket_stream.cc b/src/containers/archive/socket_stream.cc deleted file mode 100644 index 073295d6260..00000000000 --- a/src/containers/archive/socket_stream.cc +++ /dev/null @@ -1,420 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifdef _WIN32 - -#include "containers/archive/socket_stream.hpp" -#include "errors.hpp" -#include "logger.hpp" -#include "concurrency/wait_any.hpp" -#include "concurrency/interruptor.hpp" -#include "utils.hpp" - -int64_t socket_stream_t::read(void *buf, int64_t count) { - DWORD ret; - DWORD error; - if (event_watcher == nullptr) { - BOOL res = ReadFile(fd, buf, count, &ret, nullptr); - error = GetLastError(); - } else { - overlapped_operation_t op(event_watcher); - BOOL res = ReadFile(fd, buf, count, nullptr, &op.overlapped); - error = GetLastError(); - if (res || error == ERROR_IO_PENDING) { - op.wait_interruptible(interruptor); - error = op.error; - } else { - op.set_result(0, error); - } - ret = op.nb_bytes; - } - if (error == ERROR_BROKEN_PIPE) { - return 0; - } - if (error != NO_ERROR) { - logWRN("ReadFile failed: %s", winerr_string(error).c_str()); - set_errno(EIO); - return -1; - } - return ret; -} - -int64_t socket_stream_t::write(const void *buf, int64_t count) { - DWORD ret; - DWORD error; - if (event_watcher == nullptr) { - BOOL res = WriteFile(fd, buf, count, &ret, nullptr); - error = GetLastError(); - } else { - overlapped_operation_t op(event_watcher); - BOOL res = WriteFile(fd, buf, count, nullptr, &op.overlapped); - error = GetLastError(); - if (res || error == ERROR_IO_PENDING) { - op.wait_interruptible(interruptor); - error = op.error; - } else { - op.set_result(0, error); - } - ret = op.nb_bytes; - } - if (error != NO_ERROR) { - logWRN("WriteFile failed: %s", winerr_string(error).c_str()); - set_errno(EIO); - return -1; - } - return ret; -} - -void socket_stream_t::wait_for_pipe_client(signal_t *interruptor) { - rassert(event_watcher != nullptr); - overlapped_operation_t op(event_watcher); - // TODO WINDOWS: msdn claim that the overlapped must contain - // a valid event handle, but it seems to work without one - BOOL res = ConnectNamedPipe(fd, &op.overlapped); - DWORD error = GetLastError(); - if (res || error == ERROR_PIPE_CONNECTED) { - return; - } - if (error == ERROR_IO_PENDING) { - op.wait_interruptible(interruptor); - error = op.error; - } else { - op.set_result(0, error); - } - if (error) { - crash("ConnectNamedPipe failed: %s", winerr_string(error).c_str()); - } -} - -#else - -#include "containers/archive/socket_stream.hpp" - -#include -#include -#include -#include - -#include - -#include "arch/fd_send_recv.hpp" -#include "arch/runtime/event_queue.hpp" // format_poll_event -#include "concurrency/interruptor.hpp" -#include "concurrency/wait_any.hpp" // wait_any_t -#include "errors.hpp" -#include "logger.hpp" // logERR - -// -------------------- blocking_fd_watcher_t -------------------- -blocking_fd_watcher_t::blocking_fd_watcher_t() - : read_open_(true), write_open_(true) {} - -bool blocking_fd_watcher_t::is_read_open() { return read_open_; } -bool blocking_fd_watcher_t::is_write_open() { return write_open_; } - -void blocking_fd_watcher_t::on_shutdown_read() { - guarantee(read_open_); - read_open_ = false; -} - -void blocking_fd_watcher_t::on_shutdown_write() { - guarantee(write_open_); - write_open_ = false; -} - -bool blocking_fd_watcher_t::wait_for_read(signal_t *interruptor) { - guarantee(read_open_); - // blocking IO and interruption don't go together nicely - guarantee(!interruptor, "blocking_fd_watcher_t doesn't support interruption"); - return true; -} - -bool blocking_fd_watcher_t::wait_for_write(signal_t *interruptor) { - guarantee(write_open_); - // blocking IO and interruption don't go together nicely - guarantee(!interruptor, "blocking_fd_watcher_t doesn't support interruption"); - return true; -} - -void blocking_fd_watcher_t::init_callback(UNUSED linux_event_callback_t *cb) {} - - -// -------------------- linux_event_fd_watcher_t -------------------- -linux_event_fd_watcher_t::linux_event_fd_watcher_t(fd_t fd) - : io_in_progress_(false), - event_callback_(nullptr), - event_watcher_(fd, this) -{ - int res = fcntl(fd, F_SETFL, O_NONBLOCK); - guarantee_err(res == 0, "Could not make fd non-blocking."); -} - -void linux_event_fd_watcher_t::init_callback(linux_event_callback_t *cb) { - guarantee(!event_callback_); - guarantee(cb); - event_callback_ = cb; -} - -void linux_event_fd_watcher_t::on_event(int events) { - assert_thread(); - guarantee(event_callback_); - event_callback_->on_event(events); - event_watcher_.stop_watching_for_errors(); -} - -bool linux_event_fd_watcher_t::is_read_open() { return !read_closed_.is_pulsed(); } -bool linux_event_fd_watcher_t::is_write_open() { return !write_closed_.is_pulsed(); } - -void linux_event_fd_watcher_t::on_shutdown_read() { - assert_thread(); - rassert(is_read_open()); - read_closed_.pulse(); -} - -void linux_event_fd_watcher_t::on_shutdown_write() { - assert_thread(); - rassert(is_write_open()); - write_closed_.pulse(); -} - -bool linux_event_fd_watcher_t::wait_for_read(signal_t *interruptor) { - // Wait for a notification from the event queue, or an order to shut down - assert_thread(); - guarantee(!io_in_progress_); - io_in_progress_ = true; - - linux_event_watcher_t::watch_t watch(&event_watcher_, poll_event_in); - wait_any_t waiter(&watch, &read_closed_); - if (interruptor) { - waiter.add(interruptor); - } - waiter.wait_lazily_unordered(); - - guarantee(io_in_progress_); - io_in_progress_ = false; - - if (interruptor && interruptor->is_pulsed()) - throw interrupted_exc_t(); - - return is_read_open(); -} - -bool linux_event_fd_watcher_t::wait_for_write(signal_t *interruptor) { - // Wait for a notification from the event queue, or an order to shut down - assert_thread(); - guarantee(!io_in_progress_); - io_in_progress_ = true; - - linux_event_watcher_t::watch_t watch(&event_watcher_, poll_event_out); - wait_any_t waiter(&watch, &write_closed_); - if (interruptor) { - waiter.add(interruptor); - } - waiter.wait_lazily_unordered(); - - guarantee(io_in_progress_); - io_in_progress_ = false; - - if (interruptor && interruptor->is_pulsed()) - throw interrupted_exc_t(); - - return is_write_open(); -} - -linux_event_fd_watcher_t::~linux_event_fd_watcher_t() { - assert_thread(); -} - - -// -------------------- socket_stream_t -------------------- -socket_stream_t::socket_stream_t(fd_t fd) - : fd_(fd), - fd_watcher_(make_scoped(fd)), - interruptor(NULL) -{ - guarantee(fd != INVALID_FD); - fd_watcher_->init_callback(this); -} - -socket_stream_t::socket_stream_t(fd_t fd, scoped_ptr_t &&watcher) - : fd_(fd), - fd_watcher_(std::move(watcher)), - interruptor(NULL) -{ - guarantee(fd != INVALID_FD); - fd_watcher_->init_callback(this); -} - -socket_stream_t::~socket_stream_t() { - assert_thread(); -} - -int64_t socket_stream_t::read(void *buf, int64_t size) { - assert_thread(); - guarantee(size > 0); - - if (!check_can_read()) { - return -1; - } - - char *bufp = static_cast(buf); - for (;;) { - rassert(is_read_open()); - - ssize_t res = ::read(fd_, bufp, size); - - if (res > 0) { - // We read some data. - guarantee(res <= size); - return res; - - } else if (res == -1 && get_errno() == EINTR) { - // Go around loop & retry system call. - - } else if (res == -1 && (get_errno() == EAGAIN || get_errno() == EWOULDBLOCK)) { - // Wait until we can read, or we shut down, or we're interrupted. - if (!wait_for_read()) - return -1; // we shut down. - // Go around the loop to try to read again - - } else { - // EOF or error - if (res == -1) { - if (get_errno() != EPIPE && get_errno() != ECONNRESET && get_errno() != ENOTCONN) { - // Unexpected error (not just "we closed"). - logERR("Could not read from socket: %s", errno_string(get_errno()).c_str()); - } - } else { - guarantee(res == 0); // sanity - } - - shutdown_read(); - return res; - } - } -} - -int64_t socket_stream_t::write(const void *buf, int64_t size) { - assert_thread(); - guarantee(size > 0); - - if (!check_can_write()) - return -1; - - int64_t orig_size = size; - const char *bufp = static_cast(buf); - while (size > 0) { - rassert(is_write_open()); - - ssize_t res = ::write(fd_, bufp, size); - - if (res > 0) { - // We wrote something! - guarantee(res <= size); - bufp += res; - size -= res; - // Go around the loop; keep writing until done. - - } else if (res == -1 && get_errno() == EINTR) { - // Go around the loop & retry system call - - } else if (res == -1 && (get_errno() == EAGAIN || get_errno() == EWOULDBLOCK)) { - // Wait until we can write, or we shut down, or we're interrupted. - if (!wait_for_write()) - return -1; // we shut down. - // Go around the loop and write some more. - - } else { - if (res == -1) { - if (get_errno() != EPIPE && get_errno() != ENOTCONN && get_errno() != ECONNRESET) { - // Unexpected error (not just "we closed") - logERR("Could not write to socket: %s", errno_string(get_errno()).c_str()); - } - } else { - logERR("Didn't expect write() to return %zd.", res); - } - - shutdown_write(); - return -1; - } - } - - return orig_size; -} - -void socket_stream_t::shutdown_read() { - assert_thread(); - - int res = ::shutdown(fd_, SHUT_RD); - if (res != 0 && get_errno() != ENOTCONN) { - logERR("Could not shutdown socket for reading: %s", errno_string(get_errno()).c_str()); - } - - if (fd_watcher_->is_read_open()) - fd_watcher_->on_shutdown_read(); -} - -void socket_stream_t::shutdown_write() { - assert_thread(); - - int res = ::shutdown(fd_, SHUT_WR); - if (res != 0 && get_errno() != ENOTCONN) { - logERR("Could not shutdown socket for writing: %s", errno_string(get_errno()).c_str()); - } - - if (fd_watcher_->is_write_open()) - fd_watcher_->on_shutdown_write(); -} - -bool socket_stream_t::wait_for_read() { - interrupted_exc_t saved_exception; - try { - return fd_watcher_->wait_for_read(interruptor); - } catch (const interrupted_exc_t &ex) { - // Don't shutdown_read here, as is it possible that it may cause a coroutine - // switch, which we are not allowed to do in a catch statement - saved_exception = ex; - } - shutdown_read(); - throw saved_exception; -} - -bool socket_stream_t::wait_for_write() { - interrupted_exc_t saved_exception; - try { - return fd_watcher_->wait_for_write(interruptor); - } catch (const interrupted_exc_t &ex) { - // Don't shutdown_write here, as is it possible that it may cause a coroutine - // switch, which we are not allowed to do in a catch statement - saved_exception = ex; - } - shutdown_write(); - throw saved_exception; -} - -bool socket_stream_t::check_can_read() { - if (!is_read_open()) return false; - if (interruptor && interruptor->is_pulsed()) throw interrupted_exc_t(); - return true; -} - -bool socket_stream_t::check_can_write() { - if (!is_write_open()) return false; - if (interruptor && interruptor->is_pulsed()) throw interrupted_exc_t(); - return true; -} - -void socket_stream_t::do_on_event(int /*events*/) { - // The default behavior is to do nothing. -} - -void socket_stream_t::on_event(int events) { - assert_thread(); - do_on_event(events); - - /* This is called by linux_event_watcher_t when error events occur. Ordinary - poll_event_in/poll_event_out events are not sent through this function. */ - - // Shut down. - if (fd_watcher_->is_write_open()) shutdown_write(); - if (fd_watcher_->is_read_open()) shutdown_read(); -} - -#endif diff --git a/src/containers/archive/socket_stream.hpp b/src/containers/archive/socket_stream.hpp deleted file mode 100644 index 7bcf664c8ec..00000000000 --- a/src/containers/archive/socket_stream.hpp +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONTAINERS_ARCHIVE_SOCKET_STREAM_HPP_ -#define CONTAINERS_ARCHIVE_SOCKET_STREAM_HPP_ - -#ifdef _WIN32 - -#include "concurrency/signal.hpp" -#include "containers/archive/archive.hpp" -#include "containers/scoped.hpp" -#include "arch/io/event_watcher.hpp" -#include "arch/runtime/runtime.hpp" - -class blocking_fd_watcher_t { }; - -class socket_stream_t : - public read_stream_t, - public write_stream_t { -public: - socket_stream_t(fd_t fd_, scoped_ptr_t) - : fd(fd_), - interruptor(nullptr), - event_watcher(nullptr) { } - socket_stream_t(fd_t fd_, windows_event_watcher_t *ew) - : fd(fd_), - event_watcher(ew), - interruptor(nullptr) { - rassert(ew != nullptr); - rassert(ew->current_thread() == get_thread_id()); - } - - socket_stream_t(const socket_stream_t &) = default; - - int64_t read(void *buf, int64_t count); - int64_t write(const void *buf, int64_t count); - void wait_for_pipe_client(signal_t *interruptor); - - void set_interruptor(signal_t *_interruptor) { interruptor = _interruptor; } - -private: - fd_t fd; - signal_t *interruptor; - windows_event_watcher_t *event_watcher; -}; - -#else - -#include "concurrency/signal.hpp" -#include "containers/archive/archive.hpp" - -#include "arch/io/event_watcher.hpp" -#include "arch/io/io_utils.hpp" -#include "concurrency/cond_var.hpp" -#include "containers/scoped.hpp" -#include "errors.hpp" - -/* fd_watcher_t exists to factor the problem of "how to wait for I/O on an fd" - * out of fd_stream_t. The best answer, if available, is to use a - * linux_event_watcher_t to do nonblocking I/O. However, we may want to use - * classes descended from fd_stream_t in contexts where the infrastructure - * needed for linux_event_watcher_t is not in place (eg. external JS processes). - */ - -class fd_watcher_t : public home_thread_mixin_debug_only_t { -public: - fd_watcher_t() {} - virtual ~fd_watcher_t() {} - - virtual void init_callback(linux_event_callback_t *cb) = 0; - - virtual bool is_read_open() = 0; - virtual bool is_write_open() = 0; - virtual void on_shutdown_read() = 0; - virtual void on_shutdown_write() = 0; - - // wait_for_{read,write} wait for either an opportunity to read/write, or - // for us to shutdown for reading/writing, or for `interruptor` (if - // non-NULL) to be pulsed, whichever happens first. It returns true if we - // can read/write and false if we have shut down (in which case - // on_shutdown_{read,write} has already been called). - // - // They raise interrupted_exc_t if `interruptor` is pulsed. - virtual MUST_USE bool wait_for_read(signal_t *interruptor) = 0; - virtual MUST_USE bool wait_for_write(signal_t *interruptor) = 0; - -private: - DISABLE_COPYING(fd_watcher_t); -}; - -/* blocking_fd_watcher_t is the simplest fd_watcher_t: it doesn't wait for IO. - */ -class blocking_fd_watcher_t : public fd_watcher_t { -public: - blocking_fd_watcher_t(); - - virtual bool is_read_open(); - virtual bool is_write_open(); - virtual void on_shutdown_read(); - virtual void on_shutdown_write(); - virtual MUST_USE bool wait_for_read(signal_t *interruptor); - virtual MUST_USE bool wait_for_write(signal_t *interruptor); - virtual void init_callback(linux_event_callback_t *cb); - -private: - bool read_open_, write_open_; -}; - -/* linux_event_fd_watcher_t uses a linux_event_watcher to wait for IO, and makes - * its corresponding fd use non-blocking I/O. - */ -class linux_event_fd_watcher_t : - public fd_watcher_t, private linux_event_callback_t { -public: - // does not take ownership of fd - explicit linux_event_fd_watcher_t(fd_t fd); - virtual ~linux_event_fd_watcher_t(); - - virtual void init_callback(linux_event_callback_t *cb); - virtual void on_event(int events); - - virtual bool is_read_open(); - virtual bool is_write_open(); - virtual void on_shutdown_read(); - virtual void on_shutdown_write(); - virtual MUST_USE bool wait_for_read(signal_t *interruptor); - virtual MUST_USE bool wait_for_write(signal_t *interruptor); - -private: - // True iff there is a waiting read/write operation. Used to ensure that we - // are used in a single-threaded fashion. - bool io_in_progress_; - - /* These are pulsed if and only if the read/write end of the connection has - * been closed. */ - cond_t read_closed_, write_closed_; - - // We forward to this callback on error events. - linux_event_callback_t *event_callback_; - - // The linux_event_watcher that we use to wait for IO events. - linux_event_watcher_t event_watcher_; - - DISABLE_COPYING(linux_event_fd_watcher_t); -}; - -class socket_stream_t : - public read_stream_t, - public write_stream_t, - private linux_event_callback_t { -public: - explicit socket_stream_t(fd_t fd); - explicit socket_stream_t(fd_t fd, scoped_ptr_t &&watcher); - virtual ~socket_stream_t(); - - // interruptible {read,write}_stream_t functions - virtual MUST_USE int64_t read(void *p, int64_t n); - virtual int64_t write(const void *p, int64_t n); - - void set_interruptor(signal_t *_interruptor) { interruptor = _interruptor; } - - void assert_thread() { fd_watcher_->assert_thread(); } - - bool is_read_open() { return fd_watcher_->is_read_open(); } - bool is_write_open() { return fd_watcher_->is_write_open(); } - -private: - void shutdown_read(); - void shutdown_write(); - - // Returns false if we are closed for {read,write}. - // Raises interrupted_exc_t if we are open but interruptor is pulsed. - // Returns true otherwise. - bool check_can_read(); - bool check_can_write(); - - // Wrappers for fd_watcher_->wait_for_{read,write} that shut us down if - // interrupted. - bool wait_for_read(); - bool wait_for_write(); - - // Member fields - // For subclasses to override on_event behavior. Is evaluated as the first - // thing done in on_event. - virtual void do_on_event(int events); - - fd_t fd_; - scoped_ptr_t fd_watcher_; - signal_t *interruptor; - - void on_event(int events); // for linux_callback_t - - DISABLE_COPYING(socket_stream_t); -}; - -#endif // _WIN32 - -#endif // CONTAINERS_ARCHIVE_SOCKET_STREAM_HPP_ diff --git a/src/containers/archive/stl_types.cc b/src/containers/archive/stl_types.cc deleted file mode 100644 index 9b5b3a19a82..00000000000 --- a/src/containers/archive/stl_types.cc +++ /dev/null @@ -1,42 +0,0 @@ -#include "containers/archive/stl_types.hpp" - -#include "containers/archive/versioned.hpp" - -// You're not allowed to change the wire format of serialize_universal -// implementations. -size_t serialize_universal_size(const std::string &s) { - return varint_uint64_serialized_size(s.size()) + s.size(); -} - -void serialize_universal(write_message_t *wm, const std::string &s) { - serialize_varint_uint64(wm, s.size()); - wm->append(s.data(), s.size()); -} - -archive_result_t deserialize_universal(read_stream_t *s, std::string *out) { - out->clear(); - - uint64_t sz; - archive_result_t res = deserialize_varint_uint64(s, &sz); - if (bad(res)) { return res; } - - if (sz > std::numeric_limits::max()) { - return archive_result_t::RANGE_ERROR; - } - - // Unfortunately we have to do an extra copy before dumping data - // into a std::string. - std::vector v(sz); - - int64_t num_read = force_read(s, v.data(), sz); - if (num_read == -1) { - return archive_result_t::SOCK_ERROR; - } - if (static_cast(num_read) < sz) { - return archive_result_t::SOCK_EOF; - } - - out->assign(v.data(), v.size()); - - return archive_result_t::SUCCESS; -} diff --git a/src/containers/archive/stl_types.hpp b/src/containers/archive/stl_types.hpp deleted file mode 100644 index 47bb557beeb..00000000000 --- a/src/containers/archive/stl_types.hpp +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CONTAINERS_ARCHIVE_STL_TYPES_HPP_ -#define CONTAINERS_ARCHIVE_STL_TYPES_HPP_ - -#include - -#include -#include -#include -#include // ugh -#include -#include -#include -#include -#include - -#include "containers/archive/archive.hpp" -#include "containers/archive/varint.hpp" -#include "version.hpp" - -// Implementations for deque, pair, map, set, string, vector, and array. - -template -size_t serialized_size(const std::pair &p) { - return serialized_size(p.first) + serialized_size(p.second); -} - -// Keep in sync with serialized_size. -template -void serialize(write_message_t *wm, const std::pair &p) { - serialize(wm, p.first); - serialize(wm, p.second); -} - -template -MUST_USE archive_result_t deserialize(read_stream_t *s, std::pair *p) { - archive_result_t res = deserialize(s, &p->first); - if (bad(res)) { return res; } - res = deserialize(s, &p->second); - return res; -} - -// Keep in sync with serialize. -template -size_t serialized_size(const std::map &m) { - size_t ret = varint_uint64_serialized_size(m.size()); - for (auto it = m.begin(), e = m.end(); it != e; ++it) { - ret += serialized_size(*it); - } - return ret; -} - -// Keep in sync with serialized_size. -template -void serialize(write_message_t *wm, const std::map &m) { - serialize_varint_uint64(wm, m.size()); - for (auto it = m.begin(), e = m.end(); it != e; ++it) { - serialize(wm, *it); - } -} - -template -MUST_USE archive_result_t deserialize(read_stream_t *s, std::map *m) { - m->clear(); - - uint64_t sz; - archive_result_t res = deserialize_varint_uint64(s, &sz); - if (bad(res)) { return res; } - - if (sz > std::numeric_limits::max()) { - return archive_result_t::RANGE_ERROR; - } - - // Using position should make this function take linear time, not - // sz*log(sz) time. - typename std::map::iterator position = m->begin(); - - for (uint64_t i = 0; i < sz; ++i) { - std::pair p; - res = deserialize(s, &p); - if (bad(res)) { return res; } - position = m->insert(position, p); - } - - return archive_result_t::SUCCESS; -} - -template -void serialize(write_message_t *wm, const std::set &s) { - serialize_varint_uint64(wm, s.size()); - for (typename std::set::iterator it = s.begin(), e = s.end(); it != e; ++it) { - serialize(wm, *it); - } -} - -template -MUST_USE archive_result_t deserialize(read_stream_t *s, std::set *out) { - out->clear(); - - uint64_t sz; - archive_result_t res = deserialize_varint_uint64(s, &sz); - if (bad(res)) { return res; } - - if (sz > std::numeric_limits::max()) { - return archive_result_t::RANGE_ERROR; - } - - typename std::set::iterator position = out->begin(); - - for (uint64_t i = 0; i < sz; ++i) { - T value; - res = deserialize(s, &value); - if (bad(res)) { return res; } - position = out->insert(position, value); - } - - return archive_result_t::SUCCESS; -} - -size_t serialize_universal_size(const std::string &s); -void serialize_universal(write_message_t *wm, const std::string &s); -MUST_USE archive_result_t deserialize_universal(read_stream_t *s, std::string *out); - -template -size_t serialized_size(const std::string &s) { - return serialize_universal_size(s); -} -template -void serialize(write_message_t *wm, const std::string &s) { - serialize_universal(wm, s); -} -template -MUST_USE archive_result_t deserialize(read_stream_t *s, std::string *out) { - return deserialize_universal(s, out); -} - - -// Think twice before using this function on vectors containing a primitive type -- -// it'll take O(n) time! -// Keep in sync with serialize. -template -size_t serialized_size(const std::vector &v) { - size_t ret = varint_uint64_serialized_size(v.size()); - for (auto it = v.begin(), e = v.end(); it != e; ++it) { - ret += serialized_size(*it); - } - return ret; -} - - -// Keep in sync with serialized_size. -template -void serialize(write_message_t *wm, const std::vector &v) { - serialize_varint_uint64(wm, v.size()); - for (auto it = v.begin(), e = v.end(); it != e; ++it) { - serialize(wm, *it); - } -} - -template -MUST_USE archive_result_t deserialize(read_stream_t *s, std::vector *v) { - v->clear(); - - uint64_t sz; - archive_result_t res = deserialize_varint_uint64(s, &sz); - if (bad(res)) { return res; } - - if (sz > std::numeric_limits::max()) { - return archive_result_t::RANGE_ERROR; - } - - v->resize(sz); - for (uint64_t i = 0; i < sz; ++i) { - res = deserialize(s, &(*v)[i]); - if (bad(res)) { return res; } - } - - return archive_result_t::SUCCESS; -} - -template -void serialize(write_message_t *wm, const std::deque &v) { - serialize_varint_uint64(wm, v.size()); - for (typename std::deque::const_iterator it = v.begin(); it != v.end(); ++it) { - serialize(wm, *it); - } -} - -template -MUST_USE archive_result_t deserialize(read_stream_t *s, std::deque *v) { - uint64_t sz; - archive_result_t res = deserialize_varint_uint64(s, &sz); - if (bad(res)) { return res; } - - if (sz > std::numeric_limits::max()) { - return archive_result_t::RANGE_ERROR; - } - - for (uint64_t i = 0; i < sz; ++i) { - // We avoid copying a non-empty value. - v->push_back(T()); - res = deserialize(s, &v->back()); - if (bad(res)) { return res; } - } - - return archive_result_t::SUCCESS; -} - -// TODO: Stop using std::list! What are you thinking? -template -void serialize(write_message_t *wm, const std::list &v) { - serialize_varint_uint64(wm, v.size()); - for (typename std::list::const_iterator it = v.begin(), e = v.end(); it != e; ++it) { - serialize(wm, *it); - } -} - -template -MUST_USE archive_result_t deserialize(read_stream_t *s, std::list *v) { - // Omit assertions because it's not a shame if a std::list gets corrupted. - - uint64_t sz; - archive_result_t res = deserialize_varint_uint64(s, &sz); - if (bad(res)) { return res; } - - if (sz > std::numeric_limits::max()) { - return archive_result_t::RANGE_ERROR; - } - - for (uint64_t i = 0; i < sz; ++i) { - // We avoid copying a non-empty value. - v->push_back(T()); - res = deserialize(s, &v->back()); - if (bad(res)) { return res; } - } - - return archive_result_t::SUCCESS; -} - -template -void serialize(write_message_t *write_message, std::array const &values) { - serialize_varint_uint64(write_message, N); - for (T const & value : values) { - serialize(write_message, value); - } -} - -template -MUST_USE archive_result_t deserialize(read_stream_t *read_stream, std::array *values) { - uint64_t sz = 0; - archive_result_t archive_result = deserialize_varint_uint64(read_stream, &sz); - if (bad(archive_result)) { - return archive_result; - } - if (sz > std::numeric_limits::max()) { - return archive_result_t::RANGE_ERROR; - } - size_t size = static_cast(sz); - if (size != N) { - return archive_result_t::RANGE_ERROR; - } - - for (std::size_t i = 0; i < size; ++i) { - archive_result = deserialize(read_stream, &((*values)[i])); - if (bad(archive_result)) { - return archive_result; - } - } - - return archive_result_t::SUCCESS; -} - -#endif // CONTAINERS_ARCHIVE_STL_TYPES_HPP_ diff --git a/src/containers/archive/string_stream.cc b/src/containers/archive/string_stream.cc deleted file mode 100644 index e08da872fa1..00000000000 --- a/src/containers/archive/string_stream.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "containers/archive/string_stream.hpp" - -string_stream_t::string_stream_t() { } - -string_stream_t::~string_stream_t() { } - -int64_t string_stream_t::write(const void *p, int64_t n) { - const char * chp = static_cast(p); - str_.insert(str_.end(), chp, chp + n); - return n; -} - -string_read_stream_t::string_read_stream_t(std::string &&_source, int64_t _offset) : - source(std::move(_source)), offset(_offset) { - guarantee(offset >= 0); - guarantee(static_cast(offset) <= source.size()); -} - -string_read_stream_t::~string_read_stream_t() { } - -int64_t string_read_stream_t::read(void *p, int64_t n) { - int64_t num_left = source.size() - offset; - int64_t num_to_read = n < num_left ? n : num_left; - - memcpy(p, source.data() + offset, num_to_read); - - offset += num_to_read; - - return num_to_read; -} - -void string_read_stream_t::swap(std::string *other_source, int64_t *other_offset) { - int64_t temp_offset = *other_offset; - *other_offset = offset; - offset = temp_offset; - - source.swap(*other_source); - - guarantee(offset >= 0); - guarantee(static_cast(offset) <= source.size()); -} diff --git a/src/containers/archive/string_stream.hpp b/src/containers/archive/string_stream.hpp deleted file mode 100644 index 91e1d111ff1..00000000000 --- a/src/containers/archive/string_stream.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CONTAINERS_ARCHIVE_STRING_STREAM_HPP_ -#define CONTAINERS_ARCHIVE_STRING_STREAM_HPP_ - -#include - -#include "containers/archive/archive.hpp" - -class string_stream_t : public write_stream_t { -public: - string_stream_t(); - virtual ~string_stream_t(); - - virtual MUST_USE int64_t write(const void *p, int64_t n); - - std::string &str() { return str_; } - -private: - std::string str_; - - DISABLE_COPYING(string_stream_t); -}; - -class string_read_stream_t : public read_stream_t { -public: - explicit string_read_stream_t(std::string &&_source, int64_t _offset); - virtual ~string_read_stream_t(); - - virtual MUST_USE int64_t read(void *p, int64_t n); - - void swap(std::string *other_source, int64_t *other_offset); - -private: - std::string source; - int64_t offset; - - DISABLE_COPYING(string_read_stream_t); -}; - -#endif // CONTAINERS_ARCHIVE_STRING_STREAM_HPP_ diff --git a/src/containers/archive/tcp_conn_stream.cc b/src/containers/archive/tcp_conn_stream.cc deleted file mode 100644 index cc3ca6d069c..00000000000 --- a/src/containers/archive/tcp_conn_stream.cc +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "containers/archive/tcp_conn_stream.hpp" - -#include "arch/io/network.hpp" - -tcp_conn_stream_t::tcp_conn_stream_t( - tls_ctx_t *tls_ctx, const ip_address_t &host, int port, - signal_t *interruptor, int local_port -) : - conn_( -#ifdef ENABLE_TLS - (nullptr != tls_ctx) ? - new secure_tcp_conn_t(tls_ctx, host, port, interruptor, local_port) : -#endif - new tcp_conn_t(host, port, interruptor, local_port) - ) { } - -tcp_conn_stream_t::tcp_conn_stream_t(tcp_conn_t *conn) : conn_(conn) { - rassert(conn_ != nullptr); -} - -tcp_conn_stream_t::~tcp_conn_stream_t() { - delete conn_; -} - -int64_t tcp_conn_stream_t::read(void *p, int64_t n) { - // Returns the number of bytes read, or 0 upon EOF, -1 upon error. - // Right now this function cannot "error". - // It also always returns either `n` or 0. It never reads only parts of the data. - try { - cond_t non_closer; - guarantee(n >= 0); - conn_->read_buffered(p, static_cast(n), &non_closer); - return n; - } catch (const tcp_conn_read_closed_exc_t &) { - return 0; - } -} - -int64_t tcp_conn_stream_t::write(const void *p, int64_t n) { - try { - // write writes everything or throws an exception. - cond_t non_closer; - conn_->write(p, n, &non_closer); - return n; - } catch (const tcp_conn_write_closed_exc_t &) { - return -1; - } -} - -int64_t tcp_conn_stream_t::write_buffered(const void *p, int64_t n) { - try { - cond_t non_closer; - conn_->write_buffered(p, n, &non_closer); - return n; - } catch (const tcp_conn_write_closed_exc_t &) { - return -1; - } -} - -bool tcp_conn_stream_t::flush_buffer() { - try { - cond_t non_closer; - conn_->flush_buffer(&non_closer); - return true; - } catch (const tcp_conn_write_closed_exc_t &) { - return false; - } -} - -void tcp_conn_stream_t::rethread(threadnum_t new_thread) { - conn_->rethread(new_thread); -} - -threadnum_t tcp_conn_stream_t::home_thread() const { - return conn_->home_thread(); -} - -void tcp_conn_stream_t::shutdown_read() { - conn_->shutdown_read(); -} - -void tcp_conn_stream_t::shutdown_write() { - conn_->shutdown_write(); -} - -bool tcp_conn_stream_t::is_read_open() { - return conn_->is_read_open(); -} - -bool tcp_conn_stream_t::is_write_open() { - return conn_->is_write_open(); -} - - -make_buffered_tcp_conn_stream_wrapper_t::make_buffered_tcp_conn_stream_wrapper_t( - tcp_conn_stream_t *inner) - : inner_(inner) { } - -int64_t make_buffered_tcp_conn_stream_wrapper_t::write(const void *p, int64_t n) { - return inner_->write_buffered(p, n); -} - - -keepalive_tcp_conn_stream_t::keepalive_tcp_conn_stream_t( - tls_ctx_t *tls_ctx, const ip_address_t &host, int port, - signal_t *interruptor, int local_port -) : - tcp_conn_stream_t(tls_ctx, host, port, interruptor, local_port), - keepalive_callback(NULL) { } - -keepalive_tcp_conn_stream_t::keepalive_tcp_conn_stream_t(tcp_conn_t *conn) : - tcp_conn_stream_t(conn), - keepalive_callback(nullptr) { } - -keepalive_tcp_conn_stream_t::~keepalive_tcp_conn_stream_t() { - // Do nothing -} - -void keepalive_tcp_conn_stream_t::set_keepalive_callback(keepalive_callback_t *_keepalive_callback) { - keepalive_callback = _keepalive_callback; -} - -int64_t keepalive_tcp_conn_stream_t::read(void *p, int64_t n) { - int64_t result = tcp_conn_stream_t::read(p, n); - - if (result > 0 && keepalive_callback != nullptr) { - keepalive_callback->keepalive_read(); - } - - return result; -} - -int64_t keepalive_tcp_conn_stream_t::write(const void *p, int64_t n) { - if (keepalive_callback != nullptr) { - keepalive_callback->keepalive_write(); - } - - return tcp_conn_stream_t::write(p, n); -} - -int64_t keepalive_tcp_conn_stream_t::write_buffered(const void *p, int64_t n) { - if (keepalive_callback != nullptr) { - keepalive_callback->keepalive_write(); - } - - return tcp_conn_stream_t::write_buffered(p, n); -} - -bool keepalive_tcp_conn_stream_t::flush_buffer() { - if (keepalive_callback != nullptr) { - keepalive_callback->keepalive_write(); - } - - return tcp_conn_stream_t::flush_buffer(); -} - -rethread_tcp_conn_stream_t::rethread_tcp_conn_stream_t(tcp_conn_stream_t *conn, threadnum_t thread) - : conn_(conn), old_thread_(conn->home_thread()), new_thread_(thread) { - conn->rethread(thread); - guarantee(conn->home_thread() == thread); -} - -rethread_tcp_conn_stream_t::~rethread_tcp_conn_stream_t() { - guarantee(conn_->home_thread() == new_thread_); - conn_->rethread(old_thread_); - guarantee(conn_->home_thread() == old_thread_); -} diff --git a/src/containers/archive/tcp_conn_stream.hpp b/src/containers/archive/tcp_conn_stream.hpp deleted file mode 100644 index 3f79b55de2e..00000000000 --- a/src/containers/archive/tcp_conn_stream.hpp +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONTAINERS_ARCHIVE_TCP_CONN_STREAM_HPP_ -#define CONTAINERS_ARCHIVE_TCP_CONN_STREAM_HPP_ - -#include "arch/address.hpp" -#include "arch/io/openssl.hpp" -#include "arch/types.hpp" -#include "containers/archive/archive.hpp" -#include "threading.hpp" - -class signal_t; - -class tcp_conn_stream_t : public read_stream_t, public write_stream_t { -public: - tcp_conn_stream_t( - tls_ctx_t *tls_ctx, const ip_address_t &host, int port, - signal_t *interruptor, int local_port = 0); - - // Takes ownership. - explicit tcp_conn_stream_t(tcp_conn_t *conn); - virtual ~tcp_conn_stream_t(); - - virtual MUST_USE int64_t read(void *p, int64_t n); - virtual MUST_USE int64_t write(const void *p, int64_t n); - virtual MUST_USE int64_t write_buffered(const void *p, int64_t n); - virtual bool flush_buffer(); - - void rethread(threadnum_t new_thread); - - threadnum_t home_thread() const; - - void shutdown_read(); - void shutdown_write(); - bool is_read_open(); - bool is_write_open(); - - tcp_conn_t *get_underlying_conn() { - return conn_; - } - -private: - tcp_conn_t *conn_; - - DISABLE_COPYING(tcp_conn_stream_t); -}; - -// Wraps around a `tcp_conn_stream_t` and redirects `write()` to `write_buffered()` -class make_buffered_tcp_conn_stream_wrapper_t : public write_stream_t { -public: - explicit make_buffered_tcp_conn_stream_wrapper_t(tcp_conn_stream_t *inner); - virtual MUST_USE int64_t write(const void *p, int64_t n); -private: - tcp_conn_stream_t *inner_; -}; - -class keepalive_tcp_conn_stream_t : public tcp_conn_stream_t { -public: - keepalive_tcp_conn_stream_t( - tls_ctx_t *tls_ctx, const ip_address_t &host, int port, - signal_t *interruptor, int local_port = 0); - - // Takes ownership. - explicit keepalive_tcp_conn_stream_t(tcp_conn_t *conn); - virtual ~keepalive_tcp_conn_stream_t(); - - class keepalive_callback_t { - public: - virtual ~keepalive_callback_t() { } - virtual void keepalive_read() = 0; - virtual void keepalive_write() = 0; - }; - - void set_keepalive_callback(keepalive_callback_t *_keepalive_callback); - - virtual MUST_USE int64_t read(void *p, int64_t n); - virtual MUST_USE int64_t write(const void *p, int64_t n); - virtual MUST_USE int64_t write_buffered(const void *p, int64_t n); - virtual bool flush_buffer(); - -private: - keepalive_callback_t *keepalive_callback; -}; - - -class rethread_tcp_conn_stream_t { -public: - rethread_tcp_conn_stream_t(tcp_conn_stream_t *conn, threadnum_t thread); - ~rethread_tcp_conn_stream_t(); -private: - tcp_conn_stream_t *conn_; - threadnum_t old_thread_; - threadnum_t new_thread_; - DISABLE_COPYING(rethread_tcp_conn_stream_t); -}; - - - - -#endif // CONTAINERS_ARCHIVE_TCP_CONN_STREAM_HPP_ diff --git a/src/containers/archive/tuple.hpp b/src/containers/archive/tuple.hpp deleted file mode 100644 index 9ce30f71a86..00000000000 --- a/src/containers/archive/tuple.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef CONTAINERS_ARCHIVE_TUPLE_HPP_ -#define CONTAINERS_ARCHIVE_TUPLE_HPP_ - -#include - -#include "containers/archive/archive.hpp" - -// When there's library support, we can use the C++14 std::index_sequence. - -template -struct emit_type { - using type = T; -}; - -template -struct rindex_sequence { }; - -template -struct build_rindex_sequence - : std::conditional>, - build_rindex_sequence>::type { }; - -template -struct make_rindex_sequence : build_rindex_sequence::type { }; - -template -size_t serialized_size_helper(const tuple_type &tup, rindex_sequence) { - (void)tup; - size_t acc = 0; - // We specify the array size, and add 1, to satisfy VS 2015 - // complaining about array of size zero. - int ign[sizeof...(Is) + 1] - = { ((acc += serialized_size(std::get(tup))), 0)... }; - (void)ign; - return acc; -} - -template -size_t serialized_size(const std::tuple &tup) { - return serialized_size_helper(tup, make_rindex_sequence()); -} - -template -void serialize_helper(write_message_t *wm, const tuple_type &tup, rindex_sequence) { - // Silence unused variable warning in gcc for tuple<>. - (void)wm; - (void)tup; - // We specify the array size, and add 1, to satisfy VS 2015 - // complaining about array of size zero. - int ign[sizeof...(Is) + 1] - = { (serialize(wm, std::get(tup)), 0)... }; - (void)ign; -} - -template -void serialize(write_message_t *wm, const std::tuple &tup) { - serialize_helper(wm, tup, make_rindex_sequence()); -} - -template -MUST_USE archive_result_t deserialize_helper(read_stream_t *s, tuple_type *tup, rindex_sequence) { - (void)s; - (void)tup; - archive_result_t res = archive_result_t::SUCCESS; - int ign[sizeof...(Is) + 1] - = { (res == archive_result_t::SUCCESS - ? ((res = deserialize(s, &std::get(*tup))), 0) - : 0)... }; - (void)ign; - return res; -} - -template -MUST_USE archive_result_t deserialize(read_stream_t *s, std::tuple *tup) { - return deserialize_helper(s, tup, make_rindex_sequence()); -} - - -#endif // CONTAINERS_ARCHIVE_TUPLE_HPP_ diff --git a/src/containers/archive/varint.cc b/src/containers/archive/varint.cc deleted file mode 100644 index a98b450c9e3..00000000000 --- a/src/containers/archive/varint.cc +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "containers/archive/varint.hpp" - -size_t varint_uint64_serialized_size(uint64_t value) { - size_t count = 0; - - ++count; - value /= 128; - - while (value > 0) { - value /= 128; - ++count; - } - - return count; -} - -void serialize_varint_uint64(write_message_t *wm, const uint64_t value) { - // buf needs to be 10 or more -- ceil(64/7) is 10. - uint8_t buf[16]; - size_t size = serialize_varint_uint64_into_buf(value, buf); - wm->append(buf, size); -} - -size_t serialize_varint_uint64_into_buf(const uint64_t value, uint8_t *buf_out) { - size_t size = 0; - if (value == 0) { - buf_out[0] = 0; - size = 1; - } else { - uint64_t n = value; - while (n >= (1 << 7)) { - buf_out[size] = ((n & ((1 << 7) - 1)) | (1 << 7)); - ++size; - n >>= 7; - } - buf_out[size] = n; - ++size; - } - return size; -} diff --git a/src/containers/archive/varint.hpp b/src/containers/archive/varint.hpp deleted file mode 100644 index a45a2779803..00000000000 --- a/src/containers/archive/varint.hpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONTAINERS_ARCHIVE_VARINT_HPP_ -#define CONTAINERS_ARCHIVE_VARINT_HPP_ - -#include "containers/archive/archive.hpp" - -// We don't use google::protobuf::io::CodedOutputStream::WriteVarint64ToArray because -// we have no clean way of _reading_ varints without constructing a CodedInputStream, -// which would require knowing the varint structure anyway in order to know how many -// bytes of the input stream to read off and load into an array. - -// Varint encoding encodes a number in base-128 in little-endian order, with the MSB -// the last byte 0, all other bytes 1. -// -// Some sample encodings: -// -// 1 -> [0000 0001] -// 127 -> [0111 1111] -// 138 -> [1000 1010, 0000 0001] -// -// Unlike protocol buffers does (or what its documentation claims it does), we don't -// silently truncate out-of-range varints when decoding. - -size_t varint_uint64_serialized_size(uint64_t value); - -void serialize_varint_uint64(write_message_t *wm, const uint64_t value); -// buf_out must have a size of at least varint_uint64_serialized_size(value). -// The actually used size is returned. -size_t serialize_varint_uint64_into_buf(const uint64_t value, uint8_t *buf_out); - -// Note: This is defined in the header file because inlining this yields -// significant performance gains due to its frequent use in datum_string_t and -// other datum deserialization functions. (measured on GCC 4.8) -// If we end up compiling with whole-program link-time optimization some day, -// we can probably move this back to the .cc file. -inline archive_result_t deserialize_varint_uint64(read_stream_t *s, uint64_t *value_out) { - uint64_t value = 0; - - int offset = 0; - for (;;) { - uint8_t buf[1]; - int64_t res = s->read(buf, 1); - if (res == 1) { - uint64_t x = (buf[0] & ((1 << 7) - 1)); - value |= (x << offset); - if ((buf[0] & (1 << 7)) == 0) { - if (offset == 63 && x > 1) { - return archive_result_t::RANGE_ERROR; - } else { - *value_out = value; - return archive_result_t::SUCCESS; - } - } - if (offset == 63) { - return archive_result_t::RANGE_ERROR; - } - offset += 7; - } else if (res == -1) { - return archive_result_t::SOCK_ERROR; - } else if (res == 0) { - return archive_result_t::SOCK_EOF; - } else { - unreachable("deserialize_varint_uint64: read: unexpected result"); - } - } -} - -#endif // CONTAINERS_ARCHIVE_VARINT_HPP_ diff --git a/src/containers/archive/vector_stream.cc b/src/containers/archive/vector_stream.cc deleted file mode 100644 index 4fe15b4c4ac..00000000000 --- a/src/containers/archive/vector_stream.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "containers/archive/vector_stream.hpp" - -#include - -vector_stream_t::vector_stream_t() { } - -vector_stream_t::~vector_stream_t() { } - -int64_t vector_stream_t::write(const void *p, int64_t n) { - const char *chp = static_cast(p); - vec_.insert(vec_.end(), chp, chp + n); - - return n; -} - -void vector_stream_t::swap(std::vector *other) { - other->swap(vec_); -} - -void vector_stream_t::reserve(size_t reserve_size) { - vec_.reserve(reserve_size); -} - -vector_read_stream_t::vector_read_stream_t(std::vector &&vector, int64_t offset) - : pos_(offset), vec_(std::move(vector)) { - guarantee(pos_ >= 0); - guarantee(static_cast(pos_) <= vec_.size()); -} -vector_read_stream_t::~vector_read_stream_t() { } - -int64_t vector_read_stream_t::read(void *p, int64_t n) { - int64_t num_left = vec_.size() - pos_; - int64_t num_to_read = n < num_left ? n : num_left; - - memcpy(p, vec_.data() + pos_, num_to_read); - - pos_ += num_to_read; - - return num_to_read; -} - -void vector_read_stream_t::swap(std::vector *other_vec, int64_t *other_pos) { - int64_t temp_pos = *other_pos; - *other_pos = pos_; - pos_ = temp_pos; - - vec_.swap(*other_vec); - - guarantee(pos_ >= 0); - guarantee(static_cast(pos_) <= vec_.size()); -} diff --git a/src/containers/archive/vector_stream.hpp b/src/containers/archive/vector_stream.hpp deleted file mode 100644 index 65dfcf9e50d..00000000000 --- a/src/containers/archive/vector_stream.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONTAINERS_ARCHIVE_VECTOR_STREAM_HPP_ -#define CONTAINERS_ARCHIVE_VECTOR_STREAM_HPP_ - -#include - -#include "containers/archive/archive.hpp" - -class vector_stream_t : public write_stream_t { -public: - vector_stream_t(); - virtual ~vector_stream_t(); - - virtual MUST_USE int64_t write(const void *p, int64_t n); - - const std::vector &vector() { return vec_; } - - void swap(std::vector *other); - - void reserve(size_t reserve_size); - -private: - std::vector vec_; - - DISABLE_COPYING(vector_stream_t); -}; - -class vector_read_stream_t : public read_stream_t { -public: - explicit vector_read_stream_t(std::vector &&vector, int64_t offset = 0); - virtual ~vector_read_stream_t(); - - virtual MUST_USE int64_t read(void *p, int64_t n); - - void swap(std::vector *other_vec, int64_t *other_pos); - -private: - int64_t pos_; - std::vector vec_; - - DISABLE_COPYING(vector_read_stream_t); -}; - -#endif // CONTAINERS_ARCHIVE_VECTOR_STREAM_HPP_ diff --git a/src/containers/archive/versioned.hpp b/src/containers/archive/versioned.hpp deleted file mode 100644 index 7cac298f3e8..00000000000 --- a/src/containers/archive/versioned.hpp +++ /dev/null @@ -1,274 +0,0 @@ -#ifndef CONTAINERS_ARCHIVE_VERSIONED_HPP_ -#define CONTAINERS_ARCHIVE_VERSIONED_HPP_ - -#include - -#include "containers/archive/archive.hpp" -#include "version.hpp" - - -namespace archive_internal { - -class bogus_made_up_type_t; - -} // namespace archive_internal - -// These are generally universal. They must not have their behavior change -- except -// if we remove some cluster_version_t value, in which case... maybe would fail on a -// range error with the specific removed values. Or maybe, we would do something -// differently. -inline void serialize_cluster_version(write_message_t *wm, cluster_version_t v) { - int8_t raw = static_cast(v); - serialize(wm, raw); -} - -inline MUST_USE archive_result_t deserialize_cluster_version( - read_stream_t *s, - cluster_version_t *thing, - const std::function &obsolete_cb) { - // Initialize `thing` to *something* because GCC 4.6.3 thinks that `thing` - // could be used uninitialized, even when the return value of this function - // is checked through `guarantee_deserialization()`. - // See https://github.com/rethinkdb/rethinkdb/issues/2640 - *thing = cluster_version_t::LATEST_OVERALL; - int8_t raw; - archive_result_t res = deserialize(s, &raw); - if (bad(res)) { return res; } - if (raw == static_cast(obsolete_cluster_version_t::v1_13) - || raw == static_cast(obsolete_cluster_version_t::v1_13_2)) { - obsolete_cb(); - crash("Outdated index handling did not crash or throw."); - } else { - // This is the same rassert in `ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE`. - if (raw >= static_cast(cluster_version_t::v1_14) - && raw <= static_cast(cluster_version_t::v2_4_is_latest)) { - *thing = static_cast(raw); - } else { - throw archive_exc_t{"Unrecognized cluster serialization version."}; - } - } - return res; -} - -inline MUST_USE archive_result_t deserialize_reql_version( - read_stream_t *s, reql_version_t *thing, - const std::function &obsolete_cb) { - // Initialize `thing` to *something* because GCC 4.6.3 thinks that `thing` - // could be used uninitialized, even when the return value of this function - // is checked through `guarantee_deserialization()`. - // See https://github.com/rethinkdb/rethinkdb/issues/2640 - *thing = reql_version_t::LATEST; - int8_t raw; - archive_result_t res = deserialize_universal(s, &raw); - if (bad(res)) { return res; } - if (raw < static_cast(reql_version_t::EARLIEST)) { - guarantee(raw >= static_cast(obsolete_reql_version_t::EARLIEST) - && raw <= static_cast(obsolete_reql_version_t::LATEST)); - obsolete_cb(static_cast(raw)); - crash("Outdated index handling did not crash or throw."); - } else { - // This is the same rassert in `ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE`. - guarantee(raw >= static_cast(reql_version_t::EARLIEST) - && raw <= static_cast(reql_version_t::LATEST)); - *thing = static_cast(raw); - } - return res; -} - - -// Serializes a value for a given version. DOES NOT SERIALIZE THE VERSION NUMBER! -template -void serialize_for_version(cluster_version_t version, write_message_t *wm, - const T &value) { - // We currently only support serializing either the current disk or current - // cluster version, no previous versions. - if (version == cluster_version_t::CLUSTER) { - serialize(wm, value); - } else if (version == cluster_version_t::LATEST_DISK) { - serialize(wm, value); - } else { - crash("Attempted to serialize for a non-current version"); - } -} - -// Deserializes a value, assuming it's serialized for a given version. (This doesn't -// deserialize any version numbers.) -template -archive_result_t deserialize_for_version(cluster_version_t version, - read_stream_t *s, - T *thing) { - switch (version) { - case cluster_version_t::v1_14: - return deserialize(s, thing); - case cluster_version_t::v1_15: - return deserialize(s, thing); - case cluster_version_t::v1_16: - return deserialize(s, thing); - case cluster_version_t::v2_0: - return deserialize(s, thing); - case cluster_version_t::v2_1: - return deserialize(s, thing); - case cluster_version_t::v2_2: - return deserialize(s, thing); - case cluster_version_t::v2_3: - return deserialize(s, thing); - case cluster_version_t::v2_4_is_latest: - return deserialize(s, thing); - default: - unreachable("deserialize_for_version: unsupported cluster version"); - } -} - -// Some serialized_size needs to be visible, apparently, so that -// serialized_size_for_version will actually parse. -template -size_t serialized_size(const archive_internal::bogus_made_up_type_t &); - -template -size_t serialized_size_for_version(cluster_version_t version, - const T &thing) { - switch (version) { - case cluster_version_t::v1_14: - return serialized_size(thing); - case cluster_version_t::v1_15: - return serialized_size(thing); - case cluster_version_t::v1_16: - return serialized_size(thing); - case cluster_version_t::v2_0: - return serialized_size(thing); - case cluster_version_t::v2_1: - return serialized_size(thing); - case cluster_version_t::v2_2: - return serialized_size(thing); - case cluster_version_t::v2_3: - return serialized_size(thing); - case cluster_version_t::v2_4_is_latest: - return serialized_size(thing); - default: - unreachable("serialize_size_for_version: unsupported version"); - } -} - -// We want to express explicitly whether a given serialization function -// is used for cluster messages or disk serialization in case the latest cluster -// and latest disk versions diverge. -// -// If you see either the INSTANTIATE_SERIALIZE_FOR_CLUSTER_AND_DISK -// of INSTANTIATE_SERIALIZE_FOR_DISK macro used somewhere, you know that if you -// change the serialization format of that type that will break the disk format, -// and you should consider writing a deserialize function for the older versions. - -#define INSTANTIATE_SERIALIZE_FOR_DISK(typ) \ - template void serialize( \ - write_message_t *, const typ &) - -#define INSTANTIATE_SERIALIZE_FOR_CLUSTER(typ) \ - template void serialize( \ - write_message_t *, const typ &) - -#define INSTANTIATE_DESERIALIZE_FOR_CLUSTER(typ) \ - template archive_result_t deserialize( \ - read_stream_t *, typ *) - -#ifdef CLUSTER_AND_DISK_VERSIONS_ARE_SAME -#define INSTANTIATE_SERIALIZE_FOR_CLUSTER_AND_DISK(typ) \ - template void serialize( \ - write_message_t *, const typ &) -#else -#define INSTANTIATE_SERIALIZE_FOR_CLUSTER_AND_DISK(typ) \ - template void serialize( \ - write_message_t *, const typ &); \ - template void serialize( \ - write_message_t *, const typ &) -#endif - -#define INSTANTIATE_DESERIALIZE_SINCE_v1_13(typ) \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *) - -#define INSTANTIATE_SERIALIZABLE_SINCE_v1_13(typ) \ - INSTANTIATE_SERIALIZE_FOR_CLUSTER_AND_DISK(typ); \ - INSTANTIATE_DESERIALIZE_SINCE_v1_13(typ) - -#define INSTANTIATE_DESERIALIZE_SINCE_v1_16(typ) \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *) - -#define INSTANTIATE_SERIALIZABLE_SINCE_v1_16(typ) \ - INSTANTIATE_SERIALIZE_FOR_CLUSTER_AND_DISK(typ); \ - INSTANTIATE_DESERIALIZE_SINCE_v1_16(typ) - -#define INSTANTIATE_DESERIALIZE_SINCE_v2_1(typ) \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *) - -#define INSTANTIATE_SERIALIZABLE_SINCE_v2_1(typ) \ - INSTANTIATE_SERIALIZE_FOR_CLUSTER_AND_DISK(typ); \ - INSTANTIATE_DESERIALIZE_SINCE_v2_1(typ) - -#define INSTANTIATE_DESERIALIZE_SINCE_v2_2(typ) \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *) - -#define INSTANTIATE_SERIALIZABLE_SINCE_v2_2(typ) \ - INSTANTIATE_SERIALIZE_FOR_CLUSTER_AND_DISK(typ); \ - INSTANTIATE_DESERIALIZE_SINCE_v2_2(typ) - -#define INSTANTIATE_DESERIALIZE_SINCE_v2_3(typ) \ - template archive_result_t deserialize( \ - read_stream_t *, typ *); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *) - -#define INSTANTIATE_SERIALIZABLE_SINCE_v2_3(typ) \ - INSTANTIATE_SERIALIZE_FOR_CLUSTER_AND_DISK(typ); \ - INSTANTIATE_DESERIALIZE_SINCE_v2_3(typ) - -#define INSTANTIATE_DESERIALIZE_SINCE_v2_4(typ) \ - template archive_result_t deserialize( \ - read_stream_t *, typ *) - -#define INSTANTIATE_SERIALIZABLE_SINCE_v2_4(typ) \ - INSTANTIATE_SERIALIZE_FOR_CLUSTER_AND_DISK(typ); \ - INSTANTIATE_DESERIALIZE_SINCE_v2_4(typ) - -#define INSTANTIATE_SERIALIZABLE_FOR_CLUSTER(typ) \ - INSTANTIATE_SERIALIZE_FOR_CLUSTER(typ); \ - template archive_result_t deserialize( \ - read_stream_t *, typ *) - -#endif // CONTAINERS_ARCHIVE_VERSIONED_HPP_ diff --git a/src/containers/auth_key.cc b/src/containers/auth_key.cc deleted file mode 100644 index 18939a4317c..00000000000 --- a/src/containers/auth_key.cc +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "containers/auth_key.hpp" - -#include "containers/archive/stl_types.hpp" - -auth_key_t::auth_key_t() { } - -bool auth_key_t::assign_value(const std::string &new_key) { - if (new_key.length() > static_cast(max_length)) { - return false; - } - - key = new_key; - return true; -} - -RDB_IMPL_SERIALIZABLE_1_SINCE_v1_13(auth_key_t, key); - - -bool timing_sensitive_equals(const auth_key_t &x, const auth_key_t &y) { - const std::string &s = x.str(); - const std::string &t = y.str(); - - if (s.size() != t.size()) { - return false; - } - - const size_t size = s.size(); - int all_equal = 1; - for (size_t i = 0; i < size; ++i) { - all_equal &= (s[i] == t[i]); - } - - return all_equal; -} - diff --git a/src/containers/auth_key.hpp b/src/containers/auth_key.hpp deleted file mode 100644 index 5f9098ad0a9..00000000000 --- a/src/containers/auth_key.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CONTAINERS_AUTH_KEY_HPP_ -#define CONTAINERS_AUTH_KEY_HPP_ - -#include - -#include "rpc/serialize_macros.hpp" - -// A type to enforce authorization key length - -class auth_key_t { -public: - // Initializes to the empty string. - auth_key_t(); - - // Returns true if the new key is of a valid length - MUST_USE bool assign_value(const std::string &new_key); - - const std::string &str() const { return key; } - - static const int32_t max_length = 2048; - - RDB_DECLARE_ME_SERIALIZABLE(auth_key_t); - -private: - std::string key; -}; - -bool timing_sensitive_equals(const auth_key_t &x, const auth_key_t &y); - -inline bool operator==(const auth_key_t &x, const auth_key_t &y) { - // Might as well use timing_sensitive_equals this in case of accidental misuse. - return timing_sensitive_equals(x, y); -} - -inline bool operator!=(const auth_key_t &x, const auth_key_t &y) { - return !(x == y); -} - -inline bool operator<(const auth_key_t &x, const auth_key_t &y) { - return x.str() < y.str(); -} - -#endif // CONTAINERS_AUTH_KEY_HPP_ diff --git a/src/containers/backindex_bag.hpp b/src/containers/backindex_bag.hpp deleted file mode 100644 index a2dbd2e6d49..00000000000 --- a/src/containers/backindex_bag.hpp +++ /dev/null @@ -1,151 +0,0 @@ -#ifndef CONTAINERS_BACKINDEX_BAG_HPP_ -#define CONTAINERS_BACKINDEX_BAG_HPP_ - -#include - -#include "containers/segmented_vector.hpp" - -// A backindex_bag_t is a bag of elements of type T. Typically T is a pointer -// type. Why not just use an intrusive list? The reason is, with an intrusive list, -// you can't efficiently select a random element from the list. A backindex_bag_t -// lets you do that. It also automatically makes it efficient to detect whether an -// element is contained by a specific bag (while with an intrusive list, you can only -// tell whether the node is an element of _some_ list). To use, define a type T: -// -// struct T { -// ... -// backindex_bag_index_t index; -// }; -// -// And then define a function -// -// backindex_bag_index_t *access_backindex(T *ptr) { -// return &ptr->index; -// } -// -// And then you can use a backindex_bag_t. -// -// Or you could define a different function, -// -// backindex_bag_index_t *access_backindex(counted_t ptr) { -// return &ptr->index; -// } -// -// And then you could use a backindex_bag_t< counted_t >. -// -// A backindex_bag_t is essentially a vector, and a backindex_bag_index_t is the -// element's modifiable index in that vector. The index of other elements can -// change, behind the scenes, as things get added and removed from the bag. - -class backindex_bag_index_t { -public: - backindex_bag_index_t() - : index_(NOT_IN_A_BAG) { } - - ~backindex_bag_index_t() { - guarantee(index_ == NOT_IN_A_BAG); - } - -private: - template - friend class backindex_bag_t; - - static const size_t NOT_IN_A_BAG = SIZE_MAX; - - // The item's index into a (specific) backindex_bag_t, or NOT_IN_A_BAG if it - // doesn't belong to the backindex_bag_t. - size_t index_; - - DISABLE_COPYING(backindex_bag_index_t); -}; - -// An unordered bag of elements, maintained using backindexes. T is typically a -// pointer type (see the description above). You can randomly select elements and -// test for membership. -template -class backindex_bag_t { -public: - backindex_bag_t() { } - - ~backindex_bag_t() { - // Another way to implement this would be to simply remove all its elements. - guarantee(vector_.size() == 0); - } - - // Returns true if the potential element of this container is in fact an element - // of this container. The idea behind this function is that some value of type T - // could be a member of one of several backindex_bag_t's (or none). We see if - // it's a memory of this one. - bool has_element(T potential_element) const { - const backindex_bag_index_t *const backindex - = access_backindex(potential_element); - return backindex->index_ < vector_.size() - && vector_[backindex->index_] == potential_element; - } - - // Removes the element from the bag. - void remove(T element) { - backindex_bag_index_t *const backindex - = access_backindex(element); - rassert(backindex->index_ != backindex_bag_index_t::NOT_IN_A_BAG); - guarantee(backindex->index_ < vector_.size(), - "early index has wrong value: index=%zu, size=%zu", - backindex->index_, vector_.size()); - - const size_t index = backindex->index_; - - // Move the element in the last position to the removed element's position. - // The code here feels weird when back_element == element (i.e. when - // backindex->index_ == back_element_backindex->index_) but it works (I - // hope). - const T back_element = vector_.back(); - backindex_bag_index_t *const back_element_backindex - = access_backindex(back_element); - - rassert(back_element_backindex->index_ == vector_.size() - 1, - "bag %p: index %p has wrong value: index_ = %zu, size = %zu", - this, back_element_backindex, - back_element_backindex->index_, vector_.size()); - - back_element_backindex->index_ = index; - vector_[index] = back_element; - - vector_.pop_back(); - - backindex->index_ = backindex_bag_index_t::NOT_IN_A_BAG; - } - - // Adds the element to the bag. - void add(T element) { - backindex_bag_index_t *const backindex - = access_backindex(element); - guarantee(backindex->index_ == backindex_bag_index_t::NOT_IN_A_BAG, - "bag %p, backindex = %p", this, backindex); - - backindex->index_ = vector_.size(); - vector_.push_back(element); - } - - size_t size() const { - return vector_.size(); - } - - // Accesses an element by index. This is called "access random" because - // hopefully the index was randomly generated. There are no guarantees about the - // ordering of elements in this bag. - T access_random(size_t index) const { - rassert(index != backindex_bag_index_t::NOT_IN_A_BAG); - guarantee(index < vector_.size()); - return vector_[index]; - } - -private: - segmented_vector_t vector_; - - DISABLE_COPYING(backindex_bag_t); -}; - - - - -#endif // CONTAINERS_BACKINDEX_BAG_HPP_ diff --git a/src/containers/binary_blob.cc b/src/containers/binary_blob.cc deleted file mode 100644 index 5e40aab695c..00000000000 --- a/src/containers/binary_blob.cc +++ /dev/null @@ -1,15 +0,0 @@ -#include "containers/binary_blob.hpp" - -#include "containers/printf_buffer.hpp" -#include "containers/archive/stl_types.hpp" - -void debug_print(printf_buffer_t *buf, const binary_blob_t &blob) { - const uint8_t *data = static_cast(blob.data()); - const uint8_t *end = data + blob.size(); - - buf->appendf("bb{"); - for (const uint8_t *p = data; p != end; ++p) { - buf->appendf("%s%02X", (p == data ? "" : " "), *p); - } - buf->appendf("}"); -} diff --git a/src/containers/binary_blob.hpp b/src/containers/binary_blob.hpp deleted file mode 100644 index e89ac392dff..00000000000 --- a/src/containers/binary_blob.hpp +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CONTAINERS_BINARY_BLOB_HPP_ -#define CONTAINERS_BINARY_BLOB_HPP_ - -#include -#include - -#include - -#include "containers/archive/archive.hpp" -#include "rpc/serialize_macros.hpp" - -class printf_buffer_t; - -/* Binary blob that represents some unknown POD type */ -class binary_blob_t { -public: - binary_blob_t() { } - - template - explicit binary_blob_t(const obj_t &o) : storage(reinterpret_cast(&o), reinterpret_cast(&o + 1)) { } - - binary_blob_t(const uint8_t *_data, size_t _size) : storage(_data, _data + _size) { } - template - binary_blob_t(InputIterator begin, InputIterator end) : storage(begin, end) { } - - /* Constructor in static method form so we can use it as a functor */ - template - static binary_blob_t make(const obj_t &o) { - return binary_blob_t(o); - } - - size_t size() const { - return storage.size(); - } - - template - static const obj_t &get(const binary_blob_t &blob) { - guarantee(blob.size() == sizeof(obj_t), "blob.size() = %zu, sizeof(obj_t) = %zu", blob.size(), sizeof(obj_t)); - return *reinterpret_cast(blob.data()); - } - - void *data() { - return storage.data(); - } - - const void *data() const { - return storage.data(); - } - -private: - std::vector storage; -}; - -inline bool operator==(const binary_blob_t &left, const binary_blob_t &right) { - return left.size() == right.size() && memcmp(left.data(), right.data(), left.size()) == 0; -} - -inline bool operator!=(const binary_blob_t &left, const binary_blob_t &right) { - return !(left == right); -} - -void debug_print(printf_buffer_t *buf, const binary_blob_t &blob); - -#endif // CONTAINERS_BINARY_BLOB_HPP_ diff --git a/src/containers/buffer_group.cc b/src/containers/buffer_group.cc deleted file mode 100644 index 26eebbd0582..00000000000 --- a/src/containers/buffer_group.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "containers/buffer_group.hpp" -#include -#include - -void buffer_group_copy_data(const buffer_group_t *dest, const const_buffer_group_t *source) { - - // TODO: Is a buffer group size an int? - int64_t bytes = source->get_size(); - rassert(bytes == static_cast(dest->get_size())); - - /* Copy data between source and dest; we have to always copy the minimum of the sizes of the - next chunk that each one has */ - int64_t source_buf = 0, source_off = 0, dest_buf = 0, dest_off = 0; - while (bytes > 0) { - while (source->get_buffer(source_buf).size == source_off) { - source_buf++; - source_off = 0; - } - while (dest->get_buffer(dest_buf).size == dest_off) { - dest_buf++; - dest_off = 0; - } - int chunk = std::min( - source->get_buffer(source_buf).size - source_off, - dest->get_buffer(dest_buf).size - dest_off); - memcpy( - reinterpret_cast(dest->get_buffer(dest_buf).data) + dest_off, - reinterpret_cast(source->get_buffer(source_buf).data) + source_off, - chunk); - source_off += chunk; - dest_off += chunk; - bytes -= chunk; - } - - /* Make sure we reached the end of both source and dest */ - rassert((source_buf == static_cast(source->num_buffers()) && source_off == 0) || - (source_buf == static_cast(source->num_buffers()) - 1 && source_off == source->get_buffer(source_buf).size)); - rassert((dest_buf == static_cast(dest->num_buffers()) && dest_off == 0) || - (dest_buf == static_cast(dest->num_buffers()) - 1 && dest_off == dest->get_buffer(dest_buf).size)); -} - -void buffer_group_copy_data(const buffer_group_t *out, const char *in, int64_t size) { - buffer_group_t group; - group.add_buffer(size, in); - buffer_group_copy_data(out, const_view(&group)); -} diff --git a/src/containers/buffer_group.hpp b/src/containers/buffer_group.hpp deleted file mode 100644 index 0403db68d65..00000000000 --- a/src/containers/buffer_group.hpp +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONTAINERS_BUFFER_GROUP_HPP_ -#define CONTAINERS_BUFFER_GROUP_HPP_ - -#include -#include -#include -#include - -#include "errors.hpp" - -class const_buffer_group_t { -public: - struct buffer_t { - ssize_t size; - const void *data; - }; - - const_buffer_group_t() { } - - void add_buffer(size_t s, const void *d) { - buffer_t b; - b.size = s; - b.data = d; - buffers_.push_back(b); - } - - size_t num_buffers() const { return buffers_.size(); } - - buffer_t get_buffer(size_t i) const { - rassert(i < buffers_.size()); - return buffers_[i]; - } - - size_t get_size() const { - size_t s = 0; - for (size_t i = 0; i < buffers_.size(); ++i) { - s += buffers_[i].size; - } - return s; - } - -private: - std::vector buffers_; - DISABLE_COPYING(const_buffer_group_t); -}; - -class buffer_group_t { -public: - struct buffer_t { - ssize_t size; - void *data; - }; - - buffer_group_t() { } - - void add_buffer(size_t s, const void *d) { inner_.add_buffer(s, d); } - - size_t num_buffers() const { return inner_.num_buffers(); } - - buffer_t get_buffer(size_t i) const { - rassert(i < inner_.num_buffers()); - buffer_t ret; - const_buffer_group_t::buffer_t tmp = inner_.get_buffer(i); - ret.size = tmp.size; - ret.data = const_cast(tmp.data); - return ret; - } - - size_t get_size() const { return inner_.get_size(); } - - friend const const_buffer_group_t *const_view(const buffer_group_t *group); - -private: - const_buffer_group_t inner_; - DISABLE_COPYING(buffer_group_t); -}; - -inline const const_buffer_group_t *const_view(const buffer_group_t *group) { - return &group->inner_; -} - -/* Copies all the bytes from "in" to "out". "in" and "out" must be the same size. */ -void buffer_group_copy_data(const buffer_group_t *out, const const_buffer_group_t *in); - -void buffer_group_copy_data(const buffer_group_t *out, const char *in, int64_t size); - -#endif // CONTAINERS_BUFFER_GROUP_HPP_ diff --git a/src/containers/clone_ptr.hpp b/src/containers/clone_ptr.hpp deleted file mode 100644 index 44f97ced686..00000000000 --- a/src/containers/clone_ptr.hpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONTAINERS_CLONE_PTR_HPP_ -#define CONTAINERS_CLONE_PTR_HPP_ - -#include "containers/archive/archive.hpp" -#include "containers/scoped.hpp" -#include "rpc/serialize_macros.hpp" - -/* `clone_ptr_t` is a smart pointer that calls the `clone()` method on its -underlying object whenever the `clone_ptr_t`'s copy constructor is called. It's -primarily useful when you have a type that effectively acts like a piece of -data (i.e. it can be meaningfully copied) but it also has virtual methods. -Remember to declare `clone()` as a virtual method! */ - -template -class clone_ptr_t { -public: - clone_ptr_t(); - - /* Takes ownership of the argument. */ - explicit clone_ptr_t(T *p); - - // (We have noexcept specifiers on move operations in particular so that STL - // containers don't have to copy.) - clone_ptr_t(clone_ptr_t &&movee) noexcept : object(std::move(movee.object)) { } - - template - clone_ptr_t(clone_ptr_t &&movee) noexcept : object(std::move(movee.object)) { } - - clone_ptr_t(const clone_ptr_t &x); - template - clone_ptr_t(const clone_ptr_t &x); // NOLINT(runtime/explicit) - - clone_ptr_t &operator=(const clone_ptr_t &x); - clone_ptr_t &operator=(const clone_ptr_t &&x) noexcept; - - template - clone_ptr_t &operator=(const clone_ptr_t &x); - template - clone_ptr_t &operator=(const clone_ptr_t &&x) noexcept; - - - - T &operator*() const; - T *operator->() const; - T *get() const; - - bool has() const { - return object.has(); - } - -private: - template friend class clone_ptr_t; - - scoped_ptr_t object; -}; - -#include "containers/clone_ptr.tcc" - -#endif /* CONTAINERS_CLONE_PTR_HPP_ */ diff --git a/src/containers/clone_ptr.tcc b/src/containers/clone_ptr.tcc deleted file mode 100644 index fca0ca4f461..00000000000 --- a/src/containers/clone_ptr.tcc +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "containers/clone_ptr.hpp" - -template -clone_ptr_t::clone_ptr_t() : object(NULL) { } - -template -clone_ptr_t::clone_ptr_t(T *o) : object(o) { } - -template -clone_ptr_t::clone_ptr_t(const clone_ptr_t &x) { - if (x.object.has()) { - object.init(x.object->clone()); - } -} - -template template -clone_ptr_t::clone_ptr_t(const clone_ptr_t &x) { - if (x.object.has()) { - object.init(x.object->clone()); - } -} - -template -clone_ptr_t &clone_ptr_t::operator=(const clone_ptr_t &x) { - if (&x != this) { - object.reset(); - if (x.object.has()) { - object.init(x.object->clone()); - } - } - return *this; -} - -template -clone_ptr_t &clone_ptr_t::operator=(const clone_ptr_t &&x) noexcept { - clone_ptr_t tmp(std::move(x)); - object = std::move(tmp.object); - return *this; -} - -template template -clone_ptr_t &clone_ptr_t::operator=(const clone_ptr_t &x) { - if (&x != this) { - object.reset(); - if (x.object.has()) { - object.init(x.object->clone()); - } - } - return *this; -} - -template template -clone_ptr_t &clone_ptr_t::operator=(const clone_ptr_t &&x) noexcept { - clone_ptr_t tmp(std::move(x)); - object = std::move(tmp.object); - return *this; -} - -template -T &clone_ptr_t::operator*() const { - return *object.get(); -} - -template -T *clone_ptr_t::operator->() const { - return object.get(); -} - -template -T *clone_ptr_t::get() const { - return object.get(); -} diff --git a/src/containers/counted.hpp b/src/containers/counted.hpp deleted file mode 100644 index b0e2f7c2f10..00000000000 --- a/src/containers/counted.hpp +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONTAINERS_COUNTED_HPP_ -#define CONTAINERS_COUNTED_HPP_ - -#include -#include - -#include -#include - -#include "containers/scoped.hpp" -#include "errors.hpp" -#include "threading.hpp" - -// Yes, this is a clone of boost::intrusive_ptr. This will probably -// not be the case in the future. - -// Now it supports .unique(), and in order to use it, your type needs -// to provide an counted_use_count implementation. - -template -class counted_t { -public: - template - friend class counted_t; - - counted_t() : p_(nullptr) { } - explicit counted_t(T *p) : p_(p) { - if (p_ != nullptr) { counted_add_ref(p_); } - } - - explicit counted_t(scoped_ptr_t &&p) : p_(p.release()) { - if (p_ != nullptr) { counted_add_ref(p_); } - } - - counted_t(const counted_t ©ee) : p_(copyee.p_) { - if (p_ != nullptr) { counted_add_ref(p_); } - } - - template - counted_t(const counted_t ©ee) : p_(copyee.p_) { - if (p_ != nullptr) { counted_add_ref(p_); } - } - - counted_t(counted_t &&movee) noexcept : p_(movee.p_) { - movee.p_ = nullptr; - } - - template - counted_t(counted_t &&movee) noexcept : p_(movee.p_) { - movee.p_ = nullptr; - } - -// Avoid a spurious warning when building on Saucy. See issue #1674 -#if defined(__GNUC__) && (100 * __GNUC__ + __GNUC_MINOR__ >= 408) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#endif - - ~counted_t() { - if (p_) { counted_release(p_); } - } - -#if defined(__GNUC__) && (100 * __GNUC__ + __GNUC_MINOR__ >= 408) -#pragma GCC diagnostic pop -#endif - - void swap(counted_t &other) noexcept { - T *tmp = p_; - p_ = other.p_; - other.p_ = tmp; - } - - counted_t &operator=(counted_t &&other) noexcept { - counted_t tmp(std::move(other)); - swap(tmp); - return *this; - } - - template - counted_t &operator=(counted_t &&other) noexcept { - counted_t tmp(std::move(other)); - swap(tmp); - return *this; - } - - counted_t &operator=(const counted_t &other) { - counted_t tmp(other); - swap(tmp); - return *this; - } - - void reset() { - counted_t tmp; - swap(tmp); - } - - void reset(T *other) { - counted_t tmp(other); - swap(tmp); - } - - T *operator->() const { - return p_; - } - - T &operator*() const { - return *p_; - } - - T *get() const { - return p_; - } - - bool has() const { - return p_ != nullptr; - } - - bool unique() const { - return counted_use_count(p_) == 1; - } - - explicit operator bool() const { - return p_ != nullptr; - } - -private: - T *p_; -}; - -template -counted_t make_counted(Args &&... args) { - return counted_t(new T(std::forward(args)...)); -} - -template class single_threaded_countable_t; - -template -inline void counted_add_ref(const single_threaded_countable_t *p); -template -inline void counted_release(const single_threaded_countable_t *p); -template -inline intptr_t counted_use_count(const single_threaded_countable_t *p); - -template -class single_threaded_countable_t : private home_thread_mixin_debug_only_t { -public: - single_threaded_countable_t() : refcount_(0) { } - -protected: - counted_t counted_from_this() { - assert_thread(); - rassert(refcount_ > 0); - return counted_t(static_cast(this)); - } - - counted_t counted_from_this() const { - assert_thread(); - rassert(refcount_ > 0); - return counted_t(static_cast(this)); - } - - ~single_threaded_countable_t() { - assert_thread(); - rassert(refcount_ == 0); - } - -private: - friend void counted_add_ref(const single_threaded_countable_t *p); - friend void counted_release(const single_threaded_countable_t *p); - friend intptr_t counted_use_count(const single_threaded_countable_t *p); - - mutable intptr_t refcount_; - DISABLE_COPYING(single_threaded_countable_t); -}; - -template -inline void counted_add_ref(const single_threaded_countable_t *p) { - p->assert_thread(); - p->refcount_ += 1; - rassert(p->refcount_ > 0); -} - -template -inline void counted_release(const single_threaded_countable_t *p) { - p->assert_thread(); - rassert(p->refcount_ > 0); - --p->refcount_; - if (p->refcount_ == 0) { - delete static_cast(const_cast *>(p)); - } -} - -template -inline intptr_t counted_use_count(const single_threaded_countable_t *p) { - p->assert_thread(); - return p->refcount_; -} - -template class slow_atomic_countable_t; - -template -inline void counted_add_ref(const slow_atomic_countable_t *p); -template -inline void counted_release(const slow_atomic_countable_t *p); -template -inline intptr_t counted_use_count(const slow_atomic_countable_t *p); - -template -class slow_atomic_countable_t { -public: - slow_atomic_countable_t() : refcount_(0) { } - -protected: - ~slow_atomic_countable_t() { - rassert(refcount_ == 0); - } - - counted_t counted_from_this() { - rassert(counted_use_count(this) > 0); - return counted_t(static_cast(this)); - } - - counted_t counted_from_this() const { - rassert(counted_use_count(this) > 0); - return counted_t(static_cast(this)); - } - -private: - friend void counted_add_ref(const slow_atomic_countable_t *p); - friend void counted_release(const slow_atomic_countable_t *p); - friend intptr_t counted_use_count(const slow_atomic_countable_t *p); - - mutable std::atomic refcount_; - DISABLE_COPYING(slow_atomic_countable_t); -}; - -template -inline void counted_add_ref(const slow_atomic_countable_t *p) { - DEBUG_VAR intptr_t res = ++(p->refcount_); - rassert(res > 0); -} - -template -inline void counted_release(const slow_atomic_countable_t *p) { - intptr_t res = --(p->refcount_); - rassert(res >= 0); - if (res == 0) { - delete static_cast(const_cast *>(p)); - } -} - -template -inline intptr_t counted_use_count(const slow_atomic_countable_t *p) { - // Finally a practical use for volatile. - intptr_t tmp = static_cast(p->refcount_); - rassert(tmp > 0); - return tmp; -} - - -// A noncopyable reference to a reference-counted object. -template -class movable_t { -public: - explicit movable_t(const counted_t ©ee) : ptr_(copyee) { } - movable_t(movable_t &&movee) noexcept : ptr_(std::move(movee.ptr_)) { } - movable_t &operator=(movable_t &&movee) noexcept { - ptr_ = std::move(movee.ptr_); - return *this; - } - - void reset() { ptr_.reset(); } - - T *get() const { return ptr_.get(); } - T *operator->() const { return ptr_.get(); } - T &operator*() const { return *ptr_; } - - bool has() const { return ptr_.has(); } - -private: - counted_t ptr_; - DISABLE_COPYING(movable_t); -}; - -// Extends an arbitrary object with a slow_atomic_countable_t -template -class countable_wrapper_t : public T, - public slow_atomic_countable_t > { -public: - template - explicit countable_wrapper_t(Args &&... args) - : T(std::forward(args)...) { } -}; - -#endif // CONTAINERS_COUNTED_HPP_ diff --git a/src/containers/death_runner.hpp b/src/containers/death_runner.hpp deleted file mode 100644 index 52372f3b3ba..00000000000 --- a/src/containers/death_runner.hpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CONTAINERS_DEATH_RUNNER_HPP_ -#define CONTAINERS_DEATH_RUNNER_HPP_ - -#include - -/* `death_runner_t` runs an arbitrary function in its destructor */ -class death_runner_t { -public: - death_runner_t() { } - explicit death_runner_t(const std::function &f) : fun(f) { } - ~death_runner_t() { - if (fun) { - fun(); - } - } - std::function fun; -}; - - -#endif // CONTAINERS_DEATH_RUNNER_HPP_ diff --git a/src/containers/disk_backed_queue.cc b/src/containers/disk_backed_queue.cc deleted file mode 100644 index 19bde302fa9..00000000000 --- a/src/containers/disk_backed_queue.cc +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "containers/disk_backed_queue.hpp" - -#include "arch/io/disk.hpp" -#include "buffer_cache/alt.hpp" -#include "buffer_cache/blob.hpp" -#include "buffer_cache/cache_balancer.hpp" -#include "buffer_cache/serialize_onto_blob.hpp" -#include "serializer/log/log_serializer.hpp" - - -#define DBQ_MAX_REF_SIZE 251 - -internal_disk_backed_queue_t::internal_disk_backed_queue_t(io_backender_t *io_backender, - const serializer_filepath_t &filename, - perfmon_collection_t *stats_parent) - : perfmon_membership(stats_parent, &perfmon_collection, - filename.permanent_path().c_str()), - queue_size(0), - head_block_id(NULL_BLOCK_ID), - tail_block_id(NULL_BLOCK_ID), - file_opener(new filepath_file_opener_t(filename, io_backender)) { - log_serializer_t::create(file_opener.get(), - log_serializer_t::static_config_t()); - - serializer.init(new log_serializer_t(log_serializer_t::dynamic_config_t(), - file_opener.get(), - &perfmon_collection)); - - balancer.init(new dummy_cache_balancer_t(2 * MEGABYTE)); - cache.init(new cache_t(serializer.get(), balancer.get(), &perfmon_collection)); - cache_conn.init(new cache_conn_t(cache.get())); - // Emulate cache_t::create behavior by zeroing the block with id SUPERBLOCK_ID. - txn_t txn(cache_conn.get(), write_durability_t::HARD, 1); - { - buf_lock_t block(&txn, SUPERBLOCK_ID, alt_create_t::create); - buf_write_t write(&block); - const block_size_t block_size = cache->max_block_size(); - void *buf = write.get_data_write(block_size.value()); - memset(buf, 0, block_size.value()); - } - txn.commit(); -} - -internal_disk_backed_queue_t::~internal_disk_backed_queue_t() { - /* First destroy the serializer, then remove the temporary file. - This avoids issues with certain file systems (specifically VirtualBox - shared folders), see https://github.com/rethinkdb/rethinkdb/issues/3791. */ - cache_conn.reset(); - cache.reset(); - balancer.reset(); - serializer.reset(); - - file_opener->unlink_serializer_file(); -} - -void internal_disk_backed_queue_t::push(const write_message_t &wm) { - mutex_t::acq_t mutex_acq(&mutex); - - // There's no need for hard durability with an unlinked dbq file. - txn_t txn(cache_conn.get(), write_durability_t::SOFT, 2); - - push_single(&txn, wm); - - txn.commit(); -} - -void internal_disk_backed_queue_t::push(const scoped_array_t &wms) { - mutex_t::acq_t mutex_acq(&mutex); - - // There's no need for hard durability with an unlinked dbq file. - txn_t txn(cache_conn.get(), write_durability_t::SOFT, 2); - - for (size_t i = 0; i < wms.size(); ++i) { - push_single(&txn, wms[i]); - } - - txn.commit(); -} - -void internal_disk_backed_queue_t::push_single(txn_t *txn, const write_message_t &wm) { - if (head_block_id == NULL_BLOCK_ID) { - add_block_to_head(txn); - } - - auto _head = make_scoped(buf_parent_t(txn), head_block_id, - access_t::write); - auto write = make_scoped(_head.get()); - queue_block_t *head = static_cast(write->get_data_write()); - - char buffer[DBQ_MAX_REF_SIZE]; - memset(buffer, 0, DBQ_MAX_REF_SIZE); - - blob_t blob(cache->max_block_size(), buffer, DBQ_MAX_REF_SIZE); - - write_onto_blob(buf_parent_t(_head.get()), &blob, wm); - - if (static_cast((head->data + head->data_size) - reinterpret_cast(head)) + blob.refsize(cache->max_block_size()) > cache->max_block_size().value()) { - // The data won't fit in our current head block, so it's time to make a new one. - head = nullptr; - write.reset(); - _head.reset(); - add_block_to_head(txn); - _head.init(new buf_lock_t(buf_parent_t(txn), head_block_id, - access_t::write)); - write.init(new buf_write_t(_head.get())); - head = static_cast(write->get_data_write()); - } - - memcpy(head->data + head->data_size, buffer, - blob.refsize(cache->max_block_size())); - head->data_size += blob.refsize(cache->max_block_size()); - - queue_size++; -} - -void internal_disk_backed_queue_t::pop(buffer_group_viewer_t *viewer) { - guarantee(size() != 0); - mutex_t::acq_t mutex_acq(&mutex); - - char buffer[DBQ_MAX_REF_SIZE]; - // No need for hard durability with an unlinked dbq file. - txn_t txn(cache_conn.get(), write_durability_t::SOFT, 2); - - buf_lock_t _tail(buf_parent_t(&txn), tail_block_id, access_t::write); - - /* Grab the data from the blob and delete it. */ - { - buf_read_t read(&_tail); - const queue_block_t *tail - = static_cast(read.get_data_read()); - rassert(tail->data_size != tail->live_data_offset); - memcpy(buffer, tail->data + tail->live_data_offset, - blob::ref_size(cache->max_block_size(), - tail->data + tail->live_data_offset, - DBQ_MAX_REF_SIZE)); - } - - /* Grab the data from the blob and delete it. */ - - std::vector data_vec; - - blob_t blob(cache->max_block_size(), buffer, DBQ_MAX_REF_SIZE); - { - blob_acq_t acq_group; - buffer_group_t blob_group; - blob.expose_all(buf_parent_t(&_tail), access_t::read, - &blob_group, &acq_group); - - viewer->view_buffer_group(const_view(&blob_group)); - } - - int32_t data_size; - int32_t live_data_offset; - { - buf_write_t write(&_tail); - queue_block_t *tail = static_cast(write.get_data_write()); - /* Record how far along in the blob we are. */ - tail->live_data_offset += blob.refsize(cache->max_block_size()); - data_size = tail->data_size; - live_data_offset = tail->live_data_offset; - } - - blob.clear(buf_parent_t(&_tail)); - - queue_size--; - - _tail.reset_buf_lock(); - - /* If that was the last blob in this block move on to the next one. */ - if (live_data_offset == data_size) { - remove_block_from_tail(&txn); - } - - txn.commit(); -} - -bool internal_disk_backed_queue_t::empty() { - return queue_size == 0; -} - -int64_t internal_disk_backed_queue_t::size() { - return queue_size; -} - -void internal_disk_backed_queue_t::add_block_to_head(txn_t *txn) { - buf_lock_t _new_head(buf_parent_t(txn), alt_create_t::create); - buf_write_t write(&_new_head); - queue_block_t *new_head = static_cast(write.get_data_write()); - if (head_block_id == NULL_BLOCK_ID) { - rassert(tail_block_id == NULL_BLOCK_ID); - head_block_id = tail_block_id = _new_head.block_id(); - } else { - buf_lock_t _old_head(buf_parent_t(txn), head_block_id, - access_t::write); - buf_write_t old_write(&_old_head); - queue_block_t *old_head - = static_cast(old_write.get_data_write()); - rassert(old_head->next == NULL_BLOCK_ID); - old_head->next = _new_head.block_id(); - head_block_id = _new_head.block_id(); - } - - new_head->next = NULL_BLOCK_ID; - new_head->data_size = 0; - new_head->live_data_offset = 0; -} - -void internal_disk_backed_queue_t::remove_block_from_tail(txn_t *txn) { - rassert(tail_block_id != NULL_BLOCK_ID); - buf_lock_t _old_tail(buf_parent_t(txn), tail_block_id, - access_t::write); - - { - buf_write_t old_write(&_old_tail); - queue_block_t *old_tail = static_cast(old_write.get_data_write()); - - if (old_tail->next == NULL_BLOCK_ID) { - rassert(head_block_id == _old_tail.block_id()); - tail_block_id = head_block_id = NULL_BLOCK_ID; - } else { - tail_block_id = old_tail->next; - } - } - - _old_tail.mark_deleted(); -} - diff --git a/src/containers/disk_backed_queue.hpp b/src/containers/disk_backed_queue.hpp deleted file mode 100644 index 25bae04daf8..00000000000 --- a/src/containers/disk_backed_queue.hpp +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONTAINERS_DISK_BACKED_QUEUE_HPP_ -#define CONTAINERS_DISK_BACKED_QUEUE_HPP_ - -#include -#include - -#include "concurrency/fifo_checker.hpp" -#include "concurrency/mutex.hpp" -#include "containers/buffer_group.hpp" -#include "containers/archive/buffer_group_stream.hpp" -#include "containers/archive/vector_stream.hpp" -#include "containers/scoped.hpp" -#include "perfmon/core.hpp" -#include "serializer/types.hpp" - -class cache_balancer_t; -class cache_conn_t; -class cache_t; -class txn_t; -class io_backender_t; -class perfmon_collection_t; -class serializer_filepath_t; - -ATTR_PACKED(struct queue_block_t { - block_id_t next; - int32_t data_size, live_data_offset; - char data[0]; -}); - -class value_acquisition_object_t; - -class buffer_group_viewer_t { -public: - virtual void view_buffer_group(const const_buffer_group_t *group) = 0; - -protected: - buffer_group_viewer_t() { } - virtual ~buffer_group_viewer_t() { } - - DISABLE_COPYING(buffer_group_viewer_t); -}; - -class internal_disk_backed_queue_t { -public: - internal_disk_backed_queue_t(io_backender_t *io_backender, const serializer_filepath_t& filename, perfmon_collection_t *stats_parent); - ~internal_disk_backed_queue_t(); - - void push(const write_message_t &value); - void push(const scoped_array_t &values); - - void pop(buffer_group_viewer_t *viewer); - - bool empty(); - - int64_t size(); - -private: - void add_block_to_head(txn_t *txn); - void remove_block_from_tail(txn_t *txn); - void push_single(txn_t *txn, const write_message_t &value); - - mutex_t mutex; - - // Serves more as sanity-checking for the cache than this type's ordering. - order_source_t cache_order_source; - perfmon_collection_t perfmon_collection; - perfmon_membership_t perfmon_membership; - - int64_t queue_size; - - // The end we push onto. - block_id_t head_block_id; - // The end we pop from. - block_id_t tail_block_id; - - scoped_ptr_t file_opener; - scoped_ptr_t serializer; - scoped_ptr_t balancer; - scoped_ptr_t cache; - scoped_ptr_t cache_conn; - - DISABLE_COPYING(internal_disk_backed_queue_t); -}; - -template -class deserializing_viewer_t : public buffer_group_viewer_t { -public: - explicit deserializing_viewer_t(T *value_out) : value_out_(value_out) { } - virtual ~deserializing_viewer_t() { } - - virtual void view_buffer_group(const const_buffer_group_t *group) { - // TODO: We assume here that the data was serialized by _other_ code using - // LATEST -- some in disk_backed_queue_t::push, but also in btree_store.cc, - // which uses internal_disk_backed_queue_t directly. (There's no good reason - // for this today: it needed to be generic when that code was templatized on - // protocol_t.) - deserialize_from_group(group, value_out_); - } - -private: - T *value_out_; - - DISABLE_COPYING(deserializing_viewer_t); -}; - -// Copies the buffer group into a write_message_t -class copying_viewer_t : public buffer_group_viewer_t { -public: - explicit copying_viewer_t(write_message_t *wm_out) : wm_out_(wm_out) { } - ~copying_viewer_t() { } - - void view_buffer_group(const const_buffer_group_t *group) { - buffer_group_read_stream_t stream(group); - char buf[1024]; - while (!stream.entire_stream_consumed()) { - int64_t c = stream.read(&buf, 1024); - wm_out_->append(&buf, c); - } - } - -private: - write_message_t *wm_out_; - - DISABLE_COPYING(copying_viewer_t); -}; - -template -class disk_backed_queue_t { -public: - disk_backed_queue_t(io_backender_t *io_backender, const serializer_filepath_t& filename, perfmon_collection_t *stats_parent) - : internal_(io_backender, filename, stats_parent) { } - - void push(const T &t) { - // TODO: There's an unnecessary copying of data here (which would require a - // serialization_size overloaded function to be implemented in order to eliminate). - // TODO: We have such a serialization_size function. - write_message_t wm; - // Despite that we are serializing this *to disk* disk backed - // queues are not intended to persist across restarts, so this - // is safe. - serialize(&wm, t); - internal_.push(wm); - } - - void pop(T *out) { - deserializing_viewer_t viewer(out); - internal_.pop(&viewer); - } - - bool empty() { - return internal_.empty(); - } - - int64_t size() { - return internal_.size(); - } - -private: - internal_disk_backed_queue_t internal_; - DISABLE_COPYING(disk_backed_queue_t); -}; - -#endif /* CONTAINERS_DISK_BACKED_QUEUE_HPP_ */ diff --git a/src/containers/empty_value.hpp b/src/containers/empty_value.hpp deleted file mode 100644 index 0566455f1d3..00000000000 --- a/src/containers/empty_value.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CONTAINERS_EMPTY_VALUE_HPP_ -#define CONTAINERS_EMPTY_VALUE_HPP_ - -#include "rpc/serialize_macros.hpp" - -/* `empty_value_t` is a class with exactly one legal instance. It can be used as the -value type of a `watchable_map_t` in order to simulate a "watchable set". */ - -class empty_value_t { -public: - empty_value_t() { } - bool operator==(const empty_value_t &) const { - return true; - } - RDB_MAKE_ME_SERIALIZABLE_0(empty_value_t); -}; - -#endif /* CONTAINERS_EMPTY_VALUE_HPP_ */ - diff --git a/src/containers/half_intrusive_list.hpp b/src/containers/half_intrusive_list.hpp deleted file mode 100644 index 9be584c4b09..00000000000 --- a/src/containers/half_intrusive_list.hpp +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef CONTAINERS_HALF_INTRUSIVE_LIST_HPP_ -#define CONTAINERS_HALF_INTRUSIVE_LIST_HPP_ - -#include "errors.hpp" - -template -class half_intrusive_list_node_t; - -template -class half_intrusive_list_t; - -template -class half_intrusive_list_half_node_t { -public: - half_intrusive_list_half_node_t() : next_(nullptr) { } - ~half_intrusive_list_half_node_t() { - guarantee(next_ == nullptr, "non-detached intrusive list half-node destroyed"); - } - -protected: - friend class half_intrusive_list_t; - half_intrusive_list_node_t *next_; - - DISABLE_COPYING(half_intrusive_list_half_node_t); -}; - -template -class half_intrusive_list_node_t : private half_intrusive_list_half_node_t { -public: - half_intrusive_list_node_t() : prev_(nullptr) { } - ~half_intrusive_list_node_t() { - guarantee(prev_ == nullptr, - "non-detached half-intrusive-list node destroyed"); - } - - bool in_a_list() const { - return prev_ != nullptr; - } - -private: - friend class half_intrusive_list_t; - half_intrusive_list_half_node_t *prev_; - - DISABLE_COPYING(half_intrusive_list_node_t); -}; - - -template -class half_intrusive_list_t : private half_intrusive_list_half_node_t { -public: - half_intrusive_list_t() { } - ~half_intrusive_list_t() { } - - bool empty() const { - return this->next_ == nullptr; - } - - T *head() const { - rassert(this->next_ == nullptr || this->next_->prev_ == this); - return static_cast(this->next_); - } - - T *next(T *elem) const { - guarantee(elem->in_a_list()); - rassert(elem->next_ == nullptr || elem->next_->prev_ == elem); - rassert(elem->prev_->next_ == elem); - half_intrusive_list_node_t *p = elem; - return static_cast(p->next_); - } - - void push_front(T *elem) { - guarantee(!elem->in_a_list()); - elem->prev_ = this; - elem->next_ = this->next_; - if (this->next_ != nullptr) { - rassert(this->next_->prev_ == this); - this->next_->prev_ = elem; - } - this->next_ = elem; - } - - void remove(T *elem) { - guarantee(elem->in_a_list()); - if (elem->next_ != nullptr) { - elem->next_->prev_ = elem->prev_; - } - elem->prev_->next_ = elem->next_; - elem->next_ = nullptr; - elem->prev_ = nullptr; - } -}; - - -#endif // CONTAINERS_HALF_INTRUSIVE_LIST_HPP_ - diff --git a/src/containers/incremental_lenses.hpp b/src/containers/incremental_lenses.hpp deleted file mode 100644 index 1fd583645d8..00000000000 --- a/src/containers/incremental_lenses.hpp +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONTAINERS_INCREMENTAL_LENSES_HPP_ -#define CONTAINERS_INCREMENTAL_LENSES_HPP_ - -#include -#include -#include - -#include "containers/scoped.hpp" -#include "containers/uuid.hpp" - -/* `change_tracking_map_t` is like an `std::map`. However it keeps track - * of which keys have been modified. - * You can get access to the map's data through `get_inner()`. - * Before changing any of the values, you must call `begin_version()`. - * This resets the set of changed keys. It is therefore important that whatever - * processes the changes to the change_tracking_map is called once per call to - * begin_version. The subscription_t interface keeps track of the last processed - * version and detects cases where a version has been skipped, but - * this effectively degrades the change_tracking_map_t to a regular map if it - * happens frequently. - */ -template -class change_tracking_map_t { -public: - change_tracking_map_t() : current_version(0), uuid(generate_uuid()) { } - - // Write operations - void begin_version() { - changed_keys.clear(); - ++current_version; - } - void set_value(const key_type &key, const inner_type &value) { - rassert(current_version > 0, "You must call begin_version() before " - "performing any changes."); - changed_keys.insert(key); - inner[key] = value; - } - // Same as above but with move semantics - void set_value(const key_type &key, inner_type &&value) { - rassert(current_version > 0, "You must call begin_version() before " - "performing any changes."); - changed_keys.insert(key); - inner[key] = std::move(value); - } - void delete_value(const key_type &key) { - rassert(current_version > 0, "You must call begin_version() before " - "performing any changes."); - changed_keys.insert(key); - inner.erase(key); - } - void clear() { - rassert(current_version > 0, "You must call begin_version() before " - "performing any changes."); - for (auto it = inner.begin(); it != inner.end(); ++it) { - changed_keys.insert(it->first); - } - inner.clear(); - } - - // Getters - class subscription_t { - public: - bool is_valid(const change_tracking_map_t &source) { - if (source.get_uuid() != source_id) { - return false; - } - // Check if we have missed any changes. - const bool missed_version = - source.get_current_version() != at_version + 1; - return !missed_version; - } - const std::set &get_changed_keys( - const change_tracking_map_t &source) { - guarantee(is_valid(source)); - ++at_version; - return source.get_changed_keys(); - } - private: - friend class change_tracking_map_t; - DISABLE_COPYING(subscription_t); - explicit subscription_t(const change_tracking_map_t &_source) - : source_id(_source.get_uuid()), - at_version(_source.get_current_version()) { } - const uuid_u source_id; - unsigned int at_version; - }; - scoped_ptr_t subscribe() const { - return scoped_ptr_t(new subscription_t(*this)); - } - const std::map &get_inner() const { return inner; } - unsigned int get_current_version() const { return current_version; } - - // Operators - bool operator==(const change_tracking_map_t &o) const { - return o.current_version == current_version - && o.changed_keys == changed_keys - && o.inner == inner - && o.uuid == uuid; - } - bool operator!=(const change_tracking_map_t &o) const { - return !(o == *this); - } - -private: - // Use subscription_t to access this information in a safe way - const std::set &get_changed_keys() const { return changed_keys; } - uuid_u get_uuid() const { return uuid; } - - std::map inner; - std::set changed_keys; - unsigned int current_version; - uuid_u uuid; -}; - - -/* Given an `inner_lens`, the `incremental_map_lens_t` maintains a mapped - * version of a change_tracking_map, where each value v_out of the output is - * inner_lens(v_in). It tries to reduce computational costs by only applying - * inner_lens to values in the input map that are marked as changed. - * - * `incremental_map_lens_t` is designed for being used in the context of a - * `subview_watchable_t`. - */ -template -class incremental_map_lens_t { -public: - typedef typename std::result_of::type inner_result_type; - typedef change_tracking_map_t result_type; - - explicit incremental_map_lens_t(const callable_type &_inner_lens) : - inner_lens(_inner_lens) { - } - - // Copy operators - incremental_map_lens_t(const incremental_map_lens_t &_other) : - inner_lens(_other.inner_lens) /* Don't copy subscription */ { } - incremental_map_lens_t &operator=(const incremental_map_lens_t &_other) { - inner_lens = _other.inner_lens; - subscription.reset(); - return *this; - } - - bool operator()(const change_tracking_map_t &input, - result_type *current_out) { - guarantee(current_out != NULL); - - bool do_init = false; - if (!subscription.has() || !subscription->is_valid(input)) { - subscription = input.subscribe(); - do_init = true; - } - guarantee(current_out->get_current_version() != 0 || do_init); - - bool anything_changed = false; - - // Begin a new version - current_out->begin_version(); - - if (do_init) { - // Compute all values - current_out->clear(); - for (auto it = input.get_inner().begin(); it != input.get_inner().end(); ++it) { - current_out->set_value(it->first, inner_lens(it->second)); - } - anything_changed = true; - } else { - // Update changed values only - const std::set &changed_keys = - subscription->get_changed_keys(input); - for (auto it = changed_keys.begin(); it != changed_keys.end(); ++it) { - auto input_value_it = input.get_inner().find(*it); - auto existing_it = current_out->get_inner().find(*it); - if (input_value_it == input.get_inner().end()) { - // The value was deleted - if (existing_it != current_out->get_inner().end()) { - current_out->delete_value(*it); - anything_changed = true; - } - } else { - // This is to determine if the value has changed or not - inner_result_type old_value; - bool has_old_value = false; - if (existing_it == current_out->get_inner().end()) { - // New value - anything_changed = true; - } else if (!anything_changed) { - // Changed value. We must check for changes later. Keep a copy - // of the old value around. - // We can use move here because we are going to overwrite it anyways - old_value = std::move(existing_it->second); - has_old_value = true; - } - - // The value was added or changed, (re-)compute it. - current_out->set_value(*it, inner_lens(input_value_it->second)); - - if (has_old_value) { - if (!(old_value == current_out->get_inner().find(*it)->second)) { - anything_changed = true; - } - } - } - } - } - return anything_changed; - } - -private: - scoped_ptr_t::subscription_t> - subscription; - callable_type inner_lens; -}; - -#endif /* CONTAINERS_INCREMENTAL_LENSES_HPP_ */ diff --git a/src/containers/intrusive_list.hpp b/src/containers/intrusive_list.hpp deleted file mode 100644 index 8ec15c09a6b..00000000000 --- a/src/containers/intrusive_list.hpp +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CONTAINERS_INTRUSIVE_LIST_HPP_ -#define CONTAINERS_INTRUSIVE_LIST_HPP_ - -#include "errors.hpp" - -template class intrusive_list_t; - -template -class intrusive_list_node_t { -public: - bool in_a_list() const { - guarantee((next_ == nullptr) == (prev_ == nullptr)); - return prev_ != nullptr; - } - -protected: - intrusive_list_node_t() : prev_(nullptr), next_(nullptr) { } - ~intrusive_list_node_t() { - guarantee(prev_ == nullptr, - "non-detached intrusive list node destroyed"); - guarantee(next_ == nullptr, - "inconsistent intrusive list node!"); - } - - intrusive_list_node_t(intrusive_list_node_t &&movee) { - guarantee((movee.prev_ == nullptr) == (movee.next_ == nullptr)); - guarantee(movee.prev_ != &movee, - "Only intrusive_list_t can be a self-pointing node."); - prev_ = movee.prev_; - next_ = movee.next_; - if (prev_ != nullptr) { - prev_->next_ = this; - next_->prev_ = this; - } - movee.prev_ = nullptr; - movee.next_ = nullptr; - } - - // Don't implement this. _Maybe_ you won't fuck it up. Just use the - // move-constructor in subclasses' assignment operator implementations. - intrusive_list_node_t &operator=(intrusive_list_node_t &&movee) = delete; - -private: - friend class intrusive_list_t; - - intrusive_list_node_t *prev_; - intrusive_list_node_t *next_; - - DISABLE_COPYING(intrusive_list_node_t); -}; - -template -class intrusive_list_t : private intrusive_list_node_t { -public: - intrusive_list_t() : size_(0) { - this->prev_ = this; - this->next_ = this; - } - - intrusive_list_t(intrusive_list_t &&movee) : size_(0) { - // We just initialize ourselves to empty and then use append_and_clear. - this->prev_ = this; - this->next_ = this; - - append_and_clear(&movee); - } - - // We don't support generic assignment because non-empty intrusive lists may not - // be destroyed. You have to manually remove all elements of the intrusive list - // before destroying it. - void operator=(intrusive_list_t &&movee) = delete; - - ~intrusive_list_t() { - guarantee(this->prev_ == this, "non-empty intrusive list destroyed"); - guarantee(this->next_ == this, "inconsistent intrusive list (end node)!"); - guarantee(size_ == 0, "empty intrusive list destroyed with non-zero size"); - - // Set these to NULL to appease base class destructor's assertions. - this->prev_ = nullptr; - this->next_ = nullptr; - } - - bool empty() const { - return this->prev_ == this; - } - - T *head() const { - return null_if_self(this->next_); - } - - T *tail() const { - return null_if_self(this->prev_); - } - - T *next(T *elem) const { - return null_if_self(elem->next_); - } - - T *prev(T *elem) const { - return null_if_self(elem->prev_); - } - - void push_front(T *node) { - insert_between(node, this, this->next_); - ++size_; - } - - void push_back(T *node) { - insert_between(node, this->prev_, this); - ++size_; - } - - void remove(T *value) { - intrusive_list_node_t *node = value; - guarantee(node->in_a_list()); - remove_node(value); - --size_; - } - - void pop_front() { - guarantee(!empty()); - remove_node(static_cast(this->next_)); - --size_; - } - - void pop_back() { - guarantee(!empty()); - remove_node(static_cast(this->prev_)); - --size_; - } - - size_t size() const { - return size_; - } - - void append_and_clear(intrusive_list_t *appendee) { - if (!appendee->empty()) { - intrusive_list_node_t *t = this->prev_; - t->next_ = appendee->next_; - appendee->next_->prev_ = t; - this->prev_ = appendee->prev_; - appendee->prev_->next_ = this; - - appendee->next_ = appendee; - appendee->prev_ = appendee; - - size_ += appendee->size_; - appendee->size_ = 0; - } - } - -#ifndef NDEBUG - void validate() const { - const intrusive_list_node_t *node = this; - size_t count = 0; - do { - ++count; - guarantee(node->prev_ != NULL, "count = %zu", count); - guarantee(node->prev_->next_ == node); - guarantee(node->next_ != NULL); - guarantee(node->next_->prev_ == node); - node = node->next_; - } while (node != this); - guarantee(count == size_ + 1); - } -#endif // NDEBUG - -private: - static void insert_between(T *item, - intrusive_list_node_t *before, - intrusive_list_node_t *after) { - intrusive_list_node_t *node = item; - guarantee(!node->in_a_list()); - guarantee(before != nullptr); - guarantee(before->in_a_list()); - guarantee(after != nullptr); - guarantee(after->in_a_list()); - before->next_ = node; - after->prev_ = node; - node->prev_ = before; - node->next_ = after; - } - - static void remove_node(T *item) { - intrusive_list_node_t *node = item; - guarantee(node->in_a_list()); - node->prev_->next_ = node->next_; - node->next_->prev_ = node->prev_; - node->prev_ = nullptr; - node->next_ = nullptr; - } - - T *null_if_self(intrusive_list_node_t *node) const { - return node == this ? nullptr : static_cast(node); - } - - size_t size_; - - DISABLE_COPYING(intrusive_list_t); -}; - - -#endif // CONTAINERS_INTRUSIVE_LIST_HPP_ diff --git a/src/containers/intrusive_priority_queue.hpp b/src/containers/intrusive_priority_queue.hpp deleted file mode 100644 index 1c7cac79448..00000000000 --- a/src/containers/intrusive_priority_queue.hpp +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CONTAINERS_INTRUSIVE_PRIORITY_QUEUE_HPP_ -#define CONTAINERS_INTRUSIVE_PRIORITY_QUEUE_HPP_ - -#include -#include - -#include "errors.hpp" - -// TODO: Make this type generally not be awful: Make intrusive_priority_queue_node_t not have -// virtual functions, make it not use an O(n) vector. - -template -class intrusive_priority_queue_t; - -template -class intrusive_priority_queue_node_t { -public: - intrusive_priority_queue_node_t() -#ifndef NDEBUG - : queue(NULL) -#endif - { } - -protected: - ~intrusive_priority_queue_node_t() { - rassert(queue == NULL); - } - -private: - friend class intrusive_priority_queue_t; -#ifndef NDEBUG - intrusive_priority_queue_t *queue; -#endif - size_t index; - - DISABLE_COPYING(intrusive_priority_queue_node_t); -}; - -template -class intrusive_priority_queue_t { -public: - intrusive_priority_queue_t() { } - - ~intrusive_priority_queue_t() { - rassert(empty()); - } - - bool empty() { - return nodes.empty(); - } - - size_t size() { - return nodes.size(); - } - - void push(node_t *x) { - rassert(x != NULL); - rassert(node_queue(x) == NULL); - DEBUG_ONLY_CODE(node_queue(x) = this); - - nodes.push_back(x); - node_index(x) = nodes.size() - 1; - bubble_towards_root(x); - } - - void remove(node_t *x) { - rassert(x); - rassert(node_queue(x) == this); - DEBUG_ONLY_CODE(node_queue(x) = NULL); - if (node_index(x) == nodes.size() - 1) { - nodes.pop_back(); - } else { - node_t *replacement = nodes[node_index(x)] = nodes.back(); - node_index(replacement) = node_index(x); - nodes.pop_back(); - bubble_towards_root(replacement); - bubble_towards_leaves(replacement); - } - } - - node_t *peek() { - if (nodes.empty()) { - return nullptr; - } else { - return nodes.front(); - } - } - - node_t *pop() { - if (nodes.empty()) { - return nullptr; - } else { - node_t *x = nodes.front(); - rassert(node_queue(x) == this); - DEBUG_ONLY_CODE(x->queue = NULL); - if (nodes.size() == 1) { - nodes.pop_back(); - } else { - node_t *replacement = nodes[0] = nodes.back(); - node_index(replacement) = 0; - nodes.pop_back(); - bubble_towards_leaves(replacement); - } - return x; - } - } - - void update(node_t *node) { - rassert(node); - bubble_towards_root(node); - bubble_towards_leaves(node); - } - - void swap_in_place(node_t *to_remove, node_t *to_insert) { - rassert(to_remove); - rassert(to_insert); - rassert(node_queue(to_remove) == this); - rassert(node_queue(to_insert) == NULL); - // Pass const pointers to one call of left_is_higher_priority to enforce that it be const. - rassert(!left_is_higher_priority(const_cast(to_remove), const_cast(to_insert))); - rassert(!left_is_higher_priority(to_insert, to_remove)); - DEBUG_ONLY_CODE(node_queue(to_insert) = this); - node_index(to_insert) = node_index(to_remove); - nodes[node_index(to_remove)] = to_insert; - DEBUG_ONLY_CODE(node_queue(to_remove) = NULL); - } - -private: - static size_t& node_index(node_t *x) { - intrusive_priority_queue_node_t *node = x; - return node->index; - } - - static intrusive_priority_queue_t *& node_queue(node_t *x) { - intrusive_priority_queue_node_t *node = x; - return node->queue; - } - - static size_t parent_index(size_t index) { - rassert(index != 0); - return (index + (index % 2) - 2) / 2; - } - - static size_t left_child_index(size_t index) { - return index * 2 + 1; - } - - static size_t right_child_index(size_t index) { - return index * 2 + 2; - } - - void swap_nodes(size_t i, size_t j) { - node_index(nodes[i]) = j; - node_index(nodes[j]) = i; - std::swap(nodes[i], nodes[j]); - } - - void bubble_towards_root(node_t *node) { - while (node_index(node) != 0 && - left_is_higher_priority(node, nodes[parent_index(node_index(node))])) { - swap_nodes(node_index(node), parent_index(node_index(node))); - } - } - - void bubble_towards_leaves(node_t *node) { - while (true) { - const size_t left_index = left_child_index(node_index(node)); - const size_t right_index = right_child_index(node_index(node)); - size_t winner = node_index(node); - if (left_index < nodes.size() && - left_is_higher_priority(nodes[left_index], nodes[winner])) { - winner = left_index; - } - if (right_index < nodes.size() && - left_is_higher_priority(nodes[right_index], nodes[winner])) { - winner = right_index; - } - if (winner == node->index) { - break; - } else { - swap_nodes(winner, node->index); - } - } - } - - // TODO: Vectors are O(n). - std::vector nodes; - - DISABLE_COPYING(intrusive_priority_queue_t); -}; - -#endif // CONTAINERS_INTRUSIVE_PRIORITY_QUEUE_HPP_ diff --git a/src/containers/lazy_erase_vector.hpp b/src/containers/lazy_erase_vector.hpp deleted file mode 100644 index 8781f7b1ed7..00000000000 --- a/src/containers/lazy_erase_vector.hpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CONTAINERS_LAZY_ERASE_VECTOR_HPP_ -#define CONTAINERS_LAZY_ERASE_VECTOR_HPP_ - -#include - -/* lazy_erase_vector_t is like an std::vector, except that it offers an erase_front() -operation that batches small deletions together to be a bit more efficient. - -It currently doesn't implement the full std::vector interface, but feel free -to extend if if you need some additional functionality. */ - -template -class lazy_erase_vector_t { -public: - explicit lazy_erase_vector_t(size_t erase_threshold) - : erased_offset_(0), erase_threshold_(erase_threshold) { } - - size_t size() const { - return vec_.size() - erased_offset_; - } - - type_t *data() { - return vec_.data() + erased_offset_; - } - - const type_t *data() const { - return vec_.data() + erased_offset_; - } - - void resize(size_t new_size) { - vec_.resize(new_size + erased_offset_); - } - - void erase_front(size_t num_elems) { - erased_offset_ += num_elems; - if (erased_offset_ >= erase_threshold_) { - vec_.erase(vec_.begin(), vec_.begin() + erased_offset_); - erased_offset_ = 0; - } - } - -private: - std::vector vec_; - // This many elements have been erased from the front of `vec` - size_t erased_offset_; - // Actually erase from vec_ once erased_offset_ becomes larger than this. - size_t erase_threshold_; -}; - -#endif // CONTAINERS_LAZY_ERASE_VECTOR_HPP_ diff --git a/src/containers/lifetime.hpp b/src/containers/lifetime.hpp deleted file mode 100644 index 44328347acc..00000000000 --- a/src/containers/lifetime.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CONTAINERS_LIFETIME_HPP_ -#define CONTAINERS_LIFETIME_HPP_ - -#include - -template -class lifetime_t { -public: - // `std::add_const::type` is `T * const` instead of the `T const *` we need, and - // it will return the original type for a reference. To work around this we strip the - // type down to it's bare bones, add the const, and re-add the pointer or reference - // depending on the original type. - using const_type = typename std::conditional< - std::is_lvalue_reference::value, - typename std::add_lvalue_reference< - typename std::add_const< - typename std::remove_reference::type - >::type - >::type, - typename std::add_pointer< - typename std::add_const< - typename std::remove_pointer::type - >::type - >::type - >::type; - - explicit lifetime_t(T value) : value_(value) { - static_assert( - std::is_lvalue_reference::value || std::is_pointer::value, - "T should be an lvalue reference or pointer"); - } - - // This allows a non-const to const conversion - operator lifetime_t::const_type>() { - return lifetime_t(value_); - } - - operator T() { - return value_; - } - -private: - T value_; -}; - -template -lifetime_t make_lifetime(T &&value) { - static_assert( - std::is_lvalue_reference::value || std::is_pointer::value, - "T should be an lvalue reference or pointer"); - - return lifetime_t(std::forward(value)); -} - -#endif /* CONTAINERS_LIFETIME_HPP_ */ - diff --git a/src/containers/lru_cache.hpp b/src/containers/lru_cache.hpp deleted file mode 100644 index 4a5b7e83577..00000000000 --- a/src/containers/lru_cache.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef CONTAINERS_LRU_CACHE_HPP_ -#define CONTAINERS_LRU_CACHE_HPP_ - -#include -#include - -#include "errors.hpp" - -template -class lru_cache_t { -public: - explicit lru_cache_t(size_t max_size) : list_(), map_(), max_size_(max_size) { - guarantee(max_size > 0); - } - - size_t max_size() const { return max_size_; } - size_t size() const { return map_.size(); } - - // Looks up a key, sets *out to point at its value, and marks the entry 'most - // recently used'. Returns false if the key is not found. - bool lookup(const K &key, V **out) { - auto it = map_.find(key); - if (it != map_.end()) { - list_iterator list_iter = it->second; - list_.splice(list_.end(), list_, list_iter); - *out = &list_iter->second; - return true; - } else { - *out = nullptr; - return false; - } - } - - // Returns true if an insertion was performed. (Does nothing, doesn't even update - // LRU, if no insertion was performed.) - bool insert(K key, V value) { - auto it = map_.find(key); - if (it != map_.end()) { - return false; - } else { - if (map_.size() == max_size_) { - list_iterator evictee = list_.begin(); - DEBUG_VAR size_t count = map_.erase(evictee->first); - rassert(count == 1); - list_.pop_front(); - } - - list_.push_back(std::make_pair(key, std::move(value))); - list_iterator list_iter = list_.end(); - --list_iter; - DEBUG_VAR auto res = map_.emplace(std::move(key), list_iter); - rassert(res.second); - return true; - } - } - -private: - using list_iterator = typename std::list>::iterator; - - // Elements are pushed onto the back and popped (when evicted) off the front. - std::list> list_; - std::unordered_map map_; - - size_t max_size_; - - DISABLE_COPYING(lru_cache_t); -}; - -#endif // CONTAINERS_LRU_CACHE_HPP_ - diff --git a/src/containers/map_sentries.hpp b/src/containers/map_sentries.hpp deleted file mode 100644 index 9583f22bd21..00000000000 --- a/src/containers/map_sentries.hpp +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONTAINERS_MAP_SENTRIES_HPP_ -#define CONTAINERS_MAP_SENTRIES_HPP_ - -#include -#include -#include - -/* `map_insertion_sentry_t` inserts a value into a map on construction, and -removes it in the destructor. */ -template -class map_insertion_sentry_t { -public: - map_insertion_sentry_t() : map(nullptr) { } - map_insertion_sentry_t(std::map *m, - const key_t &key, - const value_t &value) - : map(nullptr) { - reset(m, key, value); - } - - // For `std::vector.emplace_back` this type needs to be movable, for which we need - // to implement our own move constructor and move assignment operator since this type - // has a user-defined destructor. As we set `rhs.map` to `nullptr` we must also be - // careful of self-assignment (checking `this != &rhs`). - map_insertion_sentry_t(map_insertion_sentry_t && rhs) - : map(rhs.map), - it(rhs.it) { - rhs.map = nullptr; - } - map_insertion_sentry_t& operator=(map_insertion_sentry_t && rhs) { - if (this != &rhs) { - map = rhs.map; - it = rhs.it; - - rhs.map = nullptr; - } - return *this; - } - - ~map_insertion_sentry_t() { - reset(); - } - map_insertion_sentry_t(const map_insertion_sentry_t &) = delete; - map_insertion_sentry_t &operator=(const map_insertion_sentry_t &) = delete; - - const key_t &get_key() { - guarantee(map != nullptr); - return it->first; - } - - value_t *get_value() { - guarantee(map != nullptr); - return &it->second; - } - - void reset() { - if (map != nullptr) { - map->erase(it); - map = nullptr; - } - } - - void reset(std::map *m, const key_t &key, const value_t &value) { - reset(); - map = m; - std::pair::iterator, bool> iterator_and_is_new = - map->insert(std::make_pair(key, value)); - guarantee(iterator_and_is_new.second, "value to be inserted already " - "exists. don't do that."); - it = iterator_and_is_new.first; - } - -private: - std::map *map; - typename std::map::iterator it; -}; - -/* `multimap_insertion_sentry_t` inserts a value into a multimap on -construction, and removes it in the destructor. */ -template -class multimap_insertion_sentry_t { -public: - multimap_insertion_sentry_t() : map(nullptr) { } - multimap_insertion_sentry_t(std::multimap *m, - const key_t &key, - const value_t &value) - : map(nullptr) { - reset(m, key, value); - } - - // See above. - multimap_insertion_sentry_t(multimap_insertion_sentry_t && rhs) - : map(rhs.map), - it(rhs.it) { - rhs.map = nullptr; - } - multimap_insertion_sentry_t& operator=(multimap_insertion_sentry_t && rhs) { - if (this != &rhs) { - map = rhs.map; - it = rhs.it; - - rhs.map = nullptr; - } - return *this; - } - - ~multimap_insertion_sentry_t() { - reset(); - } - multimap_insertion_sentry_t(const multimap_insertion_sentry_t &) = delete; - multimap_insertion_sentry_t &operator=(const multimap_insertion_sentry_t &) = delete; - - void reset() { - if (map != nullptr) { - map->erase(it); - map = nullptr; - } - } - - void reset(std::multimap *m, const key_t &key, const value_t &value) { - reset(); - map = m; - it = map->insert(std::make_pair(key, value)); - } - -private: - std::multimap *map; - typename std::multimap::iterator it; -}; - -/* `set_insertion_sentry_t` inserts a value into a `std::set` on construction, and -removes it in the destructor. */ -template -class set_insertion_sentry_t { -public: - set_insertion_sentry_t() : set(NULL) { } - set_insertion_sentry_t(std::set *s, const obj_t &obj) : set(nullptr) { - reset(s, obj); - } - ~set_insertion_sentry_t() { - reset(); - } - set_insertion_sentry_t(const set_insertion_sentry_t &) = delete; - set_insertion_sentry_t &operator=(const set_insertion_sentry_t &) = delete; - void reset() { - if (set) { - set->erase(it); - set = nullptr; - } - } - void reset(std::set *s, const obj_t &obj) { - reset(); - set = s; - auto pair = set->insert(obj); - guarantee(pair.second, "value to be inserted already exists"); - it = pair.first; - } -private: - std::set *set; - typename std::set::iterator it; -}; - -#endif // CONTAINERS_MAP_SENTRIES_HPP_ diff --git a/src/containers/name_string.cc b/src/containers/name_string.cc deleted file mode 100644 index 85fd3a082d2..00000000000 --- a/src/containers/name_string.cc +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "containers/name_string.hpp" - -#include - -#include "containers/archive/stl_types.hpp" -#include "debug.hpp" -#include "rdb_protocol/datum_string.hpp" - -const char *const name_string_t::valid_char_msg = "Use A-Z, a-z, 0-9, _ and - only"; - -bool is_acceptable_name_character(int ch) { - return isalpha(ch) || isdigit(ch) || ch == '_' || ch == '-'; -} - -name_string_t::name_string_t() { } - -bool name_string_t::assign_value(const std::string &s) { - if (s.empty()) { - return false; - } - - for (size_t i = 0; i < s.size(); ++i) { - if (!is_acceptable_name_character(s[i])) { - return false; - } - } - - str_ = s; - return true; -} - -bool name_string_t::assign_value(const datum_string_t &s) { - if (s.size() == 0) { - return false; - } - - for (size_t i = 0; i < s.size(); ++i) { - if (!is_acceptable_name_character(s.data()[i])) { - return false; - } - } - - str_ = s.to_std(); - return true; -} - -name_string_t name_string_t::guarantee_valid(const char *name) { - name_string_t string; - bool ok = string.assign_value(name); - guarantee(ok, "The name `%s` is not valid.", name); - return string; -} - -RDB_IMPL_SERIALIZABLE_1_SINCE_v1_13(name_string_t, str_); - -void debug_print(printf_buffer_t *buf, const name_string_t& s) { - debug_print(buf, s.str()); -} diff --git a/src/containers/name_string.hpp b/src/containers/name_string.hpp deleted file mode 100644 index 30103b00fb2..00000000000 --- a/src/containers/name_string.hpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CONTAINERS_NAME_STRING_HPP_ -#define CONTAINERS_NAME_STRING_HPP_ - -#include - -#include "rpc/serialize_macros.hpp" - -class datum_string_t; - -// The kind of string that can only contain either the empty string or acceptable names for -// things. You may only assign non-empty strings. - -class name_string_t { -public: - // Initializes to the empty string. - name_string_t(); - - // Succeeds on valid non-empty strings. - MUST_USE bool assign_value(const std::string &s); - MUST_USE bool assign_value(const datum_string_t &s); - - // Meant for use with hard-coded names. - static name_string_t guarantee_valid(const char *name); - - const std::string& str() const { return str_; } - - // TODO: We should get rid of this, and code that uses this is known to be untrustworthy. - bool empty() const { return str_.empty(); } - const char *c_str() const { return str_.c_str(); } - - RDB_DECLARE_ME_SERIALIZABLE(name_string_t); - - static const char *const valid_char_msg; - -private: - std::string str_; -}; - -inline bool operator==(const name_string_t& x, const name_string_t& y) { - return x.str() == y.str(); -} - -inline bool operator!=(const name_string_t& x, const name_string_t& y) { - return !(x == y); -} - -inline bool operator<(const name_string_t& x, const name_string_t& y) { - return x.str() < y.str(); -} - -void debug_print(printf_buffer_t *buf, const name_string_t& s); - -#endif // CONTAINERS_NAME_STRING_HPP_ diff --git a/src/containers/object_buffer.hpp b/src/containers/object_buffer.hpp deleted file mode 100644 index 92e887ac409..00000000000 --- a/src/containers/object_buffer.hpp +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONTAINERS_OBJECT_BUFFER_HPP_ -#define CONTAINERS_OBJECT_BUFFER_HPP_ - -#include -#include - -#include "errors.hpp" -#include "arch/compiler.hpp" - -// Caveat: do not use this template with an object that has a blocking destructor, if -// you are going to allocate multiple times using a single object_buffer_t. This object -// should catch it if you try to do anything particularly stupid, though. -template -class object_buffer_t { -public: - class destruction_sentinel_t { - public: - explicit destruction_sentinel_t(object_buffer_t *_parent) : parent(_parent) { } - - ~destruction_sentinel_t() { - if (parent->has()) { - parent->reset(); - } - } - private: - object_buffer_t *parent; - - DISABLE_COPYING(destruction_sentinel_t); - }; - - object_buffer_t() : state(EMPTY) { } - - ~object_buffer_t() { - // The buffer cannot be destroyed while an object is in the middle of - // constructing or destructing - if (state == INSTANTIATED) { - reset(); - } else { - rassert(state == EMPTY); - } - } - - template - T *create(Args &&... args) { - rassert(state == EMPTY, "state is %s", state_string()); - state = CONSTRUCTING; - try { - new (&object_data[0]) T(std::forward(args)...); - } catch (...) { - state = EMPTY; - throw; - } - state = INSTANTIATED; - return get(); - } - - T *get() { - rassert(state == INSTANTIATED); - return reinterpret_cast(&object_data[0]); - } - - T *operator->() { - return get(); - } - - const T *get() const { - rassert(state == INSTANTIATED); - return reinterpret_cast(&object_data[0]); - } - - const T *operator->() const { - return get(); - } - - void reset() { - guarantee(state == INSTANTIATED || state == EMPTY); - if (state == INSTANTIATED) { - T *obj_ptr = get(); - state = DESTRUCTING; - obj_ptr->~T(); - state = EMPTY; - } - } - - bool has() const { - return (state == INSTANTIATED); - } - -private: - // Force alignment of the data to the alignment of the templatized type, - // this avoids some optimization errors, see github issue #3300 for an example. - ATTR_ALIGNED(alignof(T)) char object_data[sizeof(T)]; - - enum buffer_state_t { - EMPTY, - CONSTRUCTING, - INSTANTIATED, - DESTRUCTING - } state; - - const char *state_string() { - switch (state) { - case buffer_state_t::EMPTY: return "EMPTY"; - case buffer_state_t::CONSTRUCTING: return "CONSTRUCTING"; - case buffer_state_t::INSTANTIATED: return "INSTANTIATED"; - case buffer_state_t::DESTRUCTING: return "DESTRUCTING"; - default: return "corrupted!"; - } - } - - DISABLE_COPYING(object_buffer_t); -}; - -#endif // CONTAINERS_OBJECT_BUFFER_HPP_ diff --git a/src/containers/optional.hpp b/src/containers/optional.hpp deleted file mode 100644 index 50e9a3d74e3..00000000000 --- a/src/containers/optional.hpp +++ /dev/null @@ -1,183 +0,0 @@ -#ifndef CONTAINERS_OPTIONAL_HPP_ -#define CONTAINERS_OPTIONAL_HPP_ - -#include - -#include "errors.hpp" -#include "containers/printf_buffer.hpp" - -struct r_nullopt_t { }; -static constexpr r_nullopt_t r_nullopt{}; - -template -class optional { -public: - optional() noexcept : has_value_(false) { } - optional(r_nullopt_t) noexcept : has_value_(false) { } // NOLINT(runtime/explicit) - explicit optional(const T &x) noexcept(noexcept(T(std::declval()))) - : has_value_(true) { - new (&value_) T(x); - } - explicit optional(T &&x) noexcept(noexcept(T(std::move(std::declval())))) - : has_value_(true) { - new (&value_) T(std::move(x)); - } - - ~optional() { - if (has_value_) { - value_.~T(); - } - } - - optional(const optional &c) noexcept(std::is_nothrow_copy_constructible::value) - : has_value_(c.has_value_) { - if (c.has_value_) { - new (&value_) T(c.value_); - } - } - - optional(optional &&c) noexcept(std::is_nothrow_move_constructible::value) - : has_value_(c.has_value_) { - if (c.has_value_) { - new (&value_) T(std::move(c.value_)); - } - } - - void operator=(const optional &rhs) noexcept(std::is_nothrow_copy_assignable::value && std::is_nothrow_copy_constructible::value) { - if (rhs.has_value_) { - set(rhs.value_); - } else { - reset(); - } - } - - void operator=(optional &&rhs) noexcept(std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value) { - if (rhs.has_value_) { - set(std::move(rhs.value_)); - } else { - reset(); - } - } - - bool has_value() const noexcept { - return has_value_; - } - - template - void set(U &&rhs) { - if (has_value_) { - value_ = std::forward(rhs); - } else { - new (&value_) T(std::forward(rhs)); - has_value_ = true; - } - } - - // These are called get(), like boost's optional, unlike C++17's - // optional, which uses value(). The reason is, these check with - // a guarantee and abort, instead of throwing an exception. - T &get() { - guarantee(has_value_); - return value_; - } - - const T &get() const { - guarantee(has_value_); - return value_; - } - - template - T value_or(U &&default_value) const { - if (has_value_) { - return value_; - } else { - return std::forward(default_value); - } - } - - T &operator*() noexcept { - return value_; - } - - const T &operator*() const noexcept { - return value_; - } - - const T *operator->() const noexcept { - return &value_; - } - T *operator->() noexcept { - return &value_; - } - - void reset() noexcept { - if (has_value_) { - has_value_ = false; - value_.~T(); - } - } - - bool operator<(const optional &rhs) const { - if (has_value_) { - if (rhs.has_value_) { - return value_ < rhs.value_; - } else { - return false; - } - } else { - return rhs.has_value_; - } - } - - bool operator==(const optional &rhs) const { - if (has_value_) { - if (rhs.has_value_) { - return value_ == rhs.value_; - } else { - return false; - } - } else { - return !rhs.has_value_; - } - } - - // We can't write !(*this == rhs) because this is a generic type - // that could wrap a floating point comparison. - bool operator!=(const optional &rhs) const { - if (has_value_) { - if (rhs.has_value_) { - return value_ != rhs.value_; - } else { - return true; - } - } else { - return rhs.has_value_; - } - } - - explicit operator bool() const noexcept { - return has_value(); - } - -private: - bool has_value_; - union { T value_; }; -}; - -template -optional::type> make_optional(T &&x) { - return optional::type>(std::forward(x)); -} - -template -void debug_print(printf_buffer_t *buf, const optional &x) { - if (x.has_value()) { - buf->appendf("opt{"); - debug_print(buf, *x); - buf->appendf("}"); - } else { - buf->appendf("none"); - } -} - -#endif // CONTAINERS_OPTIONAL_HPP_ diff --git a/src/containers/printf_buffer.cc b/src/containers/printf_buffer.cc deleted file mode 100644 index c39de0b5c65..00000000000 --- a/src/containers/printf_buffer.cc +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "containers/printf_buffer.hpp" -#include "math.hpp" - -printf_buffer_t::printf_buffer_t() : length_(0), ptr_(data_) { - data_[0] = 0; -} - -printf_buffer_t::printf_buffer_t(const char *format, ...) : length_(0), ptr_(data_) { - data_[0] = 0; - - va_list ap; - va_start(ap, format); - - vappendf(format, ap); - - va_end(ap); -} - -printf_buffer_t::printf_buffer_t(va_list ap, const char *format) : length_(0), ptr_(data_) { - data_[0] = 0; - - vappendf(format, ap); -} - -printf_buffer_t::~printf_buffer_t() { - if (ptr_ != data_) { - delete[] ptr_; - } -} - -void printf_buffer_t::appendf(const char *format, ...) { - va_list ap; - va_start(ap, format); - - vappendf(format, ap); - - va_end(ap); -} - -ATTR_FORMAT(printf, 5, 0) -inline void alloc_copy_and_format(const char *buf, int64_t length, int append_size, int64_t alloc_limit, const char *fmt, va_list ap, char **buf_out) { - char *new_ptr = new char[alloc_limit]; - memcpy(new_ptr, buf, length); - - DEBUG_VAR int size = vsnprintf(new_ptr + length, size_t(append_size) + 1, fmt, ap); - rassert(size == append_size); - - *buf_out = new_ptr; -} - -void printf_buffer_t::vappendf(const char *format, va_list ap) { - va_list aq; - va_copy(aq, ap); - - if (ptr_ == data_) { - rassert(length_ < STATIC_DATA_SIZE); - - // the snprintfs return the number of characters they _would_ - // have written, not including the '\0'. - int _size = vsnprintf(ptr_ + length_, STATIC_DATA_SIZE - length_, format, ap); - rassert(_size >= 0, "vsnprintf failed, bad format string?"); - - if (_size < STATIC_DATA_SIZE - length_) { - length_ += _size; - } else { - char *new_ptr; - alloc_copy_and_format(ptr_, length_, _size, - int64_round_up_to_power_of_two(length_ + _size + 1), - format, aq, &new_ptr); - - // TODO: Have valgrind mark data_ memory undefined. - ptr_ = new_ptr; - length_ += _size; - } - - } else { - rassert(length_ >= STATIC_DATA_SIZE); - - char tmp[1]; - - int _size = vsnprintf(tmp, sizeof(tmp), format, ap); - rassert(_size >= 0, "vsnprintf failed, bad format string?"); - - int64_t alloc_limit = int64_round_up_to_power_of_two(length_ + 1); - if (length_ + _size + 1 <= alloc_limit) { - DEBUG_VAR int size2 = vsnprintf(ptr_ + length_, _size + 1, format, aq); - rassert(_size == size2); - length_ += _size; - } else { - - char *new_ptr; - alloc_copy_and_format(ptr_, length_, _size, - int64_round_up_to_power_of_two(length_ + _size + 1), - format, aq, &new_ptr); - - delete[] ptr_; - ptr_ = new_ptr; - length_ += _size; - } - } - - va_end(aq); -} diff --git a/src/containers/printf_buffer.hpp b/src/containers/printf_buffer.hpp deleted file mode 100644 index bbe43312bdc..00000000000 --- a/src/containers/printf_buffer.hpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CONTAINERS_PRINTF_BUFFER_HPP_ -#define CONTAINERS_PRINTF_BUFFER_HPP_ - -#include -#include -#include -#include - -// Cannot include utils.hpp, we are included by utils.hpp. -#include "errors.hpp" - -// A static buffer for printing format strings, but it has a failsafe -// in case the buffer grows too large. -class printf_buffer_t { -public: - printf_buffer_t(); - explicit printf_buffer_t(const char *format, ...) ATTR_FORMAT(printf, 2, 3); - printf_buffer_t(va_list ap, const char *format) ATTR_FORMAT(printf, 3, 0); - ~printf_buffer_t(); - - // append and vappend become slow if the total size grows to N or - // greater. We still have std::vector-style amortized constant - // time growth, though. - void appendf(const char *format, ...) ATTR_FORMAT(printf, 2, 3); - void vappendf(const char *format, va_list ap) ATTR_FORMAT(printf, 2, 0); - - char *data() const { return ptr_; } - const char *c_str() const { return ptr_; } - int64_t size() const { return length_; } - - static const int STATIC_DATA_SIZE = 1000; - -private: - // The number of bytes that have been appended. - int64_t length_; - - // Either a pointer to data_, or, if length_ >= N, a pointer to an - // array on the heap whose size is a power of two that is at least - // length_ + 1. Also, ptr_[length_] == 0, because we store a nul - // terminator. - char *ptr_; - - // A data buffer for avoiding allocation in the common case. - char data_[STATIC_DATA_SIZE]; - - - DISABLE_COPYING(printf_buffer_t); -}; - - - -#endif // CONTAINERS_PRINTF_BUFFER_HPP_ diff --git a/src/containers/priority_queue.hpp b/src/containers/priority_queue.hpp deleted file mode 100644 index d3f7aad4f01..00000000000 --- a/src/containers/priority_queue.hpp +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#ifndef CONTAINERS_PRIORITY_QUEUE_HPP_ -#define CONTAINERS_PRIORITY_QUEUE_HPP_ - -#include -#include -#include "utils.hpp" - -/* \brief priority_queue_t - * Priority queues are by defined to be max priority queues, that is - * pop() gives you an element a such that for all b in the heap Less(b, a) == True - */ -template > -class priority_queue_t { -public: - struct entry_t { - public: - T data; - - /* these 2 fields are accessed the our friend the priority_queue_t */ - friend class priority_queue_t; - private: - priority_queue_t *pq; /* !< the priority queue the data is registered in */ - int index; /* the index withing the priority queue */ - - public: - /* \brief update() should be called after a change is made to the data - * to preserve the order in the queue - */ - void update(); - bool operator< (const entry_t &b) {return Less(b.data, data);} - entry_t() - : pq(nullptr), index(0) - {} - }; -private: - std::deque heap; -private: - inline unsigned int parent(unsigned int); - inline unsigned int left(unsigned int); - inline unsigned int right(unsigned int); - inline void swap_entries(unsigned int, unsigned int); - inline void bubble_up(int *); - inline void bubble_up(int); - inline void bubble_down(int *); - inline void bubble_down(int); -public: - priority_queue_t(); - ~priority_queue_t(); - bool empty(); - size_t size(); - T peak(); - entry_t *push(T); - void remove(entry_t *); - T pop(); - void update(int); -public: - void validate(); - -private: - DISABLE_COPYING(priority_queue_t); -}; - -#include "containers/priority_queue.tcc" -#endif /* CONTAINERS_PRIORITY_QUEUE_HPP_ */ diff --git a/src/containers/priority_queue.tcc b/src/containers/priority_queue.tcc deleted file mode 100644 index 2dbb875e4dd..00000000000 --- a/src/containers/priority_queue.tcc +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "containers/priority_queue.hpp" - -template -void priority_queue_t::entry_t::update() { - if (pq) - pq->update(index); -} - -template -void priority_queue_t::swap_entries(unsigned int i, unsigned int j) { - entry_t *tmp = heap[i]; - heap[i] = heap[j]; - heap[j] = tmp; - heap[i]->index = i; - heap[j]->index = j; -} - -template -unsigned int priority_queue_t::parent(unsigned int i) { - return ((i + 1) / 2) - 1; -} - -template -unsigned int priority_queue_t::left(unsigned int i) { - return (2 * (i + 1)) - 1; -} - -template -unsigned int priority_queue_t::right(unsigned int i) { - return 2 * (i + 1); -} - -template -void priority_queue_t::bubble_up(int *i) { - while (*i > 0 && Less()(heap[parent(*i)]->data, heap[*i]->data)) { - swap_entries(*i, parent(*i)); - *i = parent(*i); - } -} - -template -void priority_queue_t::bubble_up(int j) { - int i = j; - bubble_up(&i); -} - -template -void priority_queue_t::bubble_down(int *i) { - while ((left(*i) < heap.size() && Less()(heap[*i]->data, heap[left(*i)]->data)) || - (right(*i) < heap.size() && Less()(heap[*i]->data, heap[right(*i)]->data))) { - if ((right(*i) < heap.size()) && Less()(heap[left(*i)]->data, heap[right(*i)]->data)) { - swap_entries(*i, right(*i)); - *i = right(*i); - } else { - swap_entries(*i, left(*i)); - *i = left(*i); - } - } -} - -template -void priority_queue_t::bubble_down(int j) { - int i = j; - bubble_down(&i); -} - -template -priority_queue_t::priority_queue_t() {} - - -template -priority_queue_t::~priority_queue_t() { - for (unsigned int i = 0; i < heap.size(); i++) - delete heap[i]; -} - -template -bool priority_queue_t::empty() { - return heap.empty(); -} - - -template -size_t priority_queue_t::size() { - return heap.size(); -} - -template -T priority_queue_t::peak() { - return heap[0]->data; -} - -template -typename priority_queue_t::entry_t *priority_queue_t::push(T data) { - typename priority_queue_t::entry_t *result = new entry_t(); - result->data = data; - result->pq = this; - result->index = heap.size(); - - heap.push_back(result); - bubble_up(heap.size() - 1); - return result; -} - -template -void priority_queue_t::remove(entry_t *e) { - unsigned index = e->index; - - if (index < heap.size() - 1) { - heap[index] = heap.back(); - heap[index]->index = index; - update(index); - } - - heap.pop_back(); - delete e; -} - - -template -T priority_queue_t::pop() { - T result = heap.front()->data; - - delete heap.front(); - heap.pop_front(); - - if (!heap.empty()) { - heap.push_front(heap.back()); - heap.front()->index = 0; - heap.pop_back(); - } - - bubble_down(0); - - return result; -} - -template -void priority_queue_t::update(int index) { - rassert(index >= 0); - rassert(static_cast(index) < size()); - int i = index; - bubble_up(&i); - bubble_down(&i); -} - -template -void priority_queue_t::validate() { - for (unsigned int i = 0; i < heap.size(); i++) { - if (i != 0) - rassert(!Less()(heap[parent(i)]->data, heap[i]->data)); - - rassert(heap[i]->index == static_cast(i)); - rassert(heap[i]->pq == this); - } -} diff --git a/src/containers/range_map.hpp b/src/containers/range_map.hpp deleted file mode 100644 index dd81aa51566..00000000000 --- a/src/containers/range_map.hpp +++ /dev/null @@ -1,396 +0,0 @@ -#ifndef CONTAINERS_RANGE_MAP_HPP_ -#define CONTAINERS_RANGE_MAP_HPP_ - -#include - -#include "debug.hpp" -#include "rpc/serialize_macros.hpp" -#include "utils.hpp" - -/* `range_map_t` maps from ranges delimited by `edge_t` to values of type `value_t`. The -ranges must be contiguous and non-overlapping; adjacent ranges with the same value will -be automatically coalesced. It can be thought of as a more efficient way of storing a -mapping from `edge_t` to `value_t`. */ -template -class range_map_t { -public: - typedef edge_t edge_type; - typedef value_t mapped_type; - - /* Constructs an empty `range_map_t` that starts and ends at the default-constructed - `edge_t`. */ - range_map_t() : left(edge_t()) { } - - /* Constructs an empty `range_map_t` that starts and ends at the given point. Even - when empty, a `range_map_t` is still considered to be at some specific point in the - `edge_t` space. */ - explicit range_map_t(const edge_t &l_and_r) : left(l_and_r) { } - - /* Constructs a `range_map_t` that stretches from `l` to `r` and has value `v` within - that range. */ - range_map_t(const edge_t &l, const edge_t &r, value_t &&v = value_t()) : left(l) { - rassert(r >= l); - if (r != l) { - zones.insert(std::make_pair(r, std::move(v))); - } - DEBUG_ONLY_CODE(validate()); - } - - range_map_t(range_map_t &&movee) : - left(std::move(movee.left)), - zones(std::move(movee.zones)) { } - range_map_t(const range_map_t &) = default; - range_map_t &operator=(const range_map_t &) = default; - range_map_t &operator=(range_map_t &&) = default; - - /* Returns the left and right edges of the `range_map_t`. */ - const edge_t &left_edge() const { - return left; - } - const edge_t &right_edge() const { - if (!zones.empty()) { - return (--zones.end())->first; - } else { - return left; - } - } - - /* Returns `true` if the `range_map_t` covers a zero-width range. This is equivalent - to `left_edge() == right_edge()`. */ - bool empty_domain() const { - return zones.empty(); - } - - /* Looks up the value for the location immediately to the right of `before_point`. */ - const value_t &lookup(const edge_t &before_point) const { - rassert(before_point >= left_edge()); - rassert(before_point < right_edge()); - auto it = zones.upper_bound(before_point); - return it->second; - } - - /* Calls the given callback for every sub-range from `l` to `r`. If `l` or `r` lie - within existing sub-ranges, the sub-ranges will be effectively split. The callback is - guaranteed to be called in order. The signature of `cb` is: - void cb(const edge_t &l, const edge_t &r, const value_t &v); - */ - template - void visit(const edge_t &l, const edge_t &r, const callable_t &cb) const { - rassert(l >= left_edge()); - rassert(r <= right_edge()); - rassert(l <= r); - if (l == r) { - return; - } - auto it = zones.upper_bound(l); - edge_t prev = l; - while (it->first < r) { - cb(prev, it->first, it->second); - prev = it->first; - ++it; - } - cb(prev, r, it->second); - } - - /* Derives a new `range_map_t` from some sub-range of this one by applying a function - to every value. The signature of `cb` is: - value2_t cb(const value_t &); - and the return value will have type `range_map_t`*/ - template - auto map(const edge_t &l, const edge_t &r, const callable_t &cb) const - /* We need the `std::decay` here because if `callable_t` is a lambda, it's - likely that it will return a `const T&` when what the user expects a return - value of type `range_map_t`. */ - -> range_map_t::type>::type> { - typedef typename std::decay< - typename std::result_of::type>::type result_t; - range_map_t res(l); - visit(l, r, [&](const edge_t &l2, const edge_t &r2, const value_t &v) { - res.extend_right(l2, r2, result_t(cb(v))); - }); - return res; - } - - /* Derives a new `range_map_t` from some sub-range of this one by applying a function - to every value. Unlike in `map()` above, the function may depend on the location in - the range, and it may map a homogeneous range in the input map to a non-homogeneous - range in the output map. So the signature of `cb` is different: - range_map_t cb( - const edge_t &l, const edge_t &r, const value_t &v); - and the return value will have type `range_map_t`. */ - template - auto map_multi(const edge_t &l, const edge_t &r, const callable_t &cb) const - -> typename std::decay< - typename std::result_of::type> - ::type { - typedef typename std::decay< - typename std::result_of::type> - ::type::mapped_type result_t; - range_map_t res(l); - visit(l, r, [&](const edge_t &l2, const edge_t &r2, const value_t &v) { - range_map_t frags = cb(l2, r2, v); - rassert(frags.left_edge() == l2); - rassert(frags.right_edge() == r2); - res.extend_right(std::move(frags)); - }); - return res; - } - - /* Copies out a sub-range of this `range_map_t` as a separate `range_map_t`. */ - MUST_USE range_map_t mask(const edge_t &l, const edge_t &r) const { - range_map_t res(l); - visit(l, r, [&](const edge_t &l2, const edge_t &r2, const value_t &v) { - res.extend_right(l2, r2, clone(v)); - }); - return res; - } - - bool operator==(const range_map_t &other) const { - return left == other.left && zones == other.zones; - } - bool operator!=(const range_map_t &other) const { - return !(*this == other); - } - - /* Extends the domain of this `range_map_t` on the right-hand side, filling the space - with the contents of `other`. `other`'s left edge must be our previous right edge; - our new right edge will be `other`'s right edge. */ - void extend_right(range_map_t &&other) { - rassert(other.left_edge() == right_edge()); - if (other.empty_domain()) { - return; - } - bool empty_before = empty_domain(); - zones.insert( - std::make_move_iterator(other.zones.begin()), - std::make_move_iterator(other.zones.end())); - if (!empty_before) { - coalesce_at(other.left); - } - DEBUG_ONLY_CODE(validate()); - } - - /* Extends the domain of this `range_map_t` on the right-hand side, filling the space - with `v`. `l` must be our old right edge, and our new right edge will be `r`. */ - void extend_right(const edge_t &l, const edge_t &r, value_t &&v) { - rassert(l == right_edge()); - rassert(r >= l); - if (l == r) { - return; - } - bool empty_before = empty_domain(); - zones.insert(std::make_pair(r, std::move(v))); - if (!empty_before) { - coalesce_at(l); - } - DEBUG_ONLY_CODE(validate()); - } - - /* `extend_left()` is just like `extend_right()` except that it extends the left-hand - side instead of the right-hand side. */ - void extend_left(range_map_t &&other) { - rassert(left_edge() == other.right_edge()); - if (other.empty_domain()) { - return; - } - bool empty_before = empty_domain(); - zones.insert( - std::make_move_iterator(other.zones.begin()), - std::make_move_iterator(other.zones.end())); - if (!empty_before) { - coalesce_at(left); - } - left = other.left; - DEBUG_ONLY_CODE(validate()); - } - void extend_left(const edge_t &l, const edge_t &r, value_t &&v) { - rassert(left_edge() == r); - rassert(r >= l); - if (l == r) { - return; - } - bool empty_before = empty_domain(); - zones.insert(std::make_pair(r, std::move(v))); - if (!empty_before) { - coalesce_at(r); - } - left = l; - DEBUG_ONLY_CODE(validate()); - } - - /* Overwrites part or all of the contents of this `range_map_t` with the contents of - `other`. Does not change the domain. */ - void update(range_map_t &&other) { - if (other.empty_domain()) { - return; - } - rassert(left_edge() <= other.left_edge()); - rassert(right_edge() >= other.right_edge()); - - /* If a single existing zone spans `other.left_edge(), then split it into two - sub-zones at `other.right_edge()`. */ - if (other.left_edge() != left) { - auto split_it = zones.lower_bound(other.left_edge()); - rassert(split_it != zones.end()); - if (split_it->first == other.left_edge()) { - /* no need to split anything, `other.left_edge()` lies on a boundary - between two existing zones */ - } else { - zones.insert(std::make_pair(other.left_edge(), split_it->second)); - } - } else { - /* no need to split anything, `other.left_edge()` lies on the left edge of - our leftmost zone */ - } - - /* Erase any existing zones that lie entirely within `other`'s domain. We already - dealt with the left edge case above. The right edge case will take care of itself - naturally because one of `other`'s zones will implicitly split any existing zone - that spans `other.right_edge()`. */ - auto end = zones.lower_bound(other.right_edge()); - if (end->first == other.right_edge()) { - ++end; - } - zones.erase(zones.upper_bound(other.left_edge()), end); - - /* Move all the zones from `other` into us */ - zones.insert( - std::make_move_iterator(other.zones.begin()), - std::make_move_iterator(other.zones.end())); - - /* Coalesce adjacent zones if appropriate */ - if (left_edge() != other.left_edge()) { - coalesce_at(other.left_edge()); - } - if (right_edge() != other.right_edge()) { - coalesce_at(other.right_edge()); - } - - DEBUG_ONLY_CODE(validate()); - } - - /* Sets the value of the `range_map_t` between `l` and `r` to `v`. */ - void update(const edge_t &l, const edge_t &r, value_t &&v) { - update(range_map_t(l, r, std::move(v))); - } - - /* Calls `cb` on each sub-range between `l` and `r` with a mutable pointer to the - value of that sub-range, which `cb` may modify. If `l` or `r` lies within a - sub-range, that sub-range will be split. The signature of `cb` is: - void cb(const edge_t &l, const edge_t &r, value_t *); - For example, `update(l, r, v)` is equivalent to `visit_mutable(l, r, cb)` where `cb` - is `[&](value_t *vp) { vp = v; }`. But `visit_mutable()` can also be used for more - complex operations. */ - template - void visit_mutable(const edge_t &l, const edge_t &r, const callable_t &cb) { - rassert(l >= left_edge()); - rassert(r <= right_edge()); - rassert(l <= r); - if (l == r) { - return; - } - auto it = zones.upper_bound(l); - if (l != left && zones.count(l) == 0) { - /* We need to chop off the part to the left of `l` */ - DEBUG_VAR auto res = zones.insert(std::make_pair(l, it->second)); - rassert(res.second); - } - edge_t prev = l; - while (it->first < r) { - cb(prev, it->first, &it->second); - prev = it->first; - ++it; - } - if (it->first != r) { - /* We need to chop off the part to the right of `r` */ - auto res = zones.insert(std::make_pair(r, it->second)); - rassert(res.second); - it = res.first; - } - rassert(it->first == r); - cb(prev, r, &it->second); - coalesce_range(l, r); - DEBUG_ONLY_CODE(validate()); - } - - RDB_MAKE_ME_SERIALIZABLE_2(range_map_t, left, zones); - -private: -#ifndef NDEBUG - /* Make sure that adjacent zones have all been coalesced and that no zones are to the - left of `left`. */ - void validate() const { - if (!zones.empty()) { - rassert(left < zones.begin()->first); - for (auto it = zones.begin();;) { - auto jt = it; - ++it; - if (it == zones.end()) { - break; - } - /* Use `!(x == y)` instead of `x != y` because sometimes people are lazy - and don't define `!=` */ - rassert(!(it->second == jt->second), - "adjacent equal values should have been coalesced"); - } - } - } -#endif /* NDEBUG */ - - /* Checks if the sub-ranges to the left and right of `edge` have the same value, and - merges them if they do. `edge` must correspond to an internal boundary between two - sub-ranges. */ - void coalesce_at(const edge_t &edge) { - auto before_it = zones.find(edge); - rassert(before_it != zones.end()); - auto after_it = before_it; - ++after_it; - rassert(after_it != zones.end()); - if (before_it->second == after_it->second) { - zones.erase(before_it); - } - } - - /* Equivalent to calling `coalesce_at()` for every internal boundary from `l` to `r`, - inclusive. `l` and `r` must be boundaries of sub-ranges, but they don't necessarily - have to be internal boundaries; they can be the overall left and right edges. */ - void coalesce_range(const edge_t &l, const edge_t &r) { - auto it = (l == left) ? zones.begin() : zones.find(l); - while (it != zones.end() && it->first <= r) { - auto jt = it; - ++it; - if (it == zones.end()) { - break; - } - if (jt->second == it->second) { - zones.erase(jt); - } - } - } - - /* Each sub-range corresponds to an entry in `zones`. The entry's key is the - right-hand edge of the zone; the zone's left-hand edge is determined by the previous - entry's key. The first entry's left-hand edge is stored in `left`. Every method - coalesces adjacent zones before it returns. Sub-ranges always have non-zero width; if - the map has a zero-width domain then `zones` won't have any entries. */ - edge_t left; - std::map zones; -}; - -template -void debug_print(printf_buffer_t *buf, const range_map_t &map) { - buf->appendf("| "); - debug_print(buf, map.left_edge()); - map.visit(map.left_edge(), map.right_edge(), - [&](const E &, const E &r, const V &v) { - buf->appendf(" {"); - debug_print(buf, v); - buf->appendf("} "); - debug_print(buf, r); - }); - buf->appendf(" |"); -} - -#endif /* CONTAINERS_RANGE_MAP_HPP_ */ - diff --git a/src/containers/scoped.hpp b/src/containers/scoped.hpp deleted file mode 100644 index 25c18e297c9..00000000000 --- a/src/containers/scoped.hpp +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CONTAINERS_SCOPED_HPP_ -#define CONTAINERS_SCOPED_HPP_ - -#include - -#include - -#include "config/args.hpp" -#include "errors.hpp" -#include "memory_utils.hpp" - -// Like boost::scoped_ptr only with release, init, no bool conversion, no boost headers! -template -class scoped_ptr_t { -public: - template - friend class scoped_ptr_t; - - scoped_ptr_t() : ptr_(nullptr) { } - explicit scoped_ptr_t(T *p) : ptr_(p) { } - - // (These noexcepts don't actually do anything w.r.t. STL containers, since the - // type's not copyable. There is no specific reason why these are many other - // functions need be marked noexcept with any degree of urgency.) - scoped_ptr_t(scoped_ptr_t &&movee) noexcept : ptr_(movee.ptr_) { - movee.ptr_ = nullptr; - } - template - scoped_ptr_t(scoped_ptr_t &&movee) noexcept : ptr_(movee.ptr_) { - movee.ptr_ = nullptr; - } - - ~scoped_ptr_t() { - reset(); - } - - scoped_ptr_t &operator=(scoped_ptr_t &&movee) noexcept { - scoped_ptr_t tmp(std::move(movee)); - swap(tmp); - return *this; - } - - template - scoped_ptr_t &operator=(scoped_ptr_t &&movee) noexcept { - scoped_ptr_t tmp(std::move(movee)); - swap(tmp); - return *this; - } - - // These 'init' functions are largely obsolete, because move semantics are a - // better thing to use. - template - void init(scoped_ptr_t &&movee) { - rassert(ptr_ == NULL); - - operator=(std::move(movee)); - } - - // includes a sanity-check for first-time use. - void init(T *value) { - rassert(ptr_ == NULL); - - // This is like reset with an assert. - T *tmp = ptr_; - ptr_ = value; - delete tmp; - } - - void reset() { - T *tmp = ptr_; - ptr_ = nullptr; - delete tmp; - } - - MUST_USE T *release() { - T *tmp = ptr_; - ptr_ = nullptr; - return tmp; - } - - void swap(scoped_ptr_t &other) noexcept { - T *tmp = ptr_; - ptr_ = other.ptr_; - other.ptr_ = tmp; - } - - T &operator*() const { - rassert(ptr_); - return *ptr_; - } - - T *get() const { - rassert(ptr_); - return ptr_; - } - - T *get_or_null() const { - return ptr_; - } - - T *operator->() const { - rassert(ptr_); - return ptr_; - } - - bool has() const { - return ptr_ != nullptr; - } - - explicit operator bool() const { - return ptr_ != nullptr; - } - -private: - T *ptr_; - - DISABLE_COPYING(scoped_ptr_t); -}; - -template -scoped_ptr_t make_scoped(Args&&... args) { - return scoped_ptr_t(new T(std::forward(args)...)); -} - -// Not really like boost::scoped_array. A fascist array. -template -class scoped_array_t { -public: - scoped_array_t() : ptr_(nullptr), size_(0) { } - explicit scoped_array_t(size_t n) : ptr_(nullptr), size_(0) { - init(n); - } - - scoped_array_t(T *ptr, size_t _size) : ptr_(nullptr), size_(0) { - init(ptr, _size); - } - - // (These noexcepts don't actually do anything w.r.t. STL containers, since the - // type's not copyable. There is no specific reason why these are many other - // functions need be marked noexcept with any degree of urgency.) - scoped_array_t(scoped_array_t &&movee) noexcept - : ptr_(movee.ptr_), size_(movee.size_) { - movee.ptr_ = nullptr; - movee.size_ = 0; - } - - ~scoped_array_t() { - reset(); - } - - scoped_array_t &operator=(scoped_array_t &&movee) noexcept { - scoped_array_t tmp(std::move(movee)); - swap(tmp); - return *this; - } - - void init(size_t n) { - rassert(ptr_ == NULL); - ptr_ = new T[n]; - size_ = n; - } - - // The opposite of release. - void init(T *ptr, size_t _size) { - rassert(ptr != NULL); - rassert(ptr_ == NULL); - - ptr_ = ptr; - size_ = _size; - } - - void reset() { - T *tmp = ptr_; - ptr_ = nullptr; - size_ = 0; - delete[] tmp; - } - - MUST_USE T *release(size_t *size_out) { - *size_out = size_; - T *tmp = ptr_; - ptr_ = NULL; - size_ = 0; - return tmp; - } - - void swap(scoped_array_t &other) noexcept { - T *tmp = ptr_; - size_t tmpsize = size_; - ptr_ = other.ptr_; - size_ = other.size_; - other.ptr_ = tmp; - other.size_ = tmpsize; - } - - T &operator[](size_t i) const { - rassert(ptr_); - rassert(i < size_); - return ptr_[i]; - } - - T *data() const { - rassert(ptr_); - return ptr_; - } - - size_t size() const { - rassert(ptr_); - return size_; - } - - bool has() const { - return ptr_ != NULL; - } - -private: - T *ptr_; - size_t size_; - - DISABLE_COPYING(scoped_array_t); -}; - -// This class has move semantics in its copy constructor. -// It is meant to be used instead of C++14 generalized lambda capture, -// to capture a variable using move semantics, -// which GCC 4.6 doesn't support. -template -class copyable_unique_t { -public: - explicit copyable_unique_t(T&& x) - : it(std::move(x)) { } - copyable_unique_t(const copyable_unique_t &other) - : it(std::move(other.it)) { } - T release() const { - T x(std::move(it)); - return x; - } -private: - mutable T it; -}; - -#ifdef __clang__ -#define CAN_ALIAS_TEMPLATES 1 -#elif __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7) -#define CAN_ALIAS_TEMPLATES 0 -#else -#define CAN_ALIAS_TEMPLATES 1 -#endif - -/* - * For pointers with custom allocators and deallocators - */ -template -class scoped_alloc_t { - - static_assert(std::is_pod::value || std::is_same::value, - "refusing to malloc non-POD, non-void type"); - -public: - template - friend class scoped_alloc_t; - - scoped_alloc_t() : ptr_(nullptr) { } - explicit scoped_alloc_t(size_t n) : ptr_(static_cast(alloc(n))) { } - scoped_alloc_t(const void *beg, size_t n) { - ptr_ = static_cast(alloc(n)); - memcpy(ptr_, beg, n); - } - // (These noexcepts don't actually do anything w.r.t. STL containers, since the - // type's not copyable. There is no specific reason why these are many other - // functions need be marked noexcept with any degree of urgency.) - scoped_alloc_t(scoped_alloc_t &&movee) noexcept - : ptr_(movee.ptr_) { - movee.ptr_ = nullptr; - } - - template - scoped_alloc_t(scoped_alloc_t &&movee) noexcept - : ptr_(movee.ptr_) { - movee.ptr_ = nullptr; - } - - template - scoped_alloc_t(copyable_unique_t other) noexcept : ptr_(nullptr) { // NOLINT - scoped_alloc_t tmp(other.release()); - swap(tmp); - } - - void operator=(scoped_alloc_t &&movee) noexcept { - scoped_alloc_t tmp(std::move(movee)); - swap(tmp); - } - - T *get() const { return ptr_; } - T *operator->() const { return ptr_; } - - bool has() const { - return ptr_ != nullptr; - } - - void reset() { - scoped_alloc_t tmp; - swap(tmp); - } - -#if !CAN_ALIAS_TEMPLATES -protected: -#endif - ~scoped_alloc_t() { - dealloc(ptr_); - } - -private: - friend class released_t; - - explicit scoped_alloc_t(void*) = delete; - - void swap(scoped_alloc_t &other) noexcept { - T *tmp = ptr_; - ptr_ = other.ptr_; - other.ptr_ = tmp; - } - - T *ptr_; - - DISABLE_COPYING(scoped_alloc_t); -}; - -template -void *raw_malloc_aligned(size_t size) { - return raw_malloc_aligned(size, alignment); -} - -#if !CAN_ALIAS_TEMPLATES - -// GCC 4.6 doesn't support template aliases - -#define TEMPLATE_ALIAS(type_t, ...) \ - struct type_t : public __VA_ARGS__ { \ - template \ - type_t(arg_ts&&... args) : /* NOLINT */ \ - __VA_ARGS__(std::forward(args)...) { } \ - } - -#else - -#define TEMPLATE_ALIAS(type_t, ...) using type_t = __VA_ARGS__; - -#endif - -// A type for pointers using rmalloc/free -template -TEMPLATE_ALIAS(scoped_malloc_t, scoped_alloc_t); - -// A type for aligned pointers -// Needed because, on Windows, raw_free_aligned doesn't call free -template -TEMPLATE_ALIAS(scoped_aligned_ptr_t, scoped_alloc_t, raw_free_aligned>); - -#ifndef _WIN32 -// A type for page-aligned pointers -template -TEMPLATE_ALIAS(scoped_page_aligned_ptr_t, scoped_alloc_t); -#endif - -// A type for device-block-aligned pointers -template -TEMPLATE_ALIAS(scoped_device_block_aligned_ptr_t, scoped_alloc_t, raw_free_aligned>); - -#endif // CONTAINERS_SCOPED_HPP_ diff --git a/src/containers/segmented_vector.hpp b/src/containers/segmented_vector.hpp deleted file mode 100644 index 6df23d4f6c0..00000000000 --- a/src/containers/segmented_vector.hpp +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef CONTAINERS_SEGMENTED_VECTOR_HPP_ -#define CONTAINERS_SEGMENTED_VECTOR_HPP_ - -#include - -#include -#include -#include - -#include "errors.hpp" - -template -class segmented_vector_t { -public: - explicit segmented_vector_t(size_t _size = 0) : size_(0) { - set_size(_size); - } - - segmented_vector_t(segmented_vector_t &&movee) - : segments_(std::move(movee.segments_)), size_(movee.size_) { - movee.segments_.clear(); - movee.size_ = 0; - } - - segmented_vector_t &operator=(segmented_vector_t &&movee) { - segmented_vector_t tmp(std::move(movee)); - std::swap(segments_, tmp.segments_); - std::swap(size_, tmp.size_); - return *this; - } - - size_t size() const { - return size_; - } - - bool empty() const { - return size_ == 0; - } - - // Gets an element, without creating a segment_t if its segment doesn't exist. - // (Thus, this method doesn't return a reference.) - element_t get_sparsely(size_t index) const { - guarantee(index < size_, "index = %zu, size_ = %zu", index, size_); - const size_t segment_index = index / ELEMENTS_PER_SEGMENT; - segment_t *seg = segments_[segment_index]; - if (seg == NULL) { - return element_t(); - } else { - return seg->elements[index % ELEMENTS_PER_SEGMENT]; - } - } - - element_t &operator[](size_t index) { - return get_element(index); - } - - const element_t &operator[](size_t index) const { - return get_element(index); - } - - void push_back(const element_t &element) { - size_t old_size = size_; - set_size(old_size + 1); - (*this)[old_size] = element; - } - - void push_back(element_t &&element) { - size_t old_size = size_; - set_size(old_size + 1); - (*this)[old_size] = std::move(element); - } - - element_t &back() { - return (*this)[size_ - 1]; - } - - void pop_back() { - guarantee(size_ > 0); - set_size(size_ - 1); - } - - void clear() { - segments_.clear(); - size_ = 0; - } - - void resize_with_zeros(size_t new_size) { - set_size(new_size); - } - -private: - // Gets a non-const element with a const function... which breaks the guarantees - // but compiles and lets this be called by both the const and non-const versions - // of operator[]. - element_t &get_element(size_t index) const { - guarantee(index < size_, "index = %zu, size_ = %zu", index, size_); - const size_t segment_index = index / ELEMENTS_PER_SEGMENT; - segment_t *seg = segments_[segment_index].get(); - if (seg == nullptr) { - seg = new segment_t(); - segments_[segment_index].reset(seg); - } - return seg->elements[index % ELEMENTS_PER_SEGMENT]; - } - - // Note: sometimes elements will be initialized before you ask the - // array to grow to that size (e.g. one hundred elements might be - // initialized even though the array might be of size 1). - // - // Also some will remain initialized when you shrink. - void set_size(size_t new_size) { - const size_t new_num_segs = (new_size + (ELEMENTS_PER_SEGMENT - 1)) / ELEMENTS_PER_SEGMENT; - - const size_t cap = segments_.capacity(); - - if (new_num_segs <= cap) { - // Prevent resizing down to zero -- avoids unnecessary - // deallocation+allocation when doing push/pop/push/pop on - // zero-length segmented_vector. - if (new_num_segs != 0) { - segments_.resize(new_num_segs); - } - } else { - // Force 2x growth. - if (new_num_segs < cap * 2) { - segments_.reserve(cap * 2); - } - segments_.resize(new_num_segs); - } - - size_ = new_size; - } - - struct segment_t { - segment_t() : elements() { } // Zero-initialize array. - element_t elements[ELEMENTS_PER_SEGMENT]; - }; - - mutable std::vector> segments_; - size_t size_; - - DISABLE_COPYING(segmented_vector_t); -}; - -#endif // CONTAINERS_SEGMENTED_VECTOR_HPP_ diff --git a/src/containers/shared_buffer.cc b/src/containers/shared_buffer.cc deleted file mode 100644 index 6cc1f135432..00000000000 --- a/src/containers/shared_buffer.cc +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "containers/shared_buffer.hpp" - -#include - -#include "utils.hpp" - -counted_t shared_buf_t::create(size_t size) { - // This allocates size bytes for the data_ field (which is declared as char[1]) - size_t memory_size = sizeof(shared_buf_t) + size - 1; - void *raw_result = ::rmalloc(memory_size); - shared_buf_t *result = static_cast(raw_result); - result->refcount_ = 0; - result->size_ = size; - return counted_t(result); -} - -void shared_buf_t::operator delete(void *p) { - ::free(p); -} - -char *shared_buf_t::data(size_t offset) { - return data_ + offset; -} - -const char *shared_buf_t::data(size_t offset) const { - return data_ + offset; -} - -size_t shared_buf_t::size() const { - return size_; -} diff --git a/src/containers/shared_buffer.hpp b/src/containers/shared_buffer.hpp deleted file mode 100644 index dcbbf43d0b5..00000000000 --- a/src/containers/shared_buffer.hpp +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef CONTAINERS_SHARED_BUFFER_HPP_ -#define CONTAINERS_SHARED_BUFFER_HPP_ - -#include - -#include "containers/counted.hpp" -#include "errors.hpp" - -/* A `shared_buffer_t` is a reference counted binary buffer. -You can have multiple `shared_buf_ref_t`s pointing to different offsets in -the same `shared_buffer_t`. */ -class shared_buf_t { -public: - shared_buf_t() = delete; - - static counted_t create(size_t _size); - static void operator delete(void *p); - - char *data(size_t offset = 0); - const char *data(size_t offset = 0) const; - - size_t size() const; - -private: - // We duplicate the implementation of slow_atomic_countable_t here for the - // sole purpose of having full control over the layout of fields. This - // is required because we manually allocate memory for the data field, - // and C++ doesn't guarantee any specific field memory layout under inheritance - // (as far as I know). - friend void counted_add_ref(const shared_buf_t *p); - friend void counted_release(const shared_buf_t *p); - friend intptr_t counted_use_count(const shared_buf_t *p); - - mutable std::atomic refcount_; - - // The size of data_, for boundary checking. - size_t size_; - - // We actually allocate more memory than this. - // It's crucial that this field is the last one in this class. - char data_[1]; - - DISABLE_COPYING(shared_buf_t); -}; - - -/* A `shared_buf_ref_t` points at a specific offset of a `shared_buf_t`. -It is packed to reduce its memory footprint. Additionally you can specify -a smaller offset_t type if you don't need to access buffers of more than a certain -size. */ -// TODO (daniel): This should be templated on the offset type, so we can -// save some memory when having buffers of a limited maximum size. -template -class shared_buf_ref_t { -public: - shared_buf_ref_t() : offset(0) { } - shared_buf_ref_t(const counted_t &_buf, size_t _offset) - : buf(_buf), offset(_offset) { - rassert(buf.has()); - } - - const T *get() const { - rassert(buf.has()); - rassert(buf->size() >= offset); - return reinterpret_cast(buf->data(offset)); - } - - shared_buf_ref_t make_child(size_t relative_offset) const { - guarantee_in_boundary(relative_offset); - return shared_buf_ref_t(buf, offset + relative_offset); - } - - // Makes sure that the underlying shared buffer has space for at least - // num_elements elements of type T. - // This protects against reading into memory that doesn't belong to the - // buffer, but doesn't protect from reading into another object in case the - // buffer contains other objects in addition to what this buf ref is pointing - // to. - void guarantee_in_boundary(size_t num_elements) const { - guarantee(get_safety_boundary() >= num_elements); - } - // An upper bound on the number of elements that can be read from this buf ref - size_t get_safety_boundary() const { - rassert(buf.has()); - rassert(buf->size() >= offset); - return (buf->size() - offset) / sizeof(T); - } - -private: - counted_t buf; - size_t offset; -}; - - - -inline void counted_add_ref(const shared_buf_t *p) { - DEBUG_VAR intptr_t res = ++(p->refcount_); - rassert(res > 0); -} - -inline void counted_release(const shared_buf_t *p) { - int64_t res = --(p->refcount_); - rassert(res >= 0); - if (res == 0) { - delete const_cast(p); - } -} - -inline intptr_t counted_use_count(const shared_buf_t *p) { - // Finally a practical use for volatile. - intptr_t tmp = p->refcount_.load(); - rassert(tmp > 0); - return tmp; -} - -#endif // CONTAINERS_SHARED_BUFFER_HPP_ diff --git a/src/containers/tribool.hpp b/src/containers/tribool.hpp deleted file mode 100644 index 465a13d8428..00000000000 --- a/src/containers/tribool.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef CONTAINERS_TRIBOOL_HPP_ -#define CONTAINERS_TRIBOOL_HPP_ - -#include - -#include "containers/archive/archive.hpp" - -enum class tribool : int8_t { - False = 0, - True = 1, - Indeterminate = 2, -}; - -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE(tribool, int8_t, 0, 2); - -#endif // CONTAINERS_TRIBOOL_HPP_ diff --git a/src/containers/two_level_array.hpp b/src/containers/two_level_array.hpp deleted file mode 100644 index 86f83a848ff..00000000000 --- a/src/containers/two_level_array.hpp +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CONTAINERS_TWO_LEVEL_ARRAY_HPP_ -#define CONTAINERS_TWO_LEVEL_ARRAY_HPP_ - -#include - -#include "errors.hpp" - -/* two_level_array_t is a tree that always has exactly two levels. Its computational -complexity is similar to that of an array, but it neither allocates all of its memory -at once nor needs to realloc() as it grows. It also doesn't have a "size" -- it is -an infinite array. If a get() is called on an index in the array that set() has -never been called for, the result will be value_t(). - -It is parameterized on a value type, 'value_t'. It makes the following assumptions -about value_t: - -1. value_t has a default constructor value_t() that has no side effects, and its - destructor has no side effects. - -2. value_t has a good equality operator, where value_t() == value_t(), and - distinguishable values don't compare equal. - -*/ - -template -class two_level_array_t { -private: - static const size_t CHUNK_SIZE = 1 << 14; - - struct chunk_t { - chunk_t() - : count(0), values() // default-initialize each value in values - { } - size_t count; - value_t values[CHUNK_SIZE]; - }; - std::vector chunks; - - static size_t chunk_for_key(size_t key) { - size_t chunk_id = key / CHUNK_SIZE; - return chunk_id; - } - static size_t index_for_key(size_t key) { - return key % CHUNK_SIZE; - } - -public: - two_level_array_t() { } - ~two_level_array_t() { - for (auto it = chunks.begin(); it != chunks.end(); ++it) { - delete *it; - } - } - - value_t get(size_t key) const { - size_t chunk_id = chunk_for_key(key); - if (chunk_id < chunks.size() && chunks[chunk_id] != nullptr) { - return chunks[chunk_id]->values[index_for_key(key)]; - } else { - return value_t(); - } - } - - void set(size_t key, value_t value) { - const size_t chunk_id = chunk_for_key(key); - if (chunk_id >= chunks.size() || chunks[chunk_id] == nullptr) { - if (value == value_t()) { - return; - } else { - if (chunk_id >= chunks.size()) { - chunks.resize(chunk_id + 1, nullptr); - } - chunks[chunk_id] = new chunk_t; - } - } - - chunk_t *chunk = chunks[chunk_id]; - const size_t index = index_for_key(key); - if (!(chunk->values[index] == value_t())) { - --chunk->count; - } - chunk->values[index] = value; - if (!(value == value_t())) { - ++chunk->count; - } - - if (chunk->count == 0) { - chunks[chunk_id] = nullptr; - delete chunk; - - while (!chunks.empty() && chunks.back() == nullptr) { - chunks.pop_back(); - } - } - } -}; - -#endif // CONTAINERS_TWO_LEVEL_ARRAY_HPP_ diff --git a/src/containers/uuid.cc b/src/containers/uuid.cc deleted file mode 100644 index eb9363a1473..00000000000 --- a/src/containers/uuid.cc +++ /dev/null @@ -1,402 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. - -#include "containers/uuid.hpp" - -#include -#include -#include -#include - -#include -#include - -#include "containers/printf_buffer.hpp" -#include "containers/name_string.hpp" -#include "utils.hpp" -#include "thread_local.hpp" - -// We keep the sha1 functions in this .cc file to avoid encouraging others from using it. -namespace sha1 { -// hash is a pointer to 20-byte array, unfortunately. -void calc(const void* src, const int bytelength, unsigned char* hash); -} - -static const char *const magic_unset_uuid = "UNSET_UUID_____"; -uuid_u::uuid_u() { - rassert(strlen(magic_unset_uuid) == kStaticSize-1); - memcpy(data_, magic_unset_uuid, kStaticSize); -} -bool uuid_u::is_unset() const { - return !memcmp(data_, magic_unset_uuid, kStaticSize); -} - -bool uuid_u::is_nil() const { - for (size_t i = 0; i < kStaticSize; ++i) { - if (data_[i] != 0) return false; - } - return true; -} - -// This conforms to RFC4122 section 4.3 with the exception that we do not -// conver byte order, because the data is kept in network byte order already -uuid_u uuid_u::from_hash(const uuid_u &base, - const std::string &name) { - rassert(!base.is_unset() && !base.is_nil()); - - // Concatenate base and name - std::string str = std::string(reinterpret_cast(base.data()), - base.static_size()) + name; - - // Compute SHA-1 of the concatenated string - uint8_t output_buffer[20]; - sha1::calc(str.c_str(), str.length(), output_buffer); - - // Set some bits to obey standard for version 5 UUIDs. - output_buffer[6] = ((output_buffer[6] & 0x0f) | 0x50); - output_buffer[8] = ((output_buffer[8] & 0x3f) | 0x80); - - // Copy the beginning of the hash into our uuid - uuid_u uuid; - memcpy(uuid.data(), output_buffer, uuid_u::static_size()); - return uuid; -} - -bool operator==(const uuid_u& x, const uuid_u& y) { - return memcmp(x.data(), y.data(), uuid_u::static_size()) == 0; -} - -bool operator<(const uuid_u& x, const uuid_u& y) { - return memcmp(x.data(), y.data(), uuid_u::static_size()) < 0; -} - -TLS_with_init(bool, next_uuid_initialized, false); -typedef std::array next_uuid_t; -TLS_with_init(next_uuid_t, next_uuid, next_uuid_t{{0}}); - -uuid_u get_and_increment_uuid() { - uuid_u result; - - next_uuid_t next_uuid = TLS_get_next_uuid(); - - // Copy over the next_uuid buffer - uint8_t *result_buffer = result.data(); - memcpy(result_buffer, next_uuid.data(), uuid_u::static_size()); - - // Increment the next_uuid buffer - bool carry = true; - for (size_t i = uuid_u::static_size(); carry && i > 0; --i) { - next_uuid[i - 1] = next_uuid[i - 1] + 1; - carry = (next_uuid[i - 1] == 0); - } - TLS_set_next_uuid(next_uuid); - - return result; -} - -// TODO(sam): Make sure this isn't messed up somehow. -void hash_uuid(uuid_u *uuid) { - CT_ASSERT(20 >= uuid_u::kStaticSize); - uint8_t output_buffer[20]; - - sha1::calc(uuid->data(), uuid_u::static_size(), output_buffer); - - // Set some bits to obey standard for version 4 UUIDs. - output_buffer[6] = ((output_buffer[6] & 0x0f) | 0x40); - output_buffer[8] = ((output_buffer[8] & 0x3f) | 0x80); - - // Copy the beginning of the hash into our uuid - memcpy(uuid->data(), output_buffer, uuid_u::static_size()); -} - -void initialize_dev_random_uuid() { - next_uuid_t next_uuid; - system_random_bytes(next_uuid.data(), uuid_u::static_size()); - TLS_set_next_uuid(next_uuid); -} - -uuid_u generate_uuid() { - if (!TLS_get_next_uuid_initialized()) { - initialize_dev_random_uuid(); - TLS_set_next_uuid_initialized(true); - } - uuid_u result = get_and_increment_uuid(); - hash_uuid(&result); - return result; -} - -uuid_u nil_uuid() { - uuid_u ret; - memset(ret.data(), 0, uuid_u::static_size()); - return ret; -} - -void debug_print(printf_buffer_t *buf, const uuid_u& id) { - buf->appendf("%s", uuid_to_str(id).c_str()); -} - -void push_hex(std::string *s, uint8_t byte) { - const char *buf = "0123456789abcdef"; - s->push_back(buf[byte >> 4]); - s->push_back(buf[byte & 0x0f]); -} - -std::string uuid_to_str(uuid_u id) { - const uint8_t *data = id.data(); - - std::string ret; - ret.reserve(uuid_u::kStringSize); - size_t i = 0; - for (; i < 4; ++i) { - push_hex(&ret, data[i]); - } - ret.push_back('-'); - for (; i < 6; ++i) { - push_hex(&ret, data[i]); - } - ret.push_back('-'); - for (; i < 8; ++i) { - push_hex(&ret, data[i]); - } - ret.push_back('-'); - for (; i < 10; ++i) { - push_hex(&ret, data[i]); - } - ret.push_back('-'); - CT_ASSERT(uuid_u::kStaticSize == 16); // This code just feels this assertion in its bones. - for (; i < uuid_u::kStaticSize; ++i) { - push_hex(&ret, data[i]); - } - - return ret; -} - -uuid_u str_to_uuid(const std::string &uuid) { - uuid_u ret; - if (str_to_uuid(uuid, &ret)) { - return ret; - } else { - throw std::runtime_error("invalid uuid"); // Sigh. - } -} - -MUST_USE bool from_hexdigit(int ch, int *out) { - if (isdigit(ch)) { - *out = ch - '0'; - return true; - } - ch = tolower(ch); - if ('a' <= ch && ch <= 'f') { // Death to EBCDIC. - *out = 10 + (ch - 'a'); - return true; - } - return false; -} - -MUST_USE bool str_to_uuid(const std::string &str, uuid_u *uuid) { - if (str.size() != uuid_u::kStaticSize * 2 + 4) { - return false; - } - - uint8_t *data = uuid->data(); - - size_t j = 0; - for (size_t i = 0; i < uuid_u::kStaticSize; ++i) { - // Uh oh.. a for/switch loop! - switch (i) { - case 4: - case 6: - case 8: - case 10: - rassert(j < uuid_u::kStringSize); - if (str[j] != '-') { - return false; - } - ++j; - // fall through - default: { - rassert(j < uuid_u::kStringSize); - int high; - if (!from_hexdigit(str[j], &high)) { - return false; - } - ++j; - rassert(j < uuid_u::kStringSize); - int low; - if (!from_hexdigit(str[j], &low)) { - return false; - } - ++j; - data[i] = ((high << 4) | low); - } break; - } - } - - rassert(j == uuid_u::kStringSize); - return true; -} - -/* - For part of this file below: - - Copyright (c) 2011, Micael Hildenborg - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Micael Hildenborg nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -namespace sha1 -{ -namespace // local -{ -// Rotate an integer value to left. -inline unsigned int rol(const unsigned int value, const unsigned int steps) -{ - return ((value << steps) | (value >> (32 - steps))); -} - -// Sets the first 16 integers in the buffert to zero. -// Used for clearing the W buffert. -inline void clearWBuffert(unsigned int* buffert) -{ - for (int pos = 16; --pos >= 0;) - { - buffert[pos] = 0; - } -} - -void innerHash(unsigned int* result, unsigned int* w) -{ - unsigned int a = result[0]; - unsigned int b = result[1]; - unsigned int c = result[2]; - unsigned int d = result[3]; - unsigned int e = result[4]; - - int round = 0; - - #define sha1macro(func, val) \ - { \ - const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \ - e = d; \ - d = c; \ - c = rol(b, 30); \ - b = a; \ - a = t; \ - } - - while (round < 16) - { - sha1macro((b & c) | (~b & d), 0x5a827999) - ++round; - } - while (round < 20) - { - w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); - sha1macro((b & c) | (~b & d), 0x5a827999) - ++round; - } - while (round < 40) - { - w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); - sha1macro(b ^ c ^ d, 0x6ed9eba1) - ++round; - } - while (round < 60) - { - w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); - sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc) - ++round; - } - while (round < 80) - { - w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); - sha1macro(b ^ c ^ d, 0xca62c1d6) - ++round; - } - - #undef sha1macro - - result[0] += a; - result[1] += b; - result[2] += c; - result[3] += d; - result[4] += e; -} -} // namespace - -// hash is a pointer to a 20-byte array. -void calc(const void* src, const int bytelength, unsigned char* hash) -{ - // Init the result array. - unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 }; - - // Cast the void src pointer to be the byte array we can work with. - const unsigned char* sarray = (const unsigned char*) src; - - // The reusable round buffer - unsigned int w[80]; - - // Loop through all complete 64byte blocks. - const int endOfFullBlocks = bytelength - 64; - int endCurrentBlock; - int currentBlock = 0; - - while (currentBlock <= endOfFullBlocks) - { - endCurrentBlock = currentBlock + 64; - - // Init the round buffer with the 64 byte block data. - for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4) - { - // This line will swap endian on big endian and keep endian on little endian. - w[roundPos++] = (unsigned int) sarray[currentBlock + 3] - | (((unsigned int) sarray[currentBlock + 2]) << 8) - | (((unsigned int) sarray[currentBlock + 1]) << 16) - | (((unsigned int) sarray[currentBlock]) << 24); - } - innerHash(result, w); - } - - // Handle the last and not full 64 byte block if existing. - endCurrentBlock = bytelength - currentBlock; - clearWBuffert(w); - int lastBlockBytes = 0; - for (; lastBlockBytes < endCurrentBlock; ++lastBlockBytes) - { - w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3); - } - w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3); - if (endCurrentBlock >= 56) - { - innerHash(result, w); - clearWBuffert(w); - } - w[15] = bytelength << 3; - innerHash(result, w); - - // Store hash in result pointer, and make sure we get in in the correct order on both endian models. - for (int hashByte = 20; --hashByte >= 0;) - { - hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff; - } -} -} // namespace sha1 diff --git a/src/containers/uuid.hpp b/src/containers/uuid.hpp deleted file mode 100644 index 6f3daca30ee..00000000000 --- a/src/containers/uuid.hpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef CONTAINERS_UUID_HPP_ -#define CONTAINERS_UUID_HPP_ - -#include -#include - -#include - -#include "errors.hpp" - -class printf_buffer_t; - -// uuid_t is defined on Darwin. I have given up on what to name it. Please -// don't use guid_t, for it has a Windowsian connotation and we might run into -// the same sort of problem from that. -// UUIDs are kept in network byte order at all times. -class uuid_u { -public: - uuid_u(); - - bool is_unset() const; - bool is_nil() const; - - static const size_t kStaticSize = 16; - static const size_t kStringSize = 2 * kStaticSize + 4; // hexadecimal, 4 hyphens - static size_t static_size() { - CT_ASSERT(sizeof(uuid_u) == kStaticSize); - return kStaticSize; - } - - uint8_t *data() { return data_; } - const uint8_t *data() const { return data_; } - - static uuid_u from_hash(const uuid_u &base, const std::string &name); - -private: - uint8_t data_[kStaticSize]; -}; - -bool operator==(const uuid_u& x, const uuid_u& y); -inline bool operator!=(const uuid_u& x, const uuid_u& y) { return !(x == y); } -bool operator<(const uuid_u& x, const uuid_u& y); - -/* This does the same thing as `boost::uuids::random_generator()()`, except that -Valgrind won't complain about it. */ -uuid_u generate_uuid(); - -// Returns boost::uuids::nil_generator()(). -uuid_u nil_uuid(); - -void debug_print(printf_buffer_t *buf, const uuid_u& id); - -std::string uuid_to_str(uuid_u id); - -uuid_u str_to_uuid(const std::string &str); - -MUST_USE bool str_to_uuid(const std::string &str, uuid_u *out); - -typedef uuid_u namespace_id_t; -typedef uuid_u database_id_t; -typedef uuid_u backfill_session_id_t; -typedef uuid_u branch_id_t; -typedef uuid_u issue_id_t; - - -#endif // CONTAINERS_UUID_HPP_ diff --git a/src/crypto/base64.cc b/src/crypto/base64.cc deleted file mode 100644 index 0511343fea1..00000000000 --- a/src/crypto/base64.cc +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#include "crypto/base64.hpp" - -#include "crypto/error.hpp" - -namespace crypto { - -namespace detail { - -static char base64_encode_map[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -std::string base64_encode(unsigned char const *data, size_t size) { - std::string encoded; - encoded.reserve(((size / 3) + (size % 3 > 0)) * 4); - - int32_t buffer; - for (; size > 3; size -= 3) { - buffer = (*data++) << 16; - buffer += (*data++) << 8; - buffer += (*data++); - encoded.push_back(base64_encode_map[(buffer & 0x00fc0000) >> 18]); - encoded.push_back(base64_encode_map[(buffer & 0x0003f000) >> 12]); - encoded.push_back(base64_encode_map[(buffer & 0x00000fc0) >> 6]); - encoded.push_back(base64_encode_map[(buffer & 0x0000003f)]); - } - - switch (size) { - case 2: - buffer = (*data++) << 16; - buffer += (*data++) << 8; - encoded.push_back(base64_encode_map[(buffer & 0x00fc0000) >> 18]); - encoded.push_back(base64_encode_map[(buffer & 0x0003f000) >> 12]); - encoded.push_back(base64_encode_map[(buffer & 0x00000fc0) >> 6]); - encoded.push_back('='); - break; - case 1: - buffer = (*data++) << 16; - encoded.push_back(base64_encode_map[(buffer & 0x00fc0000) >> 18]); - encoded.push_back(base64_encode_map[(buffer & 0x0003f000) >> 12]); - encoded.append(2, '='); - break; - case 0: - break; - } - - return encoded; -} - -} // namespace detail - -std::string base64_decode(std::string const &encoded) { - std::string decoded; - decoded.reserve((encoded.size() / 4) * 3); - - int32_t buffer = 0; - std::string::const_iterator iterator = encoded.begin(); - while (iterator < encoded.end()) { - for (int i = 0; i < 4; i++) { - buffer <<= 6; - if (*iterator >= 0x41 && *iterator <= 0x5a) { - buffer |= *iterator - 0x41; - } else if (*iterator >= 0x61 && *iterator <= 0x7a) { - buffer |= *iterator - 0x47; - } else if (*iterator >= 0x30 && *iterator <= 0x39) { - buffer |= *iterator + 0x04; - } else if (*iterator == 0x2b) { - buffer |= 0x3e; - } else if (*iterator == 0x2f) { - buffer |= 0x3f; - } else if (*iterator == '=') { - switch (encoded.end() - iterator) { - case 2: - if (*++iterator == '=') { - decoded.push_back((buffer >> 10) & 0x000000ff); - return decoded; - } else { - throw error_t("Invalid padding in `" + encoded + "`"); - } - case 1: - decoded.push_back((buffer >> 16) & 0x000000ff); - decoded.push_back((buffer >> 8) & 0x000000ff); - return decoded; - default: - throw error_t("Invalid padding in `" + encoded + "`"); - } - } else { - throw error_t("Invalid padding in `" + encoded + "`"); - } - iterator++; - } - decoded.push_back((buffer >> 16) & 0x000000ff); - decoded.push_back((buffer >> 8) & 0x000000ff); - decoded.push_back(buffer & 0x000000ff); - } - - return decoded; -} - -} // namespace crypto diff --git a/src/crypto/base64.hpp b/src/crypto/base64.hpp deleted file mode 100644 index 26bf2db4d3f..00000000000 --- a/src/crypto/base64.hpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CRYPTO_BASE64_HPP -#define CRYPTO_BASE64_HPP - -#include -#include - -namespace crypto { - -namespace detail { - -std::string base64_encode(unsigned char const *, size_t); - -} // namespace detail - -std::string base64_decode(std::string const &); - -template -inline std::string base64_encode(std::array const &array) { - return detail::base64_encode(array.data(), array.size()); -} - -inline std::string base64_encode(std::string const &string) { - return detail::base64_encode( - reinterpret_cast(string.data()), string.size()); -} - -} // namespace crypto - -#endif // CRYPTO_BASE64_HPP diff --git a/src/crypto/compare_equal.hpp b/src/crypto/compare_equal.hpp deleted file mode 100644 index fe461a1b651..00000000000 --- a/src/crypto/compare_equal.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CRYPTO_COMPARE_HPP -#define CRYPTO_COMPARE_HPP - -#include - -#include - -namespace crypto { - -template -inline bool compare_equal( - std::array const &lhs, - std::array const &rhs) { - return CRYPTO_memcmp(lhs.data(), rhs.data(), lhs.size()) == 0; -} - -} // namespace crypto - -#endif // CRYPTO_COMPARE_HPP diff --git a/src/crypto/error.cc b/src/crypto/error.cc deleted file mode 100644 index d82f6d4d56b..00000000000 --- a/src/crypto/error.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#include "crypto/error.hpp" - -#include "utils.hpp" - -namespace crypto { - -openssl_error_category_t::openssl_error_category_t() { - -} - -/* virtual */ char const *openssl_error_category_t::name() const noexcept { - return "OpenSSL"; -} - -/* virtual */ std::string openssl_error_category_t::message(int condition) const { - std::string msg; - switch (condition) { - case 336027804: - msg = "Received a plain HTTP request on the HTTPS port."; - break; - case 336027900: - msg = "Unknown protocol. By default, RethinkDB requires at least TLSv1.2. " - "You can enable older TLS versions on the server through the " - "`--tls-min-protocol TLSv1` option. If your client uses an older version " - "of the OpenSSL library, you might also have to specify the option `--tls-ciphers " - "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:AES256-SHA`."; - break; - case 336109761: - msg = "No shared cipher. You can change the list of enabled ciphers on the " - "server through the `--tls-ciphers` option. Older OpenSSL clients " - "(e.g. on OS X) might require `--tls-ciphers " - "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:AES256-SHA`."; - break; - default: - char const *openssl_message = ERR_reason_error_string(condition); - msg = openssl_message == nullptr ? "unknown error" : openssl_message; - break; - } - msg += strprintf(" (OpenSSL error %d)", condition); - return msg; -} - -} // namespace crypto diff --git a/src/crypto/error.hpp b/src/crypto/error.hpp deleted file mode 100644 index 487f554496f..00000000000 --- a/src/crypto/error.hpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CRYPTO_ERROR_HPP -#define CRYPTO_ERROR_HPP - -#include - -#include -#include -#include -#include - -namespace crypto { - -class error_t : public std::runtime_error { -public: - explicit error_t(std::string const &_what) - : std::runtime_error(_what) { - } -}; - -static const class openssl_error_category_t : public std::error_category { -public: - openssl_error_category_t(); - - /* virtual */ char const *name() const noexcept; - /* virtual */ std::string message(int condition) const; -} openssl_error_category; - -class openssl_error_t : public std::system_error { -public: - explicit openssl_error_t(unsigned long ec) // NOLINT(runtime/int) - : std::system_error(ec, openssl_error_category) { - } -}; - -} // namespace crypto - -#endif // CRYPTO_ERROR_HPP diff --git a/src/crypto/hash.cc b/src/crypto/hash.cc deleted file mode 100644 index e035f695fc1..00000000000 --- a/src/crypto/hash.cc +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#include "crypto/hash.hpp" - -#include -#include - -#include - -#include "crypto/error.hpp" - -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) -#define EVP_MD_CTX_new EVP_MD_CTX_create -#define EVP_MD_CTX_free EVP_MD_CTX_destroy -#endif - -namespace crypto { - -class evp_md_ctx_wrapper_t { -public: - evp_md_ctx_wrapper_t() { - m_evp_md_ctx = EVP_MD_CTX_new(); - if (m_evp_md_ctx == nullptr) { - throw openssl_error_t(ERR_get_error()); - } - } - - ~evp_md_ctx_wrapper_t() { - EVP_MD_CTX_free(m_evp_md_ctx); - } - - EVP_MD_CTX *get() { - return m_evp_md_ctx; - } - -private: - EVP_MD_CTX *m_evp_md_ctx; -}; - -std::array detail::sha256( - unsigned char const *data, size_t size) { - std::array hash; - - evp_md_ctx_wrapper_t evp_md_ctx; - if (EVP_DigestInit(evp_md_ctx.get(), EVP_sha256()) != 1) { - throw openssl_error_t(ERR_get_error()); - } - if (EVP_DigestUpdate(evp_md_ctx.get(), data, size) != 1) { - throw openssl_error_t(ERR_get_error()); - } - if (EVP_DigestFinal(evp_md_ctx.get(), hash.data(), nullptr) != 1) { - throw openssl_error_t(ERR_get_error()); - } - - return hash; -} - -} // namespace crypto diff --git a/src/crypto/hash.hpp b/src/crypto/hash.hpp deleted file mode 100644 index 35e1c67a0d2..00000000000 --- a/src/crypto/hash.hpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CRYPTO_HASH_HPP -#define CRYPTO_HASH_HPP - -#include - -#include -#include - -namespace crypto { - -namespace detail { - -std::array sha256( - unsigned char const *data, size_t size); - -} // namespace detail - -template -inline std::array sha256( - std::array array) { - return detail::sha256(array.data(), array.size()); -} - -inline std::array sha256( - std::string const &string) { - return detail::sha256( - reinterpret_cast(string.data()), string.size()); -} - -} // namespace crypto - -#endif // CRYPTO_HASH_HPP diff --git a/src/crypto/hmac.cc b/src/crypto/hmac.cc deleted file mode 100644 index eb31eb66eb0..00000000000 --- a/src/crypto/hmac.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#include "crypto/hmac.hpp" - -#include -#include -#include - -#include "crypto/error.hpp" - -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) - -inline HMAC_CTX *HMAC_CTX_new() { - HMAC_CTX *tmp = reinterpret_cast(OPENSSL_malloc(sizeof(HMAC_CTX))); - if (tmp) - HMAC_CTX_init(tmp); - return tmp; -} - -inline void HMAC_CTX_free(HMAC_CTX *ctx) { - if (ctx) { - HMAC_CTX_cleanup(ctx); - OPENSSL_free(ctx); - } -} - -#endif - -namespace crypto { - -class hmac_ctx_wrapper_t { -public: - hmac_ctx_wrapper_t() { - m_hmac_ctx = HMAC_CTX_new(); - if (m_hmac_ctx == nullptr) { - throw openssl_error_t(ERR_get_error()); - } - } - - ~hmac_ctx_wrapper_t() { - HMAC_CTX_free(m_hmac_ctx); - } - - HMAC_CTX *get() { - return m_hmac_ctx; - } - -private: - HMAC_CTX *m_hmac_ctx; -}; - -std::array detail::hmac_sha256( - unsigned char const *key, - size_t key_size, - unsigned char const *data, - size_t data_size) { - std::array hmac; - - hmac_ctx_wrapper_t hmac_ctx; - if (HMAC_Init_ex(hmac_ctx.get(), key, key_size, EVP_sha256(), nullptr) != 1) { - throw openssl_error_t(ERR_get_error()); - } - if (HMAC_Update(hmac_ctx.get(), data, data_size) != 1) { - throw openssl_error_t(ERR_get_error()); - } - if (HMAC_Final(hmac_ctx.get(), hmac.data(), nullptr) != 1) { - throw openssl_error_t(ERR_get_error()); - } - - return hmac; -} - -} // namespace crypto diff --git a/src/crypto/hmac.hpp b/src/crypto/hmac.hpp deleted file mode 100644 index 56264685674..00000000000 --- a/src/crypto/hmac.hpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CRYPTO_HMAC_HPP -#define CRYPTO_HMAC_HPP - -#include - -#include -#include - -namespace crypto { - -namespace detail { - -std::array hmac_sha256( - unsigned char const *key, - size_t key_size, - unsigned char const *data, - size_t data_size); - -} // namespace detail - -template -inline std::array hmac_sha256( - std::string const &key, - std::array const &data) { - return detail::hmac_sha256( - reinterpret_cast(key.data()), - key.size(), - data.data(), - data.size()); -} - -template -inline std::array hmac_sha256( - std::array const &key, - std::string const &data) { - return detail::hmac_sha256( - key.data(), - key.size(), - reinterpret_cast(data.data()), - data.size()); -} - - -} // namespace crypto - -#endif // CRYPTO_HMAC_HPP diff --git a/src/crypto/initialization_guard.cc b/src/crypto/initialization_guard.cc deleted file mode 100644 index f76ffd96dac..00000000000 --- a/src/crypto/initialization_guard.cc +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#include "crypto/initialization_guard.hpp" - -#ifdef _WIN32 -#include "windows.hpp" -#endif - -#include -#include -#include - -#include - -#include "arch/io/concurrency.hpp" -#include "arch/runtime/runtime.hpp" - -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) -#define OPENSSL_init_ssl(x, y) SSL_library_init() -#define OPENSSL_init_crypto(x, y) SSL_load_error_strings() -#define OPENSSL_cleanup ERR_free_strings -#endif - -namespace crypto { - -initialization_guard_t::initialization_guard_t() { - OPENSSL_init_ssl(0, nullptr); - OPENSSL_init_crypto(0, nullptr); - - // Make OpenSSL thread-safe by registering the required callbacks - CRYPTO_THREADID_set_callback([](CRYPTO_THREADID *thread_out) { - CRYPTO_THREADID_set_numeric(thread_out, get_thread_id().threadnum); - }); - CRYPTO_set_locking_callback( - [](int mode, int n, UNUSED const char *file, UNUSED int line) { - static std::vector openssl_locks(CRYPTO_num_locks()); - rassert(n >= 0); - rassert(static_cast(n) < openssl_locks.size()); - if (mode & CRYPTO_LOCK) { - if (mode & CRYPTO_READ) { - openssl_locks[n].lock_read(); - } else { - rassert(mode & CRYPTO_WRITE); - openssl_locks[n].lock_write(); - } - } else { - rassert(mode & CRYPTO_UNLOCK); - openssl_locks[n].unlock(); - } - }); -} - -initialization_guard_t::~initialization_guard_t() { - OPENSSL_cleanup(); -} - -} // namespace crypto diff --git a/src/crypto/initialization_guard.hpp b/src/crypto/initialization_guard.hpp deleted file mode 100644 index 5f94525c265..00000000000 --- a/src/crypto/initialization_guard.hpp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CRYPTO_INITIALIZATION_GUARD_HPP -#define CRYPTO_INITIALIZATION_GUARD_HPP - -namespace crypto { - -class initialization_guard_t { -public: - initialization_guard_t(); - ~initialization_guard_t(); -}; - -} // namespace crypto - -#endif // CRYPTO_INITIALIZATION_GUARD_HPP diff --git a/src/crypto/pbkcs5_pbkdf2_hmac.cc b/src/crypto/pbkcs5_pbkdf2_hmac.cc deleted file mode 100644 index 7d53a951579..00000000000 --- a/src/crypto/pbkcs5_pbkdf2_hmac.cc +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#include "crypto/pbkcs5_pbkdf2_hmac.hpp" - -#include -#include - -#include "crypto/error.hpp" - -namespace crypto { - -std::array -detail::pbkcs5_pbkdf2_hmac_sha256( - char const *password, - size_t password_size, - unsigned char const *salt, - size_t salt_size, - uint32_t iteration_count) { - std::array hmac; - - if (PKCS5_PBKDF2_HMAC( - password, - password_size, - salt, - salt_size, - iteration_count, - EVP_sha256(), - SHA256_DIGEST_LENGTH, - hmac.data()) != 1) { - throw openssl_error_t(ERR_get_error()); - } - - return hmac; -} - -} // namespace crypto diff --git a/src/crypto/pbkcs5_pbkdf2_hmac.hpp b/src/crypto/pbkcs5_pbkdf2_hmac.hpp deleted file mode 100644 index 66e0d03e8d4..00000000000 --- a/src/crypto/pbkcs5_pbkdf2_hmac.hpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CRYPTO_PKCS5_PBKDF2_HMAC_HPP -#define CRYPTO_PKCS5_PBKDF2_HMAC_HPP - -#include - -#include -#include - -namespace crypto { - -namespace detail { - -std::array pbkcs5_pbkdf2_hmac_sha256( - char const *password, - size_t password_size, - unsigned char const *salt, - size_t salt_size, - uint32_t iteration_count); - -} // namespace detail - -template -inline std::array pbkcs5_pbkdf2_hmac_sha256( - std::string const &password, - std::array const &salt, - uint32_t iteration_count) { - return detail::pbkcs5_pbkdf2_hmac_sha256( - password.data(), - password.size(), - salt.data(), - salt.size(), - iteration_count); -} - -template -inline std::array pbkcs5_pbkdf2_hmac_sha256( - std::array const &password, - std::string const &salt, - uint32_t iteration_count) { - return detail::pbkcs5_pbkdf2_hmac_sha256( - reinterpret_cast(password.data()), - password.size(), - reinterpret_cast(salt.data()), - salt.size(), - iteration_count); -} - -} // namespace crypto - -#endif // CRYPTO_PKCS5_PBKDF2_HMAC_HPP diff --git a/src/crypto/random.cc b/src/crypto/random.cc deleted file mode 100644 index 8f7381b6c77..00000000000 --- a/src/crypto/random.cc +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#include "crypto/random.hpp" - -#include - -#include "crypto/error.hpp" - -namespace crypto { - -void detail::random_bytes(unsigned char *data, size_t size) { - if (RAND_bytes(data, size) != 1) { - throw openssl_error_t(ERR_get_error()); - } -} - -} // namespace crypto diff --git a/src/crypto/random.hpp b/src/crypto/random.hpp deleted file mode 100644 index 0cf4f92ade7..00000000000 --- a/src/crypto/random.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CRYPTO_RANDOM_HPP -#define CRYPTO_RANDOM_HPP - -#include - -#include - -namespace crypto { - -namespace detail { - -void random_bytes(unsigned char *, size_t); - -} // namespace detail - -template -inline std::array random_bytes() { - std::array bytes; - detail::random_bytes(bytes.data(), bytes.size()); - return bytes; -} - -} // namespace crypto - -#endif // CRYPTO_RANDOM_HPP diff --git a/src/crypto/saslprep.cc b/src/crypto/saslprep.cc deleted file mode 100644 index c65937157d5..00000000000 --- a/src/crypto/saslprep.cc +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#include "crypto/saslprep.hpp" - -/* Commented out by srh while removing ICU dependency. All the code using it in this - file is commented out at this time. */ -// #include -// #include - -#include - -#include "crypto/error.hpp" - -namespace crypto { - -std::string saslprep(std::string const &source) { - // FIXME missing data - return source; - - /* UErrorCode status = U_ZERO_ERROR; - UStringPrepProfile *profile = usprep_openByType(USPREP_RFC4013_SASLPREP, &status); - if (U_FAILURE(status)) { - // FIXME error - } - - icu::UnicodeString unicode_source = icu::UnicodeString::fromUTF8(source); - icu::UnicodeString unicode_destination; - - status = U_ZERO_ERROR; - int32_t length = usprep_prepare( - profile, - unicode_source.getBuffer(), - unicode_source.length(), - unicode_destination.getBuffer(unicode_destination.length()), - unicode_destination.getCapacity(), - USPREP_DEFAULT, - nullptr, // UParseError parse_error - &status); - unicode_destination.releaseBuffer(length); - if (status == U_BUFFER_OVERFLOW_ERROR) { - status = U_ZERO_ERROR; - length = usprep_prepare( - profile, - unicode_source.getBuffer(), - unicode_source.length(), - unicode_destination.getBuffer(length), - unicode_destination.getCapacity(), - USPREP_DEFAULT, - nullptr, // UParseError parse_error, - &status); - unicode_destination.releaseBuffer(length); - } - if (U_FAILURE(status)) { - // FIXME error - } - - usprep_close(profile); - - std::string destination; - unicode_destination.toUTF8String(destination); - return destination; */ -} - -} // namespace crypto diff --git a/src/crypto/saslprep.hpp b/src/crypto/saslprep.hpp deleted file mode 100644 index 5232f732eaa..00000000000 --- a/src/crypto/saslprep.hpp +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2010-2016 RethinkDB, all rights reserved. -#ifndef CRYPTO_SASLPREP_HPP -#define CRYPTO_SASLPREP_HPP - -#include - -namespace crypto { - -std::string saslprep(std::string const &source); - -} // namespace crypto - -#endif // CRYPTO_SASLPREP_HPP diff --git a/src/debug.cc b/src/debug.cc deleted file mode 100644 index 69d2145d5a7..00000000000 --- a/src/debug.cc +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#include "debug.hpp" - -#include - -#include "arch/runtime/runtime.hpp" -#include "rdb_protocol/ql2proto.hpp" -#include "time.hpp" -#include "utils.hpp" - -void debug_print_quoted_string(printf_buffer_t *buf, const uint8_t *s, size_t n) { - buf->appendf("\""); - for (size_t i = 0; i < n; ++i) { - uint8_t ch = s[i]; - - switch (ch) { - case '\"': - buf->appendf("\\\""); - break; - case '\\': - buf->appendf("\\\\"); - break; - case '\n': - buf->appendf("\\n"); - break; - case '\t': - buf->appendf("\\t"); - break; - case '\r': - buf->appendf("\\r"); - break; - default: - if (ch <= '~' && ch >= ' ') { - // ASCII dependency here - buf->appendf("%c", ch); - } else { - buf->appendf("\\x%02x", ch); - } - break; - } - } - buf->appendf("\""); -} - -#ifndef NDEBUG -// Adds the time/thread id prefix to buf. -void debugf_prefix_buf(printf_buffer_t *buf) { - struct timespec t = clock_realtime(); - - format_time(t, buf, local_or_utc_time_t::local); - - if (in_thread_pool()) { - buf->appendf(" Thread %" PRIi32 ": ", get_thread_id().threadnum); - } else { - buf->appendf(" Foreign thread: "); - } -} - -void debugf_dump_buf(printf_buffer_t *buf) { - // Writing a single buffer in one shot like this makes it less - // likely that stderr debugfs and stdout printfs get mixed - // together, and probably makes it faster too. (We can't simply - // flockfile both stderr and stdout because there's no established - // rule about which one should be locked first.) - size_t nitems = fwrite(buf->data(), 1, buf->size(), stderr); - guarantee_err(nitems == size_t(buf->size()), "trouble writing to stderr"); - int res = fflush(stderr); - guarantee_err(res == 0, "fflush(stderr) failed"); -} - -void debugf(const char *msg, ...) { - printf_buffer_t buf; - debugf_prefix_buf(&buf); - - va_list ap; - va_start(ap, msg); - - buf.vappendf(msg, ap); - - va_end(ap); - - debugf_dump_buf(&buf); -} - -#endif // NDEBUG - -void debug_print(printf_buffer_t *buf, const std::string& s) { - const char *data = s.data(); - debug_print_quoted_string(buf, reinterpret_cast(data), s.size()); -} - -debugf_in_dtor_t::debugf_in_dtor_t(const char *msg, ...) { - va_list arguments; - va_start(arguments, msg); - message = vstrprintf(msg, arguments); - va_end(arguments); -} - -debugf_in_dtor_t::~debugf_in_dtor_t() { - debugf("%s", message.c_str()); -} - -debug_timer_t::debug_timer_t(std::string _name) - : start(current_microtime()), last(start), name(_name), out("\n") { - tick("start"); -} -debug_timer_t::~debug_timer_t() { - tick("end"); -#ifndef NDEBUG - debugf("%s", out.c_str()); -#else - fprintf(stderr, "%s", out.c_str()); -#endif // NDEBUG -} -microtime_t debug_timer_t::tick(const std::string &tag) { - microtime_t prev = last; - last = current_microtime(); - out += strprintf("TIMER %s: %15s (%" PRIu64 " %12" PRIu64 " %12" PRIu64 ")\n", - name.c_str(), tag.c_str(), last, last - start, last - prev); - return last - start; -} diff --git a/src/debug.hpp b/src/debug.hpp deleted file mode 100644 index e2402f5e1cd..00000000000 --- a/src/debug.hpp +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2010-2015 RethinkDB, all rights reserved. -#ifndef DEBUG_HPP_ -#define DEBUG_HPP_ - -#include - -#include "containers/printf_buffer.hpp" -#include "time.hpp" - -#ifndef NDEBUG -#define trace_call(fn, ...) do { \ - debugf("%s:%u: %s: entered\n", __FILE__, __LINE__, stringify(fn)); \ - fn(__VA_ARGS__); \ - debugf("%s:%u: %s: returned\n", __FILE__, __LINE__, stringify(fn)); \ - } while (0) -#define TRACEPOINT debugf("%s:%u reached\n", __FILE__, __LINE__) -#else -#define trace_call(fn, ...) fn(__VA_ARGS__) -// TRACEPOINT is not defined in release, so that TRACEPOINTS do not linger in the code unnecessarily -#endif - -// HEY: Maybe debugf and log_call and TRACEPOINT should be placed in -// debugf.hpp (and debugf.cc). -/* Debugging printing API (prints current thread in addition to message) */ -void debug_print_quoted_string(printf_buffer_t *buf, const uint8_t *s, size_t n); -void debugf_prefix_buf(printf_buffer_t *buf); -void debugf_dump_buf(printf_buffer_t *buf); - -// Primitive debug_print declarations. -template -typename std::enable_if::value>::type -debug_print(printf_buffer_t *buf, T x) { - debug_print(buf, std::to_string(x)); -} - -void debug_print(printf_buffer_t *buf, const std::string& s); - -template -void debug_print(printf_buffer_t *buf, T *ptr) { - buf->appendf("%p", ptr); -} - -template -std::string debug_str(const T &t) { - printf_buffer_t buf; - debug_print(&buf, t); - return buf.c_str(); -} - -#ifndef NDEBUG -void debugf(const char *msg, ...) ATTR_FORMAT(printf, 1, 2); -template -void debugf_print(const char *msg, const T &obj) { - printf_buffer_t buf; - debugf_prefix_buf(&buf); - buf.appendf("%s: ", msg); - debug_print(&buf, obj); - buf.appendf("\n"); - debugf_dump_buf(&buf); -} -#else -#define debugf(...) ((void)0) -#define debugf_print(...) ((void)0) -#endif // NDEBUG - -template -std::string debug_strprint(const T &obj) { - printf_buffer_t buf; - debug_print(&buf, obj); - return std::string(buf.data(), buf.size()); -} - -class debugf_in_dtor_t { -public: - explicit debugf_in_dtor_t(const char *msg, ...) ATTR_FORMAT(printf, 2, 3); - ~debugf_in_dtor_t(); -private: - std::string message; -}; - -// TODO: make this more efficient (use `clock_monotonic` and use a vector of -// integers rather than accumulating a string). -class debug_timer_t { -public: - explicit debug_timer_t(std::string _name = ""); - ~debug_timer_t(); - microtime_t tick(const std::string &tag); -private: - microtime_t start, last; - std::string name, out; - DISABLE_COPYING(debug_timer_t); -}; - -#endif // DEBUG_HPP_ - diff --git a/src/do_on_thread.hpp b/src/do_on_thread.hpp deleted file mode 100644 index 2cbb1209864..00000000000 --- a/src/do_on_thread.hpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef DO_ON_THREAD_HPP_ -#define DO_ON_THREAD_HPP_ - -#include "arch/runtime/runtime.hpp" -#include "utils.hpp" - -/* Functions to do something on another core in a way that is more convenient than -continue_on_thread() is. */ - -template -struct thread_doer_t : public thread_message_t, public home_thread_mixin_t { - const callable_t callable; - threadnum_t thread; - enum state_t { - state_go_to_core, - state_go_home - } state; - - thread_doer_t(callable_t &&_callable, threadnum_t _thread) - : callable(std::forward(_callable)), - thread(_thread), - state(state_go_to_core) { - assert_good_thread_id(thread); - } - - void run() { - state = state_go_to_core; - if (continue_on_thread(thread, this)) { - do_perform_job(); - } - } - - void do_perform_job() { - rassert(thread == get_thread_id()); - callable(); - do_return_home(); - } - - // We go back to the home thread because it's nice to libtcmalloc - // if we free memory on the same thread upon which it was - // allocated. - void do_return_home() { - state = state_go_home; - - DEBUG_VAR bool no_switch = continue_on_thread(home_thread(), this); - rassert(!no_switch); - } - - void on_thread_switch() { - switch (state) { - case state_go_to_core: - do_perform_job(); - return; - case state_go_home: - delete this; - return; - default: - unreachable("Bad state."); - } - } -}; - -/* API to allow a nicer way of performing jobs on other cores than subclassing -from thread_message_t. Call do_on_thread() with an object and a method for that object. -The method will be called on the other thread. */ - -template -void do_on_thread(threadnum_t thread, callable_t &&callable) { - assert_good_thread_id(thread); - - if (thread == get_thread_id()) { - // Run the function directly since we are already in the requested thread - callable(); - } else { - thread_doer_t *fsm - = new thread_doer_t(std::forward(callable), - thread); - fsm->run(); - } -} - - -#endif // DO_ON_THREAD_HPP_ diff --git a/src/errors.cc b/src/errors.cc deleted file mode 100644 index 8ae68d223aa..00000000000 --- a/src/errors.cc +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright 2010-2012 RethinkDB, all rights reserved. -#include "errors.hpp" - -#ifndef _MSC_VER -#include -#endif - -#include -#include -#include -#include -#include - -#include - -#include "logger.hpp" -#include "utils.hpp" -#include "backtrace.hpp" -#include "thread_local.hpp" -#include "clustering/administration/logs/log_writer.hpp" -#include "arch/timing.hpp" -#include "arch/runtime/thread_pool.hpp" - -TLS_with_init(bool, crashed, false); // to prevent crashing within crashes - -NOINLINE int get_errno() { - return errno; -} -NOINLINE void set_errno(int new_errno) { - errno = new_errno; -} - -NORETURN void crash_oom() { - const char *message = "rethinkdb: Memory allocation failed. This usually means " - "that we have run out of RAM. Aborting.\n"; - UNUSED size_t res = fwrite(message, 1, strlen(message), stderr); - abort(); -} - -void report_user_error(const char *msg, ...) { - fprintf(stderr, "Version: %s\n", RETHINKDB_VERSION_STR); - if (TLS_get_crashed()) { - va_list args; - va_start(args, msg); - fprintf(stderr, "Crashing while already crashed. Printing error message to stderr.\n"); - vfprintf(stderr, msg, args); - va_end(args); - return; - } - - TLS_set_crashed(true); - - thread_log_writer_disabler_t disabler; - - va_list args; - va_start(args, msg); - vlogERR(msg, args); - va_end(args); -} - -void report_fatal_error(const char *file, int line, const char *msg, ...) { - fprintf(stderr, "Version: %s\n", RETHINKDB_VERSION_STR); - if (TLS_get_crashed()) { - va_list args; - va_start(args, msg); - fprintf(stderr, "Crashing while already crashed. Printing error message to stderr.\n"); - vfprintf(stderr, msg, args); - va_end(args); - return; - } - - TLS_set_crashed(true); - - thread_log_writer_disabler_t disabler; - - va_list args; - va_start(args, msg); - logERR("Error in thread %d in %s at line %d:", get_thread_id().threadnum, file, line); - vlogERR(msg, args); - va_end(args); - - /* Don't print backtraces in valgrind mode because valgrind issues lots of spurious - warnings when print_backtrace() is run. */ -#if !defined(VALGRIND) - logERR("Backtrace:"); - logERR("%s", format_backtrace().c_str()); -#endif - - logERR("Exiting."); -} - -const char *errno_string_maybe_using_buffer(int errsv, char *buf, size_t buflen) { -#ifdef __GLIBC__ - return strerror_r(errsv, buf, buflen); -#elif defined(_WIN32) - UNUSED errno_t res = strerror_s(buf, buflen, errsv); - return buf; -#else - // The result is either 0 or ERANGE (if the buffer is too small) or EINVAL (if the error number - // is invalid), but in either case a friendly nul-terminated buffer is written. - UNUSED int res = strerror_r(errsv, buf, buflen); - return buf; -#endif -} - -#ifdef _WIN32 -MUST_USE const std::string winerr_string(DWORD winerr) { - char *errmsg; - DWORD res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, winerr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&errmsg, 0, nullptr); - if (res != 0) { - size_t end = strlen(errmsg); - while (end > 0 && isspace(errmsg[end-1])) { - end--; - } - errmsg[end] = 0; - std::string ret(errmsg); - LocalFree(errmsg); - return ret; - } else { - return strprintf("Unkown error 0x%x (error formatting the error: 0x%x)", winerr, GetLastError()); - } -} -#endif - -/* Handlers for various signals and for unexpected exceptions or calls to std::terminate() */ - -#ifndef _WIN32 -NORETURN void generic_crash_handler(int signum) { - if (signum == SIGSEGV) { - crash("Segmentation fault."); - } else if (signum == SIGBUS) { - crash("Bus error."); - } else { - crash("Unexpected signal: %d", signum); - } -} -#endif - -NORETURN void rdb_terminate_handler() { -#ifdef _WIN32 - bool t = std::uncaught_exception(); -#else - std::type_info *t = abi::__cxa_current_exception_type(); -#endif - if (t) { -#ifdef _WIN32 - // On windows, uncaught exceptions are handled in windows_crash_handler - std::string name = "unknown"; -#else - std::string name; - try { - name = demangle_cpp_name(t->name()); - } catch (const demangle_failed_exc_t &) { - name = t->name(); - } -#endif - try { - /* This will rethrow whatever unexpected exception was thrown. */ - throw; - } catch (const std::exception& e) { - crash("Uncaught exception of type \"%s\"\n what(): %s", name.c_str(), e.what()); - } catch (...) { - crash("Uncaught exception of type \"%s\"", name.c_str()); - } - unreachable(); - } else { - crash("std::terminate() called without any exception."); - } -} - -#ifdef _WIN32 -LONG WINAPI windows_crash_handler(EXCEPTION_POINTERS *exception) { - std::string message; - EXCEPTION_RECORD *record = exception->ExceptionRecord; - switch (record->ExceptionCode) { - case 0xe06d7363: - try { - _CxxThrowException(reinterpret_cast(record->ExceptionInformation[1]), - reinterpret_cast<_ThrowInfo*>(record->ExceptionInformation[2])); - } catch (std::exception &e) { - message = strprintf("Unhandled C++ exception: %s: %s", typeid(e).name(), e.what()); - } catch (...) { - message = "Unhandled C++ exception"; - } - break; - case EXCEPTION_BREAKPOINT: - return EXCEPTION_EXECUTE_HANDLER; - case EXCEPTION_ACCESS_VIOLATION: - message = "ACCESS_VIOLATION: The thread tried to read from or write to a virtual address for which it does not have the appropriate access."; - break; - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - message = "ARRAY_BOUNDS_EXCEEDED: The thread tried to access an array element that is out of bounds and the underlying hardware supports bounds checking."; - break; - case EXCEPTION_DATATYPE_MISALIGNMENT: - message = "DATATYPE_MISALIGNMENT: The thread tried to read or write data that is misaligned on hardware that does not provide alignment. For example, 16-bit values must be aligned on 2-byte boundaries; 32-bit values on 4-byte boundaries, and so on."; - break; - case EXCEPTION_FLT_DENORMAL_OPERAND: - message = "FLT_DENORMAL_OPERAND: One of the operands in a floating-point operation is denormal. A denormal value is one that is too small to represent as a standard floating-point value."; - break; - case EXCEPTION_FLT_DIVIDE_BY_ZERO: - message = "FLT_DIVIDE_BY_ZERO: The thread tried to divide a floating-point value by a floating-point divisor of zero."; - break; - case EXCEPTION_FLT_INEXACT_RESULT: - message = "FLT_INEXACT_RESULT: The result of a floating-point operation cannot be represented exactly as a decimal fraction."; - break; - case EXCEPTION_FLT_INVALID_OPERATION: - message = "FLT_INVALID_OPERATION: This exception represents any floating-point exception not included in this list."; - break; - case EXCEPTION_FLT_OVERFLOW: - message = "FLT_OVERFLOW: The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type."; - break; - case EXCEPTION_FLT_STACK_CHECK: - message = "FLT_STACK_CHECK: The stack overflowed or underflowed as the result of a floating-point operation."; - break; - case EXCEPTION_FLT_UNDERFLOW: - message = "FLT_UNDERFLOW: The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type."; - break; - case EXCEPTION_ILLEGAL_INSTRUCTION: - message = "ILLEGAL_INSTRUCTION: The thread tried to execute an invalid instruction."; - break; - case EXCEPTION_IN_PAGE_ERROR: - message = "IN_PAGE_ERROR: The thread tried to access a page that was not present, and the system was unable to load the page. For example, this exception might occur if a network connection is lost while running a program over the network."; - break; - case EXCEPTION_INT_DIVIDE_BY_ZERO: - message = "INT_DIVIDE_BY_ZERO: The thread tried to divide an integer value by an integer divisor of zero."; - break; - case EXCEPTION_INT_OVERFLOW: - message = "INT_OVERFLOW: The result of an integer operation caused a carry out of the most significant bit of the result."; - break; - case EXCEPTION_INVALID_DISPOSITION: - message = "INVALID_DISPOSITION: An exception handler returned an invalid disposition to the exception dispatcher. Programmers using a high-level language such as C should never encounter this exception."; - break; - case EXCEPTION_NONCONTINUABLE_EXCEPTION: - message = "NONCONTINUABLE_EXCEPTION: The thread tried to continue execution after a noncontinuable exception occurred."; - break; - case EXCEPTION_PRIV_INSTRUCTION: - message = "PRIV_INSTRUCTION: The thread tried to execute an instruction whose operation is not allowed in the current machine mode."; - break; - case EXCEPTION_SINGLE_STEP: - message = "SINGLE_STEP: A trace trap or other single-instruction mechanism signaled that one instruction has been executed."; - break; - case EXCEPTION_STACK_OVERFLOW: - message = "STACK_OVERFLOW: The thread used up its stack."; - break; - default: - message = "Unkown exception."; - } - - logERR("Windows exception 0x%x: %s", exception->ExceptionRecord->ExceptionCode, message.c_str()); - logERR("backtrace:\n%s", format_backtrace().c_str()); - - // This usually results in process termination - return EXCEPTION_EXECUTE_HANDLER; -} - -int windows_runtime_debug_failure_handler(int type, char *message, int *retval) { - logERR("run-time debug failure:\n%s", message); - logERR("backtrace:\n%s", format_backtrace().c_str()); - - return false; -} - -BOOL WINAPI windows_ctrl_handler(DWORD type) { - thread_pool_t::interrupt_handler(type); - return true; -} -#endif - -void install_generic_crash_handler() { -#ifdef _WIN32 - // TODO WINDOWS: maybe call SetErrorMode - SetUnhandledExceptionFilter(windows_crash_handler); - SetConsoleCtrlHandler(windows_ctrl_handler, true); -#ifdef _MSC_VER - _CrtSetReportHook(windows_runtime_debug_failure_handler); -#endif -#else -#ifndef VALGRIND - { - struct sigaction sa = make_sa_handler(0, generic_crash_handler); - - int res = sigaction(SIGSEGV, &sa, nullptr); - guarantee_err(res == 0, "Could not install SEGV signal handler"); - res = sigaction(SIGBUS, &sa, nullptr); - guarantee_err(res == 0, "Could not install BUS signal handler"); - } -#endif - - struct sigaction sa = make_sa_handler(0, SIG_IGN); - int res = sigaction(SIGPIPE, &sa, nullptr); - guarantee_err(res == 0, "Could not install PIPE handler"); -#endif - - std::set_terminate(&rdb_terminate_handler); -} - -/* If a call to `operator new()` or `operator new[]()` fails, we have to crash -immediately. -The default behavior of this handler is to throw an `std::bad_alloc` exception. -However that is dangerous because it unwinds the stack and can cause side-effects -including data corruption. */ -NORETURN void new_oom_handler() { - crash_oom(); -} - -void install_new_oom_handler() { - std::set_new_handler(&new_oom_handler); -} - - -/* Boost will call this function when an assertion fails. */ - -namespace boost { - -void assertion_failed(char const * expr, char const * function, char const * file, long line) { // NOLINT(runtime/int) - report_fatal_error(file, line, "BOOST_ASSERT failure in '%s': %s", function, expr); - BREAKPOINT; -} - -void assertion_failed_msg(char const * expr, char const * msg, char const * function, char const * file, long line) { // NOLINT(runtime/int) - report_fatal_error(file, line, "BOOST_ASSERT_MSG failure in '%s': %s (%s)", function, expr, msg); - BREAKPOINT; -} - -} // namespace boost diff --git a/src/errors.hpp b/src/errors.hpp deleted file mode 100644 index 6727130f4bb..00000000000 --- a/src/errors.hpp +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef ERRORS_HPP_ -#define ERRORS_HPP_ - -#include -#include -#include -#include - -#include "arch/compiler.hpp" - -#ifdef _WIN32 -#include "windows.hpp" -#endif - -#ifndef DISABLE_BREAKPOINTS -#ifdef __linux__ -#if defined __i386 || defined __x86_64 -#define BREAKPOINT __asm__ volatile ("int3") -#else /* not x86/amd64 */ -#define BREAKPOINT (raise(SIGTRAP)) -#endif /* x86/amd64 */ -#elif defined(__MACH__) -#define BREAKPOINT (raise(SIGTRAP)) -#elif defined(_WIN32) -#define BREAKPOINT DebugBreak() -#else -#error "BREAKPOINT not defined for this operating system" -#endif -#else /* Breakpoints Disabled */ -#define BREAKPOINT -#endif /* DISABLE_BREAKPOINTS */ - -#define CT_ASSERT(e) static_assert(e, #e) - -#ifndef NDEBUG -#define DEBUG_ONLY(...) __VA_ARGS__ -#define DEBUG_ONLY_CODE(expr) do { expr; } while (0) -#else -#define DEBUG_ONLY(...) -#define DEBUG_ONLY_CODE(expr) ((void)(0)) -#endif - -// Static branch-prediction hint for guarantees -#if defined __clang__ || defined __GNUC__ -#define UNLIKELY(x) __builtin_expect(x, 0) -#else -#define UNLIKELY(x) x -#endif - -/* Accessors to errno. - * Please access errno *only* through these access functions. - * Accessing errno directly is unsafe in the context of - * coroutines because compiler optimizations can interfer with TLS, which - * might be used for errno. - * See thread_local.hpp for a more detailed explanation of the issue. */ -int get_errno(); -void set_errno(int new_errno); -/* The following line can be useful for identifying illegal direct access in our - * code. However it cannot be turned on in general because some system headers use - * errno and don't compile with this. */ -//#pragma GCC poison errno - -/* Error handling - * - * There are several ways to report errors in RethinkDB: - * fail_due_to_user_error(msg, ...) fail and report line number/stack trace. Should only be used when the user - * is at fault (e.g. provided wrong database file name) and it is reasonable to - * fail instead of handling the problem more gracefully. - * - * The following macros are used only for the cases of programmer error checking. For the time being they are also used - * for system error checking (especially the *_err variants). - * - * crash(msg, ...) always fails and reports line number and such. Never returns. - * crash_or_trap(msg, ...) same as above, but traps into debugger if it is present instead of terminating. - * That means that it possibly can return, and one can continue stepping through the code in the debugger. - * All off the rassert/guarantee functions use crash_or_trap. - * rassert(cond) makes sure cond is true and is a no-op in release mode - * rassert(cond, msg, ...) ditto, with additional message and possibly some arguments used for formatting - * rassert_err(cond) same as rassert(cond), but also print errno error description - * rassert_err(cond, msg, ...) same as rassert(cond, msg, ...), but also print errno error description - * guarantee(cond) same as rassert(cond), but the check is still done in release mode. Do not use for expensive checks! - * guarantee(cond, msg, ...) same as rassert(cond, msg, ...), but the check is still done in release mode. Do not use for expensive checks! - * guarantee_err(cond) same as guarantee(cond), but also print errno error description - * guarantee_err(cond, msg, ...) same as guarantee(cond, msg, ...), but also print errno error description - * guarantee_xerr(cond, err, msg, ...) same as guarantee_err(cond, msg, ...), but also allows to specify errno as err argument - * (useful for async io functions, which return negated errno) - * - * The names rassert* are used instead of assert* because /usr/include/assert.h undefines assert macro and redefines it with its own version - * every single time it gets included. - */ - -#ifndef NDEBUG -#define DEBUG_VAR -#else -#define DEBUG_VAR UNUSED -#endif - -#define fail_due_to_user_error(msg, ...) do { \ - report_user_error(msg, ##__VA_ARGS__); \ - BREAKPOINT; \ - exit(EXIT_FAILURE); \ - } while (0) - -#define crash(msg, ...) do { \ - report_fatal_error(__FILE__, __LINE__, msg, ##__VA_ARGS__); \ - BREAKPOINT; /* this used to be abort(), but it didn't cause VALGRIND to print a backtrace */ \ - ::abort(); \ - } while (0) - -#define crash_or_trap(msg, ...) do { \ - report_fatal_error(__FILE__, __LINE__, msg, ##__VA_ARGS__); \ - BREAKPOINT; \ - } while (0) - -void report_fatal_error(const char*, int, const char*, ...) ATTR_FORMAT(printf, 3, 4); -void report_user_error(const char*, ...) ATTR_FORMAT(printf, 1, 2); - -// Our usual crash() method does not work well in out-of-memory conditions, because -// it performs heap-allocations itself. Use `crash_oom()` instead for these cases. -NORETURN void crash_oom(); - -// Possibly using buf to store characters, returns a pointer to a strerror-style error string. This -// has the same contract as the GNU (char *)-returning strerror_r. The return value is a pointer to -// a nul-terminated string, either equal to buf or pointing at a statically allocated string. -MUST_USE const char *errno_string_maybe_using_buffer(int errsv, char *buf, size_t buflen); - -#ifdef _WIN32 -MUST_USE const std::string winerr_string(DWORD winerr); -#endif - -#define stringify(x) #x - -#define format_assert_message(assert_type, cond) assert_type " failed: [" stringify(cond) "] " -#define guarantee(cond, ...) do { \ - if (UNLIKELY(!(cond))) { \ - crash_or_trap(format_assert_message("Guarantee", cond) __VA_ARGS__); \ - } \ - } while (0) - -#define guarantee_xerr(cond, err, msg, ...) do { \ - int guarantee_xerr_errsv = (err); \ - if (UNLIKELY(!(cond))) { \ - if (guarantee_xerr_errsv == 0) { \ - crash_or_trap(format_assert_message("Guarantee", cond) msg, ##__VA_ARGS__); \ - } else { \ - char guarantee_xerr_buf[250]; \ - const char *errstr = errno_string_maybe_using_buffer(guarantee_xerr_errsv, guarantee_xerr_buf, sizeof(guarantee_xerr_buf)); \ - crash_or_trap(format_assert_message("Guarantee", cond) " (errno %d - %s) " msg, guarantee_xerr_errsv, errstr, ##__VA_ARGS__); \ - } \ - } \ - } while (0) -#define guarantee_err(cond, ...) guarantee_xerr(cond, get_errno(), ##__VA_ARGS__) - -#ifdef _WIN32 - -#define guarantee_xwinerr(cond, err, msg, ...) do { \ - if (UNLIKELY(!(cond))) { \ - DWORD guarantee_winerr_err = (err); \ - crash_or_trap(format_assert_message("Guarantee", (cond)) "(error 0x%x - %s) " msg, guarantee_winerr_err, winerr_string(guarantee_winerr_err).c_str(), ##__VA_ARGS__); \ - } \ - } while (0) - -#define guarantee_winerr(cond, ...) guarantee_xwinerr(cond, GetLastError(), ##__VA_ARGS__) - -#endif - -#define unreachable(...) crash("Unreachable code: " __VA_ARGS__) // can't use crash_or_trap since code needs to depend on its noreturn property - -#ifdef NDEBUG -#define rassert(cond, ...) ((void)(0)) -#define rassert_err(cond, ...) ((void)(0)) -#else -#define rassert(cond, ...) do { \ - if (UNLIKELY(!(cond))) { \ - crash_or_trap(format_assert_message("Assertion", cond) __VA_ARGS__); \ - } \ - } while (0) -#define rassert_err(cond, msg, ...) do { \ - if (UNLIKELY(!(cond))) { \ - int rassert_err_errsv = get_errno(); \ - if (rassert_err_errsv == 0) { \ - crash_or_trap(format_assert_message("Assert", cond) msg); \ - } else { \ - char rassert_err_buf[250]; \ - const char *errstr = errno_string_maybe_using_buffer(rassert_err_errsv, rassert_err_buf, sizeof(rassert_err_buf)); \ - crash_or_trap(format_assert_message("Assert", cond) " (errno %d - %s) " msg, rassert_err_errsv, errstr, ##__VA_ARGS__); \ - } \ - } \ - } while (0) -#endif - - -void install_generic_crash_handler(); -void install_new_oom_handler(); - -// If you include errors.hpp before including a Boost library, then Boost assertion -// failures will be forwarded to the RethinkDB error mechanism. -#define BOOST_ENABLE_ASSERT_HANDLER -namespace boost { -void assertion_failed(char const * expr, char const * function, char const * file, long line); // NOLINT(runtime/int) -} - -#define DISABLE_COPYING(T) \ - T(const T&) = delete; \ - T& operator=(const T&) = delete - -#define MOVABLE_BUT_NOT_COPYABLE(T) \ - DISABLE_COPYING(T); \ - T(T &&) = default; \ - T &operator=(T &&) = default - - -/* Put these after functions to indicate what they throw. In all modes, they -turn into noops so that the compiler doesn't have to generate exception-checking -code everywhere. Originally, these resolved to throw() and throw(__VA_ARGS__) -in debug mode, but C++17 removed the language feature. Consider these to be -vestigial. */ -#define THROWS_NOTHING -#define THROWS_ONLY(...) - -// This is a workaround for old versions of boost causing a compilation error -#include // NOLINT(build/include_order) -#if (BOOST_VERSION >= 104200) && (BOOST_VERSION <= 104399) -#include // NOLINT(build/include_order) -#undef BOOST_HAS_RVALUE_REFS -#endif - -#ifdef __GNUC__ -#define GNUC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) -#endif - - -/** RVALUE_THIS - * - * This macro is used to annotate methods that treat *this as an - * rvalue reference. On compilers that support it, it expands to && - * and all uses of the method on non-rvlaue *this are reported as - * errors. - * - * The supported compilers are clang >= 2.9 and gcc >= 4.8.1 - * - **/ -#if defined(__clang__) -#if __has_extension(cxx_rvalue_references) -#define RVALUE_THIS && -#else -#define RVALUE_THIS -#endif -#elif __GNUC__ > 4 || (__GNUC__ == 4 && \ - (__GNUC_MINOR__ > 8 || (__GNUC_MINOR__ == 8 && \ - __GNUC_PATCHLEVEL__ > 1))) -#define RVALUE_THIS && -#elif defined(_MSC_VER) -#define RVALUE_THIS && -#else -#define RVALUE_THIS -#endif - - -#if defined(__clang__) - #if !__has_extension(cxx_override_control) - #define override - #define final - #endif -#elif defined(__GNUC__) && GNUC_VERSION < 40700 - #define override - #define final -#endif - -#endif /* ERRORS_HPP_ */ diff --git a/src/extproc/extproc_job.cc b/src/extproc/extproc_job.cc deleted file mode 100644 index d82cab73e2c..00000000000 --- a/src/extproc/extproc_job.cc +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "extproc/extproc_job.hpp" -#include "extproc/extproc_pool.hpp" -#include "extproc/extproc_worker.hpp" - -extproc_job_t::extproc_job_t(extproc_pool_t *_pool, - bool (*worker_fn) (read_stream_t *, write_stream_t *), - signal_t *_user_interruptor) : - pool(_pool), - user_error(false), - user_interruptor(_user_interruptor), - combined_interruptor(pool->get_shutdown_signal()) -{ - if (user_interruptor != nullptr) { - combined_interruptor.add(user_interruptor); - } - - worker_lock.create(pool->get_worker_semaphore(), &combined_interruptor); - - try { - worker_lock.get()->get_value()->acquired(&combined_interruptor); - } catch (...) { - user_error = true; - throw; - } - - try { - extproc_pool_t::worker_acq_t worker_acq(pool); - worker_lock.get()->get_value()->run_job(worker_fn); - } catch (...) { - user_error = true; - worker_lock.get()->get_value()->released(user_error, user_interruptor); - throw; - } -} - -extproc_job_t::~extproc_job_t() { - assert_thread(); - worker_lock.get()->get_value()->released(user_error, user_interruptor); -} - -// All data written and read by the user must be accounted for, or things will break -read_stream_t *extproc_job_t::read_stream() { - assert_thread(); - return worker_lock.get()->get_value()->get_read_stream(); -} - -write_stream_t *extproc_job_t::write_stream() { - assert_thread(); - return worker_lock.get()->get_value()->get_write_stream(); -} - -void extproc_job_t::worker_error() { - user_error = true; -} diff --git a/src/extproc/extproc_job.hpp b/src/extproc/extproc_job.hpp deleted file mode 100644 index a49ec84c742..00000000000 --- a/src/extproc/extproc_job.hpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef EXTPROC_EXTPROC_JOB_HPP_ -#define EXTPROC_EXTPROC_JOB_HPP_ - -#include -#include - -#include "utils.hpp" -#include "containers/archive/archive.hpp" -#include "concurrency/wait_any.hpp" -#include "concurrency/cross_thread_semaphore.hpp" -#include "containers/object_buffer.hpp" - -class extproc_pool_t; -class extproc_worker_t; - -// Error that may be thrown when something goes wrong with a worker -// Should be used by any derived extproc tasks (like HTTP or JS) -class extproc_worker_exc_t : public std::exception { -public: - explicit extproc_worker_exc_t(const std::string& data) : info(data) { } - ~extproc_worker_exc_t() throw () { } - const char *what() const throw () { return info.c_str(); } -private: - std::string info; -}; - -class extproc_job_t : public home_thread_mixin_t { -public: - extproc_job_t(extproc_pool_t *_pool, - bool (*worker_fn) (read_stream_t *, write_stream_t *), - signal_t *_user_interruptor); - ~extproc_job_t(); - - // All data written and read by the user must be accounted for, or the worker will - // has to be killed and restarted - read_stream_t *read_stream(); - write_stream_t *write_stream(); - - // Called by a user when an error occurs, and they want to mark the worker as 'dirty' - // so that it will be killed later. This will skip the step where the main process - // attempts to resynchronize with the worker. - void worker_error(); - -private: - extproc_pool_t *pool; - bool user_error; - signal_t *user_interruptor; - wait_any_t combined_interruptor; - object_buffer_t::lock_t> worker_lock; -}; - -#endif /* EXTPROC_EXTPROC_JOB_HPP_ */ diff --git a/src/extproc/extproc_pool.cc b/src/extproc/extproc_pool.cc deleted file mode 100644 index 944c1a12de3..00000000000 --- a/src/extproc/extproc_pool.cc +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include "containers/object_buffer.hpp" -#include "extproc/extproc_pool.hpp" -#include "extproc/extproc_spawner.hpp" - -extproc_pool_t::extproc_pool_t(size_t worker_count) : - ct_interruptors(&interruptor), - worker_cnt(0), - prev_worker_cnt(0), - dealloc_timer(DEALLOC_TIMER_FREQ_MS, this), - worker_semaphore(worker_count, - extproc_spawner_t::get_instance()), - dealloc_pumper([this](signal_t *interruptor_) { dealloc_blocking(interruptor_); }) - { } - -extproc_pool_t::~extproc_pool_t() { - // Can only be destructed on the same thread we were created on - assert_thread(); - - // Interrupt any and all workers - interruptor.pulse(); -} - -cross_thread_semaphore_t *extproc_pool_t::get_worker_semaphore() { - return &worker_semaphore; -} - -signal_t *extproc_pool_t::get_shutdown_signal() { - return ct_interruptors.get(); -} - -void extproc_pool_t::on_ring() { - dealloc_pumper.notify(); -} - -void extproc_pool_t::dealloc_blocking(UNUSED signal_t *interruptor_) { - int cur_worker_cnt = worker_cnt; - int dealloc_cnt = (prev_worker_cnt - cur_worker_cnt) / 2; - prev_worker_cnt = cur_worker_cnt; - - for (int i = 0; i < dealloc_cnt; ++i) { - object_buffer_t::lock_t> worker_lock; - worker_lock.create(get_worker_semaphore(), get_shutdown_signal()); - - if (worker_lock.get()->get_value()->is_process_alive()) { - worker_lock.get()->get_value()->kill_process(); - } - } -} - -void extproc_pool_t::on_worker_acquired() -{ - ++worker_cnt; -} - -void extproc_pool_t::on_worker_released() -{ - --worker_cnt; -} - -extproc_pool_t::ct_interruptors_t::ct_interruptors_t(signal_t *shutdown_signal) : - ct_signals(get_num_threads()) -{ - for (int i = 0; i < get_num_threads(); ++i) { - ct_signals[i].init(new cross_thread_signal_t(shutdown_signal, threadnum_t(i))); - } -} - -signal_t *extproc_pool_t::ct_interruptors_t::get() { - return ct_signals[get_thread_id().threadnum].get(); -} diff --git a/src/extproc/extproc_pool.hpp b/src/extproc/extproc_pool.hpp deleted file mode 100644 index 71575fd9d13..00000000000 --- a/src/extproc/extproc_pool.hpp +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef EXTPROC_EXTPROC_POOL_HPP_ -#define EXTPROC_EXTPROC_POOL_HPP_ - -#include - -#include "arch/timing.hpp" -#include "utils.hpp" -#include "containers/scoped.hpp" -#include "concurrency/cond_var.hpp" -#include "concurrency/cross_thread_signal.hpp" -#include "concurrency/cross_thread_semaphore.hpp" -#include "concurrency/pump_coro.hpp" -#include "extproc/extproc_worker.hpp" - -// Extproc pool is used to acquire and release workers from any thread, -// must be created from within the thread pool -class extproc_pool_t : public home_thread_mixin_t, - public repeating_timer_callback_t { -public: - explicit extproc_pool_t(size_t worker_count); - ~extproc_pool_t(); - - // Get the signal for the current thread that will indicate when this object is being - // destroyed, make sure to combine with any other interruptors or shutdown may hang - signal_t *get_shutdown_signal(); - - // Get the semaphore of workers to obtain a lock (may be done from any thread) - cross_thread_semaphore_t *get_worker_semaphore(); - - class worker_acq_t { - public: - explicit worker_acq_t(extproc_pool_t *_pool) : pool(_pool) { - pool->on_worker_acquired(); - } - ~worker_acq_t() { - pool->on_worker_released(); - } - private: - extproc_pool_t *pool; - DISABLE_COPYING(worker_acq_t); - }; - -private: - // The interruptor to be pulsed when shutting down - cond_t interruptor; - - // Crossthreaded interruptors to notify workers on any thread when we are shutting down - // (this is its own class so we can initialize everything in the initializer list) - class ct_interruptors_t { - public: - explicit ct_interruptors_t(signal_t *shutdown_signal); - signal_t *get(); - private: - scoped_array_t > ct_signals; - } ct_interruptors; - - // Worker counters for the deallocation window. - std::atomic worker_cnt; - int prev_worker_cnt; - - // Acquire / Release worker notifications. - void on_worker_acquired(); - void on_worker_released(); - - // Timer to trigger worker deallocation. - repeating_timer_t dealloc_timer; - static const int64_t DEALLOC_TIMER_FREQ_MS = 2000; - - // Worker deallocation timer callback. - void on_ring(); - - // Callback that handles worker deallocations in a coro pool, so we don't block the - // timer callback or have multiple deallocations happening at once - void dealloc_blocking(UNUSED signal_t *interruptor); - - // Cross-threaded semaphore allowing workers to be acquired from any thread - cross_thread_semaphore_t worker_semaphore; - - // This `pump_coro_t` spawns `dealloc_blocking()` on request, making sure that there - // are never two copies running at once. Destructor order is important: - // `dealloc_pumper` must be destroyed before everything else because it stops - // `dealloc_blocking()`. - pump_coro_t dealloc_pumper; -}; - -#endif /* EXTPROC_EXTPROC_POOL_HPP_ */ diff --git a/src/extproc/extproc_spawner.cc b/src/extproc/extproc_spawner.cc deleted file mode 100644 index 9cba6f77a34..00000000000 --- a/src/extproc/extproc_spawner.cc +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef _WIN32 -#include -#include -#include -#else // _WIN32 -#include -#endif // _WIN32 - -#include -#include -#include - -#include "arch/process.hpp" -#include "extproc/extproc_job.hpp" -#include "extproc/extproc_spawner.hpp" -#include "extproc/extproc_worker.hpp" -#include "arch/fd_send_recv.hpp" -#include "utils.hpp" - -extproc_spawner_t *extproc_spawner_t::instance = nullptr; - -// This is the class that runs in the external process, doing all the work -class worker_run_t { -public: - worker_run_t(fd_t _socket, process_id_t _spawner_pid) : - socket(_socket), socket_stream(socket.get(), make_scoped()) { - -#ifdef _WIN32 - // TODO WINDOWS: make sure the worker process gets killed -#else - guarantee(spawner_pid == INVALID_PROCESS_ID); - spawner_pid = _spawner_pid; - - // Set ourselves to get interrupted when our parent dies - struct sigaction sa = make_sa_handler(0, check_ppid_for_death); - const int sigaction_res = sigaction(SIGALRM, &sa, nullptr); - guarantee_err(sigaction_res == 0, "worker: could not set action for ALRM signal"); - - struct itimerval timerval; - timerval.it_interval.tv_sec = 0; - timerval.it_interval.tv_usec = 500 * THOUSAND; - timerval.it_value = timerval.it_interval; - struct itimerval old_timerval; - const int itimer_res = setitimer(ITIMER_REAL, &timerval, &old_timerval); - guarantee_err(itimer_res == 0, "worker: setitimer failed"); - guarantee(old_timerval.it_value.tv_sec == 0 && old_timerval.it_value.tv_usec == 0, - "worker: setitimer saw that we already had an itimer!"); - // Send our pid over to the main process (because it didn't fork us directly) - write_message_t wm; - serialize(&wm, getpid()); - int res = send_write_message(&socket_stream, &wm); - guarantee(res == 0); -#endif // _WIN32 - } - - ~worker_run_t() { -#ifndef _WIN32 - guarantee(spawner_pid != INVALID_PROCESS_ID); - spawner_pid = INVALID_PROCESS_ID; -#endif - } - - // Returning from this indicates an error, orderly shutdown will exit() manually - void main_loop() { - // Receive and run a function from the main process until one returns false - bool (*fn) (read_stream_t *, write_stream_t *); - while (true) { - int64_t read_size = sizeof(fn); - const int64_t read_res = force_read(&socket_stream, &fn, read_size); - if (read_res != read_size) { - break; - } - if (!fn(&socket_stream, &socket_stream)) { - break; - } - // Trade magic numbers with the parent - uint64_t magic_from_parent; - { - archive_result_t res = - deserialize(&socket_stream, - &magic_from_parent); - if (res != archive_result_t::SUCCESS || - magic_from_parent != extproc_worker_t::parent_to_worker_magic) { - break; - } - } - write_message_t wm; - serialize( - &wm, extproc_worker_t::worker_to_parent_magic); - int res = send_write_message(&socket_stream, &wm); - if (res != 0) { - break; - } - } - } - -private: -#ifndef _WIN32 - static pid_t spawner_pid; - - static void check_ppid_for_death(int) { - pid_t ppid = getppid(); - if (spawner_pid != -1 && spawner_pid != ppid) { - ::_exit(EXIT_FAILURE); - } - } -#endif // _WIN32 - - scoped_fd_t socket; - socket_stream_t socket_stream; -}; - -#ifndef _WIN32 -pid_t worker_run_t::spawner_pid = -1; -#endif - -#ifndef _WIN32 -class spawner_run_t { -public: - explicit spawner_run_t(fd_t _socket) : - socket(_socket) { - // We set our PGID to our own PID (rather than inheriting our parent's PGID) - // so that a signal (eg. SIGINT) sent to the parent's PGID (by eg. hitting - // Ctrl-C at a terminal) will not propagate to us or our children. - // - // This is desirable because the RethinkDB engine deliberately crashes with - // an error message if the spawner or a worker process dies; but a - // command-line SIGINT should trigger a clean shutdown, not a crash. - const int setpgid_res = setpgid(0, 0); - guarantee_err(setpgid_res == 0, "spawner could not set PGID"); - - // We ignore SIGCHLD so that we don't accumulate zombie children. - { - // NB. According to `man 2 sigaction` on linux, POSIX.1-2001 says that - // this will prevent zombies, but may not be "fully portable". - struct sigaction sa = make_sa_handler(0, SIG_IGN); - const int res = sigaction(SIGCHLD, &sa, nullptr); - guarantee_err(res == 0, "spawner: Could not ignore SIGCHLD"); - } - } - - void main_loop() { - process_id_t spawner_pid = current_process(); - - while(true) { - fd_t worker_socket; - fd_recv_result_t recv_res = recv_fds(socket.get(), 1, &worker_socket); - if (recv_res != FD_RECV_OK) { - break; - } - pid_t res = ::fork(); - if (res == 0) { - // Worker process here - socket.reset(); // Don't need the spawner's pipe - worker_run_t worker_runner(worker_socket, spawner_pid); - worker_runner.main_loop(); - ::_exit(EXIT_FAILURE); - } - guarantee_err(res != -1, "could not fork worker process"); - scoped_fd_t closer(worker_socket); - } - } - -private: - scoped_fd_t socket; -}; -#endif - -extproc_spawner_t::extproc_spawner_t() { - // TODO: guarantee we aren't in a thread pool - guarantee(instance == nullptr); - instance = this; - -#ifdef _WIN32 - // TODO WINDOWS: ensure the workers die if the parent process does, - // perhaps using CreateJobObject and JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE -#else - spawner_pid = INVALID_PROCESS_ID; - fork_spawner(); -#endif -} - -extproc_spawner_t::~extproc_spawner_t() { - // TODO: guarantee we aren't in a thread pool - guarantee(instance == this); - instance = nullptr; - -#ifdef _WIN32 - // TODO WINDOWS: cleanup worker processes -#else - // This should trigger the spawner to exit - spawner_socket.reset(); - - // Wait on the spawner's return value to clean up the process - int status; - int res = waitpid(spawner_pid, &status, 0); - guarantee_err(res == spawner_pid, "failed to wait for extproc spawner process to exit"); -#endif -} - -extproc_spawner_t *extproc_spawner_t::get_instance() { - return instance; -} - -#ifndef _WIN32 -void extproc_spawner_t::fork_spawner() { - guarantee(spawner_socket.get() == INVALID_FD); - - fd_t fds[2]; - int res = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); - guarantee_err(res == 0, "could not create socket pair for spawner process"); - - res = ::fork(); - if (res == 0) { - // This is the spawner process, just instantiate ourselves and handle requests - do { - res = ::close(fds[0]); - } while (res == 0 && get_errno() == EINTR); - - spawner_run_t spawner(fds[1]); - spawner.main_loop(); - ::_exit(EXIT_SUCCESS); - } - - spawner_pid = res; - guarantee_err(spawner_pid != -1, "could not fork spawner process"); - - scoped_fd_t closer(fds[1]); - spawner_socket.reset(fds[0]); -} -#endif - -// Spawns a new worker process and returns the fd of the socket used to communicate with it -scoped_fd_t extproc_spawner_t::spawn(process_id_t *pid_out) { -#ifdef _WIN32 - static std::atomic unique = 0; - - char rethinkdb_path[MAX_PATH]; - DWORD res = GetModuleFileName(NULL, rethinkdb_path, sizeof(rethinkdb_path)); - guarantee_winerr(res != 0 && res != sizeof(rethinkdb_path), "GetModuleFileName failed"); - - scoped_fd_t fd; - std::string pipe_path = strprintf("\\\\.\\pipe\\rethinkdb-worker-%d-%d", GetCurrentProcessId(), ++unique); - fd.reset(CreateNamedPipe(pipe_path.c_str(), - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, - 1, - 2048, - 2048, - 0, - nullptr)); - guarantee_winerr(fd.get() != INVALID_FD, "CreateNamedPipe failed"); - - std::string command_line = strprintf("RethinkDB " SUBCOMMAND_START_WORKER " %s", pipe_path.c_str()); - std::vector mutable_command_line(command_line.begin(), command_line.end()); - mutable_command_line.push_back('\0'); - - STARTUPINFO startup_info; - memset(&startup_info, 0, sizeof(startup_info)); - startup_info.cb = sizeof(startup_info); - PROCESS_INFORMATION process_info; - BOOL res2 = CreateProcess(rethinkdb_path, - &mutable_command_line[0], - nullptr, - nullptr, - false, - NORMAL_PRIORITY_CLASS, - nullptr, - nullptr, - &startup_info, - &process_info); - - guarantee_winerr(res2, "CreateProcess failed"); - - // TODO WINDOWS: add new process to job group - - *pid_out = process_id_t(GetProcessId(process_info.hProcess)); - CloseHandle(process_info.hThread); - return fd; -#else // _WIN32 - guarantee(spawner_socket.get() != INVALID_FD); - - fd_t fds[2]; - int res = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); - guarantee_err(res == 0, "could not create socket pair for worker process"); - - scoped_fd_t fd0(fds[0]); - scoped_fd_t fd1(fds[1]); - - res = send_fds(spawner_socket.get(), 1, &fds[1]); - if (res != 0) { - throw extproc_worker_exc_t("could not send file descriptor to worker process"); - } - - socket_stream_t stream_out(fds[0]); - - // Get the pid of the new worker process - archive_result_t archive_res; - archive_res = deserialize(&stream_out, - pid_out); - if (archive_res != archive_result_t::SUCCESS || *pid_out == INVALID_PROCESS_ID) { - throw extproc_worker_exc_t("malformed response from fresh worker process"); - } - - return fd0; -#endif // _WIN32 -} - -#ifdef _WIN32 -bool extproc_maybe_run_worker(int argc, char **argv) { - if (argc != 3 || strcmp(argv[1], SUBCOMMAND_START_WORKER)) { - return false; - } - - // Don't handle signals like ^C in the extproc worker process - SetConsoleCtrlHandler(nullptr, true); - - fd_t fd = CreateFile(argv[2], - GENERIC_READ | GENERIC_WRITE, - 0, - nullptr, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - nullptr); - guarantee_winerr(fd != INVALID_FD, "opening '%s'", argv[2]); - - worker_run_t worker(fd, INVALID_PROCESS_ID); - worker.main_loop(); - ::_exit(EXIT_SUCCESS); - return true; -} -#endif // _WIN32 diff --git a/src/extproc/extproc_spawner.hpp b/src/extproc/extproc_spawner.hpp deleted file mode 100644 index ca14b4caeae..00000000000 --- a/src/extproc/extproc_spawner.hpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef EXTPROC_EXTPROC_SPAWNER_HPP_ -#define EXTPROC_EXTPROC_SPAWNER_HPP_ - -#include -#include "arch/io/io_utils.hpp" -#include "arch/types.hpp" -#include "arch/process.hpp" -#include "containers/archive/socket_stream.hpp" -#include "containers/object_buffer.hpp" - -#define SUBCOMMAND_START_WORKER "start-worker" - -bool extproc_maybe_run_worker(int argc, char **argv); - -#ifdef _WIN32 -// The extproc_spawner_t does not use an external process to launch workers, -// the workers are launched by calling CreateProcess from the main process -#else -// The extproc_spawner_t controls an external process which launches workers -// This is necessary to avoid some forking problems with tcmalloc, and -// should be constructed before the thread pool and extproc pool -// The problem with tcmalloc is that when forking, it is possible that another -// thread has a lock on the thread cache, but this thread disappears when the -// fork is performed. Therefore, when the forked process attempts an allocation, -// it will deadlock trying to acquire this lock that no one will ever unlock. -#endif -class extproc_spawner_t { -public: - extproc_spawner_t(); - ~extproc_spawner_t(); - - // Spawns a new worker, and returns the socket file descriptor for communication - // with the worker process - scoped_fd_t spawn(process_id_t *pid_out); - - static extproc_spawner_t *get_instance(); - -private: - static extproc_spawner_t *instance; - -#ifndef _WIN32 - void fork_spawner(); - - process_id_t spawner_pid; - scoped_fd_t spawner_socket; -#endif -}; - -#endif /* EXTPROC_EXTPROC_SPAWNER_HPP_ */ diff --git a/src/extproc/extproc_worker.cc b/src/extproc/extproc_worker.cc deleted file mode 100644 index 1700351ea74..00000000000 --- a/src/extproc/extproc_worker.cc +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#include - -#include "logger.hpp" - -#include "extproc/extproc_worker.hpp" -#include "extproc/extproc_spawner.hpp" -#include "arch/timing.hpp" -#include "arch/runtime/runtime.hpp" -#include "containers/archive/socket_stream.hpp" -#include "extproc/extproc_job.hpp" - -// Guaranteed to be random, chosen by fair dice roll -const uint64_t extproc_worker_t::parent_to_worker_magic = 0x700168fe5380e17bLL; -const uint64_t extproc_worker_t::worker_to_parent_magic = 0x9aa9a30dc74f9da1LL; - -NORETURN bool worker_exit_fn(read_stream_t *stream_in, write_stream_t *) { - int exit_code; - archive_result_t res = deserialize( - stream_in, &exit_code); - if (bad(res)) { exit_code = EXIT_FAILURE; } - ::_exit(exit_code); -} - -extproc_worker_t::extproc_worker_t(extproc_spawner_t *_spawner) : - spawner(_spawner), - worker_pid(INVALID_PROCESS_ID), - interruptor(NULL) { } - -extproc_worker_t::~extproc_worker_t() { - if (worker_pid != INVALID_PROCESS_ID) { -#ifdef _WIN32 - socket_event_watcher->rethread(get_thread_id()); - socket_stream.create(socket.get(), socket_event_watcher.get()); -#else - socket_stream.create(socket.get()); -#endif - cond_t non_interruptor; - socket_stream->set_interruptor(&non_interruptor); - try { - run_job(&worker_exit_fn); - - int exit_code = 0; - write_message_t wm; - serialize(&wm, exit_code); - int res = send_write_message(get_write_stream(), &wm); - if (res != 0) { - throw extproc_worker_exc_t("failed to send exit code to worker"); - } - - socket_stream.reset(); - - // TODO: Give some time to exit, then kill - } catch (const extproc_worker_exc_t &ex) { - logERR("Failed to shutdown worker process: '%s'. Killing it.", ex.what()); - socket_stream.reset(); - kill_process(); - } - } -} - -void extproc_worker_t::acquired(signal_t *_interruptor) { - // Only start the worker process once the worker is acquired for the first time - // This will also repair a killed worker - - // We create the streams here, since they are thread-dependant -#ifdef _WIN32 - bool new_worker = false; -#endif - if (worker_pid == INVALID_PROCESS_ID) { - socket = spawner->spawn(&worker_pid); - -#ifdef _WIN32 - new_worker = true; - socket_event_watcher.create(socket.get(), nullptr); -#endif - } - -#ifdef _WIN32 - socket_event_watcher->rethread(get_thread_id()); - socket_stream.create(socket.get(), socket_event_watcher.get()); -#else - socket_stream.create(socket.get()); -#endif - - // Apply the user interruptor to our stream along with the extproc pool's interruptor - guarantee(interruptor == nullptr); - interruptor = _interruptor; - guarantee(interruptor != nullptr); - socket_stream.get()->set_interruptor(interruptor); - -#ifdef _WIN32 - if (new_worker) { - socket_stream->wait_for_pipe_client(_interruptor); - } -#endif - -} - -void extproc_worker_t::released(bool user_error, signal_t *user_interruptor) { - guarantee(interruptor != nullptr); - bool errored = user_error; - - // If we were interrupted by the user, we can't count on the worker being valid - if (user_interruptor != nullptr && user_interruptor->is_pulsed()) { - errored = true; - } else if (!errored) { - // Set up a timeout interruptor for the final write/read - signal_timer_t timeout; - wait_any_t final_interruptor(&timeout, interruptor); - socket_stream->set_interruptor(&final_interruptor); - timeout.start(100); // Allow 100ms for the child to respond - - // Trade magic numbers with worker process to see if it is still coherent - try { - write_message_t wm; - serialize(&wm, parent_to_worker_magic); - { - int res = send_write_message(socket_stream.get(), &wm); - if (res != 0) { - throw extproc_worker_exc_t("failed to send magic number"); - } - } - - uint64_t magic_from_child; - archive_result_t res - = deserialize(socket_stream.get(), - &magic_from_child); - if (bad(res) || magic_from_child != worker_to_parent_magic) { - throw extproc_worker_exc_t("did not receive magic number"); - } - - } catch (...) { - // This would indicate a job is hanging or not reading out all its data - if (!final_interruptor.is_pulsed() || timeout.is_pulsed()) { - logERR("worker process failed to resynchronize with main process"); - } - errored = true; - } - } - - socket_stream.reset(); - interruptor = nullptr; - - // If anything went wrong, we just kill the worker and recreate it later - if (errored) { - kill_process(); - } -} - -void extproc_worker_t::kill_process() { - // TODO: this guarantee is violated when certain exception occur before worker_pid is set - guarantee(worker_pid != INVALID_PROCESS_ID); - -#ifdef _WIN32 - BOOL res; - HANDLE handle = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, true, worker_pid); - if (handle == INVALID_HANDLE_VALUE) { - res = false; - } else { - res = TerminateProcess(handle, EXIT_FAILURE); - } - if (!res) { - logWRN("failed to kill worker process: %s", winerr_string(GetLastError()).c_str()); - } - socket_event_watcher->rethread(get_thread_id()); - socket_event_watcher.reset(); -#else - ::kill(worker_pid, SIGKILL); -#endif - - worker_pid = INVALID_PROCESS_ID; - - // Clean up our socket fd - socket.reset(); -} - -bool extproc_worker_t::is_process_alive() { - return (worker_pid != INVALID_PROCESS_ID); -} - -void extproc_worker_t::run_job(bool (*fn) (read_stream_t *, write_stream_t *)) { - write_message_t wm; - wm.append(&fn, sizeof(fn)); - - int res = send_write_message(get_write_stream(), &wm); - if (res != 0) { - throw extproc_worker_exc_t("failed to send job function to worker"); - } -} - -read_stream_t *extproc_worker_t::get_read_stream() { - return socket_stream.get(); -} - -write_stream_t *extproc_worker_t::get_write_stream() { - return socket_stream.get(); -} diff --git a/src/extproc/extproc_worker.hpp b/src/extproc/extproc_worker.hpp deleted file mode 100644 index 2dad2223893..00000000000 --- a/src/extproc/extproc_worker.hpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef EXTPROC_EXTPROC_WORKER_HPP_ -#define EXTPROC_EXTPROC_WORKER_HPP_ - -#include -#include "arch/process.hpp" -#include "arch/io/io_utils.hpp" -#include "concurrency/wait_any.hpp" -#include "concurrency/cross_thread_signal.hpp" -#include "containers/object_buffer.hpp" -#include "containers/scoped.hpp" -#include "containers/archive/socket_stream.hpp" -#include "containers/archive/archive.hpp" - -class extproc_spawner_t; - -class extproc_worker_t { -public: - explicit extproc_worker_t(extproc_spawner_t *_spawner); - ~extproc_worker_t(); - - // Called whenever the worker changes hands (system -> user -> system) - void acquired(signal_t *_interruptor); - void released(bool user_error, signal_t *user_interruptor); - - // We accept jobs as functions that take a read stream and write stream - // so that they can communicate back to the job in the main process - void run_job(bool (*fn) (read_stream_t *, write_stream_t *)); - - read_stream_t *get_read_stream(); - write_stream_t *get_write_stream(); - - void kill_process(); - bool is_process_alive(); - - static const uint64_t parent_to_worker_magic; - static const uint64_t worker_to_parent_magic; - -private: - void spawn(); - - // This will run inside the blocker pool so the worker process doesn't inherit any - // of our coroutine stuff - void spawn_internal(); - - extproc_spawner_t *spawner; - process_id_t worker_pid; - scoped_fd_t socket; - - object_buffer_t socket_stream; - -#ifdef _WIN32 - object_buffer_t socket_event_watcher; -#endif - - signal_t *interruptor; -}; - -#endif /* EXTPROC_EXTPROC_WORKER_HPP_ */ diff --git a/src/extproc/http_job.cc b/src/extproc/http_job.cc deleted file mode 100644 index f360f92a342..00000000000 --- a/src/extproc/http_job.cc +++ /dev/null @@ -1,952 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "extproc/http_job.hpp" - -#include -#include - -#ifdef _WIN32 -#define CURL_STATICLIB -#endif -#include - -#include - -#include "containers/archive/boost_types.hpp" -#include "containers/archive/stl_types.hpp" -#include "extproc/extproc_job.hpp" -#include "http/http_parser.hpp" -#include "rapidjson/document.h" -#include "rapidjson/error/en.h" -#include "rdb_protocol/datum_json.hpp" -#include "rdb_protocol/env.hpp" - -#define RETHINKDB_USER_AGENT (SOFTWARE_NAME_STRING "/" RETHINKDB_VERSION) - -void parse_header(const std::string &header, - http_result_t *res_out); - -void save_cookies(CURL *curl_handle, - http_result_t *res_out); - -enum class attach_json_to_error_t { YES, NO }; -void json_to_datum(const std::string &json, - const ql::configured_limits_t &limits, - reql_version_t reql_version, - attach_json_to_error_t attach_json, - http_result_t *res_out); - -void jsonp_to_datum(const std::string &jsonp, - const ql::configured_limits_t &limits, - reql_version_t reql_version, - attach_json_to_error_t attach_json, - http_result_t *res_out); - -void perform_http(http_opts_t *opts, - http_result_t *res_out); - -class curl_exc_t : public std::exception { -public: - explicit curl_exc_t(std::string err_msg) : - error_string(std::move(err_msg)) { } - ~curl_exc_t() throw () { } - const char *what() const throw () { - return error_string.c_str(); - } - const std::string error_string; -}; - -class scoped_curl_handle_t { -public: - scoped_curl_handle_t() : - curl_handle(curl_easy_init()) { - } - - ~scoped_curl_handle_t() { - curl_easy_cleanup(curl_handle); - } - - CURL *get() { - return curl_handle; - } - -private: - CURL *curl_handle; -}; - -// Used for adding headers, which cannot be freed until after the request is done -class scoped_curl_slist_t { -public: - scoped_curl_slist_t() : - slist(nullptr) { } - - ~scoped_curl_slist_t() { - if (slist != nullptr) { - curl_slist_free_all(slist); - } - } - - curl_slist *get() { - return slist; - } - - void add(const std::string &str) { - slist = curl_slist_append(slist, str.c_str()); - if (slist == nullptr) { - throw curl_exc_t("appending header, allocation failure"); - } - } - -private: - struct curl_slist *slist; -}; - -class curl_data_t { -public: - curl_data_t() : send_data_offset(0) { } - - static size_t write_body(char *ptr, size_t size, size_t nmemb, void* instance); - static size_t write_header(char *ptr, size_t size, size_t nmemb, void* instance); - static size_t read(char *ptr, size_t size, size_t nmemb, void* instance); - - void set_send_data(std::string &&_send_data) { - send_data.assign(std::move(_send_data)); - } - - std::string steal_header_data() { - return std::move(header_data); - } - - std::string steal_body_data() { - return std::move(body_data); - } - - scoped_curl_slist_t header; - -private: - // These are called for getting data in the response from the server - size_t write_body_internal(char *ptr, size_t size); - size_t write_header_internal(char *ptr, size_t size); - - // This is called for writing data to the request when sending - size_t read_internal(char *ptr, size_t size); - - size_t send_data_offset; - std::string send_data; - std::string body_data; - std::string header_data; -}; - -size_t multiply_with_overflow_check(size_t a, size_t b, const std::string &info) { - if (b == 0 || a > std::numeric_limits::max() / b) { - throw curl_exc_t(info + " data size too large"); - } - return a * b; -} - -size_t curl_data_t::write_header(char *ptr, size_t size, size_t nmemb, void* instance) { - curl_data_t *self = static_cast(instance); - return self->write_header_internal(ptr, - multiply_with_overflow_check(size, nmemb, - "write header")); -} - -size_t curl_data_t::write_body(char *ptr, size_t size, size_t nmemb, void* instance) { - curl_data_t *self = static_cast(instance); - return self->write_body_internal(ptr, - multiply_with_overflow_check(size, nmemb, - "write body")); -} - -size_t curl_data_t::read(char *ptr, size_t size, size_t nmemb, void* instance) { - curl_data_t *self = static_cast(instance); - return self->read_internal(ptr, multiply_with_overflow_check(size, nmemb, "read")); -} - -size_t curl_data_t::write_header_internal(char *ptr, size_t size) { - header_data.append(ptr, size); - return size; -} - -size_t curl_data_t::write_body_internal(char *ptr, size_t size) { - body_data.append(ptr, size); - return size; -} - -size_t curl_data_t::read_internal(char *ptr, size_t size) { - size_t bytes_to_copy = std::min( { size, - send_data.size() - send_data_offset } ); - memcpy(ptr, send_data.data() + send_data_offset, bytes_to_copy); - send_data_offset += bytes_to_copy; - return bytes_to_copy; -} - -// The job_t runs in the context of the main rethinkdb process -http_job_t::http_job_t(extproc_pool_t *pool, signal_t *interruptor) : - extproc_job(pool, &worker_fn, interruptor) { } - -void http_job_t::http(const http_opts_t &opts, - http_result_t *res_out) { - write_message_t msg; - serialize(&msg, opts); - { - int res = send_write_message(extproc_job.write_stream(), &msg); - if (res != 0) { - throw extproc_worker_exc_t("failed to send data to the worker"); - } - } - - archive_result_t res - = deserialize(extproc_job.read_stream(), - res_out); - if (bad(res)) { - throw extproc_worker_exc_t(strprintf("failed to deserialize result from worker " - "(%s)", archive_result_as_str(res))); - } -} - -void http_job_t::worker_error() { - extproc_job.worker_error(); -} - -bool http_job_t::worker_fn(read_stream_t *stream_in, write_stream_t *stream_out) { - static bool curl_initialized(false); - http_opts_t opts; - { - archive_result_t res - = deserialize(stream_in, &opts); - if (bad(res)) { return false; } - } - - http_result_t result; - result.header = ql::datum_t::null(); - result.body = ql::datum_t::null(); - - CURLcode curl_res = CURLE_OK; - if (!curl_initialized) { - curl_res = curl_global_init(CURL_GLOBAL_SSL); - curl_initialized = true; - } - - if (curl_res == CURLE_OK) { - try { - perform_http(&opts, &result); - } catch (const std::exception &ex) { - result.error.assign(ex.what()); - } catch (...) { - result.error.assign("unknown error"); - } - } else { - result.error.assign("global initialization"); - curl_initialized = false; - } - - write_message_t msg; - serialize(&msg, result); - int res = send_write_message(stream_out, &msg); - if (res != 0) { return false; } - - return true; -} - -std::string exc_encode(CURL *curl_handle, const std::string &str) { - std::string res; - - if (str.size() != 0) { - char *curl_res = curl_easy_escape(curl_handle, str.data(), str.size()); - if (curl_res == nullptr) { - throw curl_exc_t("encode string"); - } - res.assign(curl_res); - curl_free(curl_res); - } - - return res; -} - -// We may get multiple headers due to authentication or redirection. Filter out all but -// the last response's header, which is all we care about. -void truncate_header_data(std::string *header_data) { - size_t last_crlf = header_data->rfind("\r\n\r\n"); - - if (last_crlf != std::string::npos || last_crlf > 0) { - size_t second_to_last_crlf = header_data->rfind("\r\n\r\n", last_crlf - 1); - if (second_to_last_crlf != std::string::npos) { - header_data->erase(0, second_to_last_crlf + 4); - } - } -} - -template -void exc_setopt(CURL *curl_handle, CURLoption opt, T val, const char *info) { - CURLcode curl_res = curl_easy_setopt(curl_handle, opt, val); - if (curl_res != CURLE_OK) { - throw curl_exc_t(strprintf("set option %s, '%s'", - info, curl_easy_strerror(curl_res))); - } -} - -void transfer_auth_opt(const http_opts_t::http_auth_t &auth, CURL *curl_handle) { - if (auth.type != http_auth_type_t::NONE) { - long curl_auth_type; // NOLINT(runtime/int) - switch (auth.type) { - case http_auth_type_t::BASIC: - curl_auth_type = CURLAUTH_BASIC; - break; - case http_auth_type_t::DIGEST: - curl_auth_type = CURLAUTH_DIGEST; - break; - case http_auth_type_t::NONE: - default: - unreachable(); - } - exc_setopt(curl_handle, CURLOPT_HTTPAUTH, curl_auth_type, "HTTP AUTH TYPE"); - exc_setopt(curl_handle, CURLOPT_USERNAME, - auth.username.c_str(), "HTTP AUTH USERNAME"); - exc_setopt(curl_handle, CURLOPT_PASSWORD, - auth.password.c_str(), "HTTP AUTH PASSWORD"); - } -} - -void add_read_callback(CURL *curl_handle, - std::string &&data, - curl_data_t *curl_data) { - curl_off_t size = data.size(); - if (size != 0) { - curl_data->set_send_data(std::move(data)); - exc_setopt(curl_handle, CURLOPT_READFUNCTION, - &curl_data_t::read, "READ FUNCTION"); - exc_setopt(curl_handle, CURLOPT_READDATA, curl_data, "READ DATA"); - exc_setopt(curl_handle, CURLOPT_INFILESIZE_LARGE, size, "DATA SIZE"); - } -} - -void url_encode_kv(CURL *curl_handle, - const std::string &key, - const std::string &val, - std::string *str_out) { - str_out->append(exc_encode(curl_handle, key)); - str_out->push_back('='); - str_out->append(exc_encode(curl_handle, val)); -} - -std::string url_encode_fields(CURL *curl_handle, - const std::map &fields) { - std::string data; - for (auto it = fields.begin(); it != fields.end(); ++it) { - if (it != fields.begin()) { - data.push_back('&'); - } - url_encode_kv(curl_handle, it->first, it->second, &data); - } - return data; -} - -std::string url_encode_fields(CURL *curl_handle, - const ql::datum_t &fields) { - // Convert to a common format - if (!fields.has()) { - return std::string(); - } - - std::map translated_fields; - - for (size_t field_idx = 0; field_idx < fields.obj_size(); ++field_idx) { - auto pair = fields.get_pair(field_idx); - std::string val; - if (pair.second.get_type() == ql::datum_t::R_NUM) { - val = strprintf("%" PR_RECONSTRUCTABLE_DOUBLE, - pair.second.as_num()); - } else if (pair.second.get_type() == ql::datum_t::R_STR) { - val = pair.second.as_str().to_std(); - } else if (pair.second.get_type() != ql::datum_t::R_NULL) { - // This shouldn't happen because we check this in the main process anyway - throw curl_exc_t(strprintf("expected `params.%s` to be a NUMBER, STRING, " - "or nullptr, but found %s", - pair.first.to_std().c_str(), - pair.second.get_type_name().c_str())); - } - translated_fields[pair.first.to_std()] = val; - } - - return url_encode_fields(curl_handle, translated_fields); -} - -void transfer_method_opt(http_opts_t *opts, - CURL *curl_handle, - curl_data_t *curl_data) { - // Opts will have either data or form_data set - - // - form_data is only used for post requests, and will result in a string - // of form-encoded pairs in the request - // - data is used in put, patch, and post requests, and will result in the - // given string being directly put into the body of the request - switch (opts->method) { - case http_method_t::GET: - exc_setopt(curl_handle, CURLOPT_HTTPGET, 1, "HTTP GET"); - break; - case http_method_t::PATCH: - exc_setopt(curl_handle, CURLOPT_UPLOAD, 1, "HTTP PUT"); - exc_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, "PATCH", "HTTP PATCH"); - add_read_callback(curl_handle, std::move(opts->data), curl_data); - break; - case http_method_t::PUT: - exc_setopt(curl_handle, CURLOPT_UPLOAD, 1, "HTTP PUT"); - add_read_callback(curl_handle, std::move(opts->data), curl_data); - break; - case http_method_t::POST: - if (!opts->form_data.empty()) { - // This is URL-encoding the form data, which isn't *exactly* the same as - // x-www-url-formencoded, but it should be compatible - opts->data = url_encode_fields(curl_handle, opts->form_data); - } - - exc_setopt(curl_handle, CURLOPT_POST, 1, "HTTP POST"); - exc_setopt(curl_handle, CURLOPT_POSTFIELDS, - opts->data.data(), "HTTP POST DATA"); - exc_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, - opts->data.size(), "HTTP POST DATA SIZE"); - break; - case http_method_t::HEAD: - exc_setopt(curl_handle, CURLOPT_NOBODY, 1, "HTTP HEAD"); - break; - case http_method_t::DELETE: - exc_setopt(curl_handle, CURLOPT_UPLOAD, 1, "HTTP PUT"); - exc_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, "DELETE", "HTTP DELETE"); - add_read_callback(curl_handle, std::move(opts->data), curl_data); - break; - default: - unreachable(); - } -} - -void transfer_url_opt(const std::string &url, - const ql::datum_t &url_params, - CURL *curl_handle) { - std::string full_url = url; - std::string params = url_encode_fields(curl_handle, url_params); - - // Handle cases where the url already has parameters, or is missing a '/' at the end - // We need to prepare the url for appending url-encoded parameters - // Examples: - // example.org => example.org/? - // example.org?id=0 => example.org?id=0& - This was already malformed, but w/e - // example.org/page => example.org/page? - // example.org/page?id=0 => example.org/page?id=0& - // example.org/page?id=0/page => example.org/page?id=0/page? - if (params.size() > 0) { - size_t params_pos = url.rfind('?'); - size_t slash_pos = url.rfind('/'); - - if (slash_pos == std::string::npos && - params_pos == std::string::npos) { - full_url.push_back('/'); - } - - if (params_pos == std::string::npos || - (slash_pos != std::string::npos && - slash_pos > params_pos)) { - full_url.push_back('?'); - } else if (slash_pos != std::string::npos && - params_pos != std::string::npos && - slash_pos < params_pos) { - full_url.push_back('&'); - } - - full_url.append(params); - } - - exc_setopt(curl_handle, CURLOPT_URL, full_url.c_str(), "URL"); -} - -void transfer_header_opt(const std::vector &header, - CURL *curl_handle, - curl_data_t *curl_data) { - for (auto it = header.begin(); it != header.end(); ++it) { - curl_data->header.add(*it); - } - - if (curl_data->header.get() != nullptr) { - exc_setopt(curl_handle, CURLOPT_HTTPHEADER, curl_data->header.get(), "HEADER"); - } -} - -void transfer_redirect_opt(uint32_t max_redirects, CURL *curl_handle) { - long val = (max_redirects > 0) ? 1 : 0; // NOLINT(runtime/int) - exc_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, val, "ALLOW REDIRECT"); - val = max_redirects; - exc_setopt(curl_handle, CURLOPT_MAXREDIRS, val, "MAX REDIRECTS"); - // maybe we should set CURLOPT_POSTREDIR - libcurl will, by default, - // change POST requests to GET requests if redirected -} - -void transfer_verify_opt(bool verify, CURL *curl_handle) { - long val = verify ? 1 : 0; // NOLINT(runtime/int) - exc_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, val, "SSL VERIFY PEER"); - val = verify ? 2 : 0; - exc_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, val, "SSL VERIFY HOST"); -} - -void transfer_cookies(const std::vector &cookies, CURL *curl_handle) { - for (auto const &cookie : cookies) { - exc_setopt(curl_handle, CURLOPT_COOKIELIST, cookie.c_str(), "COOKIELIST"); - } -} - -void transfer_opts(http_opts_t *opts, - CURL *curl_handle, - curl_data_t *curl_data) { - transfer_auth_opt(opts->auth, curl_handle); - transfer_url_opt(opts->url, opts->url_params, curl_handle); - transfer_redirect_opt(opts->max_redirects, curl_handle); - transfer_verify_opt(opts->verify, curl_handle); - - transfer_header_opt(opts->header, curl_handle, curl_data); - - // Set method last as it may override some options libcurl automatically sets - transfer_method_opt(opts, curl_handle, curl_data); - - // Copy any saved cookies from a previous request (e.g. in the case of depagination) - transfer_cookies(opts->cookies, curl_handle); -} - -void set_default_opts(CURL *curl_handle, - const std::string &proxy, - const curl_data_t &curl_data) { - exc_setopt(curl_handle, CURLOPT_WRITEFUNCTION, - &curl_data_t::write_body, "WRITE FUNCTION"); - exc_setopt(curl_handle, CURLOPT_WRITEDATA, &curl_data, "WRITE DATA"); - - exc_setopt(curl_handle, CURLOPT_HEADERFUNCTION, - &curl_data_t::write_header, "HEADER FUNCTION"); - exc_setopt(curl_handle, CURLOPT_HEADERDATA, &curl_data, "HEADER DATA"); - - // Only allow http protocol - exc_setopt(curl_handle, CURLOPT_PROTOCOLS, - CURLPROTO_HTTP | CURLPROTO_HTTPS, "PROTOCOLS"); - - exc_setopt(curl_handle, CURLOPT_USERAGENT, RETHINKDB_USER_AGENT, "USER AGENT"); - - // Setting this to an empty string makes libcurl enable all supported encodings. - // Normally this is gzip and deflate, though on Windows some of these might not - // be supported. - exc_setopt(curl_handle, CURLOPT_ACCEPT_ENCODING, "", "PROTOCOLS"); - - exc_setopt(curl_handle, CURLOPT_NOSIGNAL, 1, "NOSIGNAL"); - - // Enable cookies - needed for multiple requests like redirects or digest auth - exc_setopt(curl_handle, CURLOPT_COOKIEFILE, "", "COOKIEFILE"); - - // Use the proxy set when launched - if (!proxy.empty()) { - exc_setopt(curl_handle, CURLOPT_PROXY, proxy.c_str(), "PROXY"); - } -} - -// TODO: implement streaming API support -void perform_http(http_opts_t *opts, http_result_t *res_out) { - scoped_curl_handle_t curl_handle; - curl_data_t curl_data; - - if (curl_handle.get() == nullptr) { - res_out->error.assign("initialization"); - return; - } - - set_default_opts(curl_handle.get(), opts->proxy, curl_data); - transfer_opts(opts, curl_handle.get(), &curl_data); - - CURLcode curl_res = CURLE_OK; - long response_code = 0; // NOLINT(runtime/int) - for (uint64_t attempts = 0; attempts < opts->attempts; ++attempts) { - // Do the HTTP operation, then check for errors - curl_res = curl_easy_perform(curl_handle.get()); - - if (curl_res == CURLE_SEND_ERROR || - curl_res == CURLE_RECV_ERROR || - curl_res == CURLE_COULDNT_CONNECT) { - // Could be a temporary error, try again - continue; - } else if (curl_res != CURLE_OK) { - res_out->error.assign(curl_easy_strerror(curl_res)); - return; - } - - curl_res = curl_easy_getinfo(curl_handle.get(), - CURLINFO_RESPONSE_CODE, - &response_code); - - // Break on success, retry on temporary error - if (curl_res != CURLE_OK || - // Error codes that may be resolved by retrying - (response_code != 408 && - response_code != 500 && - response_code != 502 && - response_code != 503 && - response_code != 504)) { - break; - } - } - - std::string body_data(curl_data.steal_body_data()); - std::string header_data(curl_data.steal_header_data()); - truncate_header_data(&header_data); - - if (opts->attempts == 0) { - res_out->error.assign("could not perform, no attempts allowed"); - } else if (curl_res == CURLE_SEND_ERROR) { - res_out->error.assign("error when sending data"); - } else if (curl_res == CURLE_RECV_ERROR) { - res_out->error.assign("error when receiving data"); - } else if (curl_res == CURLE_COULDNT_CONNECT) { - res_out->error.assign("could not connect to server"); - } else if (curl_res != CURLE_OK) { - res_out->error = strprintf("reading response code, '%s'", - curl_easy_strerror(curl_res)); - } else if (response_code < 200 || response_code >= 300) { - if (!header_data.empty()) { - parse_header(header_data, res_out); - } - if (!body_data.empty()) { - res_out->body = ql::datum_t(datum_string_t(body_data)); - } - res_out->error = strprintf("status code %ld", response_code); - } else { - parse_header(header_data, res_out); - save_cookies(curl_handle.get(), res_out); - - // If this was a HEAD request, we should not be handling data, just return R_NULL - // so the user knows the request succeeded - if (opts->method == http_method_t::HEAD) { - res_out->body = ql::datum_t::null(); - return; - } - - rassert(res_out->error.empty()); - - switch (opts->result_format) { - case http_result_format_t::AUTO: - { - std::string content_type; - char *content_type_buffer = nullptr; - curl_easy_getinfo(curl_handle.get(), - CURLINFO_CONTENT_TYPE, - &content_type_buffer); - - if (content_type_buffer != nullptr) { - content_type.assign(content_type_buffer); - } - - for (size_t i = 0; i < content_type.length(); ++i) { - content_type[i] = tolower(content_type[i]); - } - - if (content_type.find("application/json") == 0) { - json_to_datum(body_data, opts->limits, opts->version, - attach_json_to_error_t::YES, res_out); - } else if (content_type.find("text/javascript") == 0 || - content_type.find("application/json-p") == 0 || - content_type.find("text/json-p") == 0) { - // Try to parse the result as JSON, then as JSONP, then plaintext - // Do not use move semantics here, as we retry on errors - json_to_datum(body_data, opts->limits, opts->version, - attach_json_to_error_t::NO, res_out); - if (!res_out->error.empty()) { - res_out->error.clear(); - jsonp_to_datum(body_data, opts->limits, opts->version, - attach_json_to_error_t::NO, res_out); - if (!res_out->error.empty()) { - res_out->error.clear(); - res_out->body = - ql::datum_t(datum_string_t(body_data)); - } - } - } else if (content_type.find("audio/") == 0 || - content_type.find("image/") == 0 || - content_type.find("video/") == 0 || - content_type.find("application/octet-stream") == 0) { - res_out->body = ql::datum_t::binary(datum_string_t(body_data)); - - } else { - res_out->body = - ql::datum_t(datum_string_t(body_data)); - } - } - break; - case http_result_format_t::JSON: - json_to_datum(body_data, opts->limits, opts->version, - attach_json_to_error_t::YES, res_out); - break; - case http_result_format_t::JSONP: - jsonp_to_datum(body_data, opts->limits, opts->version, - attach_json_to_error_t::YES, res_out); - break; - case http_result_format_t::TEXT: - res_out->body = ql::datum_t(datum_string_t(body_data)); - break; - case http_result_format_t::BINARY: - res_out->body = ql::datum_t::binary(datum_string_t(body_data)); - break; - default: - unreachable(); - } - } -} - -class header_parser_singleton_t { -public: - static ql::datum_t parse(const std::string &header); - -private: - static void initialize(); - static header_parser_singleton_t *instance; - - header_parser_singleton_t(); - - // Callbacks from http-parser - static int on_headers_complete(UNUSED http_parser *parser); - static int on_header_field(http_parser *parser, const char *field, size_t length); - static int on_header_value(http_parser *parser, const char *value, size_t length); - - // Helper function to special-case 'Link' header parsing - void add_link_header(const std::string &line); - - http_parser_settings settings; - http_parser parser; - - RE2 link_parser; - std::map header_fields; - std::map link_headers; - std::string current_field; -}; -header_parser_singleton_t *header_parser_singleton_t::instance = nullptr; - -// The link_parser regular expression is used to parse 'Link' headers from HTTP -// responses (see RFC 5988 - http://tools.ietf.org/html/rfc5988). We are interested -// in parsing out one or two strings for each link. Examples: -// -// Link: ; rel="next" -// - we want the strings 'http://example.org' and 'rel="next"' -// Link: ; title="Arbitrary String", -// - this is two links on the same header line, but should be parsed separately -// - for the first, we want 'http://example.org' and 'title="Arbitrary String"' -// - for the second, there is only one string, the link 'http://example.org', -// this will be stored in the result with an empty key. If more than one of -// these is given in the response, the last one will take precedence. -// -// The link_parser regular expression consists of two main chunks, with gratuitous -// whitespace padding: -// <([^>]*)> - this is the first capture group, for the link portion. Since the URL -// cannot contain '>' characters, we capture everything from the first '<' to the -// first '>'. -// (?:;([^,]+)(?:,|$) - this is the optional remainder of the link (called the param -// in the RFC). We match the semicolon, followed by the param, followed by either -// a comma or the end of the line. Like before, the capture group is configured -// to only capture the param. Note: this will fail if the param contains a comma. -header_parser_singleton_t::header_parser_singleton_t() : - link_parser("^\\s*<([^>]*)>\\s*(?:;\\s*([^,]+)\\s*(?:,|$))", RE2::Quiet) -{ - memset(&settings, 0, sizeof(settings)); - settings.on_header_field = &header_parser_singleton_t::on_header_field; - settings.on_header_value = &header_parser_singleton_t::on_header_value; - settings.on_headers_complete = &header_parser_singleton_t::on_headers_complete; -} - -void header_parser_singleton_t::initialize() { - if (instance == nullptr) { - instance = new header_parser_singleton_t(); - } - - instance->current_field.clear(); - instance->link_headers.clear(); - http_parser_init(&instance->parser, HTTP_RESPONSE); - instance->parser.data = instance; -} - -ql::datum_t -header_parser_singleton_t::parse(const std::string &header) { - initialize(); - size_t res = http_parser_execute(&instance->parser, - &instance->settings, - header.data(), - header.length()); - - if (instance->parser.upgrade) { - // Upgrade header was present - error - throw curl_exc_t("upgrade header present in response headers"); - } else if (res != header.length()) { - throw curl_exc_t(strprintf("unknown error when parsing response headers:\n%s\n", - header.c_str())); - } - - // Return an empty object if we didn't receive any headers - if (instance->header_fields.size() == 0 && - instance->link_headers.size() == 0) { - return ql::datum_t::empty_object(); - } else if (instance->link_headers.size() > 0) { - // Include the specially-parsed link header field - instance->header_fields[datum_string_t("link")] = - ql::datum_t(std::move(instance->link_headers)); - } - - ql::datum_t parsed_header = ql::datum_t(std::move(instance->header_fields)); - instance->header_fields.clear(); - return parsed_header; -} - -int header_parser_singleton_t::on_headers_complete(UNUSED http_parser *parser) { - // Always stop once we've parsed the headers - return 1; -} - -int header_parser_singleton_t::on_header_field(http_parser *parser, - const char *field, - size_t length) { - guarantee(instance == parser->data); - instance->current_field.assign(field, length); - - // Lowercase header fields so they are more reliable - for (size_t i = 0; i < instance->current_field.length(); ++i) { - instance->current_field[i] = tolower(instance->current_field[i]); - } - return 0; -} - -int header_parser_singleton_t::on_header_value(http_parser *parser, - const char *value, - size_t length) { - guarantee(instance == parser->data); - // Special handling for 'Link' headers to make them usable in the query language - if (instance->current_field == "link") { - instance->add_link_header(std::string(value, length)); - } else { - instance->header_fields[datum_string_t(instance->current_field)] = - ql::datum_t(datum_string_t(length, value)); - } - return 0; -} - -void header_parser_singleton_t::add_link_header(const std::string &line) { - // Capture the link and its parameter - re2::StringPiece line_re2(line); - std::string value; - std::string param; - while (RE2::FindAndConsume(&line_re2, link_parser, &value, ¶m)) { - link_headers[datum_string_t(param)] = - ql::datum_t(datum_string_t(value)); - } - - if (line_re2.length() != 0) { - // The entire string was not consumed, meaning something does not match - throw curl_exc_t(strprintf("failed to parse link header line: '%s'", - line.c_str())); - } -} - -void parse_header(const std::string &header, - http_result_t *res_out) { - res_out->header = header_parser_singleton_t::parse(header); -} - -void save_cookies(CURL *curl_handle, http_result_t *res_out) { - struct curl_slist *cookies; - CURLcode curl_res = curl_easy_getinfo(curl_handle, CURLINFO_COOKIELIST, &cookies); - - if (curl_res != CURLE_OK) { - throw curl_exc_t("failed to get a list of cookies from the session"); - } - - res_out->cookies.clear(); - for (curl_slist *current = cookies; current != nullptr; current = current->next) { - res_out->cookies.push_back(std::string(current->data)); - } - - if (cookies != nullptr) { - curl_slist_free_all(cookies); - } -} - -void json_to_datum(const std::string &json, - const ql::configured_limits_t &limits, - reql_version_t reql_version, - attach_json_to_error_t attach_json, - http_result_t *res_out) { - rapidjson::Document doc; - doc.Parse(json.c_str()); - if (!doc.HasParseError()) { - res_out->body = ql::to_datum(doc, limits, reql_version); - } else { - res_out->error.assign( - strprintf("failed to parse JSON response: %s", - rapidjson::GetParseError_En(doc.GetParseError()))); - if (attach_json == attach_json_to_error_t::YES) { - res_out->body = ql::datum_t(datum_string_t(json)); - } - } -} - -// This class is created on-demand, but at most once per extproc. There is -// no code path to destroy this object, but memory usage shouldn't be terrible, -// and it will be cleaned up when the extproc is shut down. -class jsonp_parser_singleton_t { -public: - static bool parse(const std::string &jsonp, std::string *json_string) { - initialize_if_needed(); - return RE2::FullMatch(jsonp, instance->parser1, json_string) || - RE2::FullMatch(jsonp, instance->parser2, json_string) || - RE2::FullMatch(jsonp, instance->parser3, json_string); - } -private: - // The JSONP specification at www.json-p.org provides the following formats: - // 1. functionName(JSON); - // 2. obj.functionName(JSON); - // 3. obj["function-name"](JSON); - // The following regular expressions will parse out the JSON section from each format - jsonp_parser_singleton_t() : - parser1(std::string(js_ident) + fn_call + expr_end, RE2::Quiet), - parser2(std::string(js_ident) + "\\." + js_ident + fn_call + expr_end, RE2::Quiet), - parser3(std::string(js_ident) + "\\[\\s*['\"].*['\"]\\s*\\]\\s*" + - fn_call + expr_end, RE2::Quiet) - { } - - static void initialize_if_needed() { - if (instance == nullptr) { - instance = new jsonp_parser_singleton_t(); - } - } - - static jsonp_parser_singleton_t *instance; - // Matches a javascript identifier with whitespace on either side - static const char *js_ident; - // Matches the function call with a capture group for the JSON data - static const char *fn_call; - // Matches the end of the javascript expression, whitespace and an optional semicolon - static const char *expr_end; - - RE2 parser1; - RE2 parser2; - RE2 parser3; -}; - -// Static const initializations for above -jsonp_parser_singleton_t *jsonp_parser_singleton_t::instance = nullptr; -const char *jsonp_parser_singleton_t::fn_call = "\\(((?s).*)\\)"; -const char *jsonp_parser_singleton_t::expr_end = "\\s*;?\\s*"; -const char *jsonp_parser_singleton_t::js_ident = - "\\s*" - "[$_\\p{Ll}\\p{Lt}\\p{Lu}\\p{Lm}\\p{Lo}\\p{Nl}]" - "[$_\\p{Ll}\\p{Lt}\\p{Lu}\\p{Lm}\\p{Lo}\\p{Nl}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}]*" - "\\s*"; - -void jsonp_to_datum(const std::string &jsonp, const ql::configured_limits_t &limits, - reql_version_t reql_version, - attach_json_to_error_t attach_json, - http_result_t *res_out) { - std::string json_string; - if (jsonp_parser_singleton_t::parse(jsonp, &json_string)) { - json_to_datum(json_string, limits, reql_version, attach_json, res_out); - } else { - res_out->error.assign("failed to parse JSONP response"); - if (attach_json == attach_json_to_error_t::YES) { - res_out->body = ql::datum_t(datum_string_t(jsonp)); - } - } -} diff --git a/src/extproc/http_job.hpp b/src/extproc/http_job.hpp deleted file mode 100644 index 113dfb16d59..00000000000 --- a/src/extproc/http_job.hpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef EXTPROC_HTTP_JOB_HPP_ -#define EXTPROC_HTTP_JOB_HPP_ - -#include "errors.hpp" - -#include "extproc/extproc_pool.hpp" -#include "extproc/extproc_job.hpp" -#include "extproc/http_runner.hpp" -#include "containers/archive/archive.hpp" -#include "concurrency/signal.hpp" - -class http_job_t { -public: - http_job_t(extproc_pool_t *pool, signal_t *interruptor); - - void http(const http_opts_t &opts, http_result_t *res_out); - - // Marks the extproc worker as errored to simplify cleanup later - void worker_error(); - -private: - static bool worker_fn(read_stream_t *stream_in, write_stream_t *stream_out); - - extproc_job_t extproc_job; - DISABLE_COPYING(http_job_t); -}; - -#endif /* EXTPROC_HTTP_JOB_HPP_ */ diff --git a/src/extproc/http_runner.cc b/src/extproc/http_runner.cc deleted file mode 100644 index a4f676ad4e8..00000000000 --- a/src/extproc/http_runner.cc +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "extproc/http_runner.hpp" - -#include "arch/timing.hpp" -#include "containers/archive/stl_types.hpp" -#include "extproc/http_job.hpp" -#include "protocol_api.hpp" -#include "rdb_protocol/serialize_datum.hpp" - -RDB_IMPL_SERIALIZABLE_4_FOR_CLUSTER(http_result_t, header, body, cookies, error); -RDB_IMPL_SERIALIZABLE_3_SINCE_v1_13(http_opts_t::http_auth_t, type, username, password); -RDB_IMPL_SERIALIZABLE_16(http_opts_t, - auth, method, result_format, url, proxy, url_params, - header, cookies, data, form_data, limits, version, timeout_ms, - attempts, max_redirects, verify); -INSTANTIATE_SERIALIZABLE_FOR_CLUSTER(http_opts_t); - -std::string http_method_to_str(http_method_t method) { - switch(method) { - case http_method_t::GET: return std::string("GET"); - case http_method_t::HEAD: return std::string("HEAD"); - case http_method_t::POST: return std::string("POST"); - case http_method_t::PUT: return std::string("PUT"); - case http_method_t::PATCH: return std::string("PATCH"); - case http_method_t::DELETE: return std::string("DELETE"); - default: return std::string("UNKNOWN"); - } -} - -http_opts_t::http_opts_t() : - auth(), - method(http_method_t::GET), - result_format(http_result_format_t::AUTO), - proxy(), - url(), - url_params(std::map()), - header(), - cookies(), - data(), - form_data(), - limits(), - version(reql_version_t::LATEST), - timeout_ms(30000), - attempts(5), - max_redirects(1), - verify(true) { } - -http_opts_t::http_auth_t::http_auth_t() : - type(http_auth_type_t::NONE), - username(), - password() { } - -http_opts_t::http_auth_t::http_auth_t(http_auth_t &&other) : - type(other.type), - username(std::move(other.username)), - password(std::move(other.password)) { } - -void http_opts_t::http_auth_t::make_basic_auth(std::string &&user, - std::string &&pass) { - type = http_auth_type_t::BASIC; - username.assign(std::move(user)); - password.assign(std::move(pass)); -} - -void http_opts_t::http_auth_t::make_digest_auth(std::string &&user, - std::string &&pass) { - type = http_auth_type_t::DIGEST; - username.assign(std::move(user)); - password.assign(std::move(pass)); -} - -http_runner_t::http_runner_t(extproc_pool_t *_pool) : - pool(_pool) { } - -void http_runner_t::http(const http_opts_t &opts, - http_result_t *res_out, - signal_t *interruptor) { - signal_timer_t timeout; - wait_any_t combined_interruptor(interruptor, &timeout); - http_job_t job(pool, &combined_interruptor); - - assert_thread(); - timeout.start(opts.timeout_ms); - - try { - job.http(opts, res_out); - } catch (const interrupted_exc_t &ex) { - if (!timeout.is_pulsed()) { - throw; - } - res_out->error = - strprintf("timed out after %" PRIu64 ".%03" PRIu64 " seconds", - opts.timeout_ms / 1000, opts.timeout_ms % 1000); - } catch (...) { - // This will mark the worker as errored so we don't try to re-sync with it - // on the next line (since we're in a catch statement, we aren't allowed) - job.worker_error(); - throw; - } -} diff --git a/src/extproc/http_runner.hpp b/src/extproc/http_runner.hpp deleted file mode 100644 index c5f4b9bd03e..00000000000 --- a/src/extproc/http_runner.hpp +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#ifndef EXTPROC_HTTP_RUNNER_HPP_ -#define EXTPROC_HTTP_RUNNER_HPP_ - -#include -#include -#include -#include -#include - -#include "errors.hpp" -#include - -#include "containers/counted.hpp" -#include "rdb_protocol/datum.hpp" -#include "concurrency/signal.hpp" -#include "extproc/extproc_job.hpp" - -// http calls result either in a DATUM return value or an error string -struct http_result_t { - ql::datum_t header; - ql::datum_t body; - - // Cookies are not used in the query language, but they should be passed to any - // subsequent HTTP requests for the query (e.g. if performing depagination). - std::vector cookies; - std::string error; -}; - -RDB_DECLARE_SERIALIZABLE(http_result_t); - -class extproc_pool_t; -class http_runner_t; -class http_job_t; - -enum class http_method_t { - GET, - HEAD, - POST, - PUT, - PATCH, - DELETE -}; - -enum class http_auth_type_t { - NONE, - BASIC, - DIGEST -}; - -enum class http_result_format_t { - AUTO, - TEXT, - JSON, - JSONP, - BINARY -}; - -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE( - http_method_t, int8_t, - http_method_t::GET, http_method_t::DELETE); - -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE( - http_auth_type_t, int8_t, - http_auth_type_t::NONE, http_auth_type_t::DIGEST); - -ARCHIVE_PRIM_MAKE_RANGED_SERIALIZABLE( - http_result_format_t, int8_t, - http_result_format_t::AUTO, http_result_format_t::BINARY); - -std::string http_method_to_str(http_method_t method); - -struct http_opts_t { - // Sets the default options - http_opts_t(); - http_opts_t(http_opts_t &&) = default; - - struct http_auth_t { - // No auth by default - http_auth_t(); - http_auth_t(http_auth_t &&auth); - - void make_basic_auth(std::string &&user, - std::string &&pass); - - void make_digest_auth(std::string &&user, - std::string &&pass); - - http_auth_type_t type; - std::string username; - std::string password; - }; - - http_auth_t auth; - - http_method_t method; - http_result_format_t result_format; - - std::string proxy; - std::string url; - ql::datum_t url_params; - std::vector header; - std::vector cookies; - - // These will be used based on the method specified - std::string data; - std::map form_data; - - // Limits on execution size - ql::configured_limits_t limits; - - // ReQL version in use - reql_version_t version; - - uint64_t timeout_ms; - uint64_t attempts; - uint32_t max_redirects; - - bool verify; -}; - -RDB_DECLARE_SERIALIZABLE(http_opts_t); -RDB_DECLARE_SERIALIZABLE(http_opts_t::http_auth_t); - - -// A handle to a running "HTTP fetcher" job. -class http_runner_t : public home_thread_mixin_t { -public: - explicit http_runner_t(extproc_pool_t *_pool); - - void http(const http_opts_t &opts, - http_result_t *res_out, - signal_t *interruptor); - -private: - extproc_pool_t *pool; - - DISABLE_COPYING(http_runner_t); -}; - -#endif /* EXTPROC_HTTP_RUNNER_HPP_ */ diff --git a/src/extproc/js_job.cc b/src/extproc/js_job.cc deleted file mode 100644 index fe5ec1165ea..00000000000 --- a/src/extproc/js_job.cc +++ /dev/null @@ -1,759 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "extproc/js_job.hpp" - -#include -#include - -#include - -#include - -#include "containers/archive/boost_types.hpp" -#include "containers/archive/stl_types.hpp" -#include "extproc/extproc_job.hpp" -#include "math.hpp" -#include "rdb_protocol/configured_limits.hpp" -#include "rdb_protocol/pseudo_time.hpp" -#include "rdb_protocol/serialize_datum.hpp" -#include "utils.hpp" - -const js_id_t MIN_ID = 1; -const js_id_t MAX_ID = std::numeric_limits::max(); - -// Picked from a hat. -#define TO_JSON_RECURSION_LIMIT 500 - -// Returns an empty counted_t on error. -ql::datum_t js_to_datum(const v8::Handle &value, - const ql::configured_limits_t &limits, - std::string *err_out); - -// Returns an empty handle on error and sets `err_out` accordingly. -v8::Handle js_from_datum(const ql::datum_t &datum, - std::string *err_out); - -#ifdef V8_NEEDS_BUFFER_ALLOCATOR -class array_buffer_allocator_t : public v8::ArrayBuffer::Allocator { -public: - void *Allocate(size_t length) { - void *data = rmalloc(length); - memset(data, 0, length); - return data; - } - void *AllocateUninitialized(size_t length) { - return rmalloc(length); - } - void Free(void *data, UNUSED size_t length) { - free(data); - } -}; -#endif - -// Each worker process should have a single instance of this class before using the v8 API -class js_instance_t { -public: - static void run_other_tasks(); - static void maybe_initialize_v8(); - static v8::Isolate *isolate(); - -private: - js_instance_t(); - ~js_instance_t(); - - static js_instance_t *instance; - - v8::Isolate *isolate_; - - scoped_ptr_t platform; -#ifdef V8_NEEDS_BUFFER_ALLOCATOR - array_buffer_allocator_t array_buffer_allocator; -#endif -}; - -js_instance_t *js_instance_t::instance = nullptr; - -js_instance_t::js_instance_t() { - v8::V8::InitializeICU(); - platform.init(v8::platform::CreateDefaultPlatform()); - v8::V8::InitializePlatform(platform.get()); - v8::V8::Initialize(); -#ifdef V8_NEEDS_BUFFER_ALLOCATOR - v8::Isolate::CreateParams params; - params.array_buffer_allocator = &array_buffer_allocator; - isolate_ = v8::Isolate::New(params); -#else - isolate_ = v8::Isolate::New(); -#endif - isolate_->Enter(); -} - -js_instance_t::~js_instance_t() { - isolate_->Exit(); - isolate_->Dispose(); - v8::V8::Dispose(); - v8::V8::ShutdownPlatform(); -} - -void js_instance_t::run_other_tasks() { - v8::platform::PumpMessageLoop(instance->platform.get(), isolate()); -} - -v8::Isolate *js_instance_t::isolate() { - return instance->isolate_; -} - -void js_instance_t::maybe_initialize_v8() { - if (instance == nullptr) { - instance = new js_instance_t; - } -} - -// Wrapper around `v8::Persistent >` that calls `Reset()` on destruction -class persistent_value_t { -public: - ~persistent_value_t() { - value.Reset(); - } - v8::Persistent value; -}; - -// Worker-side JS evaluation environment. -class js_env_t { -public: - js_env_t(); - - js_result_t eval(const std::string &source, const ql::configured_limits_t &limits); - js_result_t call(js_id_t id, const std::vector &args, - const ql::configured_limits_t &limits); - void release(js_id_t id); - void run_other_tasks(uint64_t task_counter); - -private: - js_id_t remember_value(const v8::Handle &value); - const std::shared_ptr find_value(js_id_t id); - - js_id_t next_id; - std::map > values; -}; - -// Cleans the worker process's environment when instantiated -class js_context_t { -public: - js_context_t() : - local_scope(js_instance_t::isolate()), - context(v8::Context::New(js_instance_t::isolate())), - scope(context) { } - - v8::HandleScope local_scope; - v8::Local context; - v8::Context::Scope scope; -}; - -enum js_task_t { - TASK_EVAL, - TASK_CALL, - TASK_RELEASE, - TASK_EXIT -}; - -// The job_t runs in the context of the main rethinkdb process -js_job_t::js_job_t(extproc_pool_t *pool, signal_t *interruptor, - const ql::configured_limits_t &_limits) : - extproc_job(pool, &worker_fn, interruptor), limits(_limits) { } - -js_result_t js_job_t::eval(const std::string &source) { - js_task_t task = js_task_t::TASK_EVAL; - write_message_t wm; - wm.append(&task, sizeof(task)); - serialize(&wm, source); - serialize(&wm, limits); - { - int res = send_write_message(extproc_job.write_stream(), &wm); - if (res != 0) { - throw extproc_worker_exc_t("failed to send data to the worker"); - } - } - - js_result_t result; - archive_result_t res - = deserialize(extproc_job.read_stream(), - &result); - if (bad(res)) { - throw extproc_worker_exc_t(strprintf("failed to deserialize eval result from worker " - "(%s)", archive_result_as_str(res))); - } - return result; -} - -js_result_t js_job_t::call(js_id_t id, const std::vector &args) { - js_task_t task = js_task_t::TASK_CALL; - write_message_t wm; - wm.append(&task, sizeof(task)); - serialize(&wm, id); - serialize(&wm, args); - serialize(&wm, limits); - { - int res = send_write_message(extproc_job.write_stream(), &wm); - if (res != 0) { - throw extproc_worker_exc_t("failed to send data to the worker"); - } - } - - js_result_t result; - archive_result_t res - = deserialize(extproc_job.read_stream(), - &result); - if (bad(res)) { - throw extproc_worker_exc_t(strprintf("failed to deserialize call result from worker " - "(%s)", archive_result_as_str(res))); - } - return result; -} - -void js_job_t::release(js_id_t id) { - js_task_t task = js_task_t::TASK_RELEASE; - write_message_t wm; - wm.append(&task, sizeof(task)); - serialize(&wm, id); - { - int res = send_write_message(extproc_job.write_stream(), &wm); - if (res != 0) { - throw extproc_worker_exc_t("failed to send data to the worker"); - } - } - - // Wait for a response so we don't flood the job with requests - bool dummy_result; - archive_result_t res - = deserialize(extproc_job.read_stream(), - &dummy_result); - // dummy_result should always be true - if (bad(res) || !dummy_result) { - throw extproc_worker_exc_t(strprintf("failed to deserialize release result from worker " - "(%s)", archive_result_as_str(res))); - } -} - -void js_job_t::exit() { - js_task_t task = js_task_t::TASK_EXIT; - write_message_t wm; - wm.append(&task, sizeof(task)); - { - int res = send_write_message(extproc_job.write_stream(), &wm); - if (res != 0) { - throw extproc_worker_exc_t("failed to send data to the worker"); - } - } - - // Wait for a response so we don't flood the job with requests - bool dummy_result; - archive_result_t res - = deserialize(extproc_job.read_stream(), - &dummy_result); - // dummy_result should always be true - if (bad(res) || !dummy_result) { - throw extproc_worker_exc_t(strprintf("failed to deserialize exit result from worker " - "(%s)", archive_result_as_str(res))); - } -} - -void js_job_t::worker_error() { - extproc_job.worker_error(); -} - -void js_env_t::run_other_tasks(uint64_t task_counter) { - js_instance_t::run_other_tasks(); - if (task_counter % 128 == 0) { - // Force collection of all garbage - js_instance_t::isolate()->LowMemoryNotification(); - } -} - -bool send_js_result(write_stream_t *stream_out, const js_result_t &js_result) { - write_message_t wm; - serialize(&wm, js_result); - int res = send_write_message(stream_out, &wm); - return res == 0; -} - -bool send_dummy_result(write_stream_t *stream_out) { - write_message_t wm; - serialize(&wm, true); - int res = send_write_message(stream_out, &wm); - return res == 0; -} - -bool run_eval(read_stream_t *stream_in, - write_stream_t *stream_out, - js_env_t *js_env, - uint64_t task_counter) { - std::string source; - ql::configured_limits_t limits; - { - archive_result_t res - = deserialize(stream_in, - &source); - if (bad(res)) { return false; } - res = deserialize(stream_in, - &limits); - if (bad(res)) { return false; } - } - - js_result_t js_result; - try { - js_result = js_env->eval(source, limits); - } catch (const std::exception &e) { - js_result = e.what(); - } catch (...) { - js_result = std::string("encountered an unknown exception"); - } - - js_env->run_other_tasks(task_counter); - return send_js_result(stream_out, js_result); -} - -bool run_call(read_stream_t *stream_in, - write_stream_t *stream_out, - js_env_t *js_env, - uint64_t task_counter) { - js_id_t id; - std::vector args; - ql::configured_limits_t limits; - { - archive_result_t res - = deserialize(stream_in, &id); - if (bad(res)) { return false; } - res = deserialize(stream_in, &args); - if (bad(res)) { return false; } - res = deserialize(stream_in, &limits); - if (bad(res)) { return false; } - } - - js_result_t js_result; - try { - js_result = js_env->call(id, args, limits); - } catch (const std::exception &e) { - js_result = e.what(); - } catch (...) { - js_result = std::string("encountered an unknown exception"); - } - - js_env->run_other_tasks(task_counter); - return send_js_result(stream_out, js_result); -} - -bool run_release(read_stream_t *stream_in, - write_stream_t *stream_out, - js_env_t *js_env, - uint64_t task_counter) { - js_id_t id; - archive_result_t res = deserialize(stream_in, - &id); - if (bad(res)) { return false; } - - js_env->release(id); - js_env->run_other_tasks(task_counter); - return send_dummy_result(stream_out); -} - -bool run_exit(write_stream_t *stream_out) { - return send_dummy_result(stream_out); -} - -bool js_job_t::worker_fn(read_stream_t *stream_in, write_stream_t *stream_out) { - static uint64_t task_counter = 0; - bool running = true; - js_instance_t::maybe_initialize_v8(); - js_env_t js_env; - - while (running) { - task_counter += 1; - js_task_t task; - int64_t read_size = sizeof(task); - { - int64_t res = force_read(stream_in, &task, read_size); - if (res != read_size) { return false; } - } - - switch (task) { - case TASK_EVAL: - if (!run_eval(stream_in, stream_out, &js_env, task_counter)) { - return false; - } - break; - case TASK_CALL: - if (!run_call(stream_in, stream_out, &js_env, task_counter)) { - return false; - } - break; - case TASK_RELEASE: - if (!run_release(stream_in, stream_out, &js_env, task_counter)) { - return false; - } - break; - case TASK_EXIT: - return run_exit(stream_out); - default: - return false; - } - } - unreachable(); -} - -static void append_caught_error(std::string *err_out, const v8::TryCatch &try_catch) { - if (!try_catch.HasCaught()) return; - - v8::String::Utf8Value exception(try_catch.Exception()); - const char *message = *exception; - guarantee(message != nullptr); - err_out->append(message, strlen(message)); -} - -// The env_t runs in the context of the worker process -js_env_t::js_env_t() : - next_id(MIN_ID) { } - -js_result_t js_env_t::eval(const std::string &source, - const ql::configured_limits_t &limits) { - js_context_t clean_context; - js_result_t result(""); - std::string *err_out = boost::get(&result); - - v8::Isolate *isolate = js_instance_t::isolate(); - - v8::HandleScope handle_scope(isolate); - - // TODO: use an "external resource" to avoid copy? - v8::Handle src = v8::String::NewFromUtf8(isolate, - source.data(), - v8::String::NewStringType::kNormalString, - source.size()); - - // This constructor registers itself with v8 so that any errors generated - // within v8 will be available within this object. - v8::TryCatch try_catch; - - // Firstly, compilation may fail (because of say a syntax error) - v8::Handle script = v8::Script::Compile(src); - if (script.IsEmpty()) { - // Get the error out of the TryCatch object - append_caught_error(err_out, try_catch); - } else { - // Secondly, evaluation may fail because of an exception generated - // by the code - v8::Handle result_val = script->Run(); - if (result_val.IsEmpty()) { - // Get the error from the TryCatch object - append_caught_error(err_out, try_catch); - } else { - // Scripts that evaluate to functions become RQL Func terms that - // can be passed to map, filter, reduce, etc. - if (result_val->IsFunction()) { - v8::Handle func - = v8::Handle::Cast(result_val); - result = remember_value(func); - } else { - guarantee(!result_val.IsEmpty()); - - // JSONify result. - ql::datum_t datum = js_to_datum(result_val, limits, err_out); - if (datum.has()) { - result = datum; - } - } - } - } - - return result; -} - -js_id_t js_env_t::remember_value(const v8::Handle &value) { - guarantee(next_id < MAX_ID); - js_id_t id = next_id++; - - // Save this value in a persistent handle so it isn't deallocated when - // its scope is destructed. - - std::shared_ptr persistent_handle(new persistent_value_t()); - persistent_handle->value.Reset(js_instance_t::isolate(), value); - - values.insert(std::make_pair(id, persistent_handle)); - return id; -} - -const std::shared_ptr js_env_t::find_value(js_id_t id) { - std::map >::iterator it = values.find(id); - guarantee(it != values.end()); - return it->second; -} - -v8::Local run_js_func(v8::Handle fn, - const std::vector &args, - std::string *err_out) { - v8::Isolate *isolate = js_instance_t::isolate(); - - v8::TryCatch try_catch; - v8::EscapableHandleScope scope(isolate); - - // Construct receiver object. - v8::Handle obj = v8::Object::New(isolate); - guarantee(!obj.IsEmpty()); - - // Construct arguments. - scoped_array_t > handles(args.size()); - for (size_t i = 0; i < args.size(); ++i) { - handles[i] = js_from_datum(args[i], err_out); - if (!err_out->empty()) { - return v8::Handle(); - } - } - - // Call function with environment as its receiver. - v8::Local result = fn->Call(obj, args.size(), handles.data()); - if (result.IsEmpty()) { - append_caught_error(err_out, try_catch); - } - return scope.Escape(result); -} - -js_result_t js_env_t::call(js_id_t id, - const std::vector &args, - const ql::configured_limits_t &limits) { - js_context_t clean_context; - js_result_t result(""); - std::string *err_out = boost::get(&result); - - const std::shared_ptr found_value = find_value(id); - guarantee(!found_value->value.IsEmpty()); - - v8::Isolate *isolate = js_instance_t::isolate(); - - v8::HandleScope handle_scope(isolate); - - // Construct local handle from persistent handle - v8::Local local_handle = - v8::Local::New(isolate, found_value->value); - v8::Local fn = v8::Local::Cast(local_handle); - v8::Handle value = run_js_func(fn, args, err_out); - - if (!value.IsEmpty()) { - if (value->IsFunction()) { - *err_out = "Returning functions from within `r.js` is unsupported."; - } else { - // JSONify result. - ql::datum_t datum = js_to_datum(value, limits, err_out); - if (datum.has()) { - result = datum; - } - } - } - return result; -} - -void js_env_t::release(js_id_t id) { - guarantee(id < next_id); - size_t num_erased = values.erase(id); - guarantee(1 == num_erased); -} - -// TODO: Is there a better way of detecting circular references than a recursion limit? -ql::datum_t js_make_datum(const v8::Handle &value, - int recursion_limit, - const ql::configured_limits_t &limits, - std::string *err_out) { - ql::datum_t result; - - if (0 == recursion_limit) { - err_out->assign("Recursion limit exceeded in js_to_json (circular reference?)."); - return result; - } - --recursion_limit; - - // TODO: should we handle BooleanObject, NumberObject, StringObject? - v8::HandleScope handle_scope(js_instance_t::isolate()); - - if (value->IsString()) { - v8::Handle string = value->ToString(); - guarantee(!string.IsEmpty()); - - size_t length = string->Utf8Length(); - scoped_array_t temp_buffer(length); - string->WriteUtf8(temp_buffer.data(), length); - - try { - result = ql::datum_t(datum_string_t(length, temp_buffer.data())); - } catch (const ql::base_exc_t &ex) { - err_out->assign(ex.what()); - } - } else if (value->IsObject()) { - // This case is kinda weird. Objects can have stuff in them that isn't - // represented in their JSON (eg. their prototype, v8 hidden fields). - - if (value->IsArray()) { - v8::Handle arrayh = v8::Handle::Cast(value); - std::vector datum_array; - datum_array.reserve(arrayh->Length()); - - for (uint32_t i = 0; i < arrayh->Length(); ++i) { - v8::Handle elth = arrayh->Get(i); - guarantee(!elth.IsEmpty()); - - ql::datum_t item = js_make_datum(elth, recursion_limit, limits, err_out); - if (!item.has()) { - // Result is still empty, the error message has been set - return result; - } - datum_array.push_back(std::move(item)); - } - - result = ql::datum_t(std::move(datum_array), limits); - } else if (value->IsFunction()) { - // We can't represent functions in JSON. - err_out->assign("Cannot convert function to ql::datum_t."); - } else if (value->IsRegExp()) { - // We can't represent regular expressions in datums - err_out->assign("Cannot convert RegExp to ql::datum_t."); - } else if (value->IsDate()) { - result = ql::pseudo::make_time(value->NumberValue() / 1000, - "+00:00"); - } else { - // Treat it as a dictionary. - v8::Handle objh = value->ToObject(); - guarantee(!objh.IsEmpty()); - v8::Handle properties = objh->GetPropertyNames(); - guarantee(!properties.IsEmpty()); - - ql::datum_object_builder_t builder; - - uint32_t len = properties->Length(); - for (uint32_t i = 0; i < len; ++i) { - v8::Handle keyh = properties->Get(i)->ToString(); - guarantee(!keyh.IsEmpty()); - v8::Handle valueh = objh->Get(keyh); - guarantee(!valueh.IsEmpty()); - - ql::datum_t item = js_make_datum(valueh, recursion_limit, limits, err_out); - - if (!item.has()) { - // Result is still empty, the error message has been set - return result; - } - - size_t length = keyh->Utf8Length(); - scoped_array_t temp_buffer(length); - keyh->WriteUtf8(temp_buffer.data(), length); - datum_string_t key_string(length, temp_buffer.data()); - - builder.overwrite(key_string, item); - } - result = std::move(builder).to_datum(); - } - } else if (value->IsNumber()) { - double num_val = value->NumberValue(); - - if (!risfinite(num_val)) { - err_out->assign("Number return value is not finite."); - } else { - result = ql::datum_t(num_val); - } - } else if (value->IsBoolean()) { - result = ql::datum_t::boolean(value->BooleanValue()); - } else if (value->IsNull()) { - result = ql::datum_t::null(); - } else { - err_out->assign(value->IsUndefined() ? - "Cannot convert javascript `undefined` to ql::datum_t." : - "Unrecognized value type when converting to ql::datum_t."); - } - return result; -} - -ql::datum_t js_to_datum(const v8::Handle &value, - const ql::configured_limits_t &limits, - std::string *err_out) { - guarantee(!value.IsEmpty()); - guarantee(err_out != nullptr); - - v8::HandleScope handle_scope(js_instance_t::isolate()); - err_out->assign("Unknown error when converting to ql::datum_t."); - - return js_make_datum(value, TO_JSON_RECURSION_LIMIT, limits, err_out); -} - -v8::Handle js_from_datum(const ql::datum_t &datum, - std::string *err_out) { - guarantee(datum.has()); - - v8::Isolate *isolate = js_instance_t::isolate(); - - switch (datum.get_type()) { - case ql::datum_t::type_t::MINVAL: - err_out->assign("`r.minval` cannot be passed to `r.js`."); - return v8::Handle(); - case ql::datum_t::type_t::MAXVAL: - err_out->assign("`r.maxval` cannot be passed to `r.js`."); - return v8::Handle(); - case ql::datum_t::type_t::R_BINARY: - // TODO: In order to support this, we need to link against a static version of - // V8, which provides an ArrayBuffer API. - err_out->assign("`r.binary` data cannot be used in `r.js`."); - return v8::Handle(); - case ql::datum_t::type_t::R_BOOL: - if (datum.as_bool()) { - return v8::True(isolate); - } else { - return v8::False(isolate); - } - case ql::datum_t::type_t::R_NULL: - return v8::Null(isolate); - case ql::datum_t::type_t::R_NUM: - return v8::Number::New(isolate, datum.as_num()); - case ql::datum_t::type_t::R_STR: - return v8::String::NewFromUtf8(isolate, datum.as_str().to_std().c_str()); - case ql::datum_t::type_t::R_ARRAY: { - v8::Handle array = v8::Array::New(isolate); - - for (size_t i = 0; i < datum.arr_size(); ++i) { - v8::HandleScope scope(isolate); - v8::Handle val = js_from_datum(datum.get(i), err_out); - if (val.IsEmpty()) { - // The recursive call to `js_from_datum` should have set `err_out` - guarantee(!err_out->empty()); - return v8::Handle(); - } - array->Set(i, val); - } - - return array; - } - case ql::datum_t::type_t::R_OBJECT: { - if (datum.is_ptype(ql::pseudo::time_string)) { - double epoch_time = ql::pseudo::time_to_epoch_time(datum); - v8::Handle date = v8::Date::New(isolate, epoch_time * 1000); - return date; - } else { - v8::Handle obj = v8::Object::New(isolate); - - for (size_t i = 0; i < datum.obj_size(); ++i) { - auto pair = datum.get_pair(i); - v8::HandleScope scope(isolate); - v8::Handle key = v8::String::NewFromUtf8(isolate, pair.first.to_std().c_str()); - v8::Handle val = js_from_datum(pair.second, err_out); - if (val.IsEmpty()) { - // The recursive call to `js_from_datum` should have set `err_out` - guarantee(!err_out->empty()); - return v8::Handle(); - } - guarantee(!key.IsEmpty() && !val.IsEmpty()); - obj->Set(key, val); - } - - return obj; - } - } - case ql::datum_t::type_t::UNINITIALIZED: // fallthru - default: - err_out->assign("bad datum value in js extproc"); - return v8::Handle(); - } -} diff --git a/src/extproc/js_job.hpp b/src/extproc/js_job.hpp deleted file mode 100644 index c410d3c9f2a..00000000000 --- a/src/extproc/js_job.hpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef EXTPROC_JS_JOB_HPP_ -#define EXTPROC_JS_JOB_HPP_ - -#include -#include - -#include "utils.hpp" -#include "containers/archive/archive.hpp" -#include "containers/counted.hpp" -#include "concurrency/signal.hpp" -#include "extproc/extproc_pool.hpp" -#include "extproc/extproc_job.hpp" -#include "extproc/js_runner.hpp" -#include "rdb_protocol/datum.hpp" - -class js_job_t { -public: - js_job_t(extproc_pool_t *pool, signal_t *interruptor, - const ql::configured_limits_t &limits); - - js_result_t eval(const std::string &source); - js_result_t call(js_id_t id, const std::vector &args); - void release(js_id_t id); - void exit(); - - // Marks the extproc worker as errored to simplify cleanup later - void worker_error(); - -private: - static bool worker_fn(read_stream_t *stream_in, write_stream_t *stream_out); - - extproc_job_t extproc_job; - ql::configured_limits_t limits; - DISABLE_COPYING(js_job_t); -}; - -#endif /* EXTPROC_JS_JOB_HPP_ */ diff --git a/src/extproc/js_runner.cc b/src/extproc/js_runner.cc deleted file mode 100644 index 9bfae470ab1..00000000000 --- a/src/extproc/js_runner.cc +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright 2010-2014 RethinkDB, all rights reserved. -#include "extproc/js_runner.hpp" - -#include // For PRIu64 - -#include - -#include "extproc/js_job.hpp" -#include "time.hpp" -#include "utils.hpp" - -const size_t js_runner_t::CACHE_SIZE = 100; - -// This class allows us to manage timeouts in a cleaner manner -class js_timeout_t { -public: - // Construct a sentry on the stack when performing a timed js call, and it - // will run the timer for the duration it is in scope - class sentry_t { - public: - sentry_t(js_timeout_t *_parent, uint64_t timeout_ms) : - parent(_parent) { - if (parent->timer.is_pulsed()) { - throw interrupted_exc_t(); - } - parent->timer.start(timeout_ms); - } - ~sentry_t() { - if (!parent->timer.is_pulsed()) { - parent->timer.cancel(); - } - } - private: - js_timeout_t *parent; - }; - - signal_t *get_signal() { - return &timer; - } - -private: - signal_timer_t timer; -}; - -// Contains all the data relevant to a single worker process, so we can -// easily clear it all and replace it -class js_runner_t::job_data_t { -public: - job_data_t(extproc_pool_t *pool, signal_t *interruptor, - const ql::configured_limits_t &limits) : - combined_interruptor(interruptor, js_timeout.get_signal()), - js_job(pool, &combined_interruptor, limits) { } - - explicit job_data_t(extproc_pool_t *pool, - const ql::configured_limits_t &limits) : - js_job(pool, js_timeout.get_signal(), limits) { } - - struct func_info_t { - explicit func_info_t(js_id_t _id) : - id(_id), timestamp(current_microtime()) { } - - js_id_t id; - microtime_t timestamp; - }; - - std::map id_cache; - js_timeout_t js_timeout; - wait_any_t combined_interruptor; - js_job_t js_job; -}; - -js_runner_t::js_runner_t() { } - -js_runner_t::~js_runner_t() { - assert_thread(); - if (job_data.has()) { - end(); - } -} - -void js_runner_t::end() { - assert_thread(); - // Have the worker job exit its loop - if anything fails, - // don't worry, the worker will be cleaned up - try { - for (auto it = job_data->id_cache.begin(); - it != job_data->id_cache.end(); ++it) { - job_data->js_job.release(it->second.id); - } - job_data->js_job.exit(); - } catch (...) { - // Do nothing - } - job_data.reset(); -} - -bool js_runner_t::connected() const { - assert_thread(); - return job_data.has(); -} - -// Starts the javascript function in the worker process -void js_runner_t::begin(extproc_pool_t *pool, signal_t *interruptor, - const ql::configured_limits_t &limits) { - assert_thread(); - if (interruptor == nullptr) { - job_data.init(new job_data_t(pool, limits)); - } else { - job_data.init(new job_data_t(pool, interruptor, limits)); - } -} - -js_result_t js_runner_t::eval(const std::string &source, - const req_config_t &config) { - assert_thread(); - guarantee(job_data.has()); - - js_result_t result; - - // Check if this is a function we already have cached - auto it = job_data->id_cache.find(source); - if (it != job_data->id_cache.end()) { - result = it->second.id; - return result; - } - - object_buffer_t sentry; - sentry.create(&job_data->js_timeout, config.timeout_ms); - - bool is_timeout = false; - try { - try { - result = job_data->js_job.eval(source); - } catch (...) { - // This inner try-catch block deals with cleanup after an exception, but due - // to this we must store whether we triggered the timeout signal. - is_timeout = job_data->js_timeout.get_signal()->is_pulsed(); - - // Sentry must be destroyed before the js_timeout - sentry.reset(); - // This will mark the worker as errored so we don't try to re-sync with it - // on the next line (since we're in a catch statement, we aren't allowed) - job_data->js_job.worker_error(); - job_data.reset(); - - throw; - } - } catch (interrupted_exc_t const &e) { - // This outer try-catch block explicitly checks whether it was an - // `interrupted_exc_t`, and if so deals with the timeout if set. - if (is_timeout) { - return strprintf( - "JavaScript query `%s` timed out after %" PRIu64 ".%03" PRIu64 " seconds.", - source.c_str(), config.timeout_ms / 1000, config.timeout_ms % 1000); - } else { - throw; - } - } - - // If the eval returned a function, cache it - js_id_t *any_id = boost::get(&result); - if (any_id != nullptr) { - cache_id(*any_id, source); - } - - return result; -} - -js_result_t js_runner_t::call(const std::string &source, - const std::vector &args, - const req_config_t &config) { - assert_thread(); - guarantee(job_data.has()); - - // This will retrieve the function from the cache if it's there, or re-eval it - js_result_t result = eval(source, config); - js_id_t *fn_id = boost::get(&result); - if (fn_id == nullptr) { - if (boost::get(&result) != nullptr) { - result = strprintf("Javascript query `%s` returned a value when it should " - "have returned a function.", source.c_str()); - } - return result; - } - - object_buffer_t sentry; - sentry.create(&job_data->js_timeout, config.timeout_ms); - - bool is_timeout = false; - try { - try { - result = job_data->js_job.call(*fn_id, args); - } catch (...) { - // This inner try-catch block deals with cleanup after an exception, but due - // to this we must store whether we triggered the timeout signal. - is_timeout = job_data->js_timeout.get_signal()->is_pulsed(); - - // Sentry must be destroyed before the js_timeout - sentry.reset(); - // This will mark the worker as errored so we don't try to re-sync with it - // on the next line (since we're in a catch statement, we aren't allowed) - job_data->js_job.worker_error(); - job_data.reset(); - - throw; - } - } catch (interrupted_exc_t const &e) { - // This outer try-catch block explicitly checks whether it was an - // `interrupted_exc_t`, and if so deals with the timeout if set. - if (is_timeout) { - return strprintf( - "JavaScript query `%s` timed out after %" PRIu64 ".%03" PRIu64 " seconds.", - source.c_str(), config.timeout_ms / 1000, config.timeout_ms % 1000); - } else { - throw; - } - } - - // If the call returned a function, cache it - js_id_t *any_id = boost::get(&result); - if (any_id != nullptr) { - cache_id(*any_id, source); - } - - return result; -} - -void js_runner_t::cache_id(js_id_t id, const std::string &source) { - guarantee(job_data.has()); - guarantee(id != INVALID_ID); - - trim_cache(); - - auto cache_res = job_data->id_cache.insert(std::make_pair(source, - job_data_t::func_info_t(id))); - guarantee(cache_res.second, "js_runner_t cached two functions with the same source"); -} - -void js_runner_t::release_id(js_id_t id) { - guarantee(job_data.has()); - job_data->js_job.release(id); -} - -void js_runner_t::trim_cache() { - guarantee(job_data.has()); - - if (job_data->id_cache.size() < CACHE_SIZE) { - return; - } - - auto oldest_func = job_data->id_cache.begin(); - for (auto it = ++job_data->id_cache.begin(); it != job_data->id_cache.end(); ++it) { - if (it->second.timestamp < oldest_func->second.timestamp) { - oldest_func = it; - } - } - - try { - release_id(oldest_func->second.id); - job_data->id_cache.erase(oldest_func); - } catch (...) { - // This will mark the worker as errored so we don't try to re-sync with it - // on the next line (since we're in a catch statement, we aren't allowed) - job_data->js_job.worker_error(); - job_data.reset(); - throw; - } -} diff --git a/src/extproc/js_runner.hpp b/src/extproc/js_runner.hpp deleted file mode 100644 index cf7f192ae95..00000000000 --- a/src/extproc/js_runner.hpp +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2010-2013 RethinkDB, all rights reserved. -#ifndef EXTPROC_JS_RUNNER_HPP_ -#define EXTPROC_JS_RUNNER_HPP_ - -#include -#include -#include - -#include "errors.hpp" -#include - -#include "containers/scoped.hpp" -#include "containers/counted.hpp" -#include "rdb_protocol/datum.hpp" -#include "concurrency/wait_any.hpp" -#include "arch/timing.hpp" -#include "extproc/extproc_job.hpp" - -// Unique ids used to refer to objects on the JS side. -typedef uint64_t js_id_t; -const js_id_t INVALID_ID = 0; - -// JS calls result either in a DATUM return value, a function id (which we can -// use to call the function later), or an error string -typedef boost::variant js_result_t; - -class extproc_pool_t; -class js_runner_t; -class js_job_t; -class js_timeout_sentry_t; - -// A handle to a running "javascript evaluator" job. -class js_runner_t : public home_thread_mixin_t { -public: - js_runner_t(); - ~js_runner_t(); - - bool connected() const; - - // Used for worker configuration - struct req_config_t { - uint64_t timeout_ms; - }; - - void begin(extproc_pool_t *pool, - signal_t *interruptor, - const ql::configured_limits_t &limits); - - void end(); - - // Evalute JS source string to either a value or a function ID to call later - js_result_t eval(const std::string &source, - const req_config_t &config); - - // Calls a previously compiled function. - js_result_t call(const std::string &source, - const std::vector &args, - const req_config_t &config); - -private: - static const size_t CACHE_SIZE; - - void cache_id(js_id_t id, const std::string &source); - void trim_cache(); - - // Invalidates an ID, dereferencing the object it refers to in the - // javascript evaluator process. - void release_id(js_id_t id); - - - class job_data_t; - scoped_ptr_t job_data; - - DISABLE_COPYING(js_runner_t); -}; - -#endif /* EXTPROC_JS_RUNNER_HPP_ */ diff --git a/src/gen/web_assets.cc b/src/gen/web_assets.cc deleted file mode 100644 index 4acf0f24d6d..00000000000 --- a/src/gen/web_assets.cc +++ /dev/null @@ -1,132320 +0,0 @@ - -// Generated by scripts/compile-web-assets.py - -#include -#include - -std::map static_web_assets = { - { "/favicon.ico", std::string( - "\0\0\1\0\2\0\x10\x10\0\0\1\0\b\0h\5\0\0&\0\0\0 \0\0\1\0 \0\xa8\x10\0\0\x8e\5" - "\0\0(\0\0\0\x10\0\0\0 \0\0\0\1\0\b\0\0\0\0\0\0\1\0\0\x12\v\0\0\x12\v\0\0\0\1" - "\0\0\0\1\0\0\"\"\"\0$$$\0&&&\0'''\0+++\0,,,\0---\0\x30\x30\x30\0\x33\x33\x33" - "\0\x39\x39\x39\0===\0???\0\x41\x41\x41\0QQQ\0RRR\0\x62\x62\x62\0\x66\x66\x66" - "\0ppp\0rrr\0xxx\0yyy\0{{{\0\x7f\x7f\x7f\0\x86\x86\x86\0\x89\x89\x89\0\x91\x91" - "\x91\0\x96\x96\x96\0\x98\x98\x98\0\xa1\xa1\xa1\0\xb0\xb0\xb0\0\xb9\xb9\xb9\0" - "\xcd\xcd\xcd\0\xce\xce\xce\0\xd0\xd0\xd0\0\xd2\xd2\xd2\0\xda\xda\xda\0\xdf\xdf" - "\xdf\0\xe1\xe1\xe1\0\xe8\xe8\xe8\0\xed\xed\xed\0\xf7\xf7\xf7\0\xf8\xf8\xf8\0" - "\xff\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\x11\x11\5\0\0\0\r\x11\x11\4\0\0\0" - "\0\0\n" - "**\f\0\0\r(*\x17\0\0\0\0\0\0\n" - "**\f\0\f'*\x1c\0\0\0\0\0\0\0\n" - "**\n" - "\a$*\x1e\1\0\0\0\0\0\0\0\n" - "**\x10!*\x1f\4\0\0\0\0\0\0\0\0\n" - "*****&\x13\1\1\0\0\0\0\0\0\n" - "**\x13\xf\x19)*\x13\0\0\0\0\0\0\0\n" - "**\n" - "\1\0\x1a*!\0\0\0\0\0\0\0\n" - "**\f\1\1\x13*%\0\0\0\0\0\0\0\n" - "**\f\0\t!*\x1d\0\0\0\0\0\0\0\n" - "**((**#\t\0\0\0\0\0\0\0\a\x1b\x1b\x1b\x1a\x18\xf\1\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0(\0\0\0 \0\0\0@\0\0\0\1\0 \0\0\0\0\0\0\x10\0\0\x12\v" - "\0\0\x12\v\0\0\0\0\0\0\0\0\0\0\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xffHHH\xff\xc1\xc1\xc1\xff\xc1\xc1\xc1\xff\xc1\xc1\xc1\xff\xc1\xc1" - "\xc1\xffLLL\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff@@@\xff\xbe\xbe\xbe\xff\xc1\xc1\xc1\xff\xc1\xc1\xc1\xff\xc1\xc1\xc1\xff" - "\xbb\xbb\xbb\xff\x38\x38\x38\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"" - "\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "WWW\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff]]]\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\x33\x33\x33\xff" - "\xdd\xdd\xdd\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfd\xfd\xfd" - "\xffppp\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xffWWW\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff]]]\xff\"\"\"\xff\"\"" - "\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff)))\xff\xcb\xcb\xcb\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x89\x89\x89\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xffWWW\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff]]]\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff$$$\xff\xb6\xb6\xb6\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xa4\xa4\xa4\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xffWWW\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff]]]\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\x9c" - "\x9c\x9c\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xbc\xbc\xbc\xff%%%\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"" - "\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xffWWW\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff]]]\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\x81\x81\x81\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xd0\xd0\xd0\xff,,,\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xffWWW\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff]" - "]]\xff\"\"\"\xff\"\"\"\xffhhh\xff\xfd\xfd\xfd\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xe1\xe1\xe1\xff\x36\x36\x36\xff\"\"\"\xff\"\"\"\xff\"\"" - "\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xffWWW\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff]]]\xff\"\"\"" - "\xffSSS\xff\xf6\xf6\xf6\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xee" - "\xee\xee\xff\x44\x44\x44\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xffWWW\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x9e\x9e\x9e\xff}}}\xff\xec\xec" - "\xec\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf7\xf7\xf7\xffUUU\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xffWWW\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf5\xf5\xf5\xff\xad\xad" - "\xad\xffLLL\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xffWWW\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xfc\xfc\xfc\xff\x91\x91\x91\xff###\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xffWWW\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xbc\xbc\xbc\xff\xa4\xa4\xa4" - "\xff\xa4\xa4\xa4\xff\xb0\xb0\xb0\xff\xd4\xd4\xd4\xff\xfd\xfd\xfd\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x8b\x8b\x8b\xff\"\"" - "\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xffWWW\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff]" - "]]\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xffPPP\xff\xe4\xe4\xe4\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf9\xf9\xf9\xff===\xff\"\"\"\xff\"\"" - "\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xffWWW\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff]]]\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xffUUU\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\x8d\x8d\x8d\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xffWWW\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff]]]\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff###\xff\xe2\xe2\xe2\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xbc\xbc\xbc\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xffWWW\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff]]]\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\xca\xca\xca\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xca\xca\xca" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "WWW\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff]]]\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\xdf\xdf\xdf\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xbd\xbd\xbd\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xffWWW\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff]]]\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xffVVV\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\x8d\x8d\x8d\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xffWWW\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff]]]\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff...\xff" - "rrr\xff\xec\xec\xec\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf7\xf7" - "\xf7\xff<<<\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xffWWW\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xf2\xf2\xf2\xff\xed\xed\xed\xff\xed\xed\xed\xff\xf1\xf1\xf1\xff\xfe\xfe" - "\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe" - "\xfe\xfe\xff\x7f\x7f\x7f\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xffWWW\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xf3\xf3\xf3\xffxxx\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xffWWW\xff\xfe\xfe\xfe\xff\xfe\xfe\xfe" - "\xff\xfe\xfe\xfe\xff\xfe\xfe\xfe\xff\xfe\xfe\xfe\xff\xfe\xfe\xfe\xff\xfe\xfe" - "\xfe\xff\xfd\xfd\xfd\xff\xfa\xfa\xfa\xff\xe6\xe6\xe6\xff\xc0\xc0\xc0\xff\x83" - "\x83\x83\xff\x33\x33\x33\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff&&&\xff\x31\x31\x31\xff" - "111\xff\x31\x31\x31\xff\x31\x31\x31\xff\x31\x31\x31\xff\x31\x31\x31\xff\x31\x31" - "1\xff,,,\xff$$$\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff" - "\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"" - "\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"" - "\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\"\"\"\xff\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0", - 5686 ) }, - { "/index.html", std::string( - "\n" - "\n" - " \n" - " \n" - " RethinkDB Administration Console\n" - " \n" - " \n" - " \n" - " \n" - " \n" - "\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - "\n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - "\n" - " \n" - " \n" - "
Loading...
\n" - " \n" - "\n", - 3791 ) }, - { "/cluster.css", std::string( - "article,\n" - "aside,\n" - "details,\n" - "figcaption,\n" - "figure,\n" - "footer,\n" - "header,\n" - "hgroup,\n" - "nav,\n" - "section {\n" - " display: block;\n" - "}\n" - "audio,\n" - "canvas,\n" - "video {\n" - " display: inline-block;\n" - " *display: inline;\n" - " *zoom: 1;\n" - "}\n" - "audio:not([controls]) {\n" - " display: none;\n" - "}\n" - "html {\n" - " font-size: 100%;\n" - " -webkit-text-size-adjust: 100%;\n" - " -ms-text-size-adjust: 100%;\n" - "}\n" - "a:focus {\n" - " outline: thin dotted #333;\n" - " outline: 5px auto -webkit-focus-ring-color;\n" - " outline-offset: -2px;\n" - "}\n" - "a:hover,\n" - "a:active {\n" - " outline: 0;\n" - "}\n" - "sub,\n" - "sup {\n" - " position: relative;\n" - " font-size: 75%;\n" - " line-height: 0;\n" - " vertical-align: baseline;\n" - "}\n" - "sup {\n" - " top: -0.5em;\n" - "}\n" - "sub {\n" - " bottom: -0.25em;\n" - "}\n" - "img {\n" - " height: auto;\n" - " border: 0;\n" - " -ms-interpolation-mode: bicubic;\n" - " vertical-align: middle;\n" - "}\n" - "button,\n" - "input,\n" - "select,\n" - "textarea {\n" - " margin: 0;\n" - " font-size: 100%;\n" - " vertical-align: middle;\n" - "}\n" - "button,\n" - "input {\n" - " *overflow: visible;\n" - " line-height: normal;\n" - "}\n" - "button::-moz-focus-inner,\n" - "input::-moz-focus-inner {\n" - " padding: 0;\n" - " border: 0;\n" - "}\n" - "button,\n" - "input[type=\"button\"],\n" - "input[type=\"reset\"],\n" - "input[type=\"submit\"] {\n" - " cursor: pointer;\n" - " -webkit-appearance: button;\n" - "}\n" - "input[type=\"search\"] {\n" - " -webkit-appearance: textfield;\n" - " -webkit-box-sizing: content-box;\n" - " -moz-box-sizing: content-box;\n" - " box-sizing: content-box;\n" - "}\n" - "input[type=\"search\"]::-webkit-search-decoration,\n" - "input[type=\"search\"]::-webkit-search-cancel-button {\n" - " -webkit-appearance: none;\n" - "}\n" - "textarea {\n" - " overflow: auto;\n" - " vertical-align: top;\n" - "}\n" - ".clearfix {\n" - " *zoom: 1;\n" - "}\n" - ".clearfix:before,\n" - ".clearfix:after {\n" - " display: table;\n" - " content: \"\";\n" - "}\n" - ".clearfix:after {\n" - " clear: both;\n" - "}\n" - ".hide-text {\n" - " overflow: hidden;\n" - " text-indent: 100%;\n" - " white-space: nowrap;\n" - "}\n" - ".input-block-level {\n" - " display: block;\n" - " width: 100%;\n" - " min-height: 28px;\n" - " /* Make inputs at least the height of their button counterpart */\n" - " /* Makes inputs behave like true block-level elements */\n" - " -ms-box-sizing: border-box;\n" - " -webkit-box-sizing: border-box;\n" - " -moz-box-sizing: border-box;\n" - " box-sizing: border-box;\n" - "}\n" - "body {\n" - " margin: 0;\n" - " font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n" - " font-size: 13px;\n" - " line-height: 18px;\n" - " color: #333;\n" - " background-color: #fff;\n" - "}\n" - "a {\n" - " color: #08c;\n" - " text-decoration: none;\n" - "}\n" - "a:hover {\n" - " color: #005580;\n" - " text-decoration: underline;\n" - "}\n" - ".row {\n" - " margin-left: -20px;\n" - " *zoom: 1;\n" - "}\n" - ".row:before,\n" - ".row:after {\n" - " display: table;\n" - " content: \"\";\n" - "}\n" - ".row:after {\n" - " clear: both;\n" - "}\n" - "[class*=\"span\"] {\n" - " float: left;\n" - " margin-left: 20px;\n" - "}\n" - ".container,\n" - ".navbar-fixed-top .container,\n" - ".navbar-fixed-bottom .container {\n" - " width: 940px;\n" - "}\n" - ".span12 {\n" - " width: 940px;\n" - "}\n" - ".span11 {\n" - " width: 860px;\n" - "}\n" - ".span10 {\n" - " width: 780px;\n" - "}\n" - ".span9 {\n" - " width: 700px;\n" - "}\n" - ".span8 {\n" - " width: 620px;\n" - "}\n" - ".span7 {\n" - " width: 540px;\n" - "}\n" - ".span6 {\n" - " width: 460px;\n" - "}\n" - ".span5 {\n" - " width: 380px;\n" - "}\n" - ".span4 {\n" - " width: 300px;\n" - "}\n" - ".span3 {\n" - " width: 220px;\n" - "}\n" - ".span2 {\n" - " width: 140px;\n" - "}\n" - ".span1 {\n" - " width: 60px;\n" - "}\n" - ".offset12 {\n" - " margin-left: 980px;\n" - "}\n" - ".offset11 {\n" - " margin-left: 900px;\n" - "}\n" - ".offset10 {\n" - " margin-left: 820px;\n" - "}\n" - ".offset9 {\n" - " margin-left: 740px;\n" - "}\n" - ".offset8 {\n" - " margin-left: 660px;\n" - "}\n" - ".offset7 {\n" - " margin-left: 580px;\n" - "}\n" - ".offset6 {\n" - " margin-left: 500px;\n" - "}\n" - ".offset5 {\n" - " margin-left: 420px;\n" - "}\n" - ".offset4 {\n" - " margin-left: 340px;\n" - "}\n" - ".offset3 {\n" - " margin-left: 260px;\n" - "}\n" - ".offset2 {\n" - " margin-left: 180px;\n" - "}\n" - ".offset1 {\n" - " margin-left: 100px;\n" - "}\n" - ".row-fluid {\n" - " width: 100%;\n" - " *zoom: 1;\n" - "}\n" - ".row-fluid:before,\n" - ".row-fluid:after {\n" - " display: table;\n" - " content: \"\";\n" - "}\n" - ".row-fluid:after {\n" - " clear: both;\n" - "}\n" - ".row-fluid > [class*=\"span\"] {\n" - " float: left;\n" - " margin-left: 2.12765957%;\n" - "}\n" - ".row-fluid > [class*=\"span\"]:first-child {\n" - " margin-left: 0;\n" - "}\n" - ".row-fluid > .span12 {\n" - " width: 99.99999999%;\n" - "}\n" - ".row-fluid > .span11 {\n" - " width: 91.48936169%;\n" - "}\n" - ".row-fluid > .span10 {\n" - " width: 82.9787234%;\n" - "}\n" - ".row-fluid > .span9 {\n" - " width: 74.4680851%;\n" - "}\n" - ".row-fluid > .span8 {\n" - " width: 65.9574468%;\n" - "}\n" - ".row-fluid > .span7 {\n" - " width: 57.4468085%;\n" - "}\n" - ".row-fluid > .span6 {\n" - " width: 48.93617021%;\n" - "}\n" - ".row-fluid > .span5 {\n" - " width: 40.42553191%;\n" - "}\n" - ".row-fluid > .span4 {\n" - " width: 31.91489361%;\n" - "}\n" - ".row-fluid > .span3 {\n" - " width: 23.40425532%;\n" - "}\n" - ".row-fluid > .span2 {\n" - " width: 14.89361702%;\n" - "}\n" - ".row-fluid > .span1 {\n" - " width: 6.38297872%;\n" - "}\n" - ".container {\n" - " margin-left: auto;\n" - " margin-right: auto;\n" - " *zoom: 1;\n" - "}\n" - ".container:before,\n" - ".container:after {\n" - " display: table;\n" - " content: \"\";\n" - "}\n" - ".container:after {\n" - " clear: both;\n" - "}\n" - ".container-fluid {\n" - " padding-left: 20px;\n" - " padding-right: 20px;\n" - " *zoom: 1;\n" - "}\n" - ".container-fluid:before,\n" - ".container-fluid:after {\n" - " display: table;\n" - " content: \"\";\n" - "}\n" - ".container-fluid:after {\n" - " clear: both;\n" - "}\n" - ".modal-open .dropdown-menu {\n" - " z-index: 2050;\n" - "}\n" - ".modal-open .dropdown.open {\n" - " *z-index: 2050;\n" - "}\n" - ".modal-open .popover {\n" - " z-index: 2060;\n" - "}\n" - ".modal-open .tooltip {\n" - " z-index: 2070;\n" - "}\n" - ".modal-backdrop {\n" - " position: fixed;\n" - " top: 0;\n" - " right: 0;\n" - " bottom: 0;\n" - " left: 0;\n" - " z-index: 1040;\n" - " background-color: #000;\n" - "}\n" - ".modal-backdrop.fade {\n" - " opacity: 0;\n" - "}\n" - ".modal-backdrop,\n" - ".modal-backdrop.fade.in {\n" - " opacity: 0.8;\n" - " filter: alpha(opacity=80);\n" - " -webkit-opacity: 80;\n" - " -moz-opacity: 80;\n" - " opacity: 80;\n" - "}\n" - ".modal {\n" - " position: fixed;\n" - " top: 50%;\n" - " left: 50%;\n" - " z-index: 1050;\n" - " overflow: auto;\n" - " width: 560px;\n" - " margin: -250px 0 0 -280px;\n" - " background-color: #fff;\n" - " border: 1px solid #999;\n" - " border: 1px solid rgba(0, 0, 0, 0.3);\n" - " *border: 1px solid #999;\n" - " /* IE6-7 */\n" - " -webkit-border-radius: 6px;\n" - " -moz-border-radius: 6px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 6px;\n" - " -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);\n" - " -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);\n" - " box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);\n" - " -webkit-background-clip: padding-box;\n" - " -moz-background-clip: padding-box;\n" - " background-clip: padding-box;\n" - "}\n" - ".modal.fade {\n" - " -webkit-transition: opacity .3s linear, top .3s ease-out;\n" - " -moz-transition: opacity .3s linear, top .3s ease-out;\n" - " -ms-transition: opacity .3s linear, top .3s ease-out;\n" - " -o-transition: opacity .3s linear, top .3s ease-out;\n" - " transition: opacity .3s linear, top .3s ease-out;\n" - " -webkit-transition: opacity .3s linear,top .3s ease-out;\n" - " -moz-transition: opacity .3s linear,top .3s ease-out;\n" - " -o-transition: opacity .3s linear,top .3s ease-out;\n" - " -ms-transition: opacity .3s linear,top .3s ease-out;\n" - " transition: opacity .3s linear,top .3s ease-out;\n" - " top: -25%;\n" - "}\n" - ".modal.fade.in {\n" - " top: 50%;\n" - "}\n" - ".modal-header {\n" - " padding: 9px 15px;\n" - " border-bottom: 1px solid #eee;\n" - "}\n" - ".modal-header .close {\n" - " margin-top: 2px;\n" - "}\n" - ".modal-body {\n" - " overflow-y: auto;\n" - " max-height: 400px;\n" - " padding: 15px;\n" - "}\n" - ".modal-form {\n" - " margin-bottom: 0;\n" - "}\n" - ".modal-footer {\n" - " padding: 14px 15px 15px;\n" - " margin-bottom: 0;\n" - " text-align: right;\n" - " background-color: #f5f5f5;\n" - " border-top: 1px solid #ddd;\n" - " -webkit-border-radius: 0 0 6px 6px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 0 0 6px 6px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 0 0 6px 6px;\n" - " background-clip: padding-box;\n" - " -webkit-box-shadow: inset 0 1px 0 #fff;\n" - " -moz-box-shadow: inset 0 1px 0 #fff;\n" - " box-shadow: inset 0 1px 0 #fff;\n" - " *zoom: 1;\n" - "}\n" - ".modal-footer:before,\n" - ".modal-footer:after {\n" - " display: table;\n" - " content: \"\";\n" - "}\n" - ".modal-footer:after {\n" - " clear: both;\n" - "}\n" - ".modal-footer .btn + .btn {\n" - " margin-left: 5px;\n" - " margin-bottom: 0;\n" - "}\n" - ".modal-footer .btn-group .btn + .btn {\n" - " margin-left: -1px;\n" - "}\n" - ".tooltip {\n" - " position: absolute;\n" - " z-index: 1020;\n" - " display: block;\n" - " visibility: visible;\n" - " padding: 5px;\n" - " font-size: 11px;\n" - " filter: alpha(opacity=0);\n" - " -webkit-opacity: 0;\n" - " -moz-opacity: 0;\n" - " opacity: 0;\n" - "}\n" - ".tooltip.in {\n" - " opacity: 0.8;\n" - " filter: alpha(opacity=80);\n" - " -webkit-opacity: 80;\n" - " -moz-opacity: 80;\n" - " opacity: 80;\n" - "}\n" - ".tooltip.top {\n" - " margin-top: -2px;\n" - "}\n" - ".tooltip.right {\n" - " margin-left: 2px;\n" - "}\n" - ".tooltip.bottom {\n" - " margin-top: 2px;\n" - "}\n" - ".tooltip.left {\n" - " margin-left: -2px;\n" - "}\n" - ".tooltip.top .tooltip-arrow {\n" - " bottom: 0;\n" - " left: 50%;\n" - " margin-left: -5px;\n" - " border-left: 5px solid transparent;\n" - " border-right: 5px solid transparent;\n" - " border-top: 5px solid #000;\n" - "}\n" - ".tooltip.left .tooltip-arrow {\n" - " top: 50%;\n" - " right: 0;\n" - " margin-top: -5px;\n" - " border-top: 5px solid transparent;\n" - " border-bottom: 5px solid transparent;\n" - " border-left: 5px solid #000;\n" - "}\n" - ".tooltip.bottom .tooltip-arrow {\n" - " top: 0;\n" - " left: 50%;\n" - " margin-left: -5px;\n" - " border-left: 5px solid transparent;\n" - " border-right: 5px solid transparent;\n" - " border-bottom: 5px solid #000;\n" - "}\n" - ".tooltip.right .tooltip-arrow {\n" - " top: 50%;\n" - " left: 0;\n" - " margin-top: -5px;\n" - " border-top: 5px solid transparent;\n" - " border-bottom: 5px solid transparent;\n" - " border-right: 5px solid #000;\n" - "}\n" - ".tooltip-inner {\n" - " max-width: 250px;\n" - " padding: 3px 8px;\n" - " color: #fff;\n" - " text-align: center;\n" - " text-decoration: none;\n" - " background-color: #000;\n" - " -webkit-border-radius: 4px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 4px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 4px;\n" - " background-clip: padding-box;\n" - "}\n" - ".tooltip-arrow {\n" - " position: absolute;\n" - " width: 0;\n" - " height: 0;\n" - "}\n" - ".dataexplorer_tooltip .tooltip-arrow {\n" - " opacity: 0.6;\n" - " border-color: #646464;\n" - "}\n" - ".dataexplorer_tooltip .tooltip-inner {\n" - " font-size: 13px;\n" - " font-family: 'Open Sans', sans-serif;\n" - " margin-left: 70px;\n" - " background-color: #646464;\n" - " text-align: left;\n" - "}\n" - ".popover {\n" - " position: absolute;\n" - " top: 0;\n" - " left: 0;\n" - " z-index: 1010;\n" - " display: none;\n" - " padding: 5px;\n" - "}\n" - ".popover.top {\n" - " margin-top: -5px;\n" - "}\n" - ".popover.right {\n" - " margin-left: 5px;\n" - "}\n" - ".popover.bottom {\n" - " margin-top: 5px;\n" - "}\n" - ".popover.left {\n" - " margin-left: -5px;\n" - "}\n" - ".popover.top .arrow {\n" - " bottom: 0;\n" - " left: 50%;\n" - " margin-left: -5px;\n" - " border-left: 5px solid transparent;\n" - " border-right: 5px solid transparent;\n" - " border-top: 5px solid #000;\n" - "}\n" - ".popover.right .arrow {\n" - " top: 50%;\n" - " left: 0;\n" - " margin-top: -5px;\n" - " border-top: 5px solid transparent;\n" - " border-bottom: 5px solid transparent;\n" - " border-right: 5px solid #000;\n" - "}\n" - ".popover.bottom .arrow {\n" - " top: 0;\n" - " left: 50%;\n" - " margin-left: -5px;\n" - " border-left: 5px solid transparent;\n" - " border-right: 5px solid transparent;\n" - " border-bottom: 5px solid #000;\n" - "}\n" - ".popover.left .arrow {\n" - " top: 50%;\n" - " right: 0;\n" - " margin-top: -5px;\n" - " border-top: 5px solid transparent;\n" - " border-bottom: 5px solid transparent;\n" - " border-left: 5px solid #000;\n" - "}\n" - ".popover .arrow {\n" - " position: absolute;\n" - " width: 0;\n" - " height: 0;\n" - "}\n" - ".popover-inner {\n" - " padding: 3px;\n" - " width: 280px;\n" - " overflow: hidden;\n" - " background: #000;\n" - " background: rgba(0, 0, 0, 0.8);\n" - " -webkit-border-radius: 6px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 6px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 6px;\n" - " background-clip: padding-box;\n" - " -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);\n" - " -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);\n" - " box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);\n" - "}\n" - ".popover-title {\n" - " padding: 9px 15px;\n" - " line-height: 1;\n" - " background-color: #f5f5f5;\n" - " border-bottom: 1px solid #eee;\n" - " -webkit-border-radius: 3px 3px 0 0;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px 3px 0 0;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px 3px 0 0;\n" - " background-clip: padding-box;\n" - "}\n" - ".popover-content {\n" - " padding: 14px;\n" - " background-color: #fff;\n" - " -webkit-border-radius: 0 0 3px 3px;\n" - " -moz-border-radius: 0 0 3px 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 0 0 3px 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-background-clip: padding-box;\n" - " background-clip: padding-box;\n" - "}\n" - ".popover-content p,\n" - ".popover-content ul,\n" - ".popover-content ol {\n" - " margin-bottom: 0;\n" - "}\n" - ".dropdown {\n" - " position: relative;\n" - "}\n" - ".dropdown-toggle {\n" - " *margin-bottom: -3px;\n" - "}\n" - ".dropdown-toggle:active,\n" - ".open .dropdown-toggle {\n" - " outline: 0;\n" - "}\n" - ".caret {\n" - " display: inline-block;\n" - " width: 0;\n" - " height: 0;\n" - " vertical-align: top;\n" - " border-left: 4px solid transparent;\n" - " border-right: 4px solid transparent;\n" - " border-top: 4px solid #000;\n" - " opacity: 0.3;\n" - " filter: alpha(opacity=30);\n" - " -webkit-opacity: 30;\n" - " -moz-opacity: 30;\n" - " opacity: 30;\n" - " content: \"\";\n" - "}\n" - ".dropdown .caret {\n" - " margin-top: 8px;\n" - " margin-left: 2px;\n" - "}\n" - ".dropdown:hover .caret,\n" - ".open.dropdown .caret {\n" - " opacity: 1;\n" - " filter: alpha(opacity=100);\n" - " -webkit-opacity: 100;\n" - " -moz-opacity: 100;\n" - " opacity: 100;\n" - "}\n" - ".dropdown-menu {\n" - " position: absolute;\n" - " top: 100%;\n" - " left: 7px;\n" - " z-index: 1000;\n" - " float: left;\n" - " display: none;\n" - " min-width: 160px;\n" - " padding: 4px 0;\n" - " margin: 0;\n" - " list-style: none;\n" - " background-color: #fff;\n" - " border-color: #ccc;\n" - " border-color: rgba(0, 0, 0, 0.2);\n" - " border-style: solid;\n" - " border-width: 1px;\n" - " -webkit-border-radius: 0 0 5px 5px;\n" - " -moz-border-radius: 0 0 5px 5px;\n" - " border-radius: 0 0 5px 5px;\n" - " -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n" - " -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n" - " box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n" - " -webkit-background-clip: padding-box;\n" - " -moz-background-clip: padding;\n" - " background-clip: padding-box;\n" - " *border-right-width: 2px;\n" - " *border-bottom-width: 2px;\n" - "}\n" - ".dropdown-menu.pull-right {\n" - " right: 0;\n" - " left: auto;\n" - "}\n" - ".dropdown-menu .divider {\n" - " height: 1px;\n" - " margin: 8px 1px;\n" - " overflow: hidden;\n" - " background-color: #e5e5e5;\n" - " border-bottom: 1px solid #fff;\n" - " *width: 100%;\n" - " *margin: -5px 0 5px;\n" - "}\n" - ".dropdown-menu a {\n" - " display: block;\n" - " padding: 3px 15px;\n" - " clear: both;\n" - " font-weight: normal;\n" - " line-height: 18px;\n" - " color: #333;\n" - " white-space: nowrap;\n" - "}\n" - ".dropdown-menu li > a:hover,\n" - ".dropdown-menu .active > a,\n" - ".dropdown-menu .active > a:hover {\n" - " color: #fff;\n" - " text-decoration: none;\n" - " background-color: #08c;\n" - "}\n" - ".dropdown.open {\n" - " *z-index: 1000;\n" - "}\n" - ".dropdown.open .dropdown-toggle,\n" - ".dropdown.open .dropdown-toggle:hover {\n" - " background-image: none;\n" - " -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0," - " 0, 0, 0.05);\n" - " -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0," - " 0, 0.05);\n" - " box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0" - ".05);\n" - " background-color: #e6e6e6;\n" - " background-color: #d9d9d9 \\9;\n" - " outline: 0;\n" - "}\n" - ".dropdown.open .dropdown-menu {\n" - " display: block;\n" - "}\n" - ".pull-right .dropdown-menu {\n" - " left: auto;\n" - " right: 0;\n" - "}\n" - ".dropup .caret,\n" - ".navbar-fixed-bottom .dropdown .caret {\n" - " border-top: 0;\n" - " border-bottom: 4px solid #000;\n" - " content: \"\\2191\";\n" - "}\n" - ".dropup .dropdown-menu,\n" - ".navbar-fixed-bottom .dropdown .dropdown-menu {\n" - " top: auto;\n" - " bottom: 100%;\n" - " margin-bottom: 1px;\n" - "}\n" - ".typeahead {\n" - " margin-top: 2px;\n" - " -webkit-border-radius: 4px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 4px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 4px;\n" - " background-clip: padding-box;\n" - "}\n" - "form {\n" - " margin: 0 0 18px;\n" - "}\n" - "fieldset {\n" - " padding: 0;\n" - " margin: 0;\n" - " border: 0;\n" - "}\n" - "legend {\n" - " display: block;\n" - " width: 100%;\n" - " padding: 0;\n" - " margin-bottom: 27px;\n" - " font-size: 19.5px;\n" - " line-height: 36px;\n" - " color: #333;\n" - " border: 0;\n" - " border-bottom: 1px solid #eee;\n" - "}\n" - "legend small {\n" - " font-size: 13.5px;\n" - " color: #999;\n" - "}\n" - "label,\n" - "input,\n" - "button,\n" - "select,\n" - "textarea {\n" - " font-size: 13px;\n" - " font-weight: normal;\n" - " line-height: 18px;\n" - "}\n" - "input,\n" - "button,\n" - "select,\n" - "textarea {\n" - " font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n" - "}\n" - "label {\n" - " display: block;\n" - " margin-bottom: 5px;\n" - " color: #333;\n" - "}\n" - "input,\n" - "textarea,\n" - "select,\n" - ".uneditable-input {\n" - " display: inline-block;\n" - " width: 210px;\n" - " height: 18px;\n" - " padding: 4px;\n" - " margin-bottom: 9px;\n" - " font-size: 13px;\n" - " line-height: 18px;\n" - " color: #555;\n" - " border: 1px solid #ccc;\n" - " -webkit-border-radius: 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px;\n" - " background-clip: padding-box;\n" - "}\n" - ".uneditable-textarea {\n" - " width: auto;\n" - " height: auto;\n" - "}\n" - "label input,\n" - "label textarea,\n" - "label select {\n" - " display: block;\n" - "}\n" - "input[type=\"image\"],\n" - "input[type=\"checkbox\"],\n" - "input[type=\"radio\"] {\n" - " width: auto;\n" - " height: auto;\n" - " padding: 0;\n" - " margin: 3px 0;\n" - " *margin-top: 0;\n" - " /* IE7 */\n" - " line-height: normal;\n" - " cursor: pointer;\n" - " -webkit-border-radius: 0;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 0;\n" - " -moz-background-clip: padding;\n" - " border-radius: 0;\n" - " background-clip: padding-box;\n" - " border: 0 \\9;\n" - " /* IE9 and down */\n" - "}\n" - "input[type=\"image\"] {\n" - " border: 0;\n" - "}\n" - "input[type=\"file\"] {\n" - " width: auto;\n" - " padding: initial;\n" - " line-height: initial;\n" - " border: initial;\n" - " background-color: #fff;\n" - " background-color: initial;\n" - " -webkit-box-shadow: none;\n" - " -moz-box-shadow: none;\n" - " box-shadow: none;\n" - "}\n" - "input[type=\"button\"],\n" - "input[type=\"reset\"],\n" - "input[type=\"submit\"] {\n" - " width: auto;\n" - " height: auto;\n" - "}\n" - "select,\n" - "input[type=\"file\"] {\n" - " height: 28px;\n" - " /* In IE7, the height of the select element cannot be changed by height, o" - "nly font-size */\n" - " *margin-top: 4px;\n" - " /* For IE7, add top margin to align select with labels */\n" - " line-height: 28px;\n" - "}\n" - "input[type=\"file\"] {\n" - " line-height: 18px \\9;\n" - "}\n" - "select {\n" - " width: 220px;\n" - " background-color: #fff;\n" - "}\n" - "select[multiple],\n" - "select[size] {\n" - " height: auto;\n" - "}\n" - "input[type=\"image\"] {\n" - " -webkit-box-shadow: none;\n" - " -moz-box-shadow: none;\n" - " box-shadow: none;\n" - "}\n" - "textarea {\n" - " height: auto;\n" - "}\n" - "input[type=\"hidden\"] {\n" - " display: none;\n" - "}\n" - ".radio,\n" - ".checkbox {\n" - " padding-left: 18px;\n" - "}\n" - ".radio input[type=\"radio\"],\n" - ".checkbox input[type=\"checkbox\"] {\n" - " float: left;\n" - " margin-left: -18px;\n" - "}\n" - ".controls > .radio:first-child,\n" - ".controls > .checkbox:first-child {\n" - " padding-top: 5px;\n" - "}\n" - ".radio.inline,\n" - ".checkbox.inline {\n" - " display: inline-block;\n" - " padding-top: 5px;\n" - " margin-bottom: 0;\n" - " vertical-align: middle;\n" - "}\n" - ".radio.inline + .radio.inline,\n" - ".checkbox.inline + .checkbox.inline {\n" - " margin-left: 10px;\n" - "}\n" - "input,\n" - "textarea {\n" - " -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n" - " -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n" - " box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n" - " -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;\n" - " -moz-transition: border linear 0.2s, box-shadow linear 0.2s;\n" - " -ms-transition: border linear 0.2s, box-shadow linear 0.2s;\n" - " -o-transition: border linear 0.2s, box-shadow linear 0.2s;\n" - " transition: border linear 0.2s, box-shadow linear 0.2s;\n" - " -webkit-transition: border linear 0.2s,-webkit-box-shadow linear 0.2s;\n" - " -moz-transition: border linear 0.2s,-moz-box-shadow linear 0.2s;\n" - " -o-transition: border linear 0.2s,box-shadow linear 0.2s;\n" - " -ms-transition: border linear 0.2s,box-shadow linear 0.2s;\n" - " transition: border linear 0.2s,box-shadow linear 0.2s;\n" - "}\n" - "input:focus,\n" - "textarea:focus {\n" - " border-color: rgba(82, 168, 236, 0.8);\n" - " -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82," - " 168, 236, 0.6);\n" - " -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 16" - "8, 236, 0.6);\n" - " box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 23" - "6, 0.6);\n" - " outline: 0;\n" - " outline: thin dotted \\9;\n" - " /* IE6-9 */\n" - "}\n" - "input[type=\"file\"]:focus,\n" - "input[type=\"radio\"]:focus,\n" - "input[type=\"checkbox\"]:focus,\n" - "select:focus {\n" - " -webkit-box-shadow: none;\n" - " -moz-box-shadow: none;\n" - " box-shadow: none;\n" - " outline: thin dotted #333;\n" - " outline: 5px auto -webkit-focus-ring-color;\n" - " outline-offset: -2px;\n" - "}\n" - ".input-mini {\n" - " width: 60px;\n" - "}\n" - ".input-small {\n" - " width: 90px;\n" - "}\n" - ".input-medium {\n" - " width: 150px;\n" - "}\n" - ".input-large {\n" - " width: 210px;\n" - "}\n" - ".input-xlarge {\n" - " width: 270px;\n" - "}\n" - ".input-xxlarge {\n" - " width: 530px;\n" - "}\n" - "input[class*=\"span\"],\n" - "select[class*=\"span\"],\n" - "textarea[class*=\"span\"],\n" - ".uneditable-input {\n" - " float: none;\n" - " margin-left: 0;\n" - "}\n" - "input,\n" - "textarea,\n" - ".uneditable-input {\n" - " margin-left: 0;\n" - "}\n" - "input.span12,\n" - "textarea.span12,\n" - ".uneditable-input.span12 {\n" - " width: 930px;\n" - "}\n" - "input.span11,\n" - "textarea.span11,\n" - ".uneditable-input.span11 {\n" - " width: 850px;\n" - "}\n" - "input.span10,\n" - "textarea.span10,\n" - ".uneditable-input.span10 {\n" - " width: 770px;\n" - "}\n" - "input.span9,\n" - "textarea.span9,\n" - ".uneditable-input.span9 {\n" - " width: 690px;\n" - "}\n" - "input.span8,\n" - "textarea.span8,\n" - ".uneditable-input.span8 {\n" - " width: 610px;\n" - "}\n" - "input.span7,\n" - "textarea.span7,\n" - ".uneditable-input.span7 {\n" - " width: 530px;\n" - "}\n" - "input.span6,\n" - "textarea.span6,\n" - ".uneditable-input.span6 {\n" - " width: 450px;\n" - "}\n" - "input.span5,\n" - "textarea.span5,\n" - ".uneditable-input.span5 {\n" - " width: 370px;\n" - "}\n" - "input.span4,\n" - "textarea.span4,\n" - ".uneditable-input.span4 {\n" - " width: 290px;\n" - "}\n" - "input.span3,\n" - "textarea.span3,\n" - ".uneditable-input.span3 {\n" - " width: 210px;\n" - "}\n" - "input.span2,\n" - "textarea.span2,\n" - ".uneditable-input.span2 {\n" - " width: 130px;\n" - "}\n" - "input.span1,\n" - "textarea.span1,\n" - ".uneditable-input.span1 {\n" - " width: 50px;\n" - "}\n" - "input[disabled],\n" - "select[disabled],\n" - "textarea[disabled],\n" - "input[readonly],\n" - "select[readonly],\n" - "textarea[readonly] {\n" - " background-color: #eee;\n" - " border-color: #ddd;\n" - " cursor: not-allowed;\n" - "}\n" - ".control-group.warning > label,\n" - ".control-group.warning .help-block,\n" - ".control-group.warning .help-inline {\n" - " color: #c09853;\n" - "}\n" - ".control-group.warning input,\n" - ".control-group.warning select,\n" - ".control-group.warning textarea {\n" - " color: #c09853;\n" - " border-color: #c09853;\n" - "}\n" - ".control-group.warning input:focus,\n" - ".control-group.warning select:focus,\n" - ".control-group.warning textarea:focus {\n" - " border-color: #a47e3c;\n" - " -webkit-box-shadow: 0 0 6px #dbc59e;\n" - " -moz-box-shadow: 0 0 6px #dbc59e;\n" - " box-shadow: 0 0 6px #dbc59e;\n" - "}\n" - ".control-group.warning .input-prepend .add-on,\n" - ".control-group.warning .input-append .add-on {\n" - " color: #c09853;\n" - " background-color: #fcf8e3;\n" - " border-color: #c09853;\n" - "}\n" - ".control-group.error > label,\n" - ".control-group.error .help-block,\n" - ".control-group.error .help-inline {\n" - " color: #b94a48;\n" - "}\n" - ".control-group.error input,\n" - ".control-group.error select,\n" - ".control-group.error textarea {\n" - " color: #b94a48;\n" - " border-color: #b94a48;\n" - "}\n" - ".control-group.error input:focus,\n" - ".control-group.error select:focus,\n" - ".control-group.error textarea:focus {\n" - " border-color: #953b39;\n" - " -webkit-box-shadow: 0 0 6px #d59392;\n" - " -moz-box-shadow: 0 0 6px #d59392;\n" - " box-shadow: 0 0 6px #d59392;\n" - "}\n" - ".control-group.error .input-prepend .add-on,\n" - ".control-group.error .input-append .add-on {\n" - " color: #b94a48;\n" - " background-color: #f2dede;\n" - " border-color: #b94a48;\n" - "}\n" - ".control-group.success > label,\n" - ".control-group.success .help-block,\n" - ".control-group.success .help-inline {\n" - " color: #468847;\n" - "}\n" - ".control-group.success input,\n" - ".control-group.success select,\n" - ".control-group.success textarea {\n" - " color: #468847;\n" - " border-color: #468847;\n" - "}\n" - ".control-group.success input:focus,\n" - ".control-group.success select:focus,\n" - ".control-group.success textarea:focus {\n" - " border-color: #356635;\n" - " -webkit-box-shadow: 0 0 6px #7aba7b;\n" - " -moz-box-shadow: 0 0 6px #7aba7b;\n" - " box-shadow: 0 0 6px #7aba7b;\n" - "}\n" - ".control-group.success .input-prepend .add-on,\n" - ".control-group.success .input-append .add-on {\n" - " color: #468847;\n" - " background-color: #dff0d8;\n" - " border-color: #468847;\n" - "}\n" - "input:focus:required:invalid,\n" - "textarea:focus:required:invalid,\n" - "select:focus:required:invalid {\n" - " color: #b94a48;\n" - " border-color: #ee5f5b;\n" - "}\n" - "input:focus:required:invalid:focus,\n" - "textarea:focus:required:invalid:focus,\n" - "select:focus:required:invalid:focus {\n" - " border-color: #e9322d;\n" - " -webkit-box-shadow: 0 0 6px #f8b9b7;\n" - " -moz-box-shadow: 0 0 6px #f8b9b7;\n" - " box-shadow: 0 0 6px #f8b9b7;\n" - "}\n" - ".form-actions {\n" - " padding: 17px 20px 18px;\n" - " margin-top: 18px;\n" - " margin-bottom: 18px;\n" - " background-color: #eee;\n" - " border-top: 1px solid #ddd;\n" - " *zoom: 1;\n" - "}\n" - ".form-actions:before,\n" - ".form-actions:after {\n" - " display: table;\n" - " content: \"\";\n" - "}\n" - ".form-actions:after {\n" - " clear: both;\n" - "}\n" - ".uneditable-input {\n" - " display: block;\n" - " background-color: #fff;\n" - " border-color: #eee;\n" - " -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);\n" - " -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);\n" - " box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);\n" - " cursor: not-allowed;\n" - "}\n" - ":-moz-placeholder {\n" - " color: #999;\n" - "}\n" - "::-webkit-input-placeholder {\n" - " color: #999;\n" - "}\n" - ".help-block,\n" - ".help-inline {\n" - " color: #555;\n" - "}\n" - ".help-block {\n" - " display: block;\n" - " margin-bottom: 9px;\n" - "}\n" - ".help-inline {\n" - " display: inline-block;\n" - " *display: inline;\n" - " /* IE7 inline-block hack */\n" - " *zoom: 1;\n" - " vertical-align: middle;\n" - " padding-left: 5px;\n" - "}\n" - ".input-prepend,\n" - ".input-append {\n" - " margin-bottom: 5px;\n" - "}\n" - ".input-prepend input,\n" - ".input-append input,\n" - ".input-prepend select,\n" - ".input-append select,\n" - ".input-prepend .uneditable-input,\n" - ".input-append .uneditable-input {\n" - " *margin-left: 0;\n" - " -webkit-border-radius: 0 3px 3px 0;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 0 3px 3px 0;\n" - " -moz-background-clip: padding;\n" - " border-radius: 0 3px 3px 0;\n" - " background-clip: padding-box;\n" - "}\n" - ".input-prepend input:focus,\n" - ".input-append input:focus,\n" - ".input-prepend select:focus,\n" - ".input-append select:focus,\n" - ".input-prepend .uneditable-input:focus,\n" - ".input-append .uneditable-input:focus {\n" - " position: relative;\n" - " z-index: 2;\n" - "}\n" - ".input-prepend .uneditable-input,\n" - ".input-append .uneditable-input {\n" - " border-left-color: #ccc;\n" - "}\n" - ".input-prepend .add-on,\n" - ".input-append .add-on {\n" - " display: inline-block;\n" - " width: auto;\n" - " min-width: 16px;\n" - " height: 18px;\n" - " padding: 4px 5px;\n" - " font-weight: normal;\n" - " line-height: 18px;\n" - " text-align: center;\n" - " text-shadow: 0 1px 0 #fff;\n" - " vertical-align: middle;\n" - " background-color: #eee;\n" - " border: 1px solid #ccc;\n" - "}\n" - ".input-prepend .add-on,\n" - ".input-append .add-on,\n" - ".input-prepend .btn,\n" - ".input-append .btn {\n" - " -webkit-border-radius: 3px 0 0 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px 0 0 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px 0 0 3px;\n" - " background-clip: padding-box;\n" - "}\n" - ".input-prepend .active,\n" - ".input-append .active {\n" - " background-color: #a9dba9;\n" - " border-color: #46a546;\n" - "}\n" - ".input-prepend .add-on,\n" - ".input-prepend .btn {\n" - " margin-right: -1px;\n" - "}\n" - ".input-append input,\n" - ".input-append select .uneditable-input {\n" - " -webkit-border-radius: 3px 0 0 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px 0 0 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px 0 0 3px;\n" - " background-clip: padding-box;\n" - "}\n" - ".input-append .uneditable-input {\n" - " border-left-color: #eee;\n" - " border-right-color: #ccc;\n" - "}\n" - ".input-append .add-on,\n" - ".input-append .btn {\n" - " margin-left: -1px;\n" - " -webkit-border-radius: 0 3px 3px 0;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 0 3px 3px 0;\n" - " -moz-background-clip: padding;\n" - " border-radius: 0 3px 3px 0;\n" - " background-clip: padding-box;\n" - "}\n" - ".input-prepend.input-append input,\n" - ".input-prepend.input-append select,\n" - ".input-prepend.input-append .uneditable-input {\n" - " -webkit-border-radius: 0;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 0;\n" - " -moz-background-clip: padding;\n" - " border-radius: 0;\n" - " background-clip: padding-box;\n" - "}\n" - ".input-prepend.input-append .add-on:first-child,\n" - ".input-prepend.input-append .btn:first-child {\n" - " margin-right: -1px;\n" - " -webkit-border-radius: 3px 0 0 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px 0 0 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px 0 0 3px;\n" - " background-clip: padding-box;\n" - "}\n" - ".input-prepend.input-append .add-on:last-child,\n" - ".input-prepend.input-append .btn:last-child {\n" - " margin-left: -1px;\n" - " -webkit-border-radius: 0 3px 3px 0;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 0 3px 3px 0;\n" - " -moz-background-clip: padding;\n" - " border-radius: 0 3px 3px 0;\n" - " background-clip: padding-box;\n" - "}\n" - ".search-query {\n" - " padding-left: 14px;\n" - " padding-right: 14px;\n" - " margin-bottom: 0;\n" - " -webkit-border-radius: 14px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 14px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 14px;\n" - " background-clip: padding-box;\n" - "}\n" - ".form-search input,\n" - ".form-inline input,\n" - ".form-horizontal input,\n" - ".form-search textarea,\n" - ".form-inline textarea,\n" - ".form-horizontal textarea,\n" - ".form-search select,\n" - ".form-inline select,\n" - ".form-horizontal select,\n" - ".form-search .help-inline,\n" - ".form-inline .help-inline,\n" - ".form-horizontal .help-inline,\n" - ".form-search .uneditable-input,\n" - ".form-inline .uneditable-input,\n" - ".form-horizontal .uneditable-input,\n" - ".form-search .input-prepend,\n" - ".form-inline .input-prepend,\n" - ".form-horizontal .input-prepend,\n" - ".form-search .input-append,\n" - ".form-inline .input-append,\n" - ".form-horizontal .input-append {\n" - " display: inline-block;\n" - " margin-bottom: 0;\n" - "}\n" - ".form-search .hide,\n" - ".form-inline .hide,\n" - ".form-horizontal .hide {\n" - " display: none;\n" - "}\n" - ".form-search label,\n" - ".form-inline label {\n" - " display: inline-block;\n" - "}\n" - ".form-search .input-append,\n" - ".form-inline .input-append,\n" - ".form-search .input-prepend,\n" - ".form-inline .input-prepend {\n" - " margin-bottom: 0;\n" - "}\n" - ".form-search .radio,\n" - ".form-search .checkbox,\n" - ".form-inline .radio,\n" - ".form-inline .checkbox {\n" - " padding-left: 0;\n" - " margin-bottom: 0;\n" - " vertical-align: middle;\n" - "}\n" - ".form-search .radio input[type=\"radio\"],\n" - ".form-search .checkbox input[type=\"checkbox\"],\n" - ".form-inline .radio input[type=\"radio\"],\n" - ".form-inline .checkbox input[type=\"checkbox\"] {\n" - " float: left;\n" - " margin-left: 0;\n" - " margin-right: 3px;\n" - "}\n" - ".control-group {\n" - " margin-bottom: 9px;\n" - "}\n" - "legend + .control-group {\n" - " margin-top: 18px;\n" - " -webkit-margin-top-collapse: separate;\n" - "}\n" - ".form-horizontal .control-group {\n" - " margin-bottom: 18px;\n" - " *zoom: 1;\n" - "}\n" - ".form-horizontal .control-group:before,\n" - ".form-horizontal .control-group:after {\n" - " display: table;\n" - " content: \"\";\n" - "}\n" - ".form-horizontal .control-group:after {\n" - " clear: both;\n" - "}\n" - ".form-horizontal .control-label {\n" - " float: left;\n" - " width: 140px;\n" - " padding-top: 5px;\n" - " text-align: right;\n" - "}\n" - ".form-horizontal .controls {\n" - " margin-left: 160px;\n" - " /* Super jank IE7 fix to ensure the inputs in .input-append and input-prep" - "end don't inherit the margin of the parent, in this case .controls */\n" - " *display: inline-block;\n" - " *margin-left: 0;\n" - " *padding-left: 20px;\n" - "}\n" - ".form-horizontal .help-block {\n" - " margin-top: 9px;\n" - " margin-bottom: 0;\n" - "}\n" - ".form-horizontal .form-actions {\n" - " padding-left: 160px;\n" - "}\n" - ".nav {\n" - " margin-left: 0;\n" - " margin-bottom: 18px;\n" - " list-style: none;\n" - "}\n" - ".nav > li > a {\n" - " display: block;\n" - "}\n" - ".nav > li > a:hover {\n" - " text-decoration: none;\n" - " background-color: #eee;\n" - "}\n" - ".nav .nav-header {\n" - " display: block;\n" - " padding: 3px 15px;\n" - " font-size: 11px;\n" - " font-weight: bold;\n" - " line-height: 18px;\n" - " color: #999;\n" - " text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);\n" - " text-transform: uppercase;\n" - "}\n" - ".nav li + .nav-header {\n" - " margin-top: 9px;\n" - "}\n" - ".nav-list {\n" - " padding-left: 15px;\n" - " padding-right: 15px;\n" - " margin-bottom: 0;\n" - "}\n" - ".nav-list > li > a,\n" - ".nav-list .nav-header {\n" - " margin-left: -15px;\n" - " margin-right: -15px;\n" - " text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);\n" - "}\n" - ".nav-list > li > a {\n" - " padding: 3px 15px;\n" - "}\n" - ".nav-list > .active > a,\n" - ".nav-list > .active > a:hover {\n" - " color: #fff;\n" - " text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n" - " background-color: #08c;\n" - "}\n" - ".nav-list [class^=\"icon-\"] {\n" - " margin-right: 2px;\n" - "}\n" - ".nav-list .divider {\n" - " height: 1px;\n" - " margin: 8px 1px;\n" - " overflow: hidden;\n" - " background-color: #e5e5e5;\n" - " border-bottom: 1px solid #fff;\n" - " *width: 100%;\n" - " *margin: -5px 0 5px;\n" - "}\n" - ".nav-tabs,\n" - ".nav-pills {\n" - " *zoom: 1;\n" - "}\n" - ".nav-tabs:before,\n" - ".nav-pills:before,\n" - ".nav-tabs:after,\n" - ".nav-pills:after {\n" - " display: table;\n" - " content: \"\";\n" - "}\n" - ".nav-tabs:after,\n" - ".nav-pills:after {\n" - " clear: both;\n" - "}\n" - ".nav-tabs > li,\n" - ".nav-pills > li {\n" - " float: left;\n" - "}\n" - ".nav-tabs > li > a,\n" - ".nav-pills > li > a {\n" - " padding-right: 12px;\n" - " padding-left: 12px;\n" - " margin-right: 2px;\n" - " line-height: 14px;\n" - "}\n" - ".nav-tabs {\n" - " border-bottom: 1px solid #ddd;\n" - "}\n" - ".nav-tabs > li {\n" - " margin-bottom: -1px;\n" - "}\n" - ".nav-tabs > li > a {\n" - " padding-top: 8px;\n" - " padding-bottom: 8px;\n" - " line-height: 18px;\n" - " border: 1px solid transparent;\n" - " -webkit-border-radius: 4px 4px 0 0;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 4px 4px 0 0;\n" - " -moz-background-clip: padding;\n" - " border-radius: 4px 4px 0 0;\n" - " background-clip: padding-box;\n" - "}\n" - ".nav-tabs > li > a:hover {\n" - " border-color: #eee #eee #ddd;\n" - "}\n" - ".nav-tabs > .active > a,\n" - ".nav-tabs > .active > a:hover {\n" - " color: #555;\n" - " background-color: #fff;\n" - " border: 1px solid #ddd;\n" - " border-bottom-color: transparent;\n" - " cursor: default;\n" - "}\n" - ".nav-pills > li > a {\n" - " padding-top: 8px;\n" - " padding-bottom: 8px;\n" - " margin-top: 2px;\n" - " margin-bottom: 2px;\n" - " -webkit-border-radius: 5px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 5px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 5px;\n" - " background-clip: padding-box;\n" - "}\n" - ".nav-pills > .active > a,\n" - ".nav-pills > .active > a:hover {\n" - " color: #fff;\n" - " background-color: #08c;\n" - "}\n" - ".nav-stacked > li {\n" - " float: none;\n" - "}\n" - ".nav-stacked > li > a {\n" - " margin-right: 0;\n" - "}\n" - ".nav-tabs.nav-stacked {\n" - " border-bottom: 0;\n" - "}\n" - ".nav-tabs.nav-stacked > li > a {\n" - " border: 1px solid #ddd;\n" - " -webkit-border-radius: 0;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 0;\n" - " -moz-background-clip: padding;\n" - " border-radius: 0;\n" - " background-clip: padding-box;\n" - "}\n" - ".nav-tabs.nav-stacked > li:first-child > a {\n" - " -webkit-border-radius: 4px 4px 0 0;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 4px 4px 0 0;\n" - " -moz-background-clip: padding;\n" - " border-radius: 4px 4px 0 0;\n" - " background-clip: padding-box;\n" - "}\n" - ".nav-tabs.nav-stacked > li:last-child > a {\n" - " -webkit-border-radius: 0 0 4px 4px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 0 0 4px 4px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 0 0 4px 4px;\n" - " background-clip: padding-box;\n" - "}\n" - ".nav-tabs.nav-stacked > li > a:hover {\n" - " border-color: #ddd;\n" - " z-index: 2;\n" - "}\n" - ".nav-pills.nav-stacked > li > a {\n" - " margin-bottom: 3px;\n" - "}\n" - ".nav-pills.nav-stacked > li:last-child > a {\n" - " margin-bottom: 1px;\n" - "}\n" - ".nav-tabs .dropdown-menu,\n" - ".nav-pills .dropdown-menu {\n" - " margin-top: 1px;\n" - " border-width: 1px;\n" - "}\n" - ".nav-pills .dropdown-menu {\n" - " -webkit-border-radius: 4px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 4px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 4px;\n" - " background-clip: padding-box;\n" - "}\n" - ".nav-tabs .dropdown-toggle .caret,\n" - ".nav-pills .dropdown-toggle .caret {\n" - " border-top-color: #08c;\n" - " border-bottom-color: #08c;\n" - " margin-top: 6px;\n" - "}\n" - ".nav-tabs .dropdown-toggle:hover .caret,\n" - ".nav-pills .dropdown-toggle:hover .caret {\n" - " border-top-color: #005580;\n" - " border-bottom-color: #005580;\n" - "}\n" - ".nav-tabs .active .dropdown-toggle .caret,\n" - ".nav-pills .active .dropdown-toggle .caret {\n" - " border-top-color: #333;\n" - " border-bottom-color: #333;\n" - "}\n" - ".nav > .dropdown.active > a:hover {\n" - " color: #000;\n" - " cursor: pointer;\n" - "}\n" - ".nav-tabs .open .dropdown-toggle,\n" - ".nav-pills .open .dropdown-toggle,\n" - ".nav > .open.active > a:hover {\n" - " color: #fff;\n" - " background-color: #999;\n" - " border-color: #999;\n" - "}\n" - ".nav .open .caret,\n" - ".nav .open.active .caret,\n" - ".nav .open a:hover .caret {\n" - " border-top-color: #fff;\n" - " border-bottom-color: #fff;\n" - " opacity: 1;\n" - " filter: alpha(opacity=100);\n" - " -webkit-opacity: 100;\n" - " -moz-opacity: 100;\n" - " opacity: 100;\n" - "}\n" - ".tabs-stacked .open > a:hover {\n" - " border-color: #999;\n" - "}\n" - ".tabbable {\n" - " *zoom: 1;\n" - "}\n" - ".tabbable:before,\n" - ".tabbable:after {\n" - " display: table;\n" - " content: \"\";\n" - "}\n" - ".tabbable:after {\n" - " clear: both;\n" - "}\n" - ".tab-content {\n" - " display: table;\n" - " width: 100%;\n" - "}\n" - ".tabs-below .nav-tabs,\n" - ".tabs-right .nav-tabs,\n" - ".tabs-left .nav-tabs {\n" - " border-bottom: 0;\n" - "}\n" - ".tab-content > .tab-pane,\n" - ".pill-content > .pill-pane {\n" - " display: none;\n" - "}\n" - ".tab-content > .active,\n" - ".pill-content > .active {\n" - " display: block;\n" - "}\n" - ".tabs-below .nav-tabs {\n" - " border-top: 1px solid #ddd;\n" - "}\n" - ".tabs-below .nav-tabs > li {\n" - " margin-top: -1px;\n" - " margin-bottom: 0;\n" - "}\n" - ".tabs-below .nav-tabs > li > a {\n" - " -webkit-border-radius: 0 0 4px 4px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 0 0 4px 4px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 0 0 4px 4px;\n" - " background-clip: padding-box;\n" - "}\n" - ".tabs-below .nav-tabs > li > a:hover {\n" - " border-bottom-color: transparent;\n" - " border-top-color: #ddd;\n" - "}\n" - ".tabs-below .nav-tabs .active > a,\n" - ".tabs-below .nav-tabs .active > a:hover {\n" - " border-color: transparent #ddd #ddd #ddd;\n" - "}\n" - ".tabs-left .nav-tabs > li,\n" - ".tabs-right .nav-tabs > li {\n" - " float: none;\n" - "}\n" - ".tabs-left .nav-tabs > li > a,\n" - ".tabs-right .nav-tabs > li > a {\n" - " min-width: 74px;\n" - " margin-right: 0;\n" - " margin-bottom: 3px;\n" - "}\n" - ".tabs-left .nav-tabs {\n" - " float: left;\n" - " margin-right: 19px;\n" - " border-right: 1px solid #ddd;\n" - "}\n" - ".tabs-left .nav-tabs > li > a {\n" - " margin-right: -1px;\n" - " -webkit-border-radius: 4px 0 0 4px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 4px 0 0 4px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 4px 0 0 4px;\n" - " background-clip: padding-box;\n" - "}\n" - ".tabs-left .nav-tabs > li > a:hover {\n" - " border-color: #eee #ddd #eee #eee;\n" - "}\n" - ".tabs-left .nav-tabs .active > a,\n" - ".tabs-left .nav-tabs .active > a:hover {\n" - " border-color: #ddd transparent #ddd #ddd;\n" - " *border-right-color: #fff;\n" - "}\n" - ".tabs-right .nav-tabs {\n" - " float: right;\n" - " margin-left: 19px;\n" - " border-left: 1px solid #ddd;\n" - "}\n" - ".tabs-right .nav-tabs > li > a {\n" - " margin-left: -1px;\n" - " -webkit-border-radius: 0 4px 4px 0;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 0 4px 4px 0;\n" - " -moz-background-clip: padding;\n" - " border-radius: 0 4px 4px 0;\n" - " background-clip: padding-box;\n" - "}\n" - ".tabs-right .nav-tabs > li > a:hover {\n" - " border-color: #eee #eee #eee #ddd;\n" - "}\n" - ".tabs-right .nav-tabs .active > a,\n" - ".tabs-right .nav-tabs .active > a:hover {\n" - " border-color: #ddd #ddd #ddd transparent;\n" - " *border-left-color: #fff;\n" - "}\n" - ".pull-right {\n" - " float: right;\n" - "}\n" - ".pull-left {\n" - " float: left;\n" - "}\n" - ".hide {\n" - " display: none;\n" - "}\n" - ".show {\n" - " display: block;\n" - "}\n" - ".invisible {\n" - " visibility: hidden;\n" - "}\n" - "/*\n" - " --------------------\n" - " Visualization styles\n" - " --------------------\n" - "*/\n" - "#database-view .ops-plot,\n" - "#table-view .ops-plot,\n" - "#datacenter-view .ops-plot,\n" - "#server-view .ops-plot {\n" - " /* GPU acceleration */\n" - " -webkit-transform: translateZ(0);\n" - " -o-transform: translateZ(0);\n" - " -moz-transform: translateZ(0);\n" - " transform: translateZ(0);\n" - " /* For Safari */\n" - " -webkit-perspective: 1000;\n" - " -webkit-backface-visibility: hidden;\n" - "}\n" - "#database-view .ops-plot .ops-plot-legend,\n" - "#table-view .ops-plot .ops-plot-legend,\n" - "#datacenter-view .ops-plot .ops-plot-legend,\n" - "#server-view .ops-plot .ops-plot-legend {\n" - " float: right;\n" - " color: #a3a3a3;\n" - " font: 15px \"Copse\";\n" - " text-align: right;\n" - " /* concentric circles for legend element */\n" - "}\n" - "#database-view .ops-plot .ops-plot-legend .element,\n" - "#table-view .ops-plot .ops-plot-legend .element,\n" - "#datacenter-view .ops-plot .ops-plot-legend .element,\n" - "#server-view .ops-plot .ops-plot-legend .element {\n" - " display: inline-block;\n" - " vertical-align: top;\n" - " margin-left: 40px;\n" - "}\n" - "#database-view .ops-plot .ops-plot-legend .element p,\n" - "#table-view .ops-plot .ops-plot-legend .element p,\n" - "#datacenter-view .ops-plot .ops-plot-legend .element p,\n" - "#server-view .ops-plot .ops-plot-legend .element p {\n" - " margin: 0;\n" - " margin-left: 23px;\n" - " line-height: 15px;\n" - "}\n" - "#database-view .ops-plot .ops-plot-legend .reads .color,\n" - "#table-view .ops-plot .ops-plot-legend .reads .color,\n" - "#datacenter-view .ops-plot .ops-plot-legend .reads .color,\n" - "#server-view .ops-plot .ops-plot-legend .reads .color {\n" - " background-color: #729E51;\n" - "}\n" - "#database-view .ops-plot .ops-plot-legend .writes .color,\n" - "#table-view .ops-plot .ops-plot-legend .writes .color,\n" - "#datacenter-view .ops-plot .ops-plot-legend .writes .color,\n" - "#server-view .ops-plot .ops-plot-legend .writes .color {\n" - " background-color: #983434;\n" - "}\n" - "#database-view .ops-plot .ops-plot-legend .color,\n" - "#table-view .ops-plot .ops-plot-legend .color,\n" - "#datacenter-view .ops-plot .ops-plot-legend .color,\n" - "#server-view .ops-plot .ops-plot-legend .color {\n" - " width: 8px;\n" - " height: 8px;\n" - " -webkit-border-radius: 4px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 4px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 4px;\n" - " background-clip: padding-box;\n" - " margin: 2px;\n" - "}\n" - "#database-view .ops-plot .ops-plot-legend .outer-circle,\n" - "#table-view .ops-plot .ops-plot-legend .outer-circle,\n" - "#datacenter-view .ops-plot .ops-plot-legend .outer-circle,\n" - "#server-view .ops-plot .ops-plot-legend .outer-circle {\n" - " width: 12px;\n" - " height: 12px;\n" - " -webkit-border-radius: 6px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 6px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 6px;\n" - " background-clip: padding-box;\n" - " border: thin solid #CCC;\n" - " float: left;\n" - "}\n" - "#database-view .ops-plot .plot-container,\n" - "#table-view .ops-plot .plot-container,\n" - "#datacenter-view .ops-plot .plot-container,\n" - "#server-view .ops-plot .plot-container {\n" - " /* REMOVED */\n" - "}\n" - "#database-view .ops-plot .plot,\n" - "#table-view .ops-plot .plot,\n" - "#datacenter-view .ops-plot .plot,\n" - "#server-view .ops-plot .plot {\n" - " position: relative;\n" - "}\n" - "#database-view .ops-plot .plot .chart,\n" - "#table-view .ops-plot .plot .chart,\n" - "#datacenter-view .ops-plot .plot .chart,\n" - "#server-view .ops-plot .plot .chart {\n" - " border: none;\n" - " z-index: 999;\n" - " position: relative;\n" - " overflow: hidden;\n" - " display: inline-block;\n" - "}\n" - "#database-view .ops-plot .plot .chart .title,\n" - "#table-view .ops-plot .plot .chart .title,\n" - "#datacenter-view .ops-plot .plot .chart .title,\n" - "#server-view .ops-plot .plot .chart .title {\n" - " display: none;\n" - "}\n" - "#database-view .ops-plot .plot .chart .value,\n" - "#table-view .ops-plot .plot .chart .value,\n" - "#datacenter-view .ops-plot .plot .chart .value,\n" - "#server-view .ops-plot .plot .chart .value {\n" - " display: none;\n" - "}\n" - "#database-view .ops-plot .plot .chart canvas,\n" - "#table-view .ops-plot .plot .chart canvas,\n" - "#datacenter-view .ops-plot .plot .chart canvas,\n" - "#server-view .ops-plot .plot .chart canvas {\n" - " display: block;\n" - "}\n" - "#database-view .ops-plot .plot .chart,\n" - "#table-view .ops-plot .plot .chart,\n" - "#datacenter-view .ops-plot .plot .chart,\n" - "#server-view .ops-plot .plot .chart,\n" - "#database-view .ops-plot .plot .haxis,\n" - "#table-view .ops-plot .plot .haxis,\n" - "#datacenter-view .ops-plot .plot .haxis,\n" - "#server-view .ops-plot .plot .haxis,\n" - "#database-view .ops-plot .plot .hgrid,\n" - "#table-view .ops-plot .plot .hgrid,\n" - "#datacenter-view .ops-plot .plot .hgrid,\n" - "#server-view .ops-plot .plot .hgrid,\n" - "#database-view .ops-plot .plot .line,\n" - "#table-view .ops-plot .plot .line,\n" - "#datacenter-view .ops-plot .plot .line,\n" - "#server-view .ops-plot .plot .line {\n" - " margin-left: 34px;\n" - "}\n" - "#database-view .ops-plot .plot .haxis,\n" - "#table-view .ops-plot .plot .haxis,\n" - "#datacenter-view .ops-plot .plot .haxis,\n" - "#server-view .ops-plot .plot .haxis,\n" - "#database-view .ops-plot .plot .vaxis,\n" - "#table-view .ops-plot .plot .vaxis,\n" - "#datacenter-view .ops-plot .plot .vaxis,\n" - "#server-view .ops-plot .plot .vaxis,\n" - "#database-view .ops-plot .plot .hgrid,\n" - "#table-view .ops-plot .plot .hgrid,\n" - "#datacenter-view .ops-plot .plot .hgrid,\n" - "#server-view .ops-plot .plot .hgrid,\n" - "#database-view .ops-plot .plot .vgrid,\n" - "#table-view .ops-plot .plot .vgrid,\n" - "#datacenter-view .ops-plot .plot .vgrid,\n" - "#server-view .ops-plot .plot .vgrid {\n" - " font: 7px \"Open Sans\";\n" - " opacity: 1;\n" - "}\n" - "#database-view .ops-plot .plot .haxis line,\n" - "#table-view .ops-plot .plot .haxis line,\n" - "#datacenter-view .ops-plot .plot .haxis line,\n" - "#server-view .ops-plot .plot .haxis line,\n" - "#database-view .ops-plot .plot .vaxis line,\n" - "#table-view .ops-plot .plot .vaxis line,\n" - "#datacenter-view .ops-plot .plot .vaxis line,\n" - "#server-view .ops-plot .plot .vaxis line,\n" - "#database-view .ops-plot .plot .hgrid line,\n" - "#table-view .ops-plot .plot .hgrid line,\n" - "#datacenter-view .ops-plot .plot .hgrid line,\n" - "#server-view .ops-plot .plot .hgrid line,\n" - "#database-view .ops-plot .plot .vgrid line,\n" - "#table-view .ops-plot .plot .vgrid line,\n" - "#datacenter-view .ops-plot .plot .vgrid line,\n" - "#server-view .ops-plot .plot .vgrid line {\n" - " stroke: #dcdcdc;\n" - " shape-rendering: crispEdges;\n" - "}\n" - "#database-view .ops-plot .plot .haxis line,\n" - "#table-view .ops-plot .plot .haxis line,\n" - "#datacenter-view .ops-plot .plot .haxis line,\n" - "#server-view .ops-plot .plot .haxis line,\n" - "#database-view .ops-plot .plot .vaxis line,\n" - "#table-view .ops-plot .plot .vaxis line,\n" - "#datacenter-view .ops-plot .plot .vaxis line,\n" - "#server-view .ops-plot .plot .vaxis line,\n" - "#database-view .ops-plot .plot .hgrid line,\n" - "#table-view .ops-plot .plot .hgrid line,\n" - "#datacenter-view .ops-plot .plot .hgrid line,\n" - "#server-view .ops-plot .plot .hgrid line,\n" - "#database-view .ops-plot .plot .vgrid line,\n" - "#table-view .ops-plot .plot .vgrid line,\n" - "#datacenter-view .ops-plot .plot .vgrid line,\n" - "#server-view .ops-plot .plot .vgrid line {\n" - " opacity: 1;\n" - "}\n" - "#database-view .ops-plot .plot .vaxis text,\n" - "#table-view .ops-plot .plot .vaxis text,\n" - "#datacenter-view .ops-plot .plot .vaxis text,\n" - "#server-view .ops-plot .plot .vaxis text,\n" - "#database-view .ops-plot .plot .haxis text,\n" - "#table-view .ops-plot .plot .haxis text,\n" - "#datacenter-view .ops-plot .plot .haxis text,\n" - "#server-view .ops-plot .plot .haxis text {\n" - " font: 13px \"Open Sans\";\n" - " fill: #A3A3A3;\n" - " -webkit-transition: fill-opacity 250ms linear;\n" - "}\n" - "#database-view .ops-plot .plot .vaxis,\n" - "#table-view .ops-plot .plot .vaxis,\n" - "#datacenter-view .ops-plot .plot .vaxis,\n" - "#server-view .ops-plot .plot .vaxis {\n" - " margin-top: -246px;\n" - "}\n" - "#database-view .ops-plot .plot .vaxis .tick,\n" - "#table-view .ops-plot .plot .vaxis .tick,\n" - "#datacenter-view .ops-plot .plot .vaxis .tick,\n" - "#server-view .ops-plot .plot .vaxis .tick {\n" - " display: none;\n" - "}\n" - "#database-view .ops-plot .plot .vgrid,\n" - "#table-view .ops-plot .plot .vgrid,\n" - "#datacenter-view .ops-plot .plot .vgrid,\n" - "#server-view .ops-plot .plot .vgrid {\n" - " position: absolute;\n" - " top: 0;\n" - " left: 0px;\n" - "}\n" - "#database-view .ops-plot .plot .haxis,\n" - "#table-view .ops-plot .plot .haxis,\n" - "#datacenter-view .ops-plot .plot .haxis,\n" - "#server-view .ops-plot .plot .haxis {\n" - " margin-top: -9px;\n" - "}\n" - "#database-view .ops-plot .plot .hgrid,\n" - "#table-view .ops-plot .plot .hgrid,\n" - "#datacenter-view .ops-plot .plot .hgrid,\n" - "#server-view .ops-plot .plot .hgrid {\n" - " display: none;\n" - " height: 220px;\n" - " position: absolute;\n" - " top: -3px;\n" - " left: 0;\n" - "}\n" - "#database-view .ops-plot .plot .line,\n" - "#table-view .ops-plot .plot .line,\n" - "#datacenter-view .ops-plot .plot .line,\n" - "#server-view .ops-plot .plot .line {\n" - " background: #000;\n" - " opacity: .2;\n" - " z-index: 2;\n" - "}\n" - "/* These are the conditional mixins for the different syntax for IE10 Flexbo" - "x*/\n" - "/*!\n" - "Ionicons, v2.0.0\n" - "Created by Ben Sperry for the Ionic Framework, http://ionicons.com/\n" - "https://twitter.com/benjsperry https://twitter.com/ionicframework\n" - "MIT License: https://github.com/driftyco/ionicons\n" - "*/\n" - ".ion-alert:before {\n" - " content: \"\\f101\";\n" - "}\n" - ".ion-alert-circled:before {\n" - " content: \"\\f100\";\n" - "}\n" - ".ion-android-add:before {\n" - " content: \"\\f2c7\";\n" - "}\n" - ".ion-android-add-circle:before {\n" - " content: \"\\f359\";\n" - "}\n" - ".ion-android-alarm-clock:before {\n" - " content: \"\\f35a\";\n" - "}\n" - ".ion-android-alert:before {\n" - " content: \"\\f35b\";\n" - "}\n" - ".ion-android-apps:before {\n" - " content: \"\\f35c\";\n" - "}\n" - ".ion-android-archive:before {\n" - " content: \"\\f2c9\";\n" - "}\n" - ".ion-android-arrow-back:before {\n" - " content: \"\\f2ca\";\n" - "}\n" - ".ion-android-arrow-down:before {\n" - " content: \"\\f35d\";\n" - "}\n" - ".ion-android-arrow-dropdown:before {\n" - " content: \"\\f35f\";\n" - "}\n" - ".ion-android-arrow-dropdown-circle:before {\n" - " content: \"\\f35e\";\n" - "}\n" - ".ion-android-arrow-dropleft:before {\n" - " content: \"\\f361\";\n" - "}\n" - ".ion-android-arrow-dropleft-circle:before {\n" - " content: \"\\f360\";\n" - "}\n" - ".ion-android-arrow-dropright:before {\n" - " content: \"\\f363\";\n" - "}\n" - ".ion-android-arrow-dropright-circle:before {\n" - " content: \"\\f362\";\n" - "}\n" - ".ion-android-arrow-dropup:before {\n" - " content: \"\\f365\";\n" - "}\n" - ".ion-android-arrow-dropup-circle:before {\n" - " content: \"\\f364\";\n" - "}\n" - ".ion-android-arrow-forward:before {\n" - " content: \"\\f30f\";\n" - "}\n" - ".ion-android-arrow-up:before {\n" - " content: \"\\f366\";\n" - "}\n" - ".ion-android-attach:before {\n" - " content: \"\\f367\";\n" - "}\n" - ".ion-android-bar:before {\n" - " content: \"\\f368\";\n" - "}\n" - ".ion-android-bicycle:before {\n" - " content: \"\\f369\";\n" - "}\n" - ".ion-android-boat:before {\n" - " content: \"\\f36a\";\n" - "}\n" - ".ion-android-bookmark:before {\n" - " content: \"\\f36b\";\n" - "}\n" - ".ion-android-bulb:before {\n" - " content: \"\\f36c\";\n" - "}\n" - ".ion-android-bus:before {\n" - " content: \"\\f36d\";\n" - "}\n" - ".ion-android-calendar:before {\n" - " content: \"\\f2d1\";\n" - "}\n" - ".ion-android-call:before {\n" - " content: \"\\f2d2\";\n" - "}\n" - ".ion-android-camera:before {\n" - " content: \"\\f2d3\";\n" - "}\n" - ".ion-android-cancel:before {\n" - " content: \"\\f36e\";\n" - "}\n" - ".ion-android-car:before {\n" - " content: \"\\f36f\";\n" - "}\n" - ".ion-android-cart:before {\n" - " content: \"\\f370\";\n" - "}\n" - ".ion-android-chat:before {\n" - " content: \"\\f2d4\";\n" - "}\n" - ".ion-android-checkbox:before {\n" - " content: \"\\f374\";\n" - "}\n" - ".ion-android-checkbox-blank:before {\n" - " content: \"\\f371\";\n" - "}\n" - ".ion-android-checkbox-outline:before {\n" - " content: \"\\f373\";\n" - "}\n" - ".ion-android-checkbox-outline-blank:before {\n" - " content: \"\\f372\";\n" - "}\n" - ".ion-android-checkmark-circle:before {\n" - " content: \"\\f375\";\n" - "}\n" - ".ion-android-clipboard:before {\n" - " content: \"\\f376\";\n" - "}\n" - ".ion-android-close:before {\n" - " content: \"\\f2d7\";\n" - "}\n" - ".ion-android-cloud:before {\n" - " content: \"\\f37a\";\n" - "}\n" - ".ion-android-cloud-circle:before {\n" - " content: \"\\f377\";\n" - "}\n" - ".ion-android-cloud-done:before {\n" - " content: \"\\f378\";\n" - "}\n" - ".ion-android-cloud-outline:before {\n" - " content: \"\\f379\";\n" - "}\n" - ".ion-android-color-palette:before {\n" - " content: \"\\f37b\";\n" - "}\n" - ".ion-android-compass:before {\n" - " content: \"\\f37c\";\n" - "}\n" - ".ion-android-contact:before {\n" - " content: \"\\f2d8\";\n" - "}\n" - ".ion-android-contacts:before {\n" - " content: \"\\f2d9\";\n" - "}\n" - ".ion-android-contract:before {\n" - " content: \"\\f37d\";\n" - "}\n" - ".ion-android-create:before {\n" - " content: \"\\f37e\";\n" - "}\n" - ".ion-android-delete:before {\n" - " content: \"\\f37f\";\n" - "}\n" - ".ion-android-desktop:before {\n" - " content: \"\\f380\";\n" - "}\n" - ".ion-android-document:before {\n" - " content: \"\\f381\";\n" - "}\n" - ".ion-android-done:before {\n" - " content: \"\\f383\";\n" - "}\n" - ".ion-android-done-all:before {\n" - " content: \"\\f382\";\n" - "}\n" - ".ion-android-download:before {\n" - " content: \"\\f2dd\";\n" - "}\n" - ".ion-android-drafts:before {\n" - " content: \"\\f384\";\n" - "}\n" - ".ion-android-exit:before {\n" - " content: \"\\f385\";\n" - "}\n" - ".ion-android-expand:before {\n" - " content: \"\\f386\";\n" - "}\n" - ".ion-android-favorite:before {\n" - " content: \"\\f388\";\n" - "}\n" - ".ion-android-favorite-outline:before {\n" - " content: \"\\f387\";\n" - "}\n" - ".ion-android-film:before {\n" - " content: \"\\f389\";\n" - "}\n" - ".ion-android-folder:before {\n" - " content: \"\\f2e0\";\n" - "}\n" - ".ion-android-folder-open:before {\n" - " content: \"\\f38a\";\n" - "}\n" - ".ion-android-funnel:before {\n" - " content: \"\\f38b\";\n" - "}\n" - ".ion-android-globe:before {\n" - " content: \"\\f38c\";\n" - "}\n" - ".ion-android-hand:before {\n" - " content: \"\\f2e3\";\n" - "}\n" - ".ion-android-hangout:before {\n" - " content: \"\\f38d\";\n" - "}\n" - ".ion-android-happy:before {\n" - " content: \"\\f38e\";\n" - "}\n" - ".ion-android-home:before {\n" - " content: \"\\f38f\";\n" - "}\n" - ".ion-android-image:before {\n" - " content: \"\\f2e4\";\n" - "}\n" - ".ion-android-laptop:before {\n" - " content: \"\\f390\";\n" - "}\n" - ".ion-android-list:before {\n" - " content: \"\\f391\";\n" - "}\n" - ".ion-android-locate:before {\n" - " content: \"\\f2e9\";\n" - "}\n" - ".ion-android-lock:before {\n" - " content: \"\\f392\";\n" - "}\n" - ".ion-android-mail:before {\n" - " content: \"\\f2eb\";\n" - "}\n" - ".ion-android-map:before {\n" - " content: \"\\f393\";\n" - "}\n" - ".ion-android-menu:before {\n" - " content: \"\\f394\";\n" - "}\n" - ".ion-android-microphone:before {\n" - " content: \"\\f2ec\";\n" - "}\n" - ".ion-android-microphone-off:before {\n" - " content: \"\\f395\";\n" - "}\n" - ".ion-android-more-horizontal:before {\n" - " content: \"\\f396\";\n" - "}\n" - ".ion-android-more-vertical:before {\n" - " content: \"\\f397\";\n" - "}\n" - ".ion-android-navigate:before {\n" - " content: \"\\f398\";\n" - "}\n" - ".ion-android-notifications:before {\n" - " content: \"\\f39b\";\n" - "}\n" - ".ion-android-notifications-none:before {\n" - " content: \"\\f399\";\n" - "}\n" - ".ion-android-notifications-off:before {\n" - " content: \"\\f39a\";\n" - "}\n" - ".ion-android-open:before {\n" - " content: \"\\f39c\";\n" - "}\n" - ".ion-android-options:before {\n" - " content: \"\\f39d\";\n" - "}\n" - ".ion-android-people:before {\n" - " content: \"\\f39e\";\n" - "}\n" - ".ion-android-person:before {\n" - " content: \"\\f3a0\";\n" - "}\n" - ".ion-android-person-add:before {\n" - " content: \"\\f39f\";\n" - "}\n" - ".ion-android-phone-landscape:before {\n" - " content: \"\\f3a1\";\n" - "}\n" - ".ion-android-phone-portrait:before {\n" - " content: \"\\f3a2\";\n" - "}\n" - ".ion-android-pin:before {\n" - " content: \"\\f3a3\";\n" - "}\n" - ".ion-android-plane:before {\n" - " content: \"\\f3a4\";\n" - "}\n" - ".ion-android-playstore:before {\n" - " content: \"\\f2f0\";\n" - "}\n" - ".ion-android-print:before {\n" - " content: \"\\f3a5\";\n" - "}\n" - ".ion-android-radio-button-off:before {\n" - " content: \"\\f3a6\";\n" - "}\n" - ".ion-android-radio-button-on:before {\n" - " content: \"\\f3a7\";\n" - "}\n" - ".ion-android-refresh:before {\n" - " content: \"\\f3a8\";\n" - "}\n" - ".ion-android-remove:before {\n" - " content: \"\\f2f4\";\n" - "}\n" - ".ion-android-remove-circle:before {\n" - " content: \"\\f3a9\";\n" - "}\n" - ".ion-android-restaurant:before {\n" - " content: \"\\f3aa\";\n" - "}\n" - ".ion-android-sad:before {\n" - " content: \"\\f3ab\";\n" - "}\n" - ".ion-android-search:before {\n" - " content: \"\\f2f5\";\n" - "}\n" - ".ion-android-send:before {\n" - " content: \"\\f2f6\";\n" - "}\n" - ".ion-android-settings:before {\n" - " content: \"\\f2f7\";\n" - "}\n" - ".ion-android-share:before {\n" - " content: \"\\f2f8\";\n" - "}\n" - ".ion-android-share-alt:before {\n" - " content: \"\\f3ac\";\n" - "}\n" - ".ion-android-star:before {\n" - " content: \"\\f2fc\";\n" - "}\n" - ".ion-android-star-half:before {\n" - " content: \"\\f3ad\";\n" - "}\n" - ".ion-android-star-outline:before {\n" - " content: \"\\f3ae\";\n" - "}\n" - ".ion-android-stopwatch:before {\n" - " content: \"\\f2fd\";\n" - "}\n" - ".ion-android-subway:before {\n" - " content: \"\\f3af\";\n" - "}\n" - ".ion-android-sunny:before {\n" - " content: \"\\f3b0\";\n" - "}\n" - ".ion-android-sync:before {\n" - " content: \"\\f3b1\";\n" - "}\n" - ".ion-android-textsms:before {\n" - " content: \"\\f3b2\";\n" - "}\n" - ".ion-android-time:before {\n" - " content: \"\\f3b3\";\n" - "}\n" - ".ion-android-train:before {\n" - " content: \"\\f3b4\";\n" - "}\n" - ".ion-android-unlock:before {\n" - " content: \"\\f3b5\";\n" - "}\n" - ".ion-android-upload:before {\n" - " content: \"\\f3b6\";\n" - "}\n" - ".ion-android-volume-down:before {\n" - " content: \"\\f3b7\";\n" - "}\n" - ".ion-android-volume-mute:before {\n" - " content: \"\\f3b8\";\n" - "}\n" - ".ion-android-volume-off:before {\n" - " content: \"\\f3b9\";\n" - "}\n" - ".ion-android-volume-up:before {\n" - " content: \"\\f3ba\";\n" - "}\n" - ".ion-android-walk:before {\n" - " content: \"\\f3bb\";\n" - "}\n" - ".ion-android-warning:before {\n" - " content: \"\\f3bc\";\n" - "}\n" - ".ion-android-watch:before {\n" - " content: \"\\f3bd\";\n" - "}\n" - ".ion-android-wifi:before {\n" - " content: \"\\f305\";\n" - "}\n" - ".ion-aperture:before {\n" - " content: \"\\f313\";\n" - "}\n" - ".ion-archive:before {\n" - " content: \"\\f102\";\n" - "}\n" - ".ion-arrow-down-a:before {\n" - " content: \"\\f103\";\n" - "}\n" - ".ion-arrow-down-b:before {\n" - " content: \"\\f104\";\n" - "}\n" - ".ion-arrow-down-c:before {\n" - " content: \"\\f105\";\n" - "}\n" - ".ion-arrow-expand:before {\n" - " content: \"\\f25e\";\n" - "}\n" - ".ion-arrow-graph-down-left:before {\n" - " content: \"\\f25f\";\n" - "}\n" - ".ion-arrow-graph-down-right:before {\n" - " content: \"\\f260\";\n" - "}\n" - ".ion-arrow-graph-up-left:before {\n" - " content: \"\\f261\";\n" - "}\n" - ".ion-arrow-graph-up-right:before {\n" - " content: \"\\f262\";\n" - "}\n" - ".ion-arrow-left-a:before {\n" - " content: \"\\f106\";\n" - "}\n" - ".ion-arrow-left-b:before {\n" - " content: \"\\f107\";\n" - "}\n" - ".ion-arrow-left-c:before {\n" - " content: \"\\f108\";\n" - "}\n" - ".ion-arrow-move:before {\n" - " content: \"\\f263\";\n" - "}\n" - ".ion-arrow-resize:before {\n" - " content: \"\\f264\";\n" - "}\n" - ".ion-arrow-return-left:before {\n" - " content: \"\\f265\";\n" - "}\n" - ".ion-arrow-return-right:before {\n" - " content: \"\\f266\";\n" - "}\n" - ".ion-arrow-right-a:before {\n" - " content: \"\\f109\";\n" - "}\n" - ".ion-arrow-right-b:before {\n" - " content: \"\\f10a\";\n" - "}\n" - ".ion-arrow-right-c:before {\n" - " content: \"\\f10b\";\n" - "}\n" - ".ion-arrow-shrink:before {\n" - " content: \"\\f267\";\n" - "}\n" - ".ion-arrow-swap:before {\n" - " content: \"\\f268\";\n" - "}\n" - ".ion-arrow-up-a:before {\n" - " content: \"\\f10c\";\n" - "}\n" - ".ion-arrow-up-b:before {\n" - " content: \"\\f10d\";\n" - "}\n" - ".ion-arrow-up-c:before {\n" - " content: \"\\f10e\";\n" - "}\n" - ".ion-asterisk:before {\n" - " content: \"\\f314\";\n" - "}\n" - ".ion-at:before {\n" - " content: \"\\f10f\";\n" - "}\n" - ".ion-backspace:before {\n" - " content: \"\\f3bf\";\n" - "}\n" - ".ion-backspace-outline:before {\n" - " content: \"\\f3be\";\n" - "}\n" - ".ion-bag:before {\n" - " content: \"\\f110\";\n" - "}\n" - ".ion-battery-charging:before {\n" - " content: \"\\f111\";\n" - "}\n" - ".ion-battery-empty:before {\n" - " content: \"\\f112\";\n" - "}\n" - ".ion-battery-full:before {\n" - " content: \"\\f113\";\n" - "}\n" - ".ion-battery-half:before {\n" - " content: \"\\f114\";\n" - "}\n" - ".ion-battery-low:before {\n" - " content: \"\\f115\";\n" - "}\n" - ".ion-beaker:before {\n" - " content: \"\\f269\";\n" - "}\n" - ".ion-beer:before {\n" - " content: \"\\f26a\";\n" - "}\n" - ".ion-bluetooth:before {\n" - " content: \"\\f116\";\n" - "}\n" - ".ion-bonfire:before {\n" - " content: \"\\f315\";\n" - "}\n" - ".ion-bookmark:before {\n" - " content: \"\\f26b\";\n" - "}\n" - ".ion-bowtie:before {\n" - " content: \"\\f3c0\";\n" - "}\n" - ".ion-briefcase:before {\n" - " content: \"\\f26c\";\n" - "}\n" - ".ion-bug:before {\n" - " content: \"\\f2be\";\n" - "}\n" - ".ion-calculator:before {\n" - " content: \"\\f26d\";\n" - "}\n" - ".ion-calendar:before {\n" - " content: \"\\f117\";\n" - "}\n" - ".ion-camera:before {\n" - " content: \"\\f118\";\n" - "}\n" - ".ion-card:before {\n" - " content: \"\\f119\";\n" - "}\n" - ".ion-cash:before {\n" - " content: \"\\f316\";\n" - "}\n" - ".ion-chatbox:before {\n" - " content: \"\\f11b\";\n" - "}\n" - ".ion-chatbox-working:before {\n" - " content: \"\\f11a\";\n" - "}\n" - ".ion-chatboxes:before {\n" - " content: \"\\f11c\";\n" - "}\n" - ".ion-chatbubble:before {\n" - " content: \"\\f11e\";\n" - "}\n" - ".ion-chatbubble-working:before {\n" - " content: \"\\f11d\";\n" - "}\n" - ".ion-chatbubbles:before {\n" - " content: \"\\f11f\";\n" - "}\n" - ".ion-checkmark:before {\n" - " content: \"\\f122\";\n" - "}\n" - ".ion-checkmark-circled:before {\n" - " content: \"\\f120\";\n" - "}\n" - ".ion-checkmark-round:before {\n" - " content: \"\\f121\";\n" - "}\n" - ".ion-chevron-down:before {\n" - " content: \"\\f123\";\n" - "}\n" - ".ion-chevron-left:before {\n" - " content: \"\\f124\";\n" - "}\n" - ".ion-chevron-right:before {\n" - " content: \"\\f125\";\n" - "}\n" - ".ion-chevron-up:before {\n" - " content: \"\\f126\";\n" - "}\n" - ".ion-clipboard:before {\n" - " content: \"\\f127\";\n" - "}\n" - ".ion-clock:before {\n" - " content: \"\\f26e\";\n" - "}\n" - ".ion-close:before {\n" - " content: \"\\f12a\";\n" - "}\n" - ".ion-close-circled:before {\n" - " content: \"\\f128\";\n" - "}\n" - ".ion-close-round:before {\n" - " content: \"\\f129\";\n" - "}\n" - ".ion-closed-captioning:before {\n" - " content: \"\\f317\";\n" - "}\n" - ".ion-cloud:before {\n" - " content: \"\\f12b\";\n" - "}\n" - ".ion-code:before {\n" - " content: \"\\f271\";\n" - "}\n" - ".ion-code-download:before {\n" - " content: \"\\f26f\";\n" - "}\n" - ".ion-code-working:before {\n" - " content: \"\\f270\";\n" - "}\n" - ".ion-coffee:before {\n" - " content: \"\\f272\";\n" - "}\n" - ".ion-compass:before {\n" - " content: \"\\f273\";\n" - "}\n" - ".ion-compose:before {\n" - " content: \"\\f12c\";\n" - "}\n" - ".ion-connection-bars:before {\n" - " content: \"\\f274\";\n" - "}\n" - ".ion-contrast:before {\n" - " content: \"\\f275\";\n" - "}\n" - ".ion-crop:before {\n" - " content: \"\\f3c1\";\n" - "}\n" - ".ion-cube:before {\n" - " content: \"\\f318\";\n" - "}\n" - ".ion-disc:before {\n" - " content: \"\\f12d\";\n" - "}\n" - ".ion-document:before {\n" - " content: \"\\f12f\";\n" - "}\n" - ".ion-document-text:before {\n" - " content: \"\\f12e\";\n" - "}\n" - ".ion-drag:before {\n" - " content: \"\\f130\";\n" - "}\n" - ".ion-earth:before {\n" - " content: \"\\f276\";\n" - "}\n" - ".ion-easel:before {\n" - " content: \"\\f3c2\";\n" - "}\n" - ".ion-edit:before {\n" - " content: \"\\f2bf\";\n" - "}\n" - ".ion-egg:before {\n" - " content: \"\\f277\";\n" - "}\n" - ".ion-eject:before {\n" - " content: \"\\f131\";\n" - "}\n" - ".ion-email:before {\n" - " content: \"\\f132\";\n" - "}\n" - ".ion-email-unread:before {\n" - " content: \"\\f3c3\";\n" - "}\n" - ".ion-erlenmeyer-flask:before {\n" - " content: \"\\f3c5\";\n" - "}\n" - ".ion-erlenmeyer-flask-bubbles:before {\n" - " content: \"\\f3c4\";\n" - "}\n" - ".ion-eye:before {\n" - " content: \"\\f133\";\n" - "}\n" - ".ion-eye-disabled:before {\n" - " content: \"\\f306\";\n" - "}\n" - ".ion-female:before {\n" - " content: \"\\f278\";\n" - "}\n" - ".ion-filing:before {\n" - " content: \"\\f134\";\n" - "}\n" - ".ion-film-marker:before {\n" - " content: \"\\f135\";\n" - "}\n" - ".ion-fireball:before {\n" - " content: \"\\f319\";\n" - "}\n" - ".ion-flag:before {\n" - " content: \"\\f279\";\n" - "}\n" - ".ion-flame:before {\n" - " content: \"\\f31a\";\n" - "}\n" - ".ion-flash:before {\n" - " content: \"\\f137\";\n" - "}\n" - ".ion-flash-off:before {\n" - " content: \"\\f136\";\n" - "}\n" - ".ion-folder:before {\n" - " content: \"\\f139\";\n" - "}\n" - ".ion-fork:before {\n" - " content: \"\\f27a\";\n" - "}\n" - ".ion-fork-repo:before {\n" - " content: \"\\f2c0\";\n" - "}\n" - ".ion-forward:before {\n" - " content: \"\\f13a\";\n" - "}\n" - ".ion-funnel:before {\n" - " content: \"\\f31b\";\n" - "}\n" - ".ion-gear-a:before {\n" - " content: \"\\f13d\";\n" - "}\n" - ".ion-gear-b:before {\n" - " content: \"\\f13e\";\n" - "}\n" - ".ion-grid:before {\n" - " content: \"\\f13f\";\n" - "}\n" - ".ion-hammer:before {\n" - " content: \"\\f27b\";\n" - "}\n" - ".ion-happy:before {\n" - " content: \"\\f31c\";\n" - "}\n" - ".ion-happy-outline:before {\n" - " content: \"\\f3c6\";\n" - "}\n" - ".ion-headphone:before {\n" - " content: \"\\f140\";\n" - "}\n" - ".ion-heart:before {\n" - " content: \"\\f141\";\n" - "}\n" - ".ion-heart-broken:before {\n" - " content: \"\\f31d\";\n" - "}\n" - ".ion-help:before {\n" - " content: \"\\f143\";\n" - "}\n" - ".ion-help-buoy:before {\n" - " content: \"\\f27c\";\n" - "}\n" - ".ion-help-circled:before {\n" - " content: \"\\f142\";\n" - "}\n" - ".ion-home:before {\n" - " content: \"\\f144\";\n" - "}\n" - ".ion-icecream:before {\n" - " content: \"\\f27d\";\n" - "}\n" - ".ion-image:before {\n" - " content: \"\\f147\";\n" - "}\n" - ".ion-images:before {\n" - " content: \"\\f148\";\n" - "}\n" - ".ion-information:before {\n" - " content: \"\\f14a\";\n" - "}\n" - ".ion-information-circled:before {\n" - " content: \"\\f149\";\n" - "}\n" - ".ion-ionic:before {\n" - " content: \"\\f14b\";\n" - "}\n" - ".ion-ios-alarm:before {\n" - " content: \"\\f3c8\";\n" - "}\n" - ".ion-ios-alarm-outline:before {\n" - " content: \"\\f3c7\";\n" - "}\n" - ".ion-ios-albums:before {\n" - " content: \"\\f3ca\";\n" - "}\n" - ".ion-ios-albums-outline:before {\n" - " content: \"\\f3c9\";\n" - "}\n" - ".ion-ios-americanfootball:before {\n" - " content: \"\\f3cc\";\n" - "}\n" - ".ion-ios-americanfootball-outline:before {\n" - " content: \"\\f3cb\";\n" - "}\n" - ".ion-ios-analytics:before {\n" - " content: \"\\f3ce\";\n" - "}\n" - ".ion-ios-analytics-outline:before {\n" - " content: \"\\f3cd\";\n" - "}\n" - ".ion-ios-arrow-back:before {\n" - " content: \"\\f3cf\";\n" - "}\n" - ".ion-ios-arrow-down:before {\n" - " content: \"\\f3d0\";\n" - "}\n" - ".ion-ios-arrow-forward:before {\n" - " content: \"\\f3d1\";\n" - "}\n" - ".ion-ios-arrow-left:before {\n" - " content: \"\\f3d2\";\n" - "}\n" - ".ion-ios-arrow-right:before {\n" - " content: \"\\f3d3\";\n" - "}\n" - ".ion-ios-arrow-thin-down:before {\n" - " content: \"\\f3d4\";\n" - "}\n" - ".ion-ios-arrow-thin-left:before {\n" - " content: \"\\f3d5\";\n" - "}\n" - ".ion-ios-arrow-thin-right:before {\n" - " content: \"\\f3d6\";\n" - "}\n" - ".ion-ios-arrow-thin-up:before {\n" - " content: \"\\f3d7\";\n" - "}\n" - ".ion-ios-arrow-up:before {\n" - " content: \"\\f3d8\";\n" - "}\n" - ".ion-ios-at:before {\n" - " content: \"\\f3da\";\n" - "}\n" - ".ion-ios-at-outline:before {\n" - " content: \"\\f3d9\";\n" - "}\n" - ".ion-ios-barcode:before {\n" - " content: \"\\f3dc\";\n" - "}\n" - ".ion-ios-barcode-outline:before {\n" - " content: \"\\f3db\";\n" - "}\n" - ".ion-ios-baseball:before {\n" - " content: \"\\f3de\";\n" - "}\n" - ".ion-ios-baseball-outline:before {\n" - " content: \"\\f3dd\";\n" - "}\n" - ".ion-ios-basketball:before {\n" - " content: \"\\f3e0\";\n" - "}\n" - ".ion-ios-basketball-outline:before {\n" - " content: \"\\f3df\";\n" - "}\n" - ".ion-ios-bell:before {\n" - " content: \"\\f3e2\";\n" - "}\n" - ".ion-ios-bell-outline:before {\n" - " content: \"\\f3e1\";\n" - "}\n" - ".ion-ios-body:before {\n" - " content: \"\\f3e4\";\n" - "}\n" - ".ion-ios-body-outline:before {\n" - " content: \"\\f3e3\";\n" - "}\n" - ".ion-ios-bolt:before {\n" - " content: \"\\f3e6\";\n" - "}\n" - ".ion-ios-bolt-outline:before {\n" - " content: \"\\f3e5\";\n" - "}\n" - ".ion-ios-book:before {\n" - " content: \"\\f3e8\";\n" - "}\n" - ".ion-ios-book-outline:before {\n" - " content: \"\\f3e7\";\n" - "}\n" - ".ion-ios-bookmarks:before {\n" - " content: \"\\f3ea\";\n" - "}\n" - ".ion-ios-bookmarks-outline:before {\n" - " content: \"\\f3e9\";\n" - "}\n" - ".ion-ios-box:before {\n" - " content: \"\\f3ec\";\n" - "}\n" - ".ion-ios-box-outline:before {\n" - " content: \"\\f3eb\";\n" - "}\n" - ".ion-ios-briefcase:before {\n" - " content: \"\\f3ee\";\n" - "}\n" - ".ion-ios-briefcase-outline:before {\n" - " content: \"\\f3ed\";\n" - "}\n" - ".ion-ios-browsers:before {\n" - " content: \"\\f3f0\";\n" - "}\n" - ".ion-ios-browsers-outline:before {\n" - " content: \"\\f3ef\";\n" - "}\n" - ".ion-ios-calculator:before {\n" - " content: \"\\f3f2\";\n" - "}\n" - ".ion-ios-calculator-outline:before {\n" - " content: \"\\f3f1\";\n" - "}\n" - ".ion-ios-calendar:before {\n" - " content: \"\\f3f4\";\n" - "}\n" - ".ion-ios-calendar-outline:before {\n" - " content: \"\\f3f3\";\n" - "}\n" - ".ion-ios-camera:before {\n" - " content: \"\\f3f6\";\n" - "}\n" - ".ion-ios-camera-outline:before {\n" - " content: \"\\f3f5\";\n" - "}\n" - ".ion-ios-cart:before {\n" - " content: \"\\f3f8\";\n" - "}\n" - ".ion-ios-cart-outline:before {\n" - " content: \"\\f3f7\";\n" - "}\n" - ".ion-ios-chatboxes:before {\n" - " content: \"\\f3fa\";\n" - "}\n" - ".ion-ios-chatboxes-outline:before {\n" - " content: \"\\f3f9\";\n" - "}\n" - ".ion-ios-chatbubble:before {\n" - " content: \"\\f3fc\";\n" - "}\n" - ".ion-ios-chatbubble-outline:before {\n" - " content: \"\\f3fb\";\n" - "}\n" - ".ion-ios-checkmark:before {\n" - " content: \"\\f3ff\";\n" - "}\n" - ".ion-ios-checkmark-empty:before {\n" - " content: \"\\f3fd\";\n" - "}\n" - ".ion-ios-checkmark-outline:before {\n" - " content: \"\\f3fe\";\n" - "}\n" - ".ion-ios-circle-filled:before {\n" - " content: \"\\f400\";\n" - "}\n" - ".ion-ios-circle-outline:before {\n" - " content: \"\\f401\";\n" - "}\n" - ".ion-ios-clock:before {\n" - " content: \"\\f403\";\n" - "}\n" - ".ion-ios-clock-outline:before {\n" - " content: \"\\f402\";\n" - "}\n" - ".ion-ios-close:before {\n" - " content: \"\\f406\";\n" - "}\n" - ".ion-ios-close-empty:before {\n" - " content: \"\\f404\";\n" - "}\n" - ".ion-ios-close-outline:before {\n" - " content: \"\\f405\";\n" - "}\n" - ".ion-ios-cloud:before {\n" - " content: \"\\f40c\";\n" - "}\n" - ".ion-ios-cloud-download:before {\n" - " content: \"\\f408\";\n" - "}\n" - ".ion-ios-cloud-download-outline:before {\n" - " content: \"\\f407\";\n" - "}\n" - ".ion-ios-cloud-outline:before {\n" - " content: \"\\f409\";\n" - "}\n" - ".ion-ios-cloud-upload:before {\n" - " content: \"\\f40b\";\n" - "}\n" - ".ion-ios-cloud-upload-outline:before {\n" - " content: \"\\f40a\";\n" - "}\n" - ".ion-ios-cloudy:before {\n" - " content: \"\\f410\";\n" - "}\n" - ".ion-ios-cloudy-night:before {\n" - " content: \"\\f40e\";\n" - "}\n" - ".ion-ios-cloudy-night-outline:before {\n" - " content: \"\\f40d\";\n" - "}\n" - ".ion-ios-cloudy-outline:before {\n" - " content: \"\\f40f\";\n" - "}\n" - ".ion-ios-cog:before {\n" - " content: \"\\f412\";\n" - "}\n" - ".ion-ios-cog-outline:before {\n" - " content: \"\\f411\";\n" - "}\n" - ".ion-ios-color-filter:before {\n" - " content: \"\\f414\";\n" - "}\n" - ".ion-ios-color-filter-outline:before {\n" - " content: \"\\f413\";\n" - "}\n" - ".ion-ios-color-wand:before {\n" - " content: \"\\f416\";\n" - "}\n" - ".ion-ios-color-wand-outline:before {\n" - " content: \"\\f415\";\n" - "}\n" - ".ion-ios-compose:before {\n" - " content: \"\\f418\";\n" - "}\n" - ".ion-ios-compose-outline:before {\n" - " content: \"\\f417\";\n" - "}\n" - ".ion-ios-contact:before {\n" - " content: \"\\f41a\";\n" - "}\n" - ".ion-ios-contact-outline:before {\n" - " content: \"\\f419\";\n" - "}\n" - ".ion-ios-copy:before {\n" - " content: \"\\f41c\";\n" - "}\n" - ".ion-ios-copy-outline:before {\n" - " content: \"\\f41b\";\n" - "}\n" - ".ion-ios-crop:before {\n" - " content: \"\\f41e\";\n" - "}\n" - ".ion-ios-crop-strong:before {\n" - " content: \"\\f41d\";\n" - "}\n" - ".ion-ios-download:before {\n" - " content: \"\\f420\";\n" - "}\n" - ".ion-ios-download-outline:before {\n" - " content: \"\\f41f\";\n" - "}\n" - ".ion-ios-drag:before {\n" - " content: \"\\f421\";\n" - "}\n" - ".ion-ios-email:before {\n" - " content: \"\\f423\";\n" - "}\n" - ".ion-ios-email-outline:before {\n" - " content: \"\\f422\";\n" - "}\n" - ".ion-ios-eye:before {\n" - " content: \"\\f425\";\n" - "}\n" - ".ion-ios-eye-outline:before {\n" - " content: \"\\f424\";\n" - "}\n" - ".ion-ios-fastforward:before {\n" - " content: \"\\f427\";\n" - "}\n" - ".ion-ios-fastforward-outline:before {\n" - " content: \"\\f426\";\n" - "}\n" - ".ion-ios-filing:before {\n" - " content: \"\\f429\";\n" - "}\n" - ".ion-ios-filing-outline:before {\n" - " content: \"\\f428\";\n" - "}\n" - ".ion-ios-film:before {\n" - " content: \"\\f42b\";\n" - "}\n" - ".ion-ios-film-outline:before {\n" - " content: \"\\f42a\";\n" - "}\n" - ".ion-ios-flag:before {\n" - " content: \"\\f42d\";\n" - "}\n" - ".ion-ios-flag-outline:before {\n" - " content: \"\\f42c\";\n" - "}\n" - ".ion-ios-flame:before {\n" - " content: \"\\f42f\";\n" - "}\n" - ".ion-ios-flame-outline:before {\n" - " content: \"\\f42e\";\n" - "}\n" - ".ion-ios-flask:before {\n" - " content: \"\\f431\";\n" - "}\n" - ".ion-ios-flask-outline:before {\n" - " content: \"\\f430\";\n" - "}\n" - ".ion-ios-flower:before {\n" - " content: \"\\f433\";\n" - "}\n" - ".ion-ios-flower-outline:before {\n" - " content: \"\\f432\";\n" - "}\n" - ".ion-ios-folder:before {\n" - " content: \"\\f435\";\n" - "}\n" - ".ion-ios-folder-outline:before {\n" - " content: \"\\f434\";\n" - "}\n" - ".ion-ios-football:before {\n" - " content: \"\\f437\";\n" - "}\n" - ".ion-ios-football-outline:before {\n" - " content: \"\\f436\";\n" - "}\n" - ".ion-ios-game-controller-a:before {\n" - " content: \"\\f439\";\n" - "}\n" - ".ion-ios-game-controller-a-outline:before {\n" - " content: \"\\f438\";\n" - "}\n" - ".ion-ios-game-controller-b:before {\n" - " content: \"\\f43b\";\n" - "}\n" - ".ion-ios-game-controller-b-outline:before {\n" - " content: \"\\f43a\";\n" - "}\n" - ".ion-ios-gear:before {\n" - " content: \"\\f43d\";\n" - "}\n" - ".ion-ios-gear-outline:before {\n" - " content: \"\\f43c\";\n" - "}\n" - ".ion-ios-glasses:before {\n" - " content: \"\\f43f\";\n" - "}\n" - ".ion-ios-glasses-outline:before {\n" - " content: \"\\f43e\";\n" - "}\n" - ".ion-ios-grid-view:before {\n" - " content: \"\\f441\";\n" - "}\n" - ".ion-ios-grid-view-outline:before {\n" - " content: \"\\f440\";\n" - "}\n" - ".ion-ios-heart:before {\n" - " content: \"\\f443\";\n" - "}\n" - ".ion-ios-heart-outline:before {\n" - " content: \"\\f442\";\n" - "}\n" - ".ion-ios-help:before {\n" - " content: \"\\f446\";\n" - "}\n" - ".ion-ios-help-empty:before {\n" - " content: \"\\f444\";\n" - "}\n" - ".ion-ios-help-outline:before {\n" - " content: \"\\f445\";\n" - "}\n" - ".ion-ios-home:before {\n" - " content: \"\\f448\";\n" - "}\n" - ".ion-ios-home-outline:before {\n" - " content: \"\\f447\";\n" - "}\n" - ".ion-ios-infinite:before {\n" - " content: \"\\f44a\";\n" - "}\n" - ".ion-ios-infinite-outline:before {\n" - " content: \"\\f449\";\n" - "}\n" - ".ion-ios-information:before {\n" - " content: \"\\f44d\";\n" - "}\n" - ".ion-ios-information-empty:before {\n" - " content: \"\\f44b\";\n" - "}\n" - ".ion-ios-information-outline:before {\n" - " content: \"\\f44c\";\n" - "}\n" - ".ion-ios-ionic-outline:before {\n" - " content: \"\\f44e\";\n" - "}\n" - ".ion-ios-keypad:before {\n" - " content: \"\\f450\";\n" - "}\n" - ".ion-ios-keypad-outline:before {\n" - " content: \"\\f44f\";\n" - "}\n" - ".ion-ios-lightbulb:before {\n" - " content: \"\\f452\";\n" - "}\n" - ".ion-ios-lightbulb-outline:before {\n" - " content: \"\\f451\";\n" - "}\n" - ".ion-ios-list:before {\n" - " content: \"\\f454\";\n" - "}\n" - ".ion-ios-list-outline:before {\n" - " content: \"\\f453\";\n" - "}\n" - ".ion-ios-location:before {\n" - " content: \"\\f456\";\n" - "}\n" - ".ion-ios-location-outline:before {\n" - " content: \"\\f455\";\n" - "}\n" - ".ion-ios-locked:before {\n" - " content: \"\\f458\";\n" - "}\n" - ".ion-ios-locked-outline:before {\n" - " content: \"\\f457\";\n" - "}\n" - ".ion-ios-loop:before {\n" - " content: \"\\f45a\";\n" - "}\n" - ".ion-ios-loop-strong:before {\n" - " content: \"\\f459\";\n" - "}\n" - ".ion-ios-medical:before {\n", - 65471) + std::string( - " content: \"\\f45c\";\n" - "}\n" - ".ion-ios-medical-outline:before {\n" - " content: \"\\f45b\";\n" - "}\n" - ".ion-ios-medkit:before {\n" - " content: \"\\f45e\";\n" - "}\n" - ".ion-ios-medkit-outline:before {\n" - " content: \"\\f45d\";\n" - "}\n" - ".ion-ios-mic:before {\n" - " content: \"\\f461\";\n" - "}\n" - ".ion-ios-mic-off:before {\n" - " content: \"\\f45f\";\n" - "}\n" - ".ion-ios-mic-outline:before {\n" - " content: \"\\f460\";\n" - "}\n" - ".ion-ios-minus:before {\n" - " content: \"\\f464\";\n" - "}\n" - ".ion-ios-minus-empty:before {\n" - " content: \"\\f462\";\n" - "}\n" - ".ion-ios-minus-outline:before {\n" - " content: \"\\f463\";\n" - "}\n" - ".ion-ios-monitor:before {\n" - " content: \"\\f466\";\n" - "}\n" - ".ion-ios-monitor-outline:before {\n" - " content: \"\\f465\";\n" - "}\n" - ".ion-ios-moon:before {\n" - " content: \"\\f468\";\n" - "}\n" - ".ion-ios-moon-outline:before {\n" - " content: \"\\f467\";\n" - "}\n" - ".ion-ios-more:before {\n" - " content: \"\\f46a\";\n" - "}\n" - ".ion-ios-more-outline:before {\n" - " content: \"\\f469\";\n" - "}\n" - ".ion-ios-musical-note:before {\n" - " content: \"\\f46b\";\n" - "}\n" - ".ion-ios-musical-notes:before {\n" - " content: \"\\f46c\";\n" - "}\n" - ".ion-ios-navigate:before {\n" - " content: \"\\f46e\";\n" - "}\n" - ".ion-ios-navigate-outline:before {\n" - " content: \"\\f46d\";\n" - "}\n" - ".ion-ios-nutrition:before {\n" - " content: \"\\f470\";\n" - "}\n" - ".ion-ios-nutrition-outline:before {\n" - " content: \"\\f46f\";\n" - "}\n" - ".ion-ios-paper:before {\n" - " content: \"\\f472\";\n" - "}\n" - ".ion-ios-paper-outline:before {\n" - " content: \"\\f471\";\n" - "}\n" - ".ion-ios-paperplane:before {\n" - " content: \"\\f474\";\n" - "}\n" - ".ion-ios-paperplane-outline:before {\n" - " content: \"\\f473\";\n" - "}\n" - ".ion-ios-partlysunny:before {\n" - " content: \"\\f476\";\n" - "}\n" - ".ion-ios-partlysunny-outline:before {\n" - " content: \"\\f475\";\n" - "}\n" - ".ion-ios-pause:before {\n" - " content: \"\\f478\";\n" - "}\n" - ".ion-ios-pause-outline:before {\n" - " content: \"\\f477\";\n" - "}\n" - ".ion-ios-paw:before {\n" - " content: \"\\f47a\";\n" - "}\n" - ".ion-ios-paw-outline:before {\n" - " content: \"\\f479\";\n" - "}\n" - ".ion-ios-people:before {\n" - " content: \"\\f47c\";\n" - "}\n" - ".ion-ios-people-outline:before {\n" - " content: \"\\f47b\";\n" - "}\n" - ".ion-ios-person:before {\n" - " content: \"\\f47e\";\n" - "}\n" - ".ion-ios-person-outline:before {\n" - " content: \"\\f47d\";\n" - "}\n" - ".ion-ios-personadd:before {\n" - " content: \"\\f480\";\n" - "}\n" - ".ion-ios-personadd-outline:before {\n" - " content: \"\\f47f\";\n" - "}\n" - ".ion-ios-photos:before {\n" - " content: \"\\f482\";\n" - "}\n" - ".ion-ios-photos-outline:before {\n" - " content: \"\\f481\";\n" - "}\n" - ".ion-ios-pie:before {\n" - " content: \"\\f484\";\n" - "}\n" - ".ion-ios-pie-outline:before {\n" - " content: \"\\f483\";\n" - "}\n" - ".ion-ios-pint:before {\n" - " content: \"\\f486\";\n" - "}\n" - ".ion-ios-pint-outline:before {\n" - " content: \"\\f485\";\n" - "}\n" - ".ion-ios-play:before {\n" - " content: \"\\f488\";\n" - "}\n" - ".ion-ios-play-outline:before {\n" - " content: \"\\f487\";\n" - "}\n" - ".ion-ios-plus:before {\n" - " content: \"\\f48b\";\n" - "}\n" - ".ion-ios-plus-empty:before {\n" - " content: \"\\f489\";\n" - "}\n" - ".ion-ios-plus-outline:before {\n" - " content: \"\\f48a\";\n" - "}\n" - ".ion-ios-pricetag:before {\n" - " content: \"\\f48d\";\n" - "}\n" - ".ion-ios-pricetag-outline:before {\n" - " content: \"\\f48c\";\n" - "}\n" - ".ion-ios-pricetags:before {\n" - " content: \"\\f48f\";\n" - "}\n" - ".ion-ios-pricetags-outline:before {\n" - " content: \"\\f48e\";\n" - "}\n" - ".ion-ios-printer:before {\n" - " content: \"\\f491\";\n" - "}\n" - ".ion-ios-printer-outline:before {\n" - " content: \"\\f490\";\n" - "}\n" - ".ion-ios-pulse:before {\n" - " content: \"\\f493\";\n" - "}\n" - ".ion-ios-pulse-strong:before {\n" - " content: \"\\f492\";\n" - "}\n" - ".ion-ios-rainy:before {\n" - " content: \"\\f495\";\n" - "}\n" - ".ion-ios-rainy-outline:before {\n" - " content: \"\\f494\";\n" - "}\n" - ".ion-ios-recording:before {\n" - " content: \"\\f497\";\n" - "}\n" - ".ion-ios-recording-outline:before {\n" - " content: \"\\f496\";\n" - "}\n" - ".ion-ios-redo:before {\n" - " content: \"\\f499\";\n" - "}\n" - ".ion-ios-redo-outline:before {\n" - " content: \"\\f498\";\n" - "}\n" - ".ion-ios-refresh:before {\n" - " content: \"\\f49c\";\n" - "}\n" - ".ion-ios-refresh-empty:before {\n" - " content: \"\\f49a\";\n" - "}\n" - ".ion-ios-refresh-outline:before {\n" - " content: \"\\f49b\";\n" - "}\n" - ".ion-ios-reload:before {\n" - " content: \"\\f49d\";\n" - "}\n" - ".ion-ios-reverse-camera:before {\n" - " content: \"\\f49f\";\n" - "}\n" - ".ion-ios-reverse-camera-outline:before {\n" - " content: \"\\f49e\";\n" - "}\n" - ".ion-ios-rewind:before {\n" - " content: \"\\f4a1\";\n" - "}\n" - ".ion-ios-rewind-outline:before {\n" - " content: \"\\f4a0\";\n" - "}\n" - ".ion-ios-rose:before {\n" - " content: \"\\f4a3\";\n" - "}\n" - ".ion-ios-rose-outline:before {\n" - " content: \"\\f4a2\";\n" - "}\n" - ".ion-ios-search:before {\n" - " content: \"\\f4a5\";\n" - "}\n" - ".ion-ios-search-strong:before {\n" - " content: \"\\f4a4\";\n" - "}\n" - ".ion-ios-settings:before {\n" - " content: \"\\f4a7\";\n" - "}\n" - ".ion-ios-settings-strong:before {\n" - " content: \"\\f4a6\";\n" - "}\n" - ".ion-ios-shuffle:before {\n" - " content: \"\\f4a9\";\n" - "}\n" - ".ion-ios-shuffle-strong:before {\n" - " content: \"\\f4a8\";\n" - "}\n" - ".ion-ios-skipbackward:before {\n" - " content: \"\\f4ab\";\n" - "}\n" - ".ion-ios-skipbackward-outline:before {\n" - " content: \"\\f4aa\";\n" - "}\n" - ".ion-ios-skipforward:before {\n" - " content: \"\\f4ad\";\n" - "}\n" - ".ion-ios-skipforward-outline:before {\n" - " content: \"\\f4ac\";\n" - "}\n" - ".ion-ios-snowy:before {\n" - " content: \"\\f4ae\";\n" - "}\n" - ".ion-ios-speedometer:before {\n" - " content: \"\\f4b0\";\n" - "}\n" - ".ion-ios-speedometer-outline:before {\n" - " content: \"\\f4af\";\n" - "}\n" - ".ion-ios-star:before {\n" - " content: \"\\f4b3\";\n" - "}\n" - ".ion-ios-star-half:before {\n" - " content: \"\\f4b1\";\n" - "}\n" - ".ion-ios-star-outline:before {\n" - " content: \"\\f4b2\";\n" - "}\n" - ".ion-ios-stopwatch:before {\n" - " content: \"\\f4b5\";\n" - "}\n" - ".ion-ios-stopwatch-outline:before {\n" - " content: \"\\f4b4\";\n" - "}\n" - ".ion-ios-sunny:before {\n" - " content: \"\\f4b7\";\n" - "}\n" - ".ion-ios-sunny-outline:before {\n" - " content: \"\\f4b6\";\n" - "}\n" - ".ion-ios-telephone:before {\n" - " content: \"\\f4b9\";\n" - "}\n" - ".ion-ios-telephone-outline:before {\n" - " content: \"\\f4b8\";\n" - "}\n" - ".ion-ios-tennisball:before {\n" - " content: \"\\f4bb\";\n" - "}\n" - ".ion-ios-tennisball-outline:before {\n" - " content: \"\\f4ba\";\n" - "}\n" - ".ion-ios-thunderstorm:before {\n" - " content: \"\\f4bd\";\n" - "}\n" - ".ion-ios-thunderstorm-outline:before {\n" - " content: \"\\f4bc\";\n" - "}\n" - ".ion-ios-time:before {\n" - " content: \"\\f4bf\";\n" - "}\n" - ".ion-ios-time-outline:before {\n" - " content: \"\\f4be\";\n" - "}\n" - ".ion-ios-timer:before {\n" - " content: \"\\f4c1\";\n" - "}\n" - ".ion-ios-timer-outline:before {\n" - " content: \"\\f4c0\";\n" - "}\n" - ".ion-ios-toggle:before {\n" - " content: \"\\f4c3\";\n" - "}\n" - ".ion-ios-toggle-outline:before {\n" - " content: \"\\f4c2\";\n" - "}\n" - ".ion-ios-trash:before {\n" - " content: \"\\f4c5\";\n" - "}\n" - ".ion-ios-trash-outline:before {\n" - " content: \"\\f4c4\";\n" - "}\n" - ".ion-ios-undo:before {\n" - " content: \"\\f4c7\";\n" - "}\n" - ".ion-ios-undo-outline:before {\n" - " content: \"\\f4c6\";\n" - "}\n" - ".ion-ios-unlocked:before {\n" - " content: \"\\f4c9\";\n" - "}\n" - ".ion-ios-unlocked-outline:before {\n" - " content: \"\\f4c8\";\n" - "}\n" - ".ion-ios-upload:before {\n" - " content: \"\\f4cb\";\n" - "}\n" - ".ion-ios-upload-outline:before {\n" - " content: \"\\f4ca\";\n" - "}\n" - ".ion-ios-videocam:before {\n" - " content: \"\\f4cd\";\n" - "}\n" - ".ion-ios-videocam-outline:before {\n" - " content: \"\\f4cc\";\n" - "}\n" - ".ion-ios-volume-high:before {\n" - " content: \"\\f4ce\";\n" - "}\n" - ".ion-ios-volume-low:before {\n" - " content: \"\\f4cf\";\n" - "}\n" - ".ion-ios-wineglass:before {\n" - " content: \"\\f4d1\";\n" - "}\n" - ".ion-ios-wineglass-outline:before {\n" - " content: \"\\f4d0\";\n" - "}\n" - ".ion-ios-world:before {\n" - " content: \"\\f4d3\";\n" - "}\n" - ".ion-ios-world-outline:before {\n" - " content: \"\\f4d2\";\n" - "}\n" - ".ion-ipad:before {\n" - " content: \"\\f1f9\";\n" - "}\n" - ".ion-iphone:before {\n" - " content: \"\\f1fa\";\n" - "}\n" - ".ion-ipod:before {\n" - " content: \"\\f1fb\";\n" - "}\n" - ".ion-jet:before {\n" - " content: \"\\f295\";\n" - "}\n" - ".ion-key:before {\n" - " content: \"\\f296\";\n" - "}\n" - ".ion-knife:before {\n" - " content: \"\\f297\";\n" - "}\n" - ".ion-laptop:before {\n" - " content: \"\\f1fc\";\n" - "}\n" - ".ion-leaf:before {\n" - " content: \"\\f1fd\";\n" - "}\n" - ".ion-levels:before {\n" - " content: \"\\f298\";\n" - "}\n" - ".ion-lightbulb:before {\n" - " content: \"\\f299\";\n" - "}\n" - ".ion-link:before {\n" - " content: \"\\f1fe\";\n" - "}\n" - ".ion-load-a:before {\n" - " content: \"\\f29a\";\n" - "}\n" - ".ion-load-b:before {\n" - " content: \"\\f29b\";\n" - "}\n" - ".ion-load-c:before {\n" - " content: \"\\f29c\";\n" - "}\n" - ".ion-load-d:before {\n" - " content: \"\\f29d\";\n" - "}\n" - ".ion-location:before {\n" - " content: \"\\f1ff\";\n" - "}\n" - ".ion-lock-combination:before {\n" - " content: \"\\f4d4\";\n" - "}\n" - ".ion-locked:before {\n" - " content: \"\\f200\";\n" - "}\n" - ".ion-log-in:before {\n" - " content: \"\\f29e\";\n" - "}\n" - ".ion-log-out:before {\n" - " content: \"\\f29f\";\n" - "}\n" - ".ion-loop:before {\n" - " content: \"\\f201\";\n" - "}\n" - ".ion-magnet:before {\n" - " content: \"\\f2a0\";\n" - "}\n" - ".ion-male:before {\n" - " content: \"\\f2a1\";\n" - "}\n" - ".ion-man:before {\n" - " content: \"\\f202\";\n" - "}\n" - ".ion-map:before {\n" - " content: \"\\f203\";\n" - "}\n" - ".ion-medkit:before {\n" - " content: \"\\f2a2\";\n" - "}\n" - ".ion-merge:before {\n" - " content: \"\\f33f\";\n" - "}\n" - ".ion-mic-a:before {\n" - " content: \"\\f204\";\n" - "}\n" - ".ion-mic-b:before {\n" - " content: \"\\f205\";\n" - "}\n" - ".ion-mic-c:before {\n" - " content: \"\\f206\";\n" - "}\n" - ".ion-minus:before {\n" - " content: \"\\f209\";\n" - "}\n" - ".ion-minus-circled:before {\n" - " content: \"\\f207\";\n" - "}\n" - ".ion-minus-round:before {\n" - " content: \"\\f208\";\n" - "}\n" - ".ion-model-s:before {\n" - " content: \"\\f2c1\";\n" - "}\n" - ".ion-monitor:before {\n" - " content: \"\\f20a\";\n" - "}\n" - ".ion-more:before {\n" - " content: \"\\f20b\";\n" - "}\n" - ".ion-mouse:before {\n" - " content: \"\\f340\";\n" - "}\n" - ".ion-music-note:before {\n" - " content: \"\\f20c\";\n" - "}\n" - ".ion-navicon:before {\n" - " content: \"\\f20e\";\n" - "}\n" - ".ion-navicon-round:before {\n" - " content: \"\\f20d\";\n" - "}\n" - ".ion-navigate:before {\n" - " content: \"\\f2a3\";\n" - "}\n" - ".ion-network:before {\n" - " content: \"\\f341\";\n" - "}\n" - ".ion-no-smoking:before {\n" - " content: \"\\f2c2\";\n" - "}\n" - ".ion-nuclear:before {\n" - " content: \"\\f2a4\";\n" - "}\n" - ".ion-outlet:before {\n" - " content: \"\\f342\";\n" - "}\n" - ".ion-paintbrush:before {\n" - " content: \"\\f4d5\";\n" - "}\n" - ".ion-paintbucket:before {\n" - " content: \"\\f4d6\";\n" - "}\n" - ".ion-paper-airplane:before {\n" - " content: \"\\f2c3\";\n" - "}\n" - ".ion-paperclip:before {\n" - " content: \"\\f20f\";\n" - "}\n" - ".ion-pause:before {\n" - " content: \"\\f210\";\n" - "}\n" - ".ion-person:before {\n" - " content: \"\\f213\";\n" - "}\n" - ".ion-person-add:before {\n" - " content: \"\\f211\";\n" - "}\n" - ".ion-person-stalker:before {\n" - " content: \"\\f212\";\n" - "}\n" - ".ion-pie-graph:before {\n" - " content: \"\\f2a5\";\n" - "}\n" - ".ion-pin:before {\n" - " content: \"\\f2a6\";\n" - "}\n" - ".ion-pinpoint:before {\n" - " content: \"\\f2a7\";\n" - "}\n" - ".ion-pizza:before {\n" - " content: \"\\f2a8\";\n" - "}\n" - ".ion-plane:before {\n" - " content: \"\\f214\";\n" - "}\n" - ".ion-planet:before {\n" - " content: \"\\f343\";\n" - "}\n" - ".ion-play:before {\n" - " content: \"\\f215\";\n" - "}\n" - ".ion-playstation:before {\n" - " content: \"\\f30a\";\n" - "}\n" - ".ion-plus:before {\n" - " content: \"\\f218\";\n" - "}\n" - ".ion-plus-circled:before {\n" - " content: \"\\f216\";\n" - "}\n" - ".ion-plus-round:before {\n" - " content: \"\\f217\";\n" - "}\n" - ".ion-podium:before {\n" - " content: \"\\f344\";\n" - "}\n" - ".ion-pound:before {\n" - " content: \"\\f219\";\n" - "}\n" - ".ion-power:before {\n" - " content: \"\\f2a9\";\n" - "}\n" - ".ion-pricetag:before {\n" - " content: \"\\f2aa\";\n" - "}\n" - ".ion-pricetags:before {\n" - " content: \"\\f2ab\";\n" - "}\n" - ".ion-printer:before {\n" - " content: \"\\f21a\";\n" - "}\n" - ".ion-pull-request:before {\n" - " content: \"\\f345\";\n" - "}\n" - ".ion-qr-scanner:before {\n" - " content: \"\\f346\";\n" - "}\n" - ".ion-quote:before {\n" - " content: \"\\f347\";\n" - "}\n" - ".ion-radio-waves:before {\n" - " content: \"\\f2ac\";\n" - "}\n" - ".ion-record:before {\n" - " content: \"\\f21b\";\n" - "}\n" - ".ion-refresh:before {\n" - " content: \"\\f21c\";\n" - "}\n" - ".ion-reply:before {\n" - " content: \"\\f21e\";\n" - "}\n" - ".ion-reply-all:before {\n" - " content: \"\\f21d\";\n" - "}\n" - ".ion-ribbon-a:before {\n" - " content: \"\\f348\";\n" - "}\n" - ".ion-ribbon-b:before {\n" - " content: \"\\f349\";\n" - "}\n" - ".ion-sad:before {\n" - " content: \"\\f34a\";\n" - "}\n" - ".ion-sad-outline:before {\n" - " content: \"\\f4d7\";\n" - "}\n" - ".ion-scissors:before {\n" - " content: \"\\f34b\";\n" - "}\n" - ".ion-search:before {\n" - " content: \"\\f21f\";\n" - "}\n" - ".ion-settings:before {\n" - " content: \"\\f2ad\";\n" - "}\n" - ".ion-share:before {\n" - " content: \"\\f220\";\n" - "}\n" - ".ion-shuffle:before {\n" - " content: \"\\f221\";\n" - "}\n" - ".ion-skip-backward:before {\n" - " content: \"\\f222\";\n" - "}\n" - ".ion-skip-forward:before {\n" - " content: \"\\f223\";\n" - "}\n" - ".ion-social-android:before {\n" - " content: \"\\f225\";\n" - "}\n" - ".ion-social-android-outline:before {\n" - " content: \"\\f224\";\n" - "}\n" - ".ion-social-angular:before {\n" - " content: \"\\f4d9\";\n" - "}\n" - ".ion-social-angular-outline:before {\n" - " content: \"\\f4d8\";\n" - "}\n" - ".ion-social-apple:before {\n" - " content: \"\\f227\";\n" - "}\n" - ".ion-social-apple-outline:before {\n" - " content: \"\\f226\";\n" - "}\n" - ".ion-social-bitcoin:before {\n" - " content: \"\\f2af\";\n" - "}\n" - ".ion-social-bitcoin-outline:before {\n" - " content: \"\\f2ae\";\n" - "}\n" - ".ion-social-buffer:before {\n" - " content: \"\\f229\";\n" - "}\n" - ".ion-social-buffer-outline:before {\n" - " content: \"\\f228\";\n" - "}\n" - ".ion-social-chrome:before {\n" - " content: \"\\f4db\";\n" - "}\n" - ".ion-social-chrome-outline:before {\n" - " content: \"\\f4da\";\n" - "}\n" - ".ion-social-codepen:before {\n" - " content: \"\\f4dd\";\n" - "}\n" - ".ion-social-codepen-outline:before {\n" - " content: \"\\f4dc\";\n" - "}\n" - ".ion-social-css3:before {\n" - " content: \"\\f4df\";\n" - "}\n" - ".ion-social-css3-outline:before {\n" - " content: \"\\f4de\";\n" - "}\n" - ".ion-social-designernews:before {\n" - " content: \"\\f22b\";\n" - "}\n" - ".ion-social-designernews-outline:before {\n" - " content: \"\\f22a\";\n" - "}\n" - ".ion-social-dribbble:before {\n" - " content: \"\\f22d\";\n" - "}\n" - ".ion-social-dribbble-outline:before {\n" - " content: \"\\f22c\";\n" - "}\n" - ".ion-social-dropbox:before {\n" - " content: \"\\f22f\";\n" - "}\n" - ".ion-social-dropbox-outline:before {\n" - " content: \"\\f22e\";\n" - "}\n" - ".ion-social-euro:before {\n" - " content: \"\\f4e1\";\n" - "}\n" - ".ion-social-euro-outline:before {\n" - " content: \"\\f4e0\";\n" - "}\n" - ".ion-social-facebook:before {\n" - " content: \"\\f231\";\n" - "}\n" - ".ion-social-facebook-outline:before {\n" - " content: \"\\f230\";\n" - "}\n" - ".ion-social-foursquare:before {\n" - " content: \"\\f34d\";\n" - "}\n" - ".ion-social-foursquare-outline:before {\n" - " content: \"\\f34c\";\n" - "}\n" - ".ion-social-freebsd-devil:before {\n" - " content: \"\\f2c4\";\n" - "}\n" - ".ion-social-github:before {\n" - " content: \"\\f233\";\n" - "}\n" - ".ion-social-github-outline:before {\n" - " content: \"\\f232\";\n" - "}\n" - ".ion-social-google:before {\n" - " content: \"\\f34f\";\n" - "}\n" - ".ion-social-google-outline:before {\n" - " content: \"\\f34e\";\n" - "}\n" - ".ion-social-googleplus:before {\n" - " content: \"\\f235\";\n" - "}\n" - ".ion-social-googleplus-outline:before {\n" - " content: \"\\f234\";\n" - "}\n" - ".ion-social-hackernews:before {\n" - " content: \"\\f237\";\n" - "}\n" - ".ion-social-hackernews-outline:before {\n" - " content: \"\\f236\";\n" - "}\n" - ".ion-social-html5:before {\n" - " content: \"\\f4e3\";\n" - "}\n" - ".ion-social-html5-outline:before {\n" - " content: \"\\f4e2\";\n" - "}\n" - ".ion-social-instagram:before {\n" - " content: \"\\f351\";\n" - "}\n" - ".ion-social-instagram-outline:before {\n" - " content: \"\\f350\";\n" - "}\n" - ".ion-social-javascript:before {\n" - " content: \"\\f4e5\";\n" - "}\n" - ".ion-social-javascript-outline:before {\n" - " content: \"\\f4e4\";\n" - "}\n" - ".ion-social-linkedin:before {\n" - " content: \"\\f239\";\n" - "}\n" - ".ion-social-linkedin-outline:before {\n" - " content: \"\\f238\";\n" - "}\n" - ".ion-social-markdown:before {\n" - " content: \"\\f4e6\";\n" - "}\n" - ".ion-social-nodejs:before {\n" - " content: \"\\f4e7\";\n" - "}\n" - ".ion-social-octocat:before {\n" - " content: \"\\f4e8\";\n" - "}\n" - ".ion-social-pinterest:before {\n" - " content: \"\\f2b1\";\n" - "}\n" - ".ion-social-pinterest-outline:before {\n" - " content: \"\\f2b0\";\n" - "}\n" - ".ion-social-python:before {\n" - " content: \"\\f4e9\";\n" - "}\n" - ".ion-social-reddit:before {\n" - " content: \"\\f23b\";\n" - "}\n" - ".ion-social-reddit-outline:before {\n" - " content: \"\\f23a\";\n" - "}\n" - ".ion-social-rss:before {\n" - " content: \"\\f23d\";\n" - "}\n" - ".ion-social-rss-outline:before {\n" - " content: \"\\f23c\";\n" - "}\n" - ".ion-social-sass:before {\n" - " content: \"\\f4ea\";\n" - "}\n" - ".ion-social-skype:before {\n" - " content: \"\\f23f\";\n" - "}\n" - ".ion-social-skype-outline:before {\n" - " content: \"\\f23e\";\n" - "}\n" - ".ion-social-snapchat:before {\n" - " content: \"\\f4ec\";\n" - "}\n" - ".ion-social-snapchat-outline:before {\n" - " content: \"\\f4eb\";\n" - "}\n" - ".ion-social-tumblr:before {\n" - " content: \"\\f241\";\n" - "}\n" - ".ion-social-tumblr-outline:before {\n" - " content: \"\\f240\";\n" - "}\n" - ".ion-social-tux:before {\n" - " content: \"\\f2c5\";\n" - "}\n" - ".ion-social-twitch:before {\n" - " content: \"\\f4ee\";\n" - "}\n" - ".ion-social-twitch-outline:before {\n" - " content: \"\\f4ed\";\n" - "}\n" - ".ion-social-twitter:before {\n" - " content: \"\\f243\";\n" - "}\n" - ".ion-social-twitter-outline:before {\n" - " content: \"\\f242\";\n" - "}\n" - ".ion-social-usd:before {\n" - " content: \"\\f353\";\n" - "}\n" - ".ion-social-usd-outline:before {\n" - " content: \"\\f352\";\n" - "}\n" - ".ion-social-vimeo:before {\n" - " content: \"\\f245\";\n" - "}\n" - ".ion-social-vimeo-outline:before {\n" - " content: \"\\f244\";\n" - "}\n" - ".ion-social-whatsapp:before {\n" - " content: \"\\f4f0\";\n" - "}\n" - ".ion-social-whatsapp-outline:before {\n" - " content: \"\\f4ef\";\n" - "}\n" - ".ion-social-windows:before {\n" - " content: \"\\f247\";\n" - "}\n" - ".ion-social-windows-outline:before {\n" - " content: \"\\f246\";\n" - "}\n" - ".ion-social-wordpress:before {\n" - " content: \"\\f249\";\n" - "}\n" - ".ion-social-wordpress-outline:before {\n" - " content: \"\\f248\";\n" - "}\n" - ".ion-social-yahoo:before {\n" - " content: \"\\f24b\";\n" - "}\n" - ".ion-social-yahoo-outline:before {\n" - " content: \"\\f24a\";\n" - "}\n" - ".ion-social-yen:before {\n" - " content: \"\\f4f2\";\n" - "}\n" - ".ion-social-yen-outline:before {\n" - " content: \"\\f4f1\";\n" - "}\n" - ".ion-social-youtube:before {\n" - " content: \"\\f24d\";\n" - "}\n" - ".ion-social-youtube-outline:before {\n" - " content: \"\\f24c\";\n" - "}\n" - ".ion-soup-can:before {\n" - " content: \"\\f4f4\";\n" - "}\n" - ".ion-soup-can-outline:before {\n" - " content: \"\\f4f3\";\n" - "}\n" - ".ion-speakerphone:before {\n" - " content: \"\\f2b2\";\n" - "}\n" - ".ion-speedometer:before {\n" - " content: \"\\f2b3\";\n" - "}\n" - ".ion-spoon:before {\n" - " content: \"\\f2b4\";\n" - "}\n" - ".ion-star:before {\n" - " content: \"\\f24e\";\n" - "}\n" - ".ion-stats-bars:before {\n" - " content: \"\\f2b5\";\n" - "}\n" - ".ion-steam:before {\n" - " content: \"\\f30b\";\n" - "}\n" - ".ion-stop:before {\n" - " content: \"\\f24f\";\n" - "}\n" - ".ion-thermometer:before {\n" - " content: \"\\f2b6\";\n" - "}\n" - ".ion-thumbsdown:before {\n" - " content: \"\\f250\";\n" - "}\n" - ".ion-thumbsup:before {\n" - " content: \"\\f251\";\n" - "}\n" - ".ion-toggle:before {\n" - " content: \"\\f355\";\n" - "}\n" - ".ion-toggle-filled:before {\n" - " content: \"\\f354\";\n" - "}\n" - ".ion-transgender:before {\n" - " content: \"\\f4f5\";\n" - "}\n" - ".ion-trash-a:before {\n" - " content: \"\\f252\";\n" - "}\n" - ".ion-trash-b:before {\n" - " content: \"\\f253\";\n" - "}\n" - ".ion-trophy:before {\n" - " content: \"\\f356\";\n" - "}\n" - ".ion-tshirt:before {\n" - " content: \"\\f4f7\";\n" - "}\n" - ".ion-tshirt-outline:before {\n" - " content: \"\\f4f6\";\n" - "}\n" - ".ion-umbrella:before {\n" - " content: \"\\f2b7\";\n" - "}\n" - ".ion-university:before {\n" - " content: \"\\f357\";\n" - "}\n" - ".ion-unlocked:before {\n" - " content: \"\\f254\";\n" - "}\n" - ".ion-upload:before {\n" - " content: \"\\f255\";\n" - "}\n" - ".ion-usb:before {\n" - " content: \"\\f2b8\";\n" - "}\n" - ".ion-videocamera:before {\n" - " content: \"\\f256\";\n" - "}\n" - ".ion-volume-high:before {\n" - " content: \"\\f257\";\n" - "}\n" - ".ion-volume-low:before {\n" - " content: \"\\f258\";\n" - "}\n" - ".ion-volume-medium:before {\n" - " content: \"\\f259\";\n" - "}\n" - ".ion-volume-mute:before {\n" - " content: \"\\f25a\";\n" - "}\n" - ".ion-wand:before {\n" - " content: \"\\f358\";\n" - "}\n" - ".ion-waterdrop:before {\n" - " content: \"\\f25b\";\n" - "}\n" - ".ion-wifi:before {\n" - " content: \"\\f25c\";\n" - "}\n" - ".ion-wineglass:before {\n" - " content: \"\\f2b9\";\n" - "}\n" - ".ion-woman:before {\n" - " content: \"\\f25d\";\n" - "}\n" - ".ion-wrench:before {\n" - " content: \"\\f2ba\";\n" - "}\n" - ".ion-xbox:before {\n" - " content: \"\\f30c\";\n" - "}\n" - "/* Many rules from the original styles.less are no longer needed. This\n" - "file is an attempt to do copying garbage collection since finding\n" - "unnecessary CSS rules is almost impossible by inspection.\n" - "\n" - "Every time new rules are needed or old ones are modified, they should\n" - "be moved to this file. Over time the other file should become smaller\n" - "and more densely packed with unused rules and it will hopefully be\n" - "easier to determine if they're still needed.\n" - "\n" - "Along with moving them here the rules should be cleaned up in a few ways:\n" - "\n" - "- rules should be unnested when possible so they can be re-used\n" - "- Color literals should be pulled out into a global variable\n" - "- Colors that are already global variables should be replaced with the varia" - "ble\n" - "- repetitive code should be pulled out into function-like mixins\n" - "\n" - "This will also help porting if we move to another CSS preprocessor (say scss" - ")\n" - " */\n" - "/* Colors */\n" - "/* Fonts */\n" - "/* End of fonts */\n" - "/* Sizes */\n" - "/* End of sizes */\n" - "/* Styling images */\n" - "/* End of styling images */\n" - "/* Icons */\n" - "/* End of Icons */\n" - "/* ionicon elements */\n" - "/* End of ionicon elements */\n" - "/* Standardized widths (keep this section minimal probably) */\n" - "/* End of standardized padding */\n" - "/* Mixin functions */\n" - ".flex-evenly-spaced-centered {\n" - " display: -webkit-box;\n" - " display: -moz-box;\n" - " display: -webkit-flex;\n" - " display: -ms-flexbox;\n" - " display: flex;\n" - " -ms-flex-align: center;\n" - " -webkit-align-items: center;\n" - " align-items: center;\n" - " -ms-flex-pack: justify;\n" - " -webkit-justify-content: space-between;\n" - " justify-content: space-between;\n" - " -ms-flex-direction: row;\n" - " -webkit-flex-direction: row;\n" - " flex-direction: row;\n" - "}\n" - "/* End of mixins */\n" - ".tab-content.unpaginated {\n" - " display: block;\n" - " resize: both;\n" - "}\n" - ".tableviewer .seek-message {\n" - " background: #ffffaf;\n" - " font-weight: bold;\n" - " margin-left: 3em;\n" - "}\n" - ".table_viewer_scroller {\n" - " height: 250px;\n" - " border: 0;\n" - " background: #ffffff;\n" - " padding: 0;\n" - " overflow: auto;\n" - " resize: both;\n" - "}\n" - ".table_viewer_holder,\n" - ".table_viewer_headers {\n" - " font: 13px/18px monospace, mono, consolas;\n" - " padding: 0px;\n" - " margin: 0px;\n" - " table-layout: fixed;\n" - " white-space: nowrap;\n" - " border-spacing: 0px;\n" - " border-collapse: collapse;\n" - " width: min-content;\n" - "}\n" - ".table_viewer_holder td,\n" - ".table_viewer_headers td {\n" - " max-width: 310px;\n" - " padding: 0px;\n" - " border: 1px solid #dfdfdf;\n" - " vertical-align: top;\n" - " overflow: hidden;\n" - "}\n" - ".table_viewer_holder td.rowNumber,\n" - ".table_viewer_headers td.rowNumber {\n" - " width: 50px;\n" - "}\n" - ".table_viewer_headers {\n" - " position: sticky;\n" - " top: 0;\n" - " z-index: 1;\n" - "}\n" - ".table_viewer_holder {\n" - " position: relative;\n" - "}\n" - ".table_viewer_holder tr:first-child td {\n" - " border-top: 0;\n" - "}\n" - ".table_viewer_holder .jta_arrow {\n" - " width: 15px;\n" - " height: 15px;\n" - " margin: 3px 2px 0px 2px;\n" - " display: block;\n" - "}\n" - ".table_viewer_holder .jta_arrow_v {\n" - " background: url('images/arrow_down.png') no-repeat;\n" - " float: left;\n" - "}\n" - ".table_viewer_holder .jta_arrow_h {\n" - " background: url('images/arrow_right.png') no-repeat;\n" - " float: left;\n" - "}\n" - ".table_viewer_holder tr.even {\n" - " background: #ffffff;\n" - "}\n" - ".table_viewer_holder tr.odd {\n" - " background: #f9f9f9;\n" - "}\n" - ".table_viewer_holder tr.seeked.even {\n" - " background: #ffffaf;\n" - "}\n" - ".table_viewer_holder tr.seeked.odd {\n" - " background: #f9f9a9;\n" - "}\n" - ".table_viewer_holder tr.seekedStart {\n" - " border-bottom: 2px solid #ffff00;\n" - "}\n" - ".table_viewer_holder tr.seekedEnd {\n" - " border-top: 2px solid #ffff00;\n" - "}\n" - ".table_viewer_holder tr td {\n" - " box-sizing: border-box;\n" - " padding: 2px;\n" - " text-overflow: ellipsis;\n" - "}\n" - ".table_viewer_holder tr td.rowNumber {\n" - " color: #aaa;\n" - " background: #f1f1f1;\n" - "}\n" - ".table_viewer_holder tr.endrow td {\n" - " border: #ffffff;\n" - " overflow: visible;\n" - " font-style: italic;\n" - " color: #aaa;\n" - "}\n" - ".table_viewer_headers tr .expand,\n" - ".table_viewer_headers tr .collapse {\n" - " float: right;\n" - " color: blue;\n" - " width: min-content;\n" - " margin: 0;\n" - " padding: 0;\n" - "}\n" - ".table_viewer_headers tr td {\n" - " box-sizing: border-box;\n" - " padding: 0 4px 0 0;\n" - " color: #444444;\n" - " background: #f1f1f1;\n" - "}\n" - ".table_viewer_headers tr td:hover {\n" - " cursor: e-resize;\n" - "}\n" - ".table_viewer_headers tr td.rowNumber {\n" - " background: white;\n" - " border-top-color: white;\n" - " border-left-color: white;\n" - "}\n" - ".table_viewer_headers tr td.rowNumber:hover {\n" - " cursor: auto;\n" - "}\n" - ".table_viewer_headers tr td div.value {\n" - " margin: 0;\n" - " padding: 2px 0 2px 2px;\n" - " text-overflow: ellipsis;\n" - " overflow: hidden;\n" - "}\n" - ".table_viewer_headers tr td div.value:hover {\n" - " cursor: auto;\n" - "}\n" - "tr.tableviewer {\n" - " /*\n" - " .jta_object {\n" - " vertical-align: top;\n" - " display: inline-block;\n" - " .json_tree {\n" - " padding: 0px 17px;\n" - " }\n" - " }\n" - " */\n" - "}\n" - "tr.tableviewer .expand:hover,\n" - "tr.tableviewer .collapse:hover {\n" - " cursor: pointer;\n" - " color: red;\n" - "}\n" - "tr.tableviewer .jta_undefined {\n" - " color: #ccc;\n" - "}\n" - "tr.tableviewer .jta_null {\n" - " color: red;\n" - "}\n" - "tr.tableviewer .jta_bool {\n" - " color: #ee00ff;\n" - "}\n" - "tr.tableviewer .jta_num {\n" - " color: blue;\n" - "}\n" - "tr.tableviewer .jta_link {\n" - " text-decoration: none;\n" - " color: #000;\n" - "}\n" - "tr.tableviewer .jta_url {\n" - " color: #08c;\n" - "}\n" - "tr.tableviewer .jta_email {\n" - " color: #08c;\n" - "}\n" - "tr.tableviewer .jta_string {\n" - " color: green;\n" - " white-space: pre;\n" - "}\n" - ".tree-view2 {\n" - " font-family: \"Open Sans\";\n" - " padding: 20px 20px 10px 25px;\n" - "}\n" - ".tree-view2 ul.parents {\n" - " padding: 0px 5px 0px 10px;\n" - " list-style: none;\n" - "}\n" - ".tree-view2 ul.parents li.parent {\n" - " position: relative;\n" - "}\n" - ".tree-view2 .parent-heading {\n" - " display: -webkit-box;\n" - " display: -moz-box;\n" - " display: -webkit-flex;\n" - " display: -ms-flexbox;\n" - " display: flex;\n" - " -ms-flex-direction: row;\n" - " -webkit-flex-direction: row;\n" - " flex-direction: row;\n" - " -webkit-justify-content: flex-begin;\n" - " justify-content: flex-begin;\n" - " -ms-flex-align: flex-end;\n" - " -webkit-align-items: flex-end;\n" - " align-items: flex-end;\n" - " font-size: 16px;\n" - " color: #5c6169;\n" - " margin: 10px 0;\n" - "}\n" - ".tree-view2 .parent-heading span.numkeys {\n" - " margin-left: 30px;\n" - " font-size: 16px;\n" - " background-image: url(images/bars-icon_server-assignments.png);\n" - " background-position: 0 50%;\n" - " background-repeat: no-repeat;\n" - " padding-left: 26px;\n" - "}\n" - ".tree-view2 .parent-heading::before {\n" - " content: \"\";\n" - " display: block;\n" - " position: absolute;\n" - " top: 4.5px;\n" - " bottom: 16px;\n" - " width: 7px;\n" - " left: -14px;\n" - " background-image: url(images/list-square-bullet.png), url(images/list-vert" - "-dash.png);\n" - " background-position: left top, 2.5px center;\n" - " background-repeat: no-repeat, repeat-y;\n" - "}\n" - ".tree-view2 .parent-title {\n" - " display: inline-block;\n" - " font-size: 16px;\n" - " word-wrap: break-word;\n" - " white-space: nowrap;\n" - "}\n" - ".tree-view2 ul.children {\n" - " padding-left: 5px;\n" - " color: #5c6169;\n" - " margin-bottom: 10px;\n" - "}\n" - ".tree-view2 li.child {\n" - " display: -webkit-box;\n" - " display: -moz-box;\n" - " display: -webkit-flex;\n" - " display: -ms-flexbox;\n" - " display: flex;\n" - " -ms-flex-align: center;\n" - " -webkit-align-items: center;\n" - " align-items: center;\n" - " -ms-flex-pack: justify;\n" - " -webkit-justify-content: space-between;\n" - " justify-content: space-between;\n" - " -ms-flex-direction: row;\n" - " -webkit-flex-direction: row;\n" - " flex-direction: row;\n" - " border-bottom: 1px solid #e4e4e4;\n" - " padding: 7px 0px;\n" - " font-size: 14px;\n" - "}\n" - ".tree-view2 li.child .child-role {\n" - " width: 205px;\n" - " position: relative;\n" - "}\n" - ".tree-view2 li.child .child-role.primary {\n" - " background-image: url(images/layers-icon.png);\n" - " background-position: 0 50%;\n" - " background-repeat: no-repeat;\n" - " padding-left: 26px;\n" - " font-weight: bold;\n" - "}\n" - ".tree-view2 li.child .child-role.primary.goal {\n" - " font-weight: normal;\n" - " background-image: url(images/layers-exclamation-icon.png);\n" - " background-position: 0 50%;\n" - " background-repeat: no-repeat;\n" - " padding-left: 26px;\n" - "}\n" - ".tree-view2 li.child .child-role.secondary {\n" - " background-image: url(images/grid-icon.png);\n" - " background-position: 0 50%;\n" - " background-repeat: no-repeat;\n" - " padding-left: 26px;\n" - "}\n" - ".tree-view2 li.child .child-name {\n" - " margin-left: 10px;\n" - " width: 35%;\n" - " position: relative;\n" - "}\n" - ".tree-view2 li.child .child-name:before {\n" - " content: \"\";\n" - " display: block;\n" - " position: absolute;\n" - " width: 22px;\n" - " top: 0;\n" - " bottom: 0px;\n" - " left: -30px;\n" - " background-image: url(images/list-square-bullet.png), url(images/list-hori" - "z-dash.png);\n" - " background-position: right center, 3.5px center;\n" - " background-repeat: no-repeat, repeat-x;\n" - "}\n" - ".tree-view2 li.child .state {\n" - " margin-left: 10px;\n" - " text-align: right;\n" - " width: 181px;\n" - "}\n" - ".tree-view2 li.child .state.yellow {\n" - " background-image: url(images/yellow-light_glow.png);\n" - " background-position: 100% 50%;\n" - " background-repeat: no-repeat;\n" - " padding-right: 26px;\n" - "}\n" - ".tree-view2 li.child .state.red {\n" - " background-image: url(images/red-light_glow.png);\n" - " background-position: 100% 50%;\n" - " background-repeat: no-repeat;\n" - " padding-right: 26px;\n" - "}\n" - ".tree-view2 li.child .state.green {\n" - " background-image: url(images/green-light_glow.png);\n" - " background-position: 100% 50%;\n" - " background-repeat: no-repeat;\n" - " padding-right: 26px;\n" - "}\n" - ".profile-row {\n" - " border-top: thin solid #f0f0f0;\n" - " position: relative;\n" - "}\n" - ".profile-row:first-child {\n" - " border-top: thin solid transparent;\n" - "}\n" - ".profile-row p {\n" - " font-family: \"Open Sans\";\n" - " color: #72bddf;\n" - " font-size: 15px;\n" - " line-height: 20px;\n" - " margin-left: 30px;\n" - " overflow: hidden;\n" - " text-overflow: ellipsis;\n" - "}\n" - ".profile-row p .big {\n" - " color: #5c6169;\n" - " font-size: 24px;\n" - " line-height: 30px;\n" - "}\n" - ".profile-row p .tags,\n" - ".profile-row p.admonition {\n" - " font-size: 18px;\n" - " color: #5c6169;\n" - "}\n" - ".profile-row.hostname::before {\n" - " content: \"\\f4f3\";\n" - " font-family: Ionicons;\n" - " font-size: 20px;\n" - " color: #d0d0d0;\n" - " padding: 0 10px 10px 0;\n" - " left: 0;\n" - " top: 50%;\n" - " position: absolute;\n" - "}\n" - ".profile-row.version::before {\n" - " content: \"\\f21c\";\n" - " font-family: Ionicons;\n" - " font-size: 20px;\n" - " color: #d0d0d0;\n" - " padding: 0 10px 10px 0;\n" - " left: 0;\n" - " top: 50%;\n" - " position: absolute;\n" - "}\n" - ".profile-row.uptime::before {\n" - " content: \"\\f26e\";\n" - " font-family: Ionicons;\n" - " font-size: 20px;\n" - " color: #d0d0d0;\n" - " padding: 0 10px 10px 0;\n" - " left: 0;\n" - " top: 50%;\n" - " position: absolute;\n" - "}\n" - ".profile-row.cache-size::before {\n" - " content: \"\\f12f\";\n" - " font-family: Ionicons;\n" - " font-size: 20px;\n" - " color: #d0d0d0;\n" - " padding: 0 10px 10px 0;\n" - " left: 3px;\n" - " top: 50%;\n" - " position: absolute;\n" - "}\n" - ".profile-row.tags::before {\n" - " content: \"\\f48e\";\n" - " font-family: Ionicons;\n" - " font-size: 20px;\n" - " color: #d0d0d0;\n" - " padding: 0 10px 10px 0;\n" - " left: 1px;\n" - " top: 45%;\n" - " position: absolute;\n" - "}\n" - "table.servers-list {\n" - " padding: 2px 11px !important;\n" - " min-width: 940px;\n" - " max-width: 940px;\n" - " background: #fafafa;\n" - " border-radius: 4px;\n" - " border-collapse: separate;\n" - " border-spacing: 0 10px;\n" - "}\n" - ".server-shadow {\n" - " box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n" - "}\n" - "tr.server-container {\n" - " background: white;\n" - " font-family: \"Open Sans\";\n" - " box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n" - "}\n" - "tr.server-container td {\n" - " border-bottom: 1px solid #cfcfd1;\n" - " border-top: 1px solid #cfcfd1;\n" - " margin: 0px 0px;\n" - " padding: 10px;\n" - "}\n" - "tr.server-container td:first-child {\n" - " border-radius: 3px 0px 0px 3px;\n" - " border-left: 1px solid #cfcfd1;\n" - "}\n" - "tr.server-container td:last-child {\n" - " border-radius: 0px 3px 3px 0px;\n" - " border-right: 1px solid #cfcfd1;\n" - "}\n" - "tr.server-container .name {\n" - " background: 20px center no-repeat url('images/server-icon.png');\n" - " padding-right: 20px;\n" - " padding-left: 60px;\n" - " overflow: hidden;\n" - " text-overflow: ellipsis;\n" - " white-space: nowrap;\n" - "}\n" - "tr.server-container .name a {\n" - " color: #0597d8;\n" - " font-size: 16px;\n" - " font-weight: bold;\n" - "}\n" - "tr.server-container .name .tags {\n" - " border-left: 0px;\n" - " font-weight: normal;\n" - " font-size: 13px;\n" - " margin-left: 5px;\n" - " color: #9898A0;\n" - " text-overflow: ellipsis;\n" - " white-space: nowrap;\n" - "}\n" - "tr.server-container .quick-info {\n" - " color: #626264;\n" - " padding-left: 20px;\n" - " padding-right: 20px;\n" - " white-space: nowrap;\n" - "}\n" - "tr.server-container .quick-info .highlight {\n" - " font-weight: bold;\n" - "}\n" - ".dashboard-container .recent-logs > h2.title {\n" - " border-bottom: thin solid #f0f0f0;\n" - " padding-bottom: 22px;\n" - " margin-bottom: 0;\n" - "}\n" - ".dashboard-container .recent-logs .btn.view-logs {\n" - " float: right;\n" - "}\n" - ".dashboard-container .recent-logs ul.log-entries {\n" - " margin-bottom: 5px;\n" - "}\n" - ".dashboard-container .recent-logs h1 {\n" - " display: none;\n" - "}\n" - ".dashboard-container .recent-logs #log-view {\n" - " padding: 0px;\n" - " border: 0px;\n" - " box-shadow: none;\n" - "}\n" - ".dashboard-container .recent-logs #log-view .log-entry {\n" - " padding-top: 5px;\n" - " padding-bottom: 5px;\n" - " background-position: left center;\n" - "}\n" - ".dashboard-container .cluster-status {\n" - " padding: 0 !important;\n" - " width: 940px;\n" - " margin-left: auto;\n" - " margin-right: auto;\n" - " overflow: auto;\n" - " border-collapse: collapse;\n" - " display: -webkit-box;\n" - " display: -moz-box;\n" - " display: -webkit-flex;\n" - " display: -ms-flexbox;\n" - " display: flex;\n" - " -ms-flex-pack: justify;\n" - " -webkit-justify-content: space-between;\n" - " justify-content: space-between;\n" - "}\n" - ".dashboard-container .cluster-status .panel {\n" - " border: thin solid #f0f0f0;\n" - " -webkit-flex-grow: 0;\n" - " -moz-flex-grow: 0;\n" - " -ms-flex-grow: 0;\n" - " flex-grow: 0;\n" - " -webkit-flex-shrink: 0;\n" - " -moz-flex-shrink: 0;\n" - " -ms-flex-shrink: 0;\n" - " flex-shrink: 0;\n" - " -webkit-flex-basis: 233px;\n" - " -moz-flex-basis: 233px;\n" - " -ms-flex-basis: 233px;\n" - " flex-basis: 233px;\n" - " vertical-align: top;\n" - " overflow: hidden;\n" - " white-space: nowrap;\n" - " display: -webkit-box;\n" - " display: -moz-box;\n" - " display: -webkit-flex;\n" - " display: -ms-flexbox;\n" - " display: flex;\n" - " -ms-flex-align: center;\n" - " -webkit-align-items: center;\n" - " align-items: center;\n" - "}\n" - ".dashboard-container .cluster-status .panel .dashboard-panel {\n" - " padding: 20px;\n" - " text-overflow: ellipsis;\n" - " margin-left: 5%;\n" - "}\n" - ".dashboard-container .cluster-status .panel h3 {\n" - " margin-top: 0;\n" - " margin-bottom: 8px;\n" - " font-family: \"Copse\";\n" - " font-size: 17px;\n" - " color: #4c4c4c;\n" - " padding-left: 21px;\n" - "}\n" - ".dashboard-container .cluster-status .panel .no-problems-detected h3 {\n" - " background: 0 3px no-repeat url('images/green-light.png');\n" - "}\n" - ".dashboard-container .cluster-status .panel .problems-detected h3 {\n" - " background: 0 3px no-repeat url('images/red-light.png');\n" - "}\n" - ".dashboard-container .cluster-status .panel ul {\n" - " padding-left: 0;\n" - " margin-bottom: 0px;\n" - " margin-top: 0px;\n" - "}\n" - ".dashboard-container .cluster-status .panel li {\n" - " list-style: none;\n" - " font-family: \"Open Sans\";\n" - " font-size: 14px;\n" - " line-height: 22px;\n" - " margin: 0;\n" - " color: #4c4c4c;\n" - "}\n" - ".dashboard-container .cluster-status .panel li span.good {\n" - " color: #3ba51a;\n" - "}\n" - ".dashboard-container .cluster-status .panel li span.bad {\n" - " color: #e85250;\n" - "}\n" - ".dashboard-container .cluster-status .panel li span.good,\n" - ".dashboard-container .cluster-status .panel li span.bad {\n" - " font-weight: bold;\n" - "}\n" - ".dashboard-container .cluster-status .panel li span.highlight {\n" - " font-weight: bold;\n" - "}\n" - ".dashboard-container .cluster-status .panel.first {\n" - " border-left: none;\n" - "}\n" - ".dashboard-container .ops-plot {\n" - " /* GPU acceleration */\n" - " -webkit-transform: translateZ(0);\n" - " -o-transform: translateZ(0);\n" - " -moz-transform: translateZ(0);\n" - " transform: translateZ(0);\n" - " /* For Safari */\n" - " -webkit-perspective: 1000;\n" - " -webkit-backface-visibility: hidden;\n" - "}\n" - ".dashboard-container .ops-plot .ops-plot-legend {\n" - " float: right;\n" - " color: #A3A3A3;\n" - " font: 15px \"Copse\";\n" - " text-align: right;\n" - " /* concentric circles for legend element */\n" - "}\n" - ".dashboard-container .ops-plot .ops-plot-legend .element {\n" - " display: inline-block;\n" - " vertical-align: top;\n" - " margin-left: 40px;\n" - "}\n" - ".dashboard-container .ops-plot .ops-plot-legend .element p {\n" - " margin: 0;\n" - " margin-left: 23px;\n" - " line-height: 15px;\n" - "}\n" - ".dashboard-container .ops-plot .ops-plot-legend .reads .color {\n" - " background-color: #729E51;\n" - "}\n" - ".dashboard-container .ops-plot .ops-plot-legend .writes .color {\n" - " background-color: #983434;\n" - "}\n" - ".dashboard-container .ops-plot .ops-plot-legend .color {\n" - " width: 8px;\n" - " height: 8px;\n" - " border-radius: 4px;\n" - " margin: 2px;\n" - "}\n" - ".dashboard-container .ops-plot .ops-plot-legend .outer-circle {\n" - " width: 12px;\n" - " height: 12px;\n" - " border-radius: 6px;\n" - " border: thin solid #cfcfd1;\n" - " float: left;\n" - "}\n" - ".dashboard-container .ops-plot .plot-container {\n" - " background-color: white;\n" - " border: thin solid #dcdcdc;\n" - " border-radius: 4px;\n" - " padding: 10px;\n" - "}\n" - ".dashboard-container .ops-plot .plot {\n" - " position: relative;\n" - "}\n" - ".dashboard-container .ops-plot .plot .chart {\n" - " border: none;\n" - " z-index: 150;\n" - " position: relative;\n" - " overflow: hidden;\n" - " display: inline-block;\n" - "}\n" - ".dashboard-container .ops-plot .plot .chart .title {\n" - " display: none;\n" - "}\n" - ".dashboard-container .ops-plot .plot .chart .value {\n" - " display: none;\n" - "}\n" - ".dashboard-container .ops-plot .plot .chart canvas {\n" - " display: block;\n" - "}\n" - ".dashboard-container .ops-plot .plot .chart,\n" - ".dashboard-container .ops-plot .plot .haxis,\n" - ".dashboard-container .ops-plot .plot .hgrid,\n" - ".dashboard-container .ops-plot .plot .line {\n" - " margin-left: 34px;\n" - "}\n" - ".dashboard-container .ops-plot .plot .haxis,\n" - ".dashboard-container .ops-plot .plot .vaxis,\n" - ".dashboard-container .ops-plot .plot .hgrid,\n" - ".dashboard-container .ops-plot .plot .vgrid {\n" - " font: 7px \"Open Sans\";\n" - " opacity: 1;\n" - "}\n" - ".dashboard-container .ops-plot .plot .haxis line,\n" - ".dashboard-container .ops-plot .plot .vaxis line,\n" - ".dashboard-container .ops-plot .plot .hgrid line,\n" - ".dashboard-container .ops-plot .plot .vgrid line {\n" - " stroke: #dcdcdc;\n" - " shape-rendering: crispEdges;\n" - "}\n" - ".dashboard-container .ops-plot .plot .haxis line,\n" - ".dashboard-container .ops-plot .plot .vaxis line,\n" - ".dashboard-container .ops-plot .plot .hgrid line,\n" - ".dashboard-container .ops-plot .plot .vgrid line {\n" - " opacity: 1;\n" - "}\n" - ".dashboard-container .ops-plot .plot .vaxis text,\n" - ".dashboard-container .ops-plot .plot .haxis text {\n" - " font: 13px \"Open Sans\";\n" - " fill: #A3A3A3;\n" - " -webkit-transition: fill-opacity 250ms linear;\n" - "}\n" - ".dashboard-container .ops-plot .plot .vaxis {\n" - " margin-top: -326px;\n" - "}\n" - ".dashboard-container .ops-plot .plot .vaxis .tick {\n" - " display: none;\n" - "}\n" - ".dashboard-container .ops-plot .plot .vgrid {\n" - " position: absolute;\n" - " top: 0;\n" - " left: 0px;\n" - "}\n" - ".dashboard-container .ops-plot .plot .haxis {\n" - " margin-top: -9px;\n" - "}\n" - ".dashboard-container .ops-plot .plot .hgrid {\n" - " display: none;\n" - " height: 300px;\n" - " position: absolute;\n" - " top: -3px;\n" - " left: 0;\n" - "}\n" - ".dashboard-container .ops-plot .plot .line {\n" - " background: black;\n" - " opacity: .2;\n" - " z-index: 2;\n" - "}\n" - "/*\n" - " ----------------------------------------------------------------------\n" - " Sticky footer and 100% height\n" - " - Uses Adham Dannaway's sticky footer approach (adhamdannaway.com)\n" - " ----------------------------------------------------------------------\n" - "*/\n" - "html,\n" - "body {\n" - " height: 100%;\n" - " background: #E5E5E5;\n" - " -webkit-font-smoothing: antialiased;\n" - "}\n" - "#sticky-wrap {\n" - " min-height: 100%;\n" - "}\n" - "#main-container {\n" - " padding-bottom: 95px;\n" - "}\n" - "#footer {\n" - " position: relative;\n" - " margin-top: -95px;\n" - " height: 60px;\n" - " clear: both;\n" - " /* Used to preload the monospace fonts for the data explorer */\n" - "}\n" - "#footer .preload_fonts {\n" - " font-family: monospace;\n" - "}\n" - ".new-infobox {\n" - " border: 1px solid #C9D9E1;\n" - " text-align: left;\n" - " background-color: #eaf5fa;\n" - " font-family: \"Open Sans\";\n" - " font-size: 13px;\n" - " border-radius: 3px;\n" - " color: #4d4d4d;\n" - " position: relative;\n" - " padding: 0px;\n" - "}\n" - ".new-infobox p {\n" - " margin: 10px 10px 10px 33px !important;\n" - "}\n" - ".new-infobox::before {\n" - " content: \"\\f149\";\n" - " font-family: 'Ionicons';\n" - " font-size: 18px;\n" - " color: #70b8db;\n" - " position: absolute;\n" - " top: 10px;\n" - " left: 10px;\n" - " height: 100%;\n" - "}\n" - ".new-errorbox {\n" - " border: 1px solid #D8C8C8;\n" - " text-align: left;\n" - " background-color: #F2E4E4;\n" - " font-family: \"Open Sans\";\n" - " font-size: 13px;\n" - " border-radius: 3px;\n" - " color: #4d4d4d;\n" - " position: relative;\n" - " padding: 0px;\n" - "}\n" - ".new-errorbox p {\n" - " margin: 10px 10px 10px 33px !important;\n" - "}\n" - ".new-errorbox::before {\n" - " content: \"\\f100\";\n" - " font-family: 'Ionicons';\n" - " font-size: 18px;\n" - " color: #df8181;\n" - " position: absolute;\n" - " top: 10px;\n" - " left: 10px;\n" - " height: 100%;\n" - "}\n" - ".unavailable-error {\n" - " border: 1px solid #D8C8C8;\n" - " text-align: left;\n" - " background-color: #F2E4E4;\n" - " font-family: \"Open Sans\";\n" - " font-size: 13px;\n" - " border-radius: 3px;\n" - " color: #4d4d4d;\n" - " position: relative;\n" - " padding: 0px;\n" - " display: none;\n" - "}\n" - ".unavailable-error p {\n" - " margin: 10px 10px 10px 33px !important;\n" - "}\n" - ".unavailable-error::before {\n" - " content: \"\\f100\";\n" - " font-family: 'Ionicons';\n" - " font-size: 18px;\n" - " color: #df8181;\n" - " position: absolute;\n" - " top: 10px;\n" - " left: 10px;\n" - " height: 100%;\n" - "}\n" - ".stale-warning {\n" - " border: 1px solid #D8C8C8;\n" - " text-align: left;\n" - " background-color: #F2E4E4;\n" - " font-family: \"Open Sans\";\n" - " font-size: 13px;\n" - " border-radius: 3px;\n" - " color: #4d4d4d;\n" - " position: relative;\n" - " padding: 0px;\n" - " display: none;\n" - "}\n" - ".stale-warning p {\n" - " margin: 10px 10px 10px 33px !important;\n" - "}\n" - ".stale-warning::before {\n" - " content: \"\\f100\";\n" - " font-family: 'Ionicons';\n" - " font-size: 18px;\n" - " color: #df8181;\n" - " position: absolute;\n" - " top: 10px;\n" - " left: 10px;\n" - " height: 100%;\n" - "}\n" - "h1.title {\n" - " font-family: \"Copse\";\n" - " color: #4d535c;\n" - " font-size: 19px;\n" - " margin-bottom: 25px;\n" - "}\n" - "#main_view {\n" - " height: 100%;\n" - "}\n" - "/* Opera fix */\n" - "body:before {\n" - " content: \"\";\n" - " height: 100%;\n" - " float: left;\n" - " width: 0;\n" - " margin-top: -32767px;\n" - "}\n" - "#options_container {\n" - " display: none;\n" - " position: absolute;\n" - " width: 100%;\n" - " background: #fafafa;\n" - " border-bottom: 1px solid #ccc;\n" - " padding: 0px;\n" - " height: 86px;\n" - " border-bottom: 2px solid #ddd;\n" - "}\n" - ".options_container_arrow_overlay {\n" - " position: absolute;\n" - " z-index: 201;\n" - " left: 50%;\n" - " top: -10px;\n" - " width: 0;\n" - " height: 0;\n" - " margin: 1px 0px 0px 185px;\n" - " border-left: 10px solid transparent;\n" - " border-right: 10px solid transparent;\n" - " border-bottom: 10px solid #F6F0E3;\n" - "}\n" - ".options_background {\n" - " width: 100%;\n" - " position: absolute;\n" - " margin: 0px auto;\n" - " text-shadow: none;\n" - " padding: 0px;\n" - "}\n" - ".options_background .title {\n" - " width: 100%;\n" - " margin: 0px;\n" - " background: #F6F0E3;\n" - " border-top: 1px solid #F0E6D1;\n" - " border-bottom: 1px solid #F0E6D1;\n" - " overflow: auto;\n" - "}\n" - ".options_background .title .title_content {\n" - " width: 940px;\n" - " margin: 0px auto;\n" - " color: #666;\n" - " padding: 9px 0px 2px 0px;\n" - "}\n" - ".options_background .title .title_content .title_text {\n" - " font-family: \"Copse\";\n" - " font-weight: bold;\n" - " font-size: 15px;\n" - "}\n" - ".options_background .title .title_content .version {\n" - " float: right;\n" - " margin: 0px 0px 7px 0px;\n" - " font-family: \"Open Sans\";\n" - " font-size: 13px;\n" - " -webkit-font-smoothing: antialiased;\n" - "}\n" - ".options_background .title .title_content .version .layers {\n" - " float: left;\n" - " display: block;\n" - " margin: 3px 8px 0px 0px;\n" - " opacity: 0.7;\n" - "}\n" - ".options_background .options_content {\n" - " width: 940px;\n" - " margin: 3px auto;\n" - " padding: 8px 0px 0px 0px;\n" - " overflow: auto;\n" - "}\n" - ".options_background .options_content .toggle-primary {\n" - " float: left;\n" - " margin: 2px 0px 3px 0px;\n" - "}\n" - ".options_background .options_content .toggle-primary .switch {\n" - " float: none;\n" - "}\n" - ".options_background .options_content .description {\n" - " margin: 5px 0px 0px 120px;\n" - " font-family: \"Open Sans\";\n" - " font-size: 14px;\n" - " color: #666;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Button styles\n" - " ----------------------------\n" - "*/\n" - "button.btn {\n" - " display: inline-block;\n" - " position: relative;\n" - " padding: 6px 14px 5px;\n" - " margin-bottom: 0;\n" - " margin-left: 7px;\n" - " vertical-align: middle;\n" - " cursor: pointer;\n" - " font-family: \"Open Sans\";\n" - " font-size: 13px;\n" - " line-height: 18px;\n" - " text-align: center;\n" - " -webkit-border-radius: 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px;\n" - " background-clip: padding-box;\n" - " -webkit-box-shadow: 0 2px 1px rgba(0,0,0,.05);\n" - " -moz-box-shadow: 0 2px 1px rgba(0,0,0,.05);\n" - " box-shadow: 0 2px 1px rgba(0,0,0,.05);\n" - " color: #7D7D7D;\n" - " text-shadow: 0 1px 0 #fff;\n" - " border: 1px solid #ccc;\n" - " background-color: #fff;\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0icmdiKDAsMCwwKSIgc3RvcC1vcGFjaXR5PSIuMDQiLz48c3" - "RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9InJnYigyNTUsMjU1LDI1NSkiIHN0b3Atb3BhY2" - "l0eT0iLjA0Ii8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaG" - "VpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPjwvc3ZnPg==);\n" - " background-image: -moz-linear-gradient(bottom, rgba(0,0,0,.04) 0%, rgba(25" - "5,255,255,.04) 100%);\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - "0%,rgba(0,0,0,.04)),color-stop(100%,rgba(255,255,255,.04)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(0,0,0,.04) 0%, rgba" - "(255,255,255,.04) 100%);\n" - " background-image: -o-linear-gradient(bottom, rgba(0,0,0,.04) 0%, rgba(255," - "255,255,.04) 100%);\n" - " background-image: linear-gradient(bottom, rgba(0,0,0,.04) 0%, rgba(255,255" - ",255,.04) 100%);\n" - "}\n" - "button.btn:not([disabled]):hover {\n" - " -webkit-box-shadow: inset 0 0 1px 1px #fff;\n" - " -moz-box-shadow: inset 0 0 1px 1px #fff;\n" - " box-shadow: inset 0 0 1px 1px #fff;\n" - " background-image: -moz-linear-gradient(rgba(255,255,255,0.96), rgba(255,25" - "5,255,0.96)),-moz-linear-gradient(bottom, rgba(51,51,51,.23) 0%, rgba(255,25" - "5,255,.23) 100%);\n" - " background-image: -webkit-linear-gradient(rgba(255,255,255,0.96), rgba(255" - ",255,255,0.96)),-webkit-linear-gradient(bottom, rgba(51,51,51,.23) 0%, rgba(" - "255,255,255,.23) 100%);\n" - " background-image: -o-linear-gradient(rgba(255,255,255,0.96), rgba(255,255," - "255,0.96)),-o-linear-gradient(bottom, rgba(51,51,51,.23) 0%, rgba(255,255,25" - "5,.23) 100%);\n" - " background-image: linear-gradient(rgba(255,255,255,0.96), rgba(255,255,255" - ",0.96)),linear-gradient(bottom, rgba(51,51,51,.23) 0%, rgba(255,255,255,.23)" - " 100%);\n" - "}\n" - "button.btn:not([disabled]):active {\n" - " background-color: #f2f2f2;\n" - " -webkit-box-shadow: inset 0 2px 1px rgba(0,0,0,.06);\n" - " -moz-box-shadow: inset 0 2px 1px rgba(0,0,0,.06);\n" - " box-shadow: inset 0 2px 1px rgba(0,0,0,.06);\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0icmdiKDI1NSwyNTUsMjU1KSIgc3RvcC1vcGFjaXR5PSIuMD" - "IiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9InJnYigwLDAsMCkiIHN0b3Atb3BhY2" - "l0eT0iLjAyIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaG" - "VpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPjwvc3ZnPg==);\n" - " background-image: -moz-linear-gradient(bottom, rgba(255,255,255,.02) 0%, r" - "gba(0,0,0,.02) 100%);\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - "0%,rgba(255,255,255,.02)),color-stop(100%,rgba(0,0,0,.02)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(255,255,255,.02) 0%" - ", rgba(0,0,0,.02) 100%);\n" - " background-image: -o-linear-gradient(bottom, rgba(255,255,255,.02) 0%, rgb" - "a(0,0,0,.02) 100%);\n" - " background-image: linear-gradient(bottom, rgba(255,255,255,.02) 0%, rgba(0" - ",0,0,.02) 100%);\n" - "}\n" - "button.btn.btn-primary {\n" - " color: #fff;\n" - " text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.3);\n" - " border: 1px solid #2f89b1;\n" - " background-color: #1da3dd;\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYigxMSwxNTUsMjIyKSIgc3RvcC1vcGFjaXR5PSIxIi8+PH" - "N0b3Agb2Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYig3NSwxODEsMjI5KSIgc3RvcC1vcGFjaXR5PS" - "IxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PS" - "IxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPjwvc3ZnPg==);\n" - " background-image: -moz-linear-gradient(bottom, rgba(11,155,222,1), rgba(75" - ",181,229, 1));\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - ",rgba(11,155,222,1)),color-stop(,rgba(75,181,229, 1)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(11,155,222,1), rgba" - "(75,181,229, 1));\n" - " background-image: -o-linear-gradient(bottom, rgba(11,155,222,1), rgba(75,1" - "81,229, 1));\n" - " background-image: linear-gradient(bottom, rgba(11,155,222,1), rgba(75,181," - "229, 1));\n" - "}\n" - "button.btn.btn-primary:not(.disabled):hover {\n" - " -webkit-box-shadow: inset 0 0 2px 0 rgba(255,255,255,0.8);\n" - " -moz-box-shadow: inset 0 0 2px 0 rgba(255,255,255,0.8);\n" - " box-shadow: inset 0 0 2px 0 rgba(255,255,255,0.8);\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYigzMiwxNjQsMjIyKSIgc3RvcC1vcGFjaXR5PSIxIi8+PH" - "N0b3Agb2Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYig3NCwxODEsMjI4KSIgc3RvcC1vcGFjaXR5PS" - "IxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PS" - "IxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPjwvc3ZnPg==);\n" - " background-image: -moz-linear-gradient(bottom, rgba(32,164,222,1), rgba(74" - ",181,228, 1));\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - ",rgba(32,164,222,1)),color-stop(,rgba(74,181,228, 1)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(32,164,222,1), rgba" - "(74,181,228, 1));\n" - " background-image: -o-linear-gradient(bottom, rgba(32,164,222,1), rgba(74,1" - "81,228, 1));\n" - " background-image: linear-gradient(bottom, rgba(32,164,222,1), rgba(74,181," - "228, 1));\n" - "}\n" - "button.btn.btn-primary:not(.disabled):active {\n" - " -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.08);\n" - " -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,0.08);\n" - " box-shadow: inset 0 1px 1px rgba(0,0,0,0.08);\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYig2NSwxNTgsMTk4KSIgc3RvcC1vcGFjaXR5PSIxIi8+PH" - "N0b3Agb2Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYig1MSwxNDMsMTg0KSIgc3RvcC1vcGFjaXR5PS" - "IxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PS" - "IxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPjwvc3ZnPg==);\n" - " background-image: -moz-linear-gradient(bottom, rgba(65,158,198,1), rgba(51" - ",143,184, 1));\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - ",rgba(65,158,198,1)),color-stop(,rgba(51,143,184, 1)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(65,158,198,1), rgba" - "(51,143,184, 1));\n" - " background-image: -o-linear-gradient(bottom, rgba(65,158,198,1), rgba(51,1" - "43,184, 1));\n" - " background-image: linear-gradient(bottom, rgba(65,158,198,1), rgba(51,143," - "184, 1));\n" - "}\n" - "button.btn.btn-danger {\n" - " color: #fff;\n" - " text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.3);\n" - " border: 1px solid #b23f37;\n" - " background-color: #d6544a;\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYigyMjYsODksNzkpIiBzdG9wLW9wYWNpdHk9IjEiLz48c3" - "RvcCBvZmZzZXQ9IiIgc3RvcC1jb2xvcj0icmdiKDIzMiwxMjEsMTEzKSIgc3RvcC1vcGFjaXR5PS" - "IxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PS" - "IxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPjwvc3ZnPg==);\n" - " background-image: -moz-linear-gradient(bottom, rgba(226,89,79,1), rgba(232" - ",121,113, 1));\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - ",rgba(226,89,79,1)),color-stop(,rgba(232,121,113, 1)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(226,89,79,1), rgba(" - "232,121,113, 1));\n" - " background-image: -o-linear-gradient(bottom, rgba(226,89,79,1), rgba(232,1" - "21,113, 1));\n" - " background-image: linear-gradient(bottom, rgba(226,89,79,1), rgba(232,121," - "113, 1));\n" - "}\n" - "button.btn.btn-danger:not([disabled]):hover {\n" - " -webkit-box-shadow: inset 0 0 2px 0 rgba(255,255,255,0.8);\n" - " -moz-box-shadow: inset 0 0 2px 0 rgba(255,255,255,0.8);\n" - " box-shadow: inset 0 0 2px 0 rgba(255,255,255,0.8);\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYigyMzAsMTE0LDEwNSkiIHN0b3Atb3BhY2l0eT0iMSIvPj" - "xzdG9wIG9mZnNldD0iIiBzdG9wLWNvbG9yPSJyZ2IoMjM1LDE0MSwxMzQpIiBzdG9wLW9wYWNpdH" - "k9IjEiLz48L2xpbmVhckdyYWRpZW50PjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxIiBoZWlnaH" - "Q9IjEiIGZpbGw9InVybCgjZ3JhZC11Y2dnLWdlbmVyYXRlZCkiIC8+PC9zdmc+);\n" - " background-image: -moz-linear-gradient(bottom, rgba(230,114,105,1), rgba(2" - "35,141,134, 1));\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - ",rgba(230,114,105,1)),color-stop(,rgba(235,141,134, 1)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(230,114,105,1), rgb" - "a(235,141,134, 1));\n" - " background-image: -o-linear-gradient(bottom, rgba(230,114,105,1), rgba(235" - ",141,134, 1));\n" - " background-image: linear-gradient(bottom, rgba(230,114,105,1), rgba(235,14" - "1,134, 1));\n" - "}\n" - "button.btn.btn-danger:not([disabled]):active {\n" - " -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.08);\n" - " -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,0.08);\n" - " box-shadow: inset 0 1px 1px rgba(0,0,0,0.08);\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYigyMTYsOTMsODUpIiBzdG9wLW9wYWNpdHk9IjEiLz48c3" - "RvcCBvZmZzZXQ9IiIgc3RvcC1jb2xvcj0icmdiKDIwMSw3OSw3MSkiIHN0b3Atb3BhY2l0eT0iMS" - "IvPjwvbGluZWFyR3JhZGllbnQ+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMS" - "IgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz48L3N2Zz4=);\n" - " background-image: -moz-linear-gradient(bottom, rgba(216,93,85,1), rgba(201" - ",79,71, 1));\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - ",rgba(216,93,85,1)),color-stop(,rgba(201,79,71, 1)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(216,93,85,1), rgba(" - "201,79,71, 1));\n" - " background-image: -o-linear-gradient(bottom, rgba(216,93,85,1), rgba(201,7" - "9,71, 1));\n" - " background-image: linear-gradient(bottom, rgba(216,93,85,1), rgba(201,79,7" - "1, 1));\n" - "}\n" - "button.btn.btn-resolve-issues {\n" - " color: #fff;\n" - " text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.3);\n" - " border: 1px solid #a94240;\n" - " background-color: #ed6f66;\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYigyMzcsMTEyLDEwMikiIHN0b3Atb3BhY2l0eT0iMSIvPj" - "xzdG9wIG9mZnNldD0iIiBzdG9wLWNvbG9yPSJyZ2IoMjM5LDEyOSwxMjEpIiBzdG9wLW9wYWNpdH" - "k9IjEiLz48L2xpbmVhckdyYWRpZW50PjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxIiBoZWlnaH" - "Q9IjEiIGZpbGw9InVybCgjZ3JhZC11Y2dnLWdlbmVyYXRlZCkiIC8+PC9zdmc+);\n" - " background-image: -moz-linear-gradient(bottom, rgba(237,112,102,1), rgba(2" - "39,129,121, 1));\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - ",rgba(237,112,102,1)),color-stop(,rgba(239,129,121, 1)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(237,112,102,1), rgb" - "a(239,129,121, 1));\n" - " background-image: -o-linear-gradient(bottom, rgba(237,112,102,1), rgba(239" - ",129,121, 1));\n" - " background-image: linear-gradient(bottom, rgba(237,112,102,1), rgba(239,12" - "9,121, 1));\n" - "}\n" - "button.btn.btn-resolve-issues:not([disabled]):hover {\n" - " -webkit-box-shadow: inset 0 0 2px 0 rgba(255,255,255,0.8);\n" - " -moz-box-shadow: inset 0 0 2px 0 rgba(255,255,255,0.8);\n" - " box-shadow: inset 0 0 2px 0 rgba(255,255,255,0.8);\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYigyMzAsMTE0LDEwNSkiIHN0b3Atb3BhY2l0eT0iMSIvPj" - "xzdG9wIG9mZnNldD0iIiBzdG9wLWNvbG9yPSJyZ2IoMjM1LDE0MSwxMzQpIiBzdG9wLW9wYWNpdH" - "k9IjEiLz48L2xpbmVhckdyYWRpZW50PjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxIiBoZWlnaH" - "Q9IjEiIGZpbGw9InVybCgjZ3JhZC11Y2dnLWdlbmVyYXRlZCkiIC8+PC9zdmc+);\n" - " background-image: -moz-linear-gradient(bottom, rgba(230,114,105,1), rgba(2" - "35,141,134, 1));\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - ",rgba(230,114,105,1)),color-stop(,rgba(235,141,134, 1)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(230,114,105,1), rgb" - "a(235,141,134, 1));\n" - " background-image: -o-linear-gradient(bottom, rgba(230,114,105,1), rgba(235" - ",141,134, 1));\n" - " background-image: linear-gradient(bottom, rgba(230,114,105,1), rgba(235,14" - "1,134, 1));\n" - "}\n" - "button.btn.btn-resolve-issues:not([disabled]):active {\n" - " -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.08);\n" - " -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,0.08);\n" - " box-shadow: inset 0 1px 1px rgba(0,0,0,0.08);\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYigyMTYsOTMsODUpIiBzdG9wLW9wYWNpdHk9IjEiLz48c3" - "RvcCBvZmZzZXQ9IiIgc3RvcC1jb2xvcj0icmdiKDIwMSw3OSw3MSkiIHN0b3Atb3BhY2l0eT0iMS" - "IvPjwvbGluZWFyR3JhZGllbnQ+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMS" - "IgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz48L3N2Zz4=);\n" - " background-image: -moz-linear-gradient(bottom, rgba(216,93,85,1), rgba(201" - ",79,71, 1));\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - ",rgba(216,93,85,1)),color-stop(,rgba(201,79,71, 1)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(216,93,85,1), rgba(" - "201,79,71, 1));\n" - " background-image: -o-linear-gradient(bottom, rgba(216,93,85,1), rgba(201,7" - "9,71, 1));\n" - " background-image: linear-gradient(bottom, rgba(216,93,85,1), rgba(201,79,7" - "1, 1));\n" - "}\n" - "button.btn.btn-success {\n" - " color: #fff;\n" - " text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.3);\n" - " border: 1px solid #4fa435;\n" - " background-color: ;\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYigxMTUsMTk2LDkxKSIgc3RvcC1vcGFjaXR5PSIxIi8+PH" - "N0b3Agb2Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYigxNDIsMjA4LDEyMykiIHN0b3Atb3BhY2l0eT" - "0iMSIvPjwvbGluZWFyR3JhZGllbnQ+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD" - "0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz48L3N2Zz4=);\n" - " background-image: -moz-linear-gradient(bottom, rgba(115,196,91,1), rgba(14" - "2,208,123,1));\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - ",rgba(115,196,91,1)),color-stop(,rgba(142,208,123,1)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(115,196,91,1), rgba" - "(142,208,123,1));\n" - " background-image: -o-linear-gradient(bottom, rgba(115,196,91,1), rgba(142," - "208,123,1));\n" - " background-image: linear-gradient(bottom, rgba(115,196,91,1), rgba(142,208" - ",123,1));\n" - "}\n" - "button.btn.btn-success:not([disabled]):hover {\n" - " -webkit-box-shadow: inset 0 0 2px 0 rgba(255,255,255,0.8);\n" - " -moz-box-shadow: inset 0 0 2px 0 rgba(255,255,255,0.8);\n" - " box-shadow: inset 0 0 2px 0 rgba(255,255,255,0.8);\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYigxMzYsMjA1LDExNSkiIHN0b3Atb3BhY2l0eT0iMSIvPn" - "JnYigxNTkyMTUxNDIxKTwvbGluZWFyR3JhZGllbnQ+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9Ij" - "EiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz48L3N2Zz4=);" - "\n" - " background-image: -moz-linear-gradient(bottom, rgba(136,205,115,1), rgba(1" - "59,215,1421));\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - ",rgba(136,205,115,1)),rgba(159,215,1421));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(136,205,115,1), rgb" - "a(159,215,1421));\n" - " background-image: -o-linear-gradient(bottom, rgba(136,205,115,1), rgba(159" - ",215,1421));\n" - " background-image: linear-gradient(bottom, rgba(136,205,115,1), rgba(159,21" - "5,1421));\n" - "}\n" - "button.btn.btn-success:not([disabled]):active {\n" - " -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.08);\n" - " -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,0.08);\n" - " box-shadow: inset 0 1px 1px rgba(0,0,0,0.08);\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYigxMTMsMTgxLDkzKSIgc3RvcC1vcGFjaXR5PSIxIi8+PH" - "N0b3Agb2Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYig5OSwxNjcsNzgpIiBzdG9wLW9wYWNpdHk9Ij" - "EiLz48L2xpbmVhckdyYWRpZW50PjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxIiBoZWlnaHQ9Ij" - "EiIGZpbGw9InVybCgjZ3JhZC11Y2dnLWdlbmVyYXRlZCkiIC8+PC9zdmc+);\n" - " background-image: -moz-linear-gradient(bottom, rgba(113,181,93,1), rgba(99" - ",167,78,1));\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - ",rgba(113,181,93,1)),color-stop(,rgba(99,167,78,1)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(113,181,93,1), rgba" - "(99,167,78,1));\n" - " background-image: -o-linear-gradient(bottom, rgba(113,181,93,1), rgba(99,1" - "67,78,1));\n" - " background-image: linear-gradient(bottom, rgba(113,181,93,1), rgba(99,167," - "78,1));\n" - "}\n" - "button.btn:disabled {\n" - " opacity: 0.0065;\n" - " filter: alpha(opacity=0.65);\n" - " -webkit-opacity: 0.65;\n" - " -moz-opacity: 0.65;\n" - " opacity: 0.65;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Alert styles\n" - " ----------------------------\n" - "*/\n" - ".alert {\n" - " display: none;\n" - " clear: both;\n" - " font-family: \"Open Sans\";\n" - " font-size: 13px;\n" - " -webkit-border-radius: 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px;\n" - " background-clip: padding-box;\n" - " padding: 10px 35px 10px 10px;\n" - " margin-bottom: 10px;\n" - " border: 1px solid #e9d295;\n" - " background-color: #faf3d7;\n" - " color: #bf9947;\n" - "}\n" - ".alert p {\n" - " margin: 0;\n" - "}\n" - ".alert.alert-success {\n" - " border: 1px solid #bdd4b6;\n" - " background-color: #effceb;\n" - " color: #69925c;\n" - "}\n" - ".alert.alert-warning {\n" - " border: 1px solid #e9d295;\n" - " background-color: #faf3d7;\n" - " color: #bf9947;\n" - "}\n" - ".alert.alert-error {\n" - " border: 1px solid #e59c9c;\n" - " background-color: #f1d5d5;\n" - " color: #d95b5b;\n" - "}\n" - ".alert .close,\n" - ".alert .close_hide {\n" - " position: relative;\n" - " float: right;\n" - " right: -23px;\n" - " font-weight: bold;\n" - " font-size: 24px;\n" - " color: black;\n" - " opacity: 0.002;\n" - " filter: alpha(opacity=0.2);\n" - " -webkit-opacity: 0.2;\n" - " -moz-opacity: 0.2;\n" - " opacity: 0.2;\n" - " cursor: pointer;\n" - "}\n" - ".alert .close:hover,\n" - ".alert .close_hide:hover {\n" - " opacity: 0.004;\n" - " filter: alpha(opacity=0.4);\n" - " -webkit-opacity: 0.4;\n" - " -moz-opacity: 0.4;\n" - " opacity: 0.4;\n" - " text-decoration: none;\n" - "}\n" - ".alert.displayed_alert {\n" - " display: block;\n" - "}\n" - ".alert .server_error {\n" - " border-top: 1px solid #f99b9b;\n" - " margin-top: 6px;\n" - " padding-top: 6px;\n" - " white-space: pre-wrap;\n" - " font-size: 12px;\n" - "}\n" - ".alert.global_loading {\n" - " width: 940px;\n" - " margin: 40px auto;\n" - " display: block;\n" - "}\n" - ".main_query_error {\n" - " word-break: break-word;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Main content\n" - " ----------------------------\n" - "*/\n" - "#sticky-wrap {\n" - " min-width: 940px;\n" - " background: url('images/body_bg_tile.png');\n" - "}\n" - "#main-container {\n" - " padding-top: 30px;\n" - "}\n" - "#main-container .cluster_with_margin {\n" - " padding: 0px 20px;\n" - "}\n" - "#main-container .section {\n" - " padding: 20px 25px;\n" - " margin-bottom: 25px;\n" - " border: 1px solid #cfcfcf;\n" - " -webkit-border-radius: 4px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 4px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 4px;\n" - " background-clip: padding-box;\n" - " background-color: #fbfbfb;\n" - " -webkit-box-shadow: 0 2px 1px rgba(0,0,0,.04), inset 0 -1px 2px rgba(0,0,0" - ",.05);\n" - " -moz-box-shadow: 0 2px 1px rgba(0,0,0,.04), inset 0 -1px 2px rgba(0,0,0,.0" - "5);\n" - " box-shadow: 0 2px 1px rgba(0,0,0,.04), inset 0 -1px 2px rgba(0,0,0,.05);\n" - "}\n" - "#main-container .section h2.title {\n" - " margin-top: 0;\n" - " font-family: \"Copse\";\n" - " color: #a3a3a3;\n" - " font-size: 19px;\n" - " margin-bottom: 20px;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Shared classes\n" - " ----------------------------\n" - "TODO: retire this\n" - "*/\n" - ".no_element {\n" - " background: #FFF;\n" - " border: thin solid #cfcfcf;\n" - " -webkit-border-radius: 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px;\n" - " background-clip: padding-box;\n" - " margin-top: 12px;\n" - " -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02), 0 1px 3px rgba(0, 0" - ", 0, 0.08);\n" - " -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02), 0 1px 3px rgba(0, 0, 0" - ", 0.08);\n" - " box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02), 0 1px 3px rgba(0, 0, 0, 0.0" - "8);\n" - " padding: 8px 0px;\n" - " text-align: center;\n" - " font-family: \"Open Sans\";\n" - " font-size: 14px;\n" - " color: #777;\n" - " background: #f6f6f6;\n" - "}\n" - ".no_element p {\n" - " margin: 0px;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Footer\n" - " ----------------------------\n" - "*/\n" - "#footer {\n" - " background-color: #e5e5e5;\n" - " color: #a3a3a3;\n" - " font-family: \"Copse\";\n" - " font-size: 14px;\n" - " padding-top: 30px;\n" - " border-top: thin solid #d0d0d0;\n" - "}\n" - "#footer ul {\n" - " float: left;\n" - " padding: 0;\n" - " margin: 0;\n" - " list-style-type: none;\n" - "}\n" - "#footer ul li {\n" - " display: inline-block;\n" - " margin-right: 63px;\n" - "}\n" - "#footer ul li a {\n" - " color: #a3a3a3;\n" - "}\n" - "#footer ul li:last-child {\n" - " margin-right: 0px;\n" - "}\n" - "#footer p.copyright {\n" - " float: right;\n" - " margin: 0;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Navbar at the top\n" - " ----------------------------\n" - "*/\n" - "#navbar-container {\n" - " width: 100%;\n" - " background-color: #272729;\n" - " -webkit-box-shadow: 0 1px 3px rgba(0,0,0,.2), inset 0 -2px 4px rgba(0,0,0," - ".15);\n" - " -moz-box-shadow: 0 1px 3px rgba(0,0,0,.2), inset 0 -2px 4px rgba(0,0,0,.15" - ");\n" - " box-shadow: 0 1px 3px rgba(0,0,0,.2), inset 0 -2px 4px rgba(0,0,0,.15);\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0icmdiKDAsMCwwKSIgc3RvcC1vcGFjaXR5PSIuMDciLz48c3" - "RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9InJnYigyNTUsMjU1LDI1NSkiIHN0b3Atb3BhY2" - "l0eT0iLjA3Ii8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaG" - "VpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPjwvc3ZnPg==);\n" - " background-image: -moz-linear-gradient(bottom, rgba(0,0,0,.07) 0%, rgba(25" - "5,255,255,.07) 100%);\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - "0%,rgba(0,0,0,.07)),color-stop(100%,rgba(255,255,255,.07)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(0,0,0,.07) 0%, rgba" - "(255,255,255,.07) 100%);\n" - " background-image: -o-linear-gradient(bottom, rgba(0,0,0,.07) 0%, rgba(255," - "255,255,.07) 100%);\n" - " background-image: linear-gradient(bottom, rgba(0,0,0,.07) 0%, rgba(255,255" - ",255,.07) 100%);\n" - "}\n" - "#navbar-container #navbar {\n" - " font-family: \"Open Sans\";\n" - " /* left nav (logo, links, etc.) */\n" - " /* right nav (search) */\n" - "}\n" - "#navbar-container #navbar ul.nav-left {\n" - " margin: 0;\n" - " padding: 0;\n" - " float: left;\n" - " /* Fonts are not rendered on the same way on Linux and OS X, making the me" - "nu having different sizes.\n" - " We fix this problem by specifying the witdth of our blocks. */\n" - "}\n" - "#navbar-container #navbar ul.nav-left li {\n" - " font-size: 15px;\n" - " display: inline-block;\n" - " text-align: center;\n" - " vertical-align: middle;\n" - " text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4);\n" - " margin-left: -3px;\n" - "}\n" - "#navbar-container #navbar ul.nav-left li a {\n" - " padding: 21px 0px;\n" - " color: #ababab;\n" - " display: block;\n" - "}\n" - "#navbar-container #navbar ul.nav-left li a:hover {\n" - " text-decoration: none;\n" - " color: white;\n" - "}\n" - "#navbar-container #navbar ul.nav-left li#nav-logo {\n" - " width: 131px;\n" - " text-align: left;\n", - 65469) + std::string( - "}\n" - "#navbar-container #navbar ul.nav-left li#nav-logo a {\n" - " font-family: \"Copse\";\n" - " font-size: 22px;\n" - " font-weight: bold;\n" - " color: white;\n" - " padding-left: 0;\n" - "}\n" - "#navbar-container #navbar ul.nav-left li#nav-logo a span.highlight {\n" - " color: #0597d8;\n" - "}\n" - "#navbar-container #navbar ul.nav-left li.active a {\n" - " color: white;\n" - "}\n" - "#navbar-container #navbar ul.nav-left #nav-dashboard {\n" - " width: 118px;\n" - "}\n" - "#navbar-container #navbar ul.nav-left #nav-tables {\n" - " width: 86px;\n" - "}\n" - "#navbar-container #navbar ul.nav-left #nav-servers {\n" - " width: 94px;\n" - "}\n" - "#navbar-container #navbar ul.nav-left #nav-dataexplorer {\n" - " width: 136px;\n" - "}\n" - "#navbar-container #navbar ul.nav-left #nav-logs {\n" - " width: 74px;\n" - "}\n" - "#navbar-container #navbar ul.nav-left #nav-options {\n" - " width: 60px;\n" - "}\n" - "#navbar-container #navbar ul.nav-left .cog_icon {\n" - " opacity: 0.004;\n" - " filter: alpha(opacity=0.4);\n" - " -webkit-opacity: 0.4;\n" - " -moz-opacity: 0.4;\n" - " opacity: 0.4;\n" - "}\n" - "#navbar-container #navbar ul.nav-left .cog_icon:hover,\n" - "#navbar-container #navbar ul.nav-left .cog_icon.active {\n" - " opacity: 0.009;\n" - " filter: alpha(opacity=0.9);\n" - " -webkit-opacity: 0.9;\n" - " -moz-opacity: 0.9;\n" - " opacity: 0.9;\n" - "}\n" - "#navbar-container #navbar ul.nav-left #nav-options {\n" - " position: relative;\n" - "}\n" - "#navbar-container #navbar .right-nav {\n" - " margin: 0;\n" - " padding: 0;\n" - " float: right;\n" - " position: relative;\n" - "}\n" - "#navbar-container #navbar .right-nav .divider {\n" - " position: absolute;\n" - " left: 0;\n" - " width: 1px;\n" - " height: 36px;\n" - " margin: 12px 0;\n" - " background-color: #212122;\n" - " -webkit-box-shadow: 1px 0 0 rgba(255,255,255,.04);\n" - " -moz-box-shadow: 1px 0 0 rgba(255,255,255,.04);\n" - " box-shadow: 1px 0 0 rgba(255,255,255,.04);\n" - "}\n" - "#navbar-container #navbar #nav-search {\n" - " margin-bottom: 0;\n" - " margin-left: 28px;\n" - "}\n" - "#navbar-container #navbar #nav-search .search-box {\n" - " font-family: \"Open Sans\";\n" - " font-size: 15px;\n" - " border: 0 none;\n" - " color: #888;\n" - " text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4);\n" - " margin-top: 15px;\n" - " padding: 6px 12px 6px 40px;\n" - " width: 145px;\n" - " line-height: normal;\n" - " -webkit-border-radius: 14px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 14px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 14px;\n" - " background-clip: padding-box;\n" - " -webkit-box-shadow: 0 1px 0 rgba(255,255,255,.07), inset 0 1px 0 rgba(0,0," - "0,.15);\n" - " -moz-box-shadow: 0 1px 0 rgba(255,255,255,.07), inset 0 1px 0 rgba(0,0,0,." - "15);\n" - " box-shadow: 0 1px 0 rgba(255,255,255,.07), inset 0 1px 0 rgba(0,0,0,.15);\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0icmdiKDI1NSwyNTUsMjU1KSIgc3RvcC1vcGFjaXR5PSIuMD" - "IiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9InJnYigwLDAsMCkiIHN0b3Atb3BhY2" - "l0eT0iLjAyIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaG" - "VpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPjwvc3ZnPg==);\n" - " background-image: -moz-linear-gradient(bottom, rgba(255,255,255,.02) 0%, r" - "gba(0,0,0,.02) 100%);\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - "0%,rgba(255,255,255,.02)),color-stop(100%,rgba(0,0,0,.02)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(255,255,255,.02) 0%" - ", rgba(0,0,0,.02) 100%);\n" - " background-image: -o-linear-gradient(bottom, rgba(255,255,255,.02) 0%, rgb" - "a(0,0,0,.02) 100%);\n" - " background-image: linear-gradient(bottom, rgba(255,255,255,.02) 0%, rgba(0" - ",0,0,.02) 100%);\n" - " background: url('images/icon-magnifying_glass.png') no-repeat 12px #232323" - ";\n" - "}\n" - ".typeahead li {\n" - " overflow: hidden;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Status bar at the top\n" - " ----------------------------\n" - "*/\n" - "#topbar {\n" - " background: url('images/status-panel_bg_tile.png');\n" - " -webkit-box-shadow: 0 1px 0 rgba(255,255,255,.4), inset 0 -1px 1px #c3c3c3" - ";\n" - " -moz-box-shadow: 0 1px 0 rgba(255,255,255,.4), inset 0 -1px 1px #c3c3c3;\n" - " box-shadow: 0 1px 0 rgba(255,255,255,.4), inset 0 -1px 1px #c3c3c3;\n" - " border-bottom: thin solid #d0d0d0;\n" - "}\n" - "#topbar .cluster-status {\n" - " padding: 20px 0;\n" - "}\n" - "#topbar .panel {\n" - " padding-left: 60px;\n" - " height: 47px;\n" - "}\n" - "#topbar .panel h5 {\n" - " font-family: \"Open Sans\";\n" - " font-size: 14px;\n" - " line-height: 15px;\n" - " margin: 0;\n" - " padding: 0;\n" - " margin-bottom: 4px;\n" - " color: #909090;\n" - "}\n" - "#topbar .panel h4 {\n" - " font-family: \"Copse\";\n" - " font-size: 18px;\n" - " line-height: 24px;\n" - " margin: 0;\n" - " padding: 0;\n" - " text-overflow: ellipsis;\n" - " overflow: hidden;\n" - " white-space: nowrap;\n" - " color: #4d535c;\n" - "}\n" - "#topbar .client-connection-status .panel {\n" - " background: url('images/status_panel-icon_1.png') no-repeat left center;\n" - "}\n" - "#topbar .client-connection-status .panel.error {\n" - " background: url('images/status_panel-icon_1-error.png') no-repeat left cen" - "ter;\n" - "}\n" - "#topbar .issues .panel {\n" - " background: url('images/status_panel-icon_2.png') no-repeat left center;\n" - "}\n" - "#topbar .issues .panel.error {\n" - " background: url('images/status_panel-icon_2-error.png') no-repeat left cen" - "ter;\n" - "}\n" - "#topbar .servers-connected .panel {\n" - " background: url('images/status_panel-icon_3.png') no-repeat left center;\n" - "}\n" - "#topbar .servers-connected .panel.error {\n" - " background: url('images/status_panel-icon_3-error.png') no-repeat left cen" - "ter;\n" - "}\n" - "#topbar .tables-available .panel {\n" - " background: url('images/status_panel-icon_4.png') no-repeat left center;\n" - "}\n" - "#topbar .tables-available .panel.error {\n" - " background: url('images/status_panel-icon_4-error.png') no-repeat left cen" - "ter;\n" - "}\n" - "#topbar .issues-banner .show-issues-banner {\n" - " margin-top: 8px;\n" - " color: #FFF;\n" - " position: relative;\n" - " background-color: #dd504e;\n" - " border-top: 1px solid #b54a48;\n" - " border-bottom: 1px solid #b54a48;\n" - "}\n" - "#topbar .issues-banner .show-issues-banner.no-issues {\n" - " background-color: #4E9258;\n" - " border-color: #41834a;\n" - "}\n" - "#topbar .issues-banner .show-issues-banner.no-issues:before {\n" - " background-color: #4E9258;\n" - " border-color: #41834a;\n" - "}\n" - "#topbar .issues-banner .show-issues-banner .message {\n" - " line-height: 42px;\n" - " margin: 0;\n" - " text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n" - " font-size: 15px;\n" - "}\n" - "#topbar .issues-banner .show-issues-banner .btn-resolve-issues {\n" - " float: right;\n" - " margin-left: 20px;\n" - " margin-top: 5.5px;\n" - "}\n" - "#topbar .issues-banner .show-issues-banner .gradient-overlay {\n" - " /* transform z-index hack */\n" - " -webkit-transform: rotate(0deg);\n" - " -moz-transform: rotate(0deg);\n" - " -o-transform: rotate(0deg);\n" - " -ms-transform: rotate(0deg);\n" - " transform: rotate(0deg);\n" - " background-color: #dd504e;\n" - "}\n" - "#topbar .issues-banner .show-issues-banner .gradient-overlay.no-issues {\n" - " background-color: #4E9258;\n" - "}\n" - "#topbar .issues-banner .show-issues-banner:before {\n" - " content: '';\n" - " position: absolute;\n" - " top: -7px;\n" - " left: 50%;\n" - " margin-left: -215px;\n" - " background: #dd504e;\n" - " display: block;\n" - " width: 14px;\n" - " height: 14px;\n" - " -webkit-transform: rotate(45deg);\n" - " -moz-transform: rotate(45deg);\n" - " -o-transform: rotate(45deg);\n" - " -ms-transform: rotate(45deg);\n" - " transform: rotate(45deg);\n" - " border: 1px solid #b54a48;\n" - "}\n" - "#topbar .all-issues {\n" - " display: none;\n" - " background-color: #fcfcfc;\n" - " border-bottom: 1px solid #ccc;\n" - "}\n" - "#topbar .all-issues #issue-alerts .alert {\n" - " display: block;\n" - " background: 15px center no-repeat url('images/resolve_issue-resolved_icon." - "png');\n" - " padding-left: 40px;\n" - " color: #628857;\n" - " border: 1px solid #cae8c3;\n" - " -webkit-border-radius: 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px;\n" - " background-clip: padding-box;\n" - " background-color: #effceb;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Overwrite modal\n" - " ----------------------------\n" - "*/\n" - ".overwrite_modal {\n" - " top: 0%;\n" - " left: 50%;\n" - " width: 900px;\n" - " padding: 20px;\n" - " max-height: 80%;\n" - " margin: 50px 0px 0px -470px;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Modal styles\n" - " ----------------------------\n" - "*/\n" - ".modal-backdrop {\n" - " opacity: 0.008;\n" - " filter: alpha(opacity=0.8);\n" - " -webkit-opacity: 0.8;\n" - " -moz-opacity: 0.8;\n" - " opacity: 0.8;\n" - "}\n" - ".add-table .hide_advanced_settings-link_container,\n" - ".add-table .advanced_settings {\n" - " display: none;\n" - "}\n" - ".modal p {\n" - " margin: 8px 0px;\n" - "}\n" - ".modal .alert {\n" - " margin-bottom: 15px;\n" - "}\n" - ".modal .alert_modal {\n" - " margin: 15px 15px 0 15px;\n" - "}\n" - ".modal .alert_modal p {\n" - " margin: 0;\n" - "}\n" - ".modal .alert_modal .close {\n" - " display: none;\n" - "}\n" - ".modal .modal-header h3 {\n" - " margin: 8px 0;\n" - " font-family: \"Copse\";\n" - " color: #a3a3a3;\n" - " font-size: 19px;\n" - "}\n" - ".modal .close_modal {\n" - " float: right;\n" - " font-weight: bold;\n" - " font-size: 24px;\n" - " color: black;\n" - " opacity: 0.002;\n" - " filter: alpha(opacity=0.2);\n" - " -webkit-opacity: 0.2;\n" - " -moz-opacity: 0.2;\n" - " opacity: 0.2;\n" - "}\n" - ".modal .close_modal:hover {\n" - " opacity: 0.004;\n" - " filter: alpha(opacity=0.4);\n" - " -webkit-opacity: 0.4;\n" - " -moz-opacity: 0.4;\n" - " opacity: 0.4;\n" - " text-decoration: none;\n" - "}\n" - ".modal .modal-body {\n" - " font-family: \"Open Sans\";\n" - " padding: 0px 15px 15px 15px;\n" - "}\n" - ".modal .modal-body hr {\n" - " border: 0px;\n" - " height: 1px;\n" - " background: #e9e9e9;\n" - " margin: 20px 0px 0px 0px;\n" - "}\n" - ".modal .large_input {\n" - " width: 294px;\n" - "}\n" - ".modal .error_verification {\n" - " float: right;\n" - " padding-right: 10px;\n" - " margin: 4px 70px 0px 0px;\n" - " color: #d95b5b;\n" - " font-weight: bold;\n" - " display: none;\n" - "}\n" - ".modal .first_question {\n" - " margin-top: 0px;\n" - "}\n" - "#server-view .recent-logs > h2.title {\n" - " border-bottom: thin solid #f0f0f0;\n" - " padding-bottom: 22px;\n" - " margin-bottom: 0;\n" - "}\n" - "#server-view .recent-logs .btn.view-logs {\n" - " float: right;\n" - "}\n" - "#server-view .recent-logs ul.log-entries {\n" - " margin-bottom: 5px;\n" - "}\n" - "#server-view .recent-logs h1 {\n" - " display: none;\n" - "}\n" - "#server-view .recent-logs #log-view {\n" - " padding: 0px;\n" - " border: 0px;\n" - " box-shadow: none;\n" - "}\n" - "#server-view .recent-logs #log-view .log-entry {\n" - " padding-top: 5px;\n" - " padding-bottom: 5px;\n" - " background-position: left center;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Server list\n" - " ----------------------------\n" - "*/\n" - "#databases_container .actions-bar {\n" - " float: right;\n" - "}\n" - "#databases_container .database_container {\n" - " padding: 0;\n" - "}\n" - "#databases_container .database_container .header_container {\n" - " border-bottom: thin solid #CCC;\n" - " padding: 13px 15px;\n" - " background: #fff;\n" - " border-radius: 4px 4px 0px 0px;\n" - "}\n" - "#databases_container .database_container .header_container .element-type,\n" - "#databases_container .database_container .header_container h3.name {\n" - " display: inline-block;\n" - " vertical-align: middle;\n" - "}\n" - "#databases_container .database_container .header_container .element-type {\n" - " background: #f7ae41;\n" - " border-radius: 2px;\n" - " padding: 4px 15px;\n" - " text-transform: uppercase;\n" - " margin-right: 20px;\n" - " color: #fff;\n" - " font-size: 10px;\n" - " font-weight: bold;\n" - "}\n" - "#databases_container .database_container .header_container h3.name {\n" - " font-family: \"Copse\";\n" - " font-size: 17px;\n" - " color: #61666e;\n" - " margin: 0;\n" - "}\n" - "#databases_container .database_container .header_container h3.name a {\n" - " color: #61666e;\n" - "}\n" - "#databases_container .database_container .header_container .buttons {\n" - " float: right;\n" - " margin-top: -2px;\n" - "}\n" - "#databases_container .database_container .element-list-container {\n" - " padding: 0px 13px 12px 13px;\n" - " background: #fafafa;\n" - " border-radius: 0px 0px 4px 4px;\n" - "}\n" - "#databases_container .database_container .table_container {\n" - " height: 44px;\n" - " background: #FFF;\n" - " border: thin solid #cfcfcf;\n" - " border-radius: 3px;\n" - " margin-top: 12px;\n" - " -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02), 0 1px 3px rgba(0, 0" - ", 0, 0.08);\n" - " -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02), 0 1px 3px rgba(0, 0, 0" - ", 0.08);\n" - " box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02), 0 1px 3px rgba(0, 0, 0, 0.0" - "8);\n" - "}\n" - "#databases_container .database_container .table_container .checkbox-containe" - "r {\n" - " float: left;\n" - "}\n" - "#databases_container .database_container .table_container .checkbox-containe" - "r label {\n" - " display: inline;\n" - " margin: 14px;\n" - "}\n" - "#databases_container .database_container .table_container .checkbox-containe" - "r input[type=\"checkbox\"] {\n" - " display: none;\n" - "}\n" - "#databases_container .database_container .table_container .checkbox-containe" - "r input[type=\"checkbox\"] + label {\n" - " border: thin solid #cfcfcf;\n" - " border-radius: 3px;\n" - " padding: 7px;\n" - " display: inline-block;\n" - " position: relative;\n" - "}\n" - "#databases_container .database_container .table_container .checkbox-containe" - "r input[type=\"checkbox\"] + label:active {\n" - " -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0 1px 3px rgba(0,0,0" - ",0.1);\n" - " -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0 1px 3px rgba(0,0,0,0." - "1);\n" - " box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0 1px 3px rgba(0,0,0,0.1);\n" - "}\n" - "#databases_container .database_container .table_container .checkbox-containe" - "r input[type=\"checkbox\"]:checked + label:after {\n" - " content: '\\2714';\n" - " font-size: 15px;\n" - " position: absolute;\n" - " top: -2px;\n" - " left: 1px;\n" - " color: #7c7c7c;\n" - "}\n" - "#databases_container .database_container .table_container .element-details {" - "\n" - " display: -webkit-box;\n" - " display: -moz-box;\n" - " display: -webkit-flex;\n" - " display: -ms-flexbox;\n" - " display: flex;\n" - " -ms-flex-direction: row;\n" - " -webkit-flex-direction: row;\n" - " flex-direction: row;\n" - " -ms-flex-pack: justify;\n" - " -webkit-justify-content: space-between;\n" - " justify-content: space-between;\n" - " -ms-flex-align: stretch;\n" - " -webkit-align-items: stretch;\n" - " align-items: stretch;\n" - " height: 100%;\n" - "}\n" - "#databases_container .database_container .table_container .element-details p" - " {\n" - " margin: 0;\n" - " font-family: \"Open Sans\";\n" - "}\n" - "#databases_container .database_container .table_container .element-details ." - "name,\n" - "#databases_container .database_container .table_container .element-details ." - "quick_info,\n" - "#databases_container .database_container .table_container .element-details ." - "status,\n" - "#databases_container .database_container .table_container .element-details ." - "table-lookup-by-id {\n" - " padding: 0 16px;\n" - " border-left: thin solid #e4e4e4;\n" - " display: -webkit-box;\n" - " display: -moz-box;\n" - " display: -webkit-flex;\n" - " display: -ms-flexbox;\n" - " display: flex;\n" - " -ms-flex-align: center;\n" - " -webkit-align-items: center;\n" - " align-items: center;\n" - " height: 100%;\n" - "}\n" - "#databases_container .database_container .table_container .element-details ." - "name {\n" - " font-size: 16px;\n" - " font-weight: bold;\n" - " overflow: hidden;\n" - " text-overflow: ellipsis;\n" - " -webkit-flex-grow: 2;\n" - " -moz-flex-grow: 2;\n" - " -ms-flex-grow: 2;\n" - " flex-grow: 2;\n" - "}\n" - "#databases_container .database_container .table_container .element-details ." - "name::before {\n" - " content: url('images/server-icon.png');\n" - " padding-right: 8px;\n" - " margin-top: 4px;\n" - "}\n" - "#databases_container .database_container .table_container .element-details ." - "name a {\n" - " color: #0597d8;\n" - "}\n" - "#databases_container .database_container .table_container .element-details ." - "quick_info {\n" - " color: #626264;\n" - " min-width: 165px;\n" - "}\n" - "#databases_container .database_container .table_container .element-details ." - "quick_info::before {\n" - " content: url('images/graph-icon.png');\n" - " padding-right: 8px;\n" - " margin-top: 4px;\n" - "}\n" - "#databases_container .database_container .table_container .element-details ." - "status {\n" - " color: #818181;\n" - " min-width: 120px;\n" - "}\n" - "#databases_container .database_container .table_container .element-details ." - "status.label-success::before {\n" - " content: url('images/green-light_glow.png');\n" - " padding-right: 8px;\n" - " margin-top: 4px;\n" - "}\n" - "#databases_container .database_container .table_container .element-details ." - "status.label-failure::before {\n" - " content: url('images/red-light_glow.png');\n" - " padding-right: 8px;\n" - " margin-top: 4px;\n" - "}\n" - "#databases_container .database_container .table_container .element-details ." - "status.label-partial-success::before {\n" - " content: url('images/yellow-light_glow.png');\n" - " padding-right: 8px;\n" - " margin-top: 4px;\n" - "}\n" - "#databases_container .database_container .table_container .element-details ." - "table-lookup-by-id input {\n" - " height: 18px;\n" - " width: 150px;\n" - " margin: 5px;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Server list and table list\n" - " ----------------------------\n" - "*/\n" - "#table-list .actions-bar {\n" - " float: right;\n" - "}\n" - "#table-list .section {\n" - " padding: 0;\n" - "}\n" - "#table-list .section .header_container {\n" - " border-bottom: thin solid #CCC;\n" - " padding: 13px 15px;\n" - " background: #fff;\n" - " -webkit-border-radius: 4px 4px 0px 0px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 4px 4px 0px 0px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 4px 4px 0px 0px;\n" - " background-clip: padding-box;\n" - "}\n" - "#table-list .section .header_container .summary .element-type,\n" - "#table-list .section .header_container .summary h3.name {\n" - " display: inline-block;\n" - " vertical-align: middle;\n" - "}\n" - "#table-list .section .header_container .summary .element-type {\n" - " background: #f7ae41;\n" - " -webkit-border-radius: 2px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 2px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 2px;\n" - " background-clip: padding-box;\n" - " padding: 4px 15px;\n" - " text-transform: uppercase;\n" - " margin-right: 20px;\n" - " color: #fff;\n" - " font-size: 10px;\n" - " font-weight: bold;\n" - "}\n" - "#table-list .section .header_container .summary h3.name {\n" - " font-family: \"Copse\";\n" - " font-size: 17px;\n" - " color: #61666e;\n" - " margin: 0;\n" - "}\n" - "#table-list .section .header_container .summary h3.name a {\n" - " color: #61666e;\n" - "}\n" - "#table-list .section .header_container .summary .buttons {\n" - " float: right;\n" - " margin-top: -2px;\n" - "}\n" - "#table-list .section .element-list-container {\n" - " padding: 0px 13px 12px 13px;\n" - " background: #fafafa;\n" - " -webkit-border-radius: 0px 0px 4px 4px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 0px 0px 4px 4px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 0px 0px 4px 4px;\n" - " background-clip: padding-box;\n" - "}\n" - "#table-list .section .element {\n" - " height: 44px;\n" - " background: #FFF;\n" - " border: thin solid #cfcfcf;\n" - " -webkit-border-radius: 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px;\n" - " background-clip: padding-box;\n" - " margin-top: 12px;\n" - " -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02), 0 1px 3px rgba(0, 0" - ", 0, 0.08);\n" - " -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02), 0 1px 3px rgba(0, 0, 0" - ", 0.08);\n" - " box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02), 0 1px 3px rgba(0, 0, 0, 0.0" - "8);\n" - "}\n" - "#table-list .section .element .checkbox-container {\n" - " float: left;\n" - "}\n" - "#table-list .section .element .checkbox-container label {\n" - " display: inline;\n" - " margin: 14px;\n" - "}\n" - "#table-list .section .element .checkbox-container input[type=\"checkbox\"] {" - "\n" - " display: none;\n" - "}\n" - "#table-list .section .element .checkbox-container input[type=\"checkbox\"] +" - " label {\n" - " border: thin solid #cfcfcf;\n" - " -webkit-border-radius: 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px;\n" - " background-clip: padding-box;\n" - " padding: 7px;\n" - " display: inline-block;\n" - " position: relative;\n" - "}\n" - "#table-list .section .element .checkbox-container input[type=\"checkbox\"] +" - " label:active {\n" - " -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0 1px 3px rgba(0,0,0" - ",0.1);\n" - " -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0 1px 3px rgba(0,0,0,0." - "1);\n" - " box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0 1px 3px rgba(0,0,0,0.1);\n" - "}\n" - "#table-list .section .element .checkbox-container input[type=\"checkbox\"]:c" - "hecked + label:after {\n" - " content: '\\2714';\n" - " font-size: 15px;\n" - " position: absolute;\n" - " top: -2px;\n" - " left: 1px;\n" - " color: #7c7c7c;\n" - "}\n" - "#table-list .section .element .element-details {\n" - " position: relative;\n" - " margin-left: 45px;\n" - "}\n" - "#table-list .section .element .element-details p {\n" - " margin: 0;\n" - " font-family: \"Open Sans\";\n" - "}\n" - "#table-list .section .element .element-details .name,\n" - "#table-list .section .element .element-details .quick_info,\n" - "#table-list .section .element .element-details .status {\n" - " position: absolute;\n" - " line-height: 28px;\n" - " margin-top: 8px;\n" - " border-left: thin solid #e4e4e4;\n" - "}\n" - "#table-list .section .element .element-details .name {\n" - " background: 20px center no-repeat url('images/server-icon.png');\n" - " padding-left: 58px;\n" - " font-size: 16px;\n" - " font-weight: bold;\n" - " width: 400px;\n" - " height: 28px;\n" - " overflow: hidden;\n" - " text-overflow: ellipsis;\n" - "}\n" - "#table-list .section .element .element-details .name a {\n" - " color: #0597d8;\n" - "}\n" - "#table-list .section .element .element-details .quick_info {\n" - " background: 20px center no-repeat url('images/graph-icon.png');\n" - " color: #626264;\n" - " padding-left: 50px;\n" - "}\n" - "#table-list .section .element .element-details .status {\n" - " padding-right: 20px;\n" - " color: #818181;\n" - "}\n" - "#table-list .section .element .element-details .status .label-success {\n" - " background: 20px center no-repeat url('images/green-light_glow.png');\n" - " padding-left: 43px;\n" - "}\n" - "#table-list .section .element .element-details .status .label-failure {\n" - " background: 20px center no-repeat url('images/red-light_glow.png');\n" - " padding-left: 43px;\n" - "}\n" - "#server-list .element-details .quick_info {\n" - " left: 475px;\n" - "}\n" - "#server-list .element-details .status {\n" - " left: 725px;\n" - "}\n" - "#table-list .element-details .quick_info {\n" - " left: 575px;\n" - "}\n" - "#table-list .element-details .status {\n" - " left: 775px;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Log view\n" - " ----------------------------\n" - "*/\n" - "#log-view ul.log-entries {\n" - " list-style-type: none;\n" - " padding-left: 0;\n" - " margin-top: 0;\n" - "}\n" - "#log-view ul.log-entries li.log-entry {\n" - " font-size: 14px;\n" - " color: #4d535c;\n" - " border-top: thin solid #f0f0f0;\n" - " background: left 22px no-repeat url('images/pencil-icon_big.png');\n" - " padding: 20px 0 20px 55px;\n" - " word-wrap: break-word;\n" - "}\n" - "#log-view ul.log-entries li.log-entry:first-child {\n" - " border-top: 0;\n" - "}\n" - "#log-view ul.log-entries li.log-entry p {\n" - " margin: 0;\n" - " font-family: \"Open Sans\";\n" - "}\n" - "#log-view ul.log-entries li.log-entry .message {\n" - " font-size: 14px;\n" - "}\n" - "#log-view ul.log-entries li.log-entry li.pretty_logs_li {\n" - " font-size: 13px;\n" - "}\n" - "#log-view ul.log-entries li.log-entry .from-server {\n" - " font-style: italic;\n" - " margin-top: 5px;\n" - " font-size: 13px;\n" - "}\n" - "#log-view ul.log-entries li.log-entry .datetime {\n" - " background: left center no-repeat url('images/clock-icon.png');\n" - " text-align: right;\n" - " color: #c4c4c4;\n" - " padding-left: 20px;\n" - "}\n" - "#log-view .no-logs {\n" - " display: none;\n" - "}\n" - "#log-view .no-more-entries {\n" - " font-family: \"Open Sans\";\n" - " display: none;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Data explorer\n" - " ----------------------------\n" - "*/\n" - "/* Prevent the user from selecting something when he's resizing a div */\n" - ".resizing {\n" - " -o-user-select: none;\n" - " user-select: none;\n" - " -webkit-user-select: none;\n" - " -moz-user-select: none;\n" - " -ms-user-select: none;\n" - "}\n" - "#dataexplorer {\n" - " margin: 0px;\n" - "}\n" - "#dataexplorer .options_container {\n" - " float: right;\n" - " margin-top: 5px;\n" - " /* Selected icons from http://somerandomdude.com/work/iconic/ */\n" - "}\n" - "#dataexplorer .options_container .option_icon {\n" - " width: 16px;\n" - " height: 16px;\n" - " opacity: 0.3;\n" - "}\n" - "#dataexplorer .options_container .fullscreen {\n" - " background: url('images/fullscreen_16x16.png') center no-repeat;\n" - "}\n" - "#dataexplorer .options_container .fullscreen_exit {\n" - " background: url('images/fullscreen_exit_16x16.png') center no-repeat;\n" - "}\n" - "#dataexplorer .options_container .show_options {\n" - " background: url('images/cog_16x16.png') center no-repeat;\n" - "}\n" - "#dataexplorer .options_container .show_history {\n" - " background: url('images/book_alt_16x16.png') center no-repeat;\n" - " float: left;\n" - " margin: 0px 8px 0px -1px;\n" - "}\n" - "#dataexplorer .options_container .clear_queries_link {\n" - " display: none;\n" - "}\n" - "#dataexplorer .options_container .clear_queries_link .clear_history {\n" - " background: url('images/trash_stroke_16x16.png') center no-repeat;\n" - " float: left;\n" - " margin: 0px 8px 0px -1px;\n" - "}\n" - "#dataexplorer .options_container .close_queries_link.active,\n" - "#dataexplorer .options_container .toggle_options_link.active {\n" - " background-color: #f2f2f2;\n" - " -webkit-box-shadow: inset 0 2px 1px rgba(0,0,0,.06);\n" - " -moz-box-shadow: inset 0 2px 1px rgba(0,0,0,.06);\n" - " box-shadow: inset 0 2px 1px rgba(0,0,0,.06);\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0icmdiKDI1NSwyNTUsMjU1KSIgc3RvcC1vcGFjaXR5PSIuMD" - "IiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9InJnYigwLDAsMCkiIHN0b3Atb3BhY2" - "l0eT0iLjAyIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaG" - "VpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPjwvc3ZnPg==);\n" - " background-image: -moz-linear-gradient(bottom, rgba(255,255,255,.02) 0%, r" - "gba(0,0,0,.02) 100%);\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - "0%,rgba(255,255,255,.02)),color-stop(100%,rgba(0,0,0,.02)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(255,255,255,.02) 0%" - ", rgba(0,0,0,.02) 100%);\n" - " background-image: -o-linear-gradient(bottom, rgba(255,255,255,.02) 0%, rgb" - "a(0,0,0,.02) 100%);\n" - " background-image: linear-gradient(bottom, rgba(255,255,255,.02) 0%, rgba(0" - ",0,0,.02) 100%);\n" - "}\n" - "#dataexplorer h1.title.small_margin_bottom {\n" - " margin-bottom: 15px;\n" - "}\n" - "#dataexplorer .input_query_full_container {\n" - " clear: both;\n" - " position: relative;\n" - " width: 100%;\n" - "}\n" - "#dataexplorer .input_query_full_container .suggestion_description_arrow {\n" - " display: none;\n" - " position: absolute;\n" - " z-index: 201;\n" - " top: 10px;\n" - " left: 0px;\n" - " width: 0;\n" - " height: 0;\n" - " border-left: 10px solid transparent;\n" - " border-right: 10px solid transparent;\n" - " border-bottom: 10px solid #bbb;\n" - "}\n" - "#dataexplorer .input_query_full_container .suggestion_description_arrow_over" - "lay {\n" - " display: none;\n" - " position: absolute;\n" - " z-index: 202;\n" - " top: 11px;\n" - " left: 0px;\n" - " width: 0;\n" - " height: 0;\n" - " border-left: 10px solid transparent;\n" - " border-right: 10px solid transparent;\n" - " border-bottom: 10px solid #fff;\n" - "}\n" - "#dataexplorer .input_query_full_container .suggestion_full_container {\n" - " position: absolute;\n" - " width: 452px;\n" - " top: 20px;\n" - " left: 0px;\n" - " z-index: 100;\n" - " font-family: monospace;\n" - " font-size: 11px;\n" - "}\n" - "#dataexplorer .input_query_full_container .suggestion_full_container .sugges" - "tion_name_list {\n" - " background: #fff;\n" - " display: none;\n" - " width: 452px;\n" - " padding: 0px;\n" - " margin: 0px 0px 9px 0px;\n" - " border: 1px solid #d7d7d7;\n" - " -webkit-border-radius: 4px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 4px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 4px;\n" - " background-clip: padding-box;\n" - " -webkit-box-shadow: inset 0 -6px 6px -6px #ddd;\n" - " -moz-box-shadow: inset 0 -6px 6px -6px #ddd;\n" - " box-shadow: inset 0 -6px 6px -6px #ddd;\n" - "}\n" - "#dataexplorer .input_query_full_container .suggestion_full_container .sugges" - "tion_name_list .suggestion_name_li {\n" - " display: inline-block;\n" - " margin: 5px 0px;\n" - " padding: 0px 5px;\n" - " width: 132px;\n" - " height: 18px;\n" - " color: #23a5df;\n" - " cursor: pointer;\n" - " text-overflow: ellipsis;\n" - "}\n" - "#dataexplorer .input_query_full_container .suggestion_full_container .sugges" - "tion_name_list .suggestion_name_li_hl {\n" - " color: #005388;\n" - "}\n" - "#dataexplorer .input_query_full_container .suggestion_full_container .sugges" - "tion_description {\n" - " display: none;\n" - " width: 442px;\n" - " padding: 5px;\n" - " margin: 0px;\n" - " background: #fcfcfc;\n" - " border: 1px solid #d7d7d7;\n" - " color: #9ea2a8;\n" - " font-family: monospace;\n" - " font-size: 11px;\n" - " word-wrap: break-word;\n" - " -webkit-border-radius: 4px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 4px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 4px;\n" - " background-clip: padding-box;\n" - " -webkit-box-shadow: inset 0 -6px 6px -6px #ddd;;\n" - " -moz-box-shadow: inset 0 -6px 6px -6px #ddd;;\n" - " box-shadow: inset 0 -6px 6px -6px #ddd;;\n" - "}\n" - "#dataexplorer .input_query_full_container .suggestion_full_container .sugges" - "tion_description .separation {\n" - " height: 1px;\n" - " border-top: 1px solid #dadada;\n" - " margin: 3px auto;\n" - " width: 300px;\n" - "}\n" - "#dataexplorer .input_query_full_container .suggestion_full_container .sugges" - "tion_description p,\n" - "#dataexplorer .input_query_full_container .suggestion_full_container .sugges" - "tion_description code {\n" - " font-family: monospace;\n" - " font-size: 11px;\n" - " margin: 3px 0px;\n" - "}\n" - "#dataexplorer .input_query_full_container .suggestion_full_container .sugges" - "tion_description p .description_name,\n" - "#dataexplorer .input_query_full_container .suggestion_full_container .sugges" - "tion_description code .description_name {\n" - " color: #222;\n" - " font-weight: bold;\n" - "}\n" - "#dataexplorer .input_query_full_container .suggestion_full_container .sugges" - "tion_description p .description_arguments,\n" - "#dataexplorer .input_query_full_container .suggestion_full_container .sugges" - "tion_description code .description_arguments {\n" - " color: #444;\n" - "}\n" - "#dataexplorer .input_query_full_container .suggestion_full_container .sugges" - "tion_description strong {\n" - " color: #666;\n" - "}\n" - "#dataexplorer .input_query_full_container .suggestion_full_container .sugges" - "tion_description .grouped_data_warning {\n" - " font-family: monospace;\n" - " font-size: 11px;\n" - " background: #f8f8f8;\n" - " color: #666;\n" - " padding: 5px 6px;\n" - " border: 1px solid #e4e4e4;\n" - " margin: 5px 0px;\n" - " -webkit-border-radius: 3px 3px 3px 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px 3px 3px 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px 3px 3px 3px;\n" - " background-clip: padding-box;\n" - "}\n" - "#dataexplorer .input_query_full_container .input_query_container {\n" - " font-family: monospace;\n" - " font-size: 13px;\n" - " padding: 0px;\n" - " margin: 10px 0px;\n" - " overflow: auto;\n" - "}\n" - "#dataexplorer .input_query_full_container .input_query_container .button_que" - "ry {\n" - " float: right;\n" - " width: 70px;\n" - "}\n" - "#dataexplorer .input_query_full_container .input_query_container .abort_quer" - "y {\n" - " display: none;\n" - "}\n" - "#dataexplorer .input_query_full_container .input_query_container .CodeMirror" - " {\n" - " background: #f8f8f8;\n" - " -ms-box-sizing: border-box;\n" - " -webkit-box-sizing: border-box;\n" - " -moz-box-sizing: border-box;\n" - " box-sizing: border-box;\n" - " border: 1px solid #e3e3e3;\n" - " cursor: text;\n" - "}\n" - "#dataexplorer .input_query_full_container .input_query_container .CodeMirror" - " .CodeMirror-gutter {\n" - " background: #f1f1f1;\n" - " border-right: 1px solid #e3e3e3;\n" - " cursor: auto;\n" - "}\n" - "#dataexplorer .input_query_full_container .loading_query {\n" - " text-align: center;\n" - " height: 16px;\n" - " width: 16px;\n" - " padding: 0px;\n" - " margin: -30px auto 10px auto;\n" - "}\n" - "#dataexplorer .input_query_full_container .loading_query .loading_query_img " - "{\n" - " display: none;\n" - "}\n" - "#dataexplorer .toggled_container .arrow_dataexplorer {\n" - " display: none;\n" - " float: right;\n" - " width: 0;\n" - " height: 0;\n" - " border-left: 6px solid transparent;\n" - " border-right: 6px solid transparent;\n" - " border-bottom: 6px solid #aaa;\n" - "}\n" - "#dataexplorer .toggled_container .nano {\n" - " visibility: hidden;\n" - " height: 0px;\n" - " padding: 0px 15px 0px 0px;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container {\n" - " display: none;\n" - " font-family: monospace;\n" - " font-size: 13px;\n" - " width: 100%;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list {\n" - " margin: 0px 15px 0px 0px;\n" - " list-style: none;\n" - " padding: 0px;\n" - " width: 100%;\n" - " display: -webkit-box;\n" - " display: -moz-box;\n" - " display: -webkit-flex;\n" - " display: -ms-flexbox;\n" - " display: flex;\n" - " -ms-flex-direction: column;\n" - " -webkit-flex-direction: column;\n" - " flex-direction: column;\n" - " overflow: hidden;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .query_history {\n" - " margin: 0px;\n" - " padding: 0px;\n" - " overflow: auto;\n" - " border-bottom: 1px solid #e4e4e4;\n" - " width: 100%;\n" - " color: #999;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .query_history:hover {\n" - " color: #444;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .query_history.displayed {\n" - " display: auto;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .query_history.hidden {\n" - " display: none;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .query_history:last-child {\n" - " margin: 0px;\n" - " border-bottom: 0px;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .query_history .query_history_content {\n" - " overflow: auto;\n" - " margin: 10px;\n" - " display: -webkit-box;\n" - " display: -moz-box;\n" - " display: -webkit-flex;\n" - " display: -ms-flexbox;\n" - " display: flex;\n" - " -ms-flex-direction: row;\n" - " -webkit-flex-direction: row;\n" - " flex-direction: row;\n" - " -ms-flex-pack: justify;\n" - " -webkit-justify-content: space-between;\n" - " justify-content: space-between;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .query_history .query_history_content .right_container {\n" - " width: 100%;\n" - " display: -webkit-box;\n" - " display: -moz-box;\n" - " display: -webkit-flex;\n" - " display: -ms-flexbox;\n" - " display: flex;\n" - " -ms-flex-pack: end;\n" - " -webkit-justify-content: flex-end;\n" - " justify-content: flex-end;\n" - " -ms-flex-align: flex-end;\n" - " -webkit-align-items: flex-end;\n" - " align-items: flex-end;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .query_history .query_history_content .right_container .btn {\n" - " padding: 2px 10px;\n" - " margin: 10px;\n" - " -webkit-box-shadow: 0px 0px 0px 0px #fff;\n" - " -moz-box-shadow: 0px 0px 0px 0px #fff;\n" - " box-shadow: 0px 0px 0px 0px #fff;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .query_history .query_history_content .query {\n" - " font-size: 13px;\n" - " margin: 0px;\n" - " font-family: monospace;\n" - " width: 100%;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .query_history .query_history_content .broken_query {\n" - " margin: 10px;\n" - " visibility: hidden;\n" - " height: 19px;\n" - " width: 22px;\n" - " opacity: 0.2;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .query_history .query_id {\n" - " margin-right: 1em;\n" - " width: 4em;\n" - " text-align: center;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .query_history .btn {\n" - " visibility: hidden;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .query_history:hover .btn,\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .query_history:hover .broken_query {\n" - " visibility: visible;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .no_history {\n" - " word-break: break-word;\n" - " white-space: pre;\n" - " margin: 0px;\n" - " padding: 11px 4px;\n" - " margin: 0px 0px 0px 26px;\n" - " color: #aaa;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .no_history.displayed {\n" - " display: block;\n" - "}\n" - "#dataexplorer .toggled_container .nano .content .history_container .history_" - "list .no_history.hidden {\n" - " display: none;\n" - "}\n" - "#dataexplorer .toggled_container .nano_border {\n" - " display: none;\n" - " clear: both;\n" - " width: 100%;\n" - " border-top: 2px solid #aaa;\n" - "}\n" - "#dataexplorer .toggled_container .nano_border_bottom {\n" - " cursor: s-resize;\n" - " padding: 0px 0px 2px 0px;\n" - "}\n" - "#dataexplorer .arrow_history {\n" - " margin-right: 154px;\n" - "}\n" - "#dataexplorer .arrow_options {\n" - " margin-right: 74px;\n" - "}\n" - "#dataexplorer .options_view ul {\n" - " list-style: none;\n" - " margin: 0px;\n" - " padding: 0px 0px 0px 0px;\n" - "}\n" - "#dataexplorer .options_view ul li {\n" - " clear: both;\n" - " margin: 0px;\n" - " padding: 0px 0px 0px 13px;\n" - " overflow: auto;\n" - " border-bottom: 1px solid #e4e4e4;\n" - " color: #999;\n" - "}\n" - "#dataexplorer .options_view ul li.displayed {\n" - " display: block;\n" - "}\n" - "#dataexplorer .options_view ul li.hidden {\n" - " display: none;\n" - "}\n" - "#dataexplorer .options_view ul li:last-child {\n" - " margin: 0px;\n" - " border-bottom: 0px;\n" - "}\n" - "#dataexplorer .options_view ul li .option_description {\n" - " margin: 0px;\n" - " padding: 9px 4px;\n" - " font-family: monospace;\n" - " font-size: 13px;\n" - "}\n" - "#dataexplorer .options_view ul li .input_description {\n" - " float: left;\n" - " margin-left: 10px;\n" - " padding: 9px 4px;\n" - " font-family: monospace;\n" - " font-size: 13px;\n" - "}\n" - "#dataexplorer .options_view ul li:hover {\n" - " color: #444;\n" - "}\n" - "#dataexplorer .options_view ul li .alert {\n" - " display: none;\n" - " visibility: hidden;\n" - " float: right;\n" - " margin: 3px 15px 3px 0px;\n" - " padding: 5px 25px 5px 10px;\n" - " font-family: \"Open Sans\";\n" - " display: block;\n" - "}\n" - "#dataexplorer .options_view .input-container {\n" - " float: left;\n" - "}\n" - "#dataexplorer .options_view .input-container input {\n" - " float: left;\n" - " height: 18px;\n" - " width: 60px;\n" - " text-align: center;\n" - " margin: 4px 0px 0px 0px;\n" - "}\n" - "#dataexplorer .options_view .checkbox-container {\n" - " float: left;\n" - " margin-left: 2px;\n" - "}\n" - "#dataexplorer .options_view .checkbox-container label {\n" - " display: inline;\n" - " margin: 14px;\n" - "}\n" - "#dataexplorer .options_view .checkbox-container input[type=\"checkbox\"] {\n" - " display: none;\n" - "}\n" - "#dataexplorer .options_view .checkbox-container input[type=\"checkbox\"] + l" - "abel {\n" - " border: thin solid #cfcfcf;\n" - " -webkit-border-radius: 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px;\n" - " background-clip: padding-box;\n" - " padding: 7px;\n" - " display: inline-block;\n" - " position: relative;\n" - " margin: 10px 17px 0px 0px;\n" - "}\n" - "#dataexplorer .options_view .checkbox-container input[type=\"checkbox\"] + l" - "abel:active {\n" - " -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0 1px 3px rgba(0,0,0" - ",0.1);\n" - " -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0 1px 3px rgba(0,0,0,0." - "1);\n" - " box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0 1px 3px rgba(0,0,0,0.1);\n" - "}\n" - "#dataexplorer .options_view .checkbox-container input[type=\"checkbox\"]:che" - "cked + label:after {\n" - " content: '\\2714';\n" - " font-size: 15px;\n" - " position: absolute;\n" - " top: -2px;\n" - " left: 1px;\n" - " color: #7c7c7c;\n" - "}\n" - "#dataexplorer .result_view {\n" - " margin: 35px 0px 0px 0px;\n" - " overflow: auto;\n" - "}\n" - "#dataexplorer .result_view.unpaginated {\n" - " overflow: visible;\n" - "}\n" - "#dataexplorer .result_view .tab-content {\n" - " clear: both;\n" - "}\n" - "#dataexplorer .result_view .error {\n" - " font-family: \"Open Sans\";\n" - "}\n" - "#dataexplorer .result_view .error_title {\n" - " font-weight: bold;\n" - "}\n" - "#dataexplorer .result_view .error_details {\n" - " font-family: monospace;\n" - " font-size: 11px;\n" - " padding: 7px 10px;\n" - " border: 1px solid #CCC;\n" - " border-radius: 3px;\n" - " background: #EEE;\n" - " white-space: pre-wrap;\n" - " /* css-3 */\n" - " white-space: -moz-pre-wrap;\n" - " /* Mozilla, since 1999 */\n" - " white-space: -pre-wrap;\n" - " /* Opera 4-6 */\n" - " white-space: -o-pre-wrap;\n" - " /* Opera 7 */\n" - " word-wrap: break-word;\n" - " /* Internet Explorer 5.5+, 6, 7, 8 compability-mode */\n" - " -ms-word-break: break-all;\n" - " /* Internet Explorer 8 */\n" - " word-break: break-word;\n" - "}\n" - "#dataexplorer .result_view .query {\n" - " font-family: monospace;\n" - " font-size: 11px;\n" - " padding: 7px 10px;\n" - " border: 1px solid #ccc;\n" - " border-radius: 3px;\n" - " background: #eee;\n" - "}\n" - "#dataexplorer .result_view .results_header .metadata {\n" - " font-family: \"Open Sans\";\n" - " width: 530px;\n" - " float: left;\n" - " margin: -30px 0px 10px 0px;\n" - "}\n" - "#dataexplorer .result_view .results_header .metadata .execution_time {\n" - " border-bottom: 1px dotted #444;\n" - " cursor: help;\n" - "}\n" - "#dataexplorer .result_view .results_header .metadata.feed_unpaused .only_if_" - "paused {\n" - " display: none !important;\n" - "}\n" - "#dataexplorer .result_view .results_header .metadata.feed_paused .only_if_un" - "paused {\n" - " display: none !important;\n" - "}\n" - "#dataexplorer .result_view .results_header .metadata .pause_feed,\n" - "#dataexplorer .result_view .results_header .metadata .unpause_feed {\n" - " opacity: 0.55;\n" - " position: relative;\n" - " top: 2px;\n" - " display: inline-block;\n" - " width: 14px;\n" - " height: 14px;\n" - " margin-right: 4px;\n" - " background-repeat: no-repeat;\n" - "}\n" - "#dataexplorer .result_view .results_header .metadata .pause_feed {\n" - " background-image: url(images/icon-pause.png);\n" - "}\n" - "#dataexplorer .result_view .results_header .metadata .unpause_feed {\n" - " background-image: url(images/icon-play.png);\n" - "}\n" - "#dataexplorer .result_view .results_header .metadata .animated-ellipsis {\n" - " margin-right: 6px;\n" - "}\n" - "#dataexplorer .result_view .results_header .metadata.floating_metadata {\n" - " z-index: 2000;\n" - " background-color: #f0f0f0;\n" - " border: 1px solid #cfcfcf;\n" - " position: fixed;\n" - " display: block;\n" - " top: -5px;\n" - " margin: 0 0 0 -9px;\n" - " padding: 10px 8px 5px 8px;\n" - " background-color: white;\n" - " border-radius: 5px;\n" - " -webkit-box-shadow: 2px 2px 10px -5px rgba(0,0,0,0.2);\n" - " -moz-box-shadow: 2px 2px 10px -5px rgba(0,0,0,0.2);\n" - " box-shadow: 2px 2px 10px -5px rgba(0,0,0,0.2);\n" - "}\n" - "#dataexplorer .result_view .more_results {\n" - " float: right;\n" - "}\n" - "#dataexplorer .result_view .more_results .more_results_paragraph {\n" - " margin: 0px;\n" - " padding: 0px;\n" - "}\n" - "#dataexplorer .result_view .more_results.hidden {\n" - " display: none;\n" - "}\n" - "#dataexplorer .result_view .info {\n" - " text-align: left;\n" - " margin: 7px 0px 0px 0px;\n" - " padding: 7px 19px 7px 15px;\n" - " border-left: 4px solid #9FCADF;\n" - " background: #ecf2f7;\n" - " font-family: \"Open Sans\";\n" - "}\n" - "#dataexplorer .result_view .activate_profiler_message {\n" - " background: #f7f7f7;\n" - " border: thin solid #E7E6E6;\n" - " padding: 4px 30px;\n" - " -webkit-border-radius: 4px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 4px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 4px;\n" - " background-clip: padding-box;\n" - "}\n" - "#dataexplorer .result_view .activate_profiler_message p {\n" - " margin: 18px 0;\n" - " font-family: 'Open Sans', sans-serif;\n" - " font-size: 16px;\n" - " color: #686868;\n" - " text-align: center;\n" - "}\n" - "#dataexplorer .result_view .activate_profiler_message p a {\n" - " border-bottom: thin dotted #0088cc;\n" - "}\n" - "#dataexplorer .result_view .activate_profiler_message p a:hover {\n" - " text-decoration: none;\n" - "}\n" - "#dataexplorer .change_view_container {\n" - " margin: 0px;\n" - " padding: 0px;\n" - " width: 100%;\n" - " float: right;\n" - "}\n" - "#dataexplorer .change_view_container li {\n" - " float: right;\n" - " position: relative;\n" - "}\n" - "#dataexplorer .change_view_container li:first-child {\n" - " margin-right: 10px;\n" - "}\n" - "#dataexplorer .change_view_container li.active a {\n" - " border-bottom-color: #FBFBFB;\n" - " background-color: #FBFBFB;\n" - "}\n" - "#dataexplorer .wrapper_scrollbar {\n" - " position: fixed;\n" - " bottom: 0px;\n" - " overflow-x: scroll;\n" - " overflow-y: hidden;\n" - " width: 888px;\n" - " z-index: 1000;\n" - "}\n" - "#dataexplorer .wrapper_scrollbar .scrollbar_fake_content {\n" - " height: 20px;\n" - "}\n" - "#dataexplorer .results {\n" - " margin: 15px 0px 0px 0px;\n" - " padding: 0px;\n" - " font-family: \"Open Sans\";\n" - " overflow: auto;\n" - " width: 100%;\n" - "}\n" - "#dataexplorer .results.table_viewer_container {\n" - " overflow: visible;\n" - "}\n" - "#dataexplorer .results .profile_summary {\n" - " margin-bottom: 20px;\n" - " background: #f7f7f7;\n" - " border: thin solid #E7E6E6;\n" - " padding: 4px 30px;\n" - " -webkit-border-radius: 4px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 4px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 4px;\n" - " background-clip: padding-box;\n" - "}\n" - "#dataexplorer .results .profile_summary p {\n" - " margin: 18px 0;\n" - " font-family: 'Open Sans', sans-serif;\n" - " font-size: 15px;\n" - " color: #72bddf;\n" - "}\n" - "#dataexplorer .results .profile_summary p span.big {\n" - " font-size: 24px;\n" - " line-height: 24px;\n" - " color: #5c6169;\n" - "}\n" - "#dataexplorer .results .profile_summary p:before {\n" - " content: '';\n" - " display: inline-block;\n" - " width: 16px;\n" - " height: 16px;\n" - " margin-right: 14px;\n" - "}\n" - "#dataexplorer .results .profile_summary p.query-duration-roundtrip {\n" - " text-align: left;\n" - "}\n" - "#dataexplorer .results .profile_summary p.query-duration-roundtrip:before {\n" - " background: left center no-repeat url('images/query_round_trip-icon.png');" - "\n" - "}\n" - "#dataexplorer .results .profile_summary p.query-duration-server {\n" - " text-align: center;\n" - "}\n" - "#dataexplorer .results .profile_summary p.query-duration-server:before {\n" - " background: left center no-repeat url('images/query_server_time-icon.png')" - ";\n" - "}\n" - "#dataexplorer .results .profile_summary p.query-tasks {\n" - " text-align: right;\n" - "}\n" - "#dataexplorer .results .profile_summary p.query-tasks:before {\n" - " background: left center no-repeat url('images/query_shard_access-icon.png'" - ");\n" - "}\n" - "#dataexplorer .results .copy_profile {\n" - " float: right;\n" - "}\n" - "#dataexplorer .results .copy_profile:after {\n" - " content: '';\n" - " display: block;\n" - " float: left;\n" - " width: 16px;\n" - " height: 16px;\n" - " margin-right: 9px;\n" - " background: url('images/copy_to_clipboard_16x16.png') left center no-repea" - "t;\n" - "}\n" - "#dataexplorer .results .copy_profile.zeroclipboard-is-hover {\n" - " -webkit-box-shadow: inset 0 0 1px 1px #fff;\n" - " -moz-box-shadow: inset 0 0 1px 1px #fff;\n" - " box-shadow: inset 0 0 1px 1px #fff;\n" - " background-image: -moz-linear-gradient(rgba(255, 255, 255, 0.96), rgba(255" - ", 255, 255, 0.96)),-moz-linear-gradient(bottom, rgba(51, 51, 51, 0.23) 0%, r" - "gba(255, 255, 255, 0.23) 100%);\n" - " background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.96), rgba(" - "255, 255, 255, 0.96)),-webkit-linear-gradient(bottom, rgba(51, 51, 51, 0.23)" - " 0%, rgba(255, 255, 255, 0.23) 100%);\n" - " background-image: -o-linear-gradient(rgba(255, 255, 255, 0.96), rgba(255, " - "255, 255, 0.96)),-o-linear-gradient(bottom, rgba(51, 51, 51, 0.23) 0%, rgba(" - "255, 255, 255, 0.23) 100%);\n" - " background-image: linear-gradient(rgba(255, 255, 255, 0.96), rgba(255, 255" - ", 255, 0.96)),linear-gradient(bottom, rgba(51, 51, 51, 0.23) 0%, rgba(255, 2" - "55, 255, 0.23) 100%);\n" - "}\n" - "#dataexplorer .results .copy_profile.zeroclipboard-is-active {\n" - " background-color: #f2f2f2;\n" - " -webkit-box-shadow: inset 0 2px 1px rgba(0,0,0,.06);\n" - " -moz-box-shadow: inset 0 2px 1px rgba(0,0,0,.06);\n" - " box-shadow: inset 0 2px 1px rgba(0,0,0,.06);\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0icmdiKDI1NSwyNTUsMjU1KSIgc3RvcC1vcGFjaXR5PSIuMD" - "IiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9InJnYigwLDAsMCkiIHN0b3Atb3BhY2" - "l0eT0iLjAyIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaG" - "VpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPjwvc3ZnPg==);\n" - " background-image: -moz-linear-gradient(bottom, rgba(255,255,255,.02) 0%, r" - "gba(0,0,0,.02) 100%);\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - "0%,rgba(255,255,255,.02)),color-stop(100%,rgba(0,0,0,.02)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(255,255,255,.02) 0%" - ", rgba(0,0,0,.02) 100%);\n" - " background-image: -o-linear-gradient(bottom, rgba(255,255,255,.02) 0%, rgb" - "a(0,0,0,.02) 100%);\n" - " background-image: linear-gradient(bottom, rgba(255,255,255,.02) 0%, rgba(0" - ",0,0,.02) 100%);\n" - "}\n" - "#dataexplorer .results .json_tree_container {\n" - " width: 888px;\n" - "}\n" - "#dataexplorer .results .json_tree_container > .json_tree {\n" - " clear: both;\n" - " margin-bottom: 6px;\n" - " padding: 2px 45px 5px 0px;\n" - "}\n" - "#dataexplorer .results .json_tree_container > .json_tree.flash {\n" - " -webkit-animation: add_row 500ms 1;\n" - " -moz-animation: add_row 500ms 1;\n" - " -o-animation: add_row 500ms 1;\n" - " -ms-animation: add_row 500ms 1;\n" - " animation: add_row 500ms 1;\n" - "}\n" - "#dataexplorer .results .json_tree {\n" - " padding: 0px 20px;\n" - " float: left;\n" - " font: 13px/18px monospace, mono, consolas;\n" - " /*.jt_b{ font-weight: bold; }*/\n" - "}\n" - "#dataexplorer .results .json_tree .jt_null {\n" - " color: red;\n" - "}\n" - "#dataexplorer .results .json_tree .jt_bool {\n" - " color: #ee00ff;\n" - "}\n" - "#dataexplorer .results .json_tree .jt_num {\n" - " color: blue;\n" - "}\n" - "#dataexplorer .results .json_tree .jt_link {\n" - " text-decoration: none;\n" - " color: #000;\n" - "}\n" - "#dataexplorer .results .json_tree .jt_url {\n" - " color: #08c;\n" - "}\n" - "#dataexplorer .results .json_tree .jt_email {\n" - " color: #08c;\n" - "}\n" - "#dataexplorer .results .json_tree .jt_string {\n" - " white-space: pre;\n" - " padding: 2px 0px;\n" - " color: green;\n" - "}\n" - "#dataexplorer .results .json_tree .jt_close {\n" - " margin-left: 6px;\n" - "}\n" - "#dataexplorer .results .json_tree .jt_array {\n" - " list-style: none;\n" - " margin-left: 7px;\n" - " padding-left: 22px;\n" - " border-left: 1px dotted #bbb;\n" - "}\n" - "#dataexplorer .results .json_tree .jt_object {\n" - " list-style: none;\n" - " margin-left: 7px;\n" - " padding-left: 22px;\n" - " border-left: 1px dotted #bbb;\n" - "}\n" - "#dataexplorer .results .json_tree .jt_arrow {\n" - " width: 15px;\n" - " height: 15px;\n" - " margin: 3px 2px 0px -15px;\n" - " float: left;\n" - " display: block;\n" - " background: url('images/arrow_down.png') no-repeat;\n" - "}\n" - "#dataexplorer .results .json_tree .jt_arrow_hidden {\n" - " background: url('images/arrow_right.png') no-repeat;\n" - "}\n" - "#dataexplorer .results .json_tree .jt_points {\n" - " display: none;\n" - "}\n" - "#dataexplorer .results .json_tree .jt_points_collapsed {\n" - " display: inline;\n" - "}\n" - "#dataexplorer .results .json_tree .jt_collapsed {\n" - " display: none;\n" - "}\n" - "#dataexplorer .results .json_tree .jt_b_collapsed {\n" - " display: inline;\n" - " margin-left: 0px;\n" - "}\n" - "#dataexplorer .results .json_tree .large_result {\n" - " white-space: pre;\n" - "}\n" - "#dataexplorer .results .json_table_container {\n" - " width: 888px;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table {\n" - " font: 13px/18px monospace, mono, consolas;\n" - " padding: 0px;\n" - " margin: 0px 0px 15px 0px;\n" - " table-layout: fixed;\n" - " white-space: nowrap;\n" - " border-spacing: 0px;\n" - " border-collapse: collapse;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .td_attr {\n" - " color: #444;\n" - " background: #f1f1f1;\n" - " -webkit-box-shadow: inset 0 4px 4px -4px #fff;\n" - " -moz-box-shadow: inset 0 4px 4px -4px #fff;\n" - " box-shadow: inset 0 4px 4px -4px #fff;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_undefined {\n" - " color: #ccc;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_null {\n" - " color: red;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_bool {\n" - " color: #ee00ff;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_num {\n" - " color: blue;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_link {\n" - " text-decoration: none;\n" - " color: #000;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_url {\n" - " color: #08c;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_email {\n" - " color: #08c;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_string {\n" - " color: green;\n" - " white-space: pre;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_object {\n" - " vertical-align: top;\n" - " display: inline-block;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_object .json_t" - "ree {\n" - " padding: 0px 17px;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table td {\n" - " max-width: 310px;\n" - " padding: 0px;\n" - " vertical-align: top;\n" - " border: 1px solid #dfdfdf;\n" - " overflow: hidden;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table tr {\n" - " padding: 20px;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table tr:nth-child(even) " - "{\n" - " background: #fff;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table tr:nth-child(odd) {" - "\n" - " background: #f9f9f9;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_attr {\n" - " margin: 0px 5px 0px 0px;\n" - " padding: 7px 0px 5px 15px;\n" - " cursor: auto;\n" - " overflow: hidden;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_value {\n" - " cursor: auto;\n" - " margin: 0px 5px 0px 0px;\n" - " padding: 7px 0px 5px 15px;\n" - " max-width: 290px;\n" - " overflow: hidden;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_array {\n" - " padding: 7px 0px 5px 0px;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_arrow {\n" - " width: 15px;\n" - " height: 15px;\n" - " margin: 3px 2px 0px 2px;\n" - " display: block;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_arrow_v {\n" - " background: url('images/arrow_down.png') no-repeat;\n" - " float: left;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_arrow_h {\n" - " background: url('images/arrow_right.png') no-repeat;\n" - " float: left;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .jta_arrow_hh {\n" - " width: 20px;\n" - " height: 10px;\n" - " background: url('images/arrow_right.png') repeat-x;\n" - " float: right;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .col-record-attr {\n" - " visibility: hidden;\n" - " border-left: 0px;\n" - " border-top: 0px;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .col-record-td {\n" - " padding: 0px;\n" - " background: #f0f0f0;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .col-record {\n" - " color: #aaa;\n" - " padding: 7px 10px 5px 10px;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .click_detector {\n" - " width: 100%;\n" - " cursor: e-resize;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .click_detector div" - " {\n" - " cursor: text;\n" - "}\n" - "#dataexplorer .results .json_table_container .json_table .click_detector div" - " .jt_arrow,\n" - "#dataexplorer .results .json_table_container .json_table .click_detector div" - " .jta_arrow {\n" - " cursor: default;\n" - "}\n" - "#dataexplorer .results .raw_view_container {\n" - " padding: 5px;\n" - "}\n" - "#dataexplorer .results .raw_view_container .raw_view_textarea {\n" - " width: 100%;\n" - " height: 300px;\n" - " padding: 4px;\n" - " margin: 0px 0px 0px -5px;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Resolve issues\n" - " ----------------------------\n" - "*/\n" - "#resolve-issues .container {\n" - " padding: 18px 0;\n" - "}\n" - "#resolve-issues .issue-container {\n" - " border: 1px solid #dcdcdc;\n" - " -webkit-border-radius: 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px;\n" - " background-clip: padding-box;\n" - " background-color: #f7f7f7;\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYigyNDcsMjQ3LDI0NykiIHN0b3Atb3BhY2l0eT0iMSIvPj" - "xzdG9wIG9mZnNldD0iIiBzdG9wLWNvbG9yPSJyZ2IoMjUwLDI1MCwyNTApIiBzdG9wLW9wYWNpdH" - "k9IjEiLz48L2xpbmVhckdyYWRpZW50PjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxIiBoZWlnaH" - "Q9IjEiIGZpbGw9InVybCgjZ3JhZC11Y2dnLWdlbmVyYXRlZCkiIC8+PC9zdmc+);\n" - " background-image: -moz-linear-gradient(bottom, rgba(247,247,247,1), rgba(2" - "50,250,250, 1));\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - ",rgba(247,247,247,1)),color-stop(,rgba(250,250,250, 1)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(247,247,247,1), rgb" - "a(250,250,250, 1));\n" - " background-image: -o-linear-gradient(bottom, rgba(247,247,247,1), rgba(250" - ",250,250, 1));\n" - " background-image: linear-gradient(bottom, rgba(247,247,247,1), rgba(250,25" - "0,250, 1));\n" - " -webkit-box-shadow: 0 2px 1px rgba(0,0,0,.05);\n" - " -moz-box-shadow: 0 2px 1px rgba(0,0,0,.05);\n" - " box-shadow: 0 2px 1px rgba(0,0,0,.05);\n" - " padding: 6px;\n" - " margin-top: 10px;\n" - "}\n" - "#resolve-issues .issue-container:first-child {\n" - " margin-top: 0;\n" - "}\n" - "#resolve-issues .issue-container p,\n" - "#resolve-issues .issue-container li {\n" - " font-family: \"Open Sans\";\n" - " font-size: 14px;\n" - "}\n" - "#resolve-issues .issue-container .solve-issue,\n" - "#resolve-issues .issue-container .try-solve-issue {\n" - " float: right;\n" - "}\n" - "#resolve-issues .issue-container hr {\n" - " border: none;\n" - " height: 1px;\n" - " background-color: #eaeaea;\n" - " -webkit-box-shadow: 0 1px 0 rgba(255,255,255,.8);\n" - " -moz-box-shadow: 0 1px 0 rgba(255,255,255,.8);\n" - " box-shadow: 0 1px 0 rgba(255,255,255,.8);\n" - "}\n" - "#resolve-issues .issue-container .issue-header {\n" - " position: relative;\n" - " height: 31px;\n" - "}\n" - "#resolve-issues .issue-container .issue-header p {\n" - " text-overflow: ellipsis;\n" - " overflow: hidden;\n" - " margin: 0;\n" - " color: #a6a6a6;\n" - " line-height: 31px;\n" - " height: 31px;\n" - "}\n" - "#resolve-issues .issue-container .issue-header p.issue-type {\n" - " background: 7px center no-repeat url('images/resolve_issue-danger_icon.png" - "');\n" - " padding-left: 35px;\n" - " width: 30%;\n" - " display: inline-block;\n" - " color: #e65b54;\n" - " font-weight: bold;\n" - " text-shadow: 0 1px 0 #fff;\n" - " box-sizing: border-box;\n" - "}\n" - "#resolve-issues .issue-container .issue-header p.message {\n" - " width: 70%;\n" - " display: inline-block;\n" - " background: 20px center no-repeat url('images/resolve_issue-message_icon.p" - "ng');\n" - " padding-left: 50px;\n" - " box-sizing: border-box;\n" - "}\n" - "#resolve-issues .issue-container .issue-header p.message:before {\n" - " content: '';\n" - " display: block;\n" - " float: left;\n" - " height: 31px;\n" - " width: 1px;\n" - " margin-left: -50px;\n" - " background-color: #eaeaea;\n" - " -webkit-box-shadow: 1px 0 0 #fff;\n" - " -moz-box-shadow: 1px 0 0 #fff;\n" - " box-shadow: 1px 0 0 #fff;\n" - "}\n" - "#resolve-issues .issue-container .issue-header p.datetime {\n" - " right: 155px;\n" - " display: none;\n" - " width: 0;\n" - " background: 20px center no-repeat url('images/resolve_issue-clock_icon.png" - "');\n" - " padding-left: 50px;\n" - "}\n" - "#resolve-issues .issue-container .issue-header p.datetime:before {\n" - " content: '';\n" - " display: block;\n" - " float: left;\n" - " height: 31px;\n" - " width: 1px;\n" - " margin-left: -50px;\n" - " background-color: #eaeaea;\n" - " -webkit-box-shadow: 1px 0 0 #fff;\n" - " -moz-box-shadow: 1px 0 0 #fff;\n" - " box-shadow: 1px 0 0 #fff;\n" - "}\n" - "#resolve-issues .issue-container .issue-details {\n" - " margin-top: 10px;\n" - " margin-bottom: 10px;\n" - " background: 7px 3px no-repeat url('images/resolve_issue-details_icon.png')" - ";\n" - " padding-left: 35px;\n" - "}\n" - "#resolve-issues .issue-container .issue-details p,\n" - "#resolve-issues .issue-container .issue-details li {\n" - " color: #838383;\n" - " font-size: 13px;\n" - "}\n" - "#resolve-issues .issue-container .issue-details p:first-child,\n" - "#resolve-issues .issue-container .issue-details li:first-child {\n" - " margin-top: 0;\n" - "}\n" - "#resolve-issues .issue-container pre {\n" - " word-wrap: break-word;\n" - " font: monospace;\n" - " font-size: 11px;\n" - "}\n" - "#resolve-issues .issue-container code {\n" - " background: #ededed;\n" - " padding: 3px 5px;\n" - " color: #555;\n" - " -webkit-border-radius: 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px;\n" - " background-clip: padding-box;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Element views\n" - " ----------------------------\n" - "*/\n" - "#database-view,\n" - "#table-view,\n" - "#datacenter-view,\n" - "#server-view,\n" - ".reconfigure-modal {\n" - " /* We flatten the views in table/table/assignments */\n" - "}\n" - "#database-view h1.title,\n" - "#table-view h1.title,\n" - "#datacenter-view h1.title,\n" - "#server-view h1.title,\n" - ".reconfigure-modal h1.title {\n" - " line-height: 20px;\n" - " width: 500px;\n" - " overflow: hidden;\n" - " text-overflow: ellipsis;\n" - " white-space: nowrap;\n" - "}\n" - "#database-view .operations,\n" - "#table-view .operations,\n" - "#datacenter-view .operations,\n" - "#server-view .operations,\n" - ".reconfigure-modal .operations {\n" - " float: right;\n" - " margin-left: 10px;\n" - "}\n" - "#database-view .explore,\n" - "#table-view .explore,\n" - "#datacenter-view .explore,\n" - "#server-view .explore,\n" - ".reconfigure-modal .explore,\n" - "#database-view .table-lookup-by-id,\n" - "#table-view .table-lookup-by-id,\n" - "#datacenter-view .table-lookup-by-id,\n" - "#server-view .table-lookup-by-id,\n" - ".reconfigure-modal .table-lookup-by-id {\n" - " float: right;\n" - " margin-left: 10px;\n" - "}\n" - "#database-view .section h2.title,\n" - "#table-view .section h2.title,\n" - "#datacenter-view .section h2.title,\n" - "#server-view .section h2.title,\n" - ".reconfigure-modal .section h2.title {\n" - " border-bottom: thin solid #f0f0f0;\n" - " padding-bottom: 15px;\n" - " margin-bottom: 10px;\n" - "}\n" - "#database-view .section.statistics > h2.title,\n" - "#table-view .section.statistics > h2.title,\n" - "#datacenter-view .section.statistics > h2.title,\n" - "#server-view .section.statistics > h2.title,\n" - ".reconfigure-modal .section.statistics > h2.title {\n" - " border-bottom: thin solid #f0f0f0;\n" - " padding-bottom: 20px;\n" - " margin-bottom: 0;\n" - "}\n" - "#database-view .section.statistics .stat-row,\n" - "#table-view .section.statistics .stat-row,\n" - "#datacenter-view .section.statistics .stat-row,\n" - "#server-view .section.statistics .stat-row,\n" - ".reconfigure-modal .section.statistics .stat-row {\n" - " border-top: thin solid #f0f0f0;\n" - "}\n" - "#database-view .section.statistics .stat-row .parenthetical,\n" - "#table-view .section.statistics .stat-row .parenthetical,\n" - "#datacenter-view .section.statistics .stat-row .parenthetical,\n" - "#server-view .section.statistics .stat-row .parenthetical,\n" - ".reconfigure-modal .section.statistics .stat-row .parenthetical {\n" - " color: #5c6169;\n" - "}\n" - "#database-view .section.statistics .stat-row p,\n" - "#table-view .section.statistics .stat-row p,\n" - "#datacenter-view .section.statistics .stat-row p,\n" - "#server-view .section.statistics .stat-row p,\n" - ".reconfigure-modal .section.statistics .stat-row p {\n" - " margin: 18px 0;\n" - " font-family: \"Open Sans\";\n" - " font-size: 15px;\n" - " color: #72bddf;\n" - " padding-left: 30px;\n" - " background-position: left bottom 2px;\n" - " background-repeat: no-repeat;\n" - "}\n" - "#database-view .section.statistics .stat-row p span.big,\n" - "#table-view .section.statistics .stat-row p span.big,\n" - "#datacenter-view .section.statistics .stat-row p span.big,\n" - "#server-view .section.statistics .stat-row p span.big,\n" - ".reconfigure-modal .section.statistics .stat-row p span.big {\n" - " font-size: 24px;\n" - " line-height: 24px;\n" - " color: #5c6169;\n" - "}\n" - "#database-view .section.statistics .stat-row p.availability,\n" - "#table-view .section.statistics .stat-row p.availability,\n" - "#datacenter-view .section.statistics .stat-row p.availability,\n" - "#server-view .section.statistics .stat-row p.availability,\n" - ".reconfigure-modal .section.statistics .stat-row p.availability {\n" - " padding-left: 0px;\n" - "}\n" - "#database-view .section.statistics .stat-row p.availability .ionicon-table-s" - "tatus,\n" - "#table-view .section.statistics .stat-row p.availability .ionicon-table-stat" - "us,\n" - "#datacenter-view .section.statistics .stat-row p.availability .ionicon-table" - "-status,\n" - "#server-view .section.statistics .stat-row p.availability .ionicon-table-sta" - "tus,\n" - ".reconfigure-modal .section.statistics .stat-row p.availability .ionicon-tab" - "le-status {\n" - " width: 24px;\n" - " height: 22px;\n" - " position: relative;\n" - " left: -4px;\n" - " bottom: -3px;\n" - " transform: scaleY(1.1);\n" - "}\n" - "#database-view .section.statistics .stat-row p.availability .ionicon-table-s" - "tatus.ready,\n" - "#table-view .section.statistics .stat-row p.availability .ionicon-table-stat" - "us.ready,\n" - "#datacenter-view .section.statistics .stat-row p.availability .ionicon-table" - "-status.ready,\n" - "#server-view .section.statistics .stat-row p.availability .ionicon-table-sta" - "tus.ready,\n" - ".reconfigure-modal .section.statistics .stat-row p.availability .ionicon-tab" - "le-status.ready {\n" - " fill: #68c868;\n" - "}\n" - "#database-view .section.statistics .stat-row p.availability .ionicon-table-s" - "tatus.ready-but-with-caveats,\n" - "#table-view .section.statistics .stat-row p.availability .ionicon-table-stat" - "us.ready-but-with-caveats,\n" - "#datacenter-view .section.statistics .stat-row p.availability .ionicon-table" - "-status.ready-but-with-caveats,\n" - "#server-view .section.statistics .stat-row p.availability .ionicon-table-sta" - "tus.ready-but-with-caveats,\n" - ".reconfigure-modal .section.statistics .stat-row p.availability .ionicon-tab" - "le-status.ready-but-with-caveats {\n" - " fill: #edad37;\n" - "}\n" - "#database-view .section.statistics .stat-row p.availability .ionicon-table-s" - "tatus.not-ready,\n" - "#table-view .section.statistics .stat-row p.availability .ionicon-table-stat" - "us.not-ready,\n" - "#datacenter-view .section.statistics .stat-row p.availability .ionicon-table" - "-status.not-ready,\n" - "#server-view .section.statistics .stat-row p.availability .ionicon-table-sta" - "tus.not-ready,\n" - ".reconfigure-modal .section.statistics .stat-row p.availability .ionicon-tab" - "le-status.not-ready {\n" - " fill: #ff4747;\n" - "}\n" - "#database-view .section.statistics .stat-row p.masters,\n" - "#table-view .section.statistics .stat-row p.masters,\n" - "#datacenter-view .section.statistics .stat-row p.masters,\n" - "#server-view .section.statistics .stat-row p.masters,\n" - ".reconfigure-modal .section.statistics .stat-row p.masters {\n" - " background-image: url('images/layers-icon.png');\n" - "}\n" - "#database-view .section.statistics .stat-row p.replicas,\n" - "#table-view .section.statistics .stat-row p.replicas,\n" - "#datacenter-view .section.statistics .stat-row p.replicas,\n" - "#server-view .section.statistics .stat-row p.replicas,\n" - ".reconfigure-modal .section.statistics .stat-row p.replicas {\n" - " background-image: url('images/grid-icon.png');\n" - "}\n" - "#database-view .section.statistics .stat-row p.documents,\n" - "#table-view .section.statistics .stat-row p.documents,\n" - "#datacenter-view .section.statistics .stat-row p.documents,\n" - "#server-view .section.statistics .stat-row p.documents,\n" - ".reconfigure-modal .section.statistics .stat-row p.documents {\n" - " background-image: url('images/document-icon.png');\n" - "}\n" - "#database-view .section.statistics .stat-row p.datacenter,\n" - "#table-view .section.statistics .stat-row p.datacenter,\n" - "#datacenter-view .section.statistics .stat-row p.datacenter,\n" - "#server-view .section.statistics .stat-row p.datacenter,\n" - ".reconfigure-modal .section.statistics .stat-row p.datacenter {\n" - " background-image: url('images/layers-icon.png');\n" - "}\n" - "#database-view .section.statistics .stat-row p.uptime,\n" - "#table-view .section.statistics .stat-row p.uptime,\n" - "#datacenter-view .section.statistics .stat-row p.uptime,\n" - "#server-view .section.statistics .stat-row p.uptime,\n" - ".reconfigure-modal .section.statistics .stat-row p.uptime {\n" - " background-image: url('images/grid-icon.png');\n" - "}\n" - "#database-view .section.statistics .stat-row p.ips,\n" - "#table-view .section.statistics .stat-row p.ips,\n" - "#datacenter-view .section.statistics .stat-row p.ips,\n" - "#server-view .section.statistics .stat-row p.ips,\n" - ".reconfigure-modal .section.statistics .stat-row p.ips {\n" - " background-image: url('images/document-icon.png');\n" - "}\n" - "#database-view .section.statistics .stat-row p.tables,\n" - "#table-view .section.statistics .stat-row p.tables,\n" - "#datacenter-view .section.statistics .stat-row p.tables,\n" - "#server-view .section.statistics .stat-row p.tables,\n" - ".reconfigure-modal .section.statistics .stat-row p.tables {\n" - " background-image: url('images/document-icon.png');\n" - "}\n" - "#database-view .section.statistics .stat-row p.datacenters,\n" - "#table-view .section.statistics .stat-row p.datacenters,\n" - "#datacenter-view .section.statistics .stat-row p.datacenters,\n" - "#server-view .section.statistics .stat-row p.datacenters,\n" - ".reconfigure-modal .section.statistics .stat-row p.datacenters {\n" - " background-image: url('images/layers-icon.png');\n" - "}\n" - "#database-view .section.statistics .stat-row p.servers,\n" - "#table-view .section.statistics .stat-row p.servers,\n" - "#datacenter-view .section.statistics .stat-row p.servers,\n" - "#server-view .section.statistics .stat-row p.servers,\n" - ".reconfigure-modal .section.statistics .stat-row p.servers {\n" - " background-image: url('images/document-icon.png');\n" - "}\n" - "#database-view .section.statistics .stat-row.first,\n" - "#table-view .section.statistics .stat-row.first,\n" - "#datacenter-view .section.statistics .stat-row.first,\n" - "#server-view .section.statistics .stat-row.first,\n" - ".reconfigure-modal .section.statistics .stat-row.first {\n" - " border-top: none;\n" - "}\n" - "#database-view .legend-container,\n" - "#table-view .legend-container,\n" - "#datacenter-view .legend-container,\n" - "#server-view .legend-container,\n" - ".reconfigure-modal .legend-container {\n" - " overflow: auto;\n" - " margin: 10px;\n" - "}\n" - "#database-view .plot-container,\n" - "#table-view .plot-container,\n" - "#datacenter-view .plot-container,\n" - "#server-view .plot-container,\n" - ".reconfigure-modal .plot-container {\n" - " margin: 10px 0px 0px 0px;\n" - "}\n", - 65454) + std::string( - "#database-view .list-view ul,\n" - "#table-view .list-view ul,\n" - "#datacenter-view .list-view ul,\n" - "#server-view .list-view ul,\n" - ".reconfigure-modal .list-view ul,\n" - "#database-view .list-view li,\n" - "#table-view .list-view li,\n" - "#datacenter-view .list-view li,\n" - "#server-view .list-view li,\n" - ".reconfigure-modal .list-view li {\n" - " list-style-type: none;\n" - " margin: 0;\n" - " padding: 0;\n" - "}\n" - "#database-view .list-view li,\n" - "#table-view .list-view li,\n" - "#datacenter-view .list-view li,\n" - "#server-view .list-view li,\n" - ".reconfigure-modal .list-view li {\n" - " border-bottom: thin solid #f0f0f0;\n" - " height: 40px;\n" - " position: relative;\n" - " list-style-type: none;\n" - "}\n" - "#database-view .list-view li p,\n" - "#table-view .list-view li p,\n" - "#datacenter-view .list-view li p,\n" - "#server-view .list-view li p,\n" - ".reconfigure-modal .list-view li p {\n" - " color: #5c;\n" - " position: absolute;\n" - " text-overflow: ellipsis;\n" - " overflow: hidden;\n" - " line-height: 40px;\n" - " margin: 0;\n" - " font-family: \"Open Sans\";\n" - " font-size: 16px;\n" - "}\n" - "#database-view .list-view li p .highlight,\n" - "#table-view .list-view li p .highlight,\n" - "#datacenter-view .list-view li p .highlight,\n" - "#server-view .list-view li p .highlight,\n" - ".reconfigure-modal .list-view li p .highlight {\n" - " color: #f7ae41;\n" - "}\n" - "#database-view .assignments_list,\n" - "#table-view .assignments_list,\n" - "#datacenter-view .assignments_list,\n" - "#server-view .assignments_list,\n" - ".reconfigure-modal .assignments_list,\n" - "#database-view .responsibilities_list,\n" - "#table-view .responsibilities_list,\n" - "#datacenter-view .responsibilities_list,\n" - "#server-view .responsibilities_list,\n" - ".reconfigure-modal .responsibilities_list {\n" - " position: relative;\n" - "}\n" - "#database-view .tree-line,\n" - "#table-view .tree-line,\n" - "#datacenter-view .tree-line,\n" - "#server-view .tree-line,\n" - ".reconfigure-modal .tree-line {\n" - " position: absolute;\n" - " top: 17.5px;\n" - " bottom: 19px;\n" - " width: 7px;\n" - " background-image: url('images/list-square-bullet.png'), url('images/list-v" - "ert-dash.png');\n" - " background-position: left top, 2.5px center;\n" - " background-repeat: no-repeat, repeat-y;\n" - "}\n" - "#database-view .tree-view,\n" - "#table-view .tree-view,\n" - "#datacenter-view .tree-view,\n" - "#server-view .tree-view,\n" - ".reconfigure-modal .tree-view {\n" - " overflow: hidden;\n" - "}\n" - "#database-view .tree-view ul,\n" - "#table-view .tree-view ul,\n" - "#datacenter-view .tree-view ul,\n" - "#server-view .tree-view ul,\n" - ".reconfigure-modal .tree-view ul,\n" - "#database-view .tree-view li,\n" - "#table-view .tree-view li,\n" - "#datacenter-view .tree-view li,\n" - "#server-view .tree-view li,\n" - ".reconfigure-modal .tree-view li {\n" - " list-style-type: none;\n" - " margin: 0;\n" - " padding: 0;\n" - "}\n" - "#database-view .tree-view li.parent,\n" - "#table-view .tree-view li.parent,\n" - "#datacenter-view .tree-view li.parent,\n" - "#server-view .tree-view li.parent,\n" - ".reconfigure-modal .tree-view li.parent {\n" - " position: relative;\n" - "}\n" - "#database-view .tree-view li.parent .tree-line,\n" - "#table-view .tree-view li.parent .tree-line,\n" - "#datacenter-view .tree-view li.parent .tree-line,\n" - "#server-view .tree-view li.parent .tree-line,\n" - ".reconfigure-modal .tree-view li.parent .tree-line {\n" - " position: absolute;\n" - " top: 17.5px;\n" - " bottom: 19px;\n" - " width: 7px;\n" - " background-image: url('images/list-square-bullet.png'), url('images/list-v" - "ert-dash.png');\n" - " background-position: left top, 2.5px center;\n" - " background-repeat: no-repeat, repeat-y;\n" - "}\n" - "#database-view .tree-view li.parent .parent-info,\n" - "#table-view .tree-view li.parent .parent-info,\n" - "#datacenter-view .tree-view li.parent .parent-info,\n" - "#server-view .tree-view li.parent .parent-info,\n" - ".reconfigure-modal .tree-view li.parent .parent-info {\n" - " margin-left: 19px;\n" - "}\n" - "#database-view .tree-view li.parent .parent-info p,\n" - "#table-view .tree-view li.parent .parent-info p,\n" - "#datacenter-view .tree-view li.parent .parent-info p,\n" - "#server-view .tree-view li.parent .parent-info p,\n" - ".reconfigure-modal .tree-view li.parent .parent-info p {\n" - " text-overflow: ellipsis;\n" - " overflow: hidden;\n" - " line-height: 40px;\n" - " margin: 0;\n" - " font-family: \"Open Sans\";\n" - " font-size: 16px;\n" - " color: #5C6169;\n" - " word-wrap: break-word;\n" - " white-space: nowrap;\n" - " width: 878px;\n" - "}\n" - "#database-view .tree-view li.child,\n" - "#table-view .tree-view li.child,\n" - "#datacenter-view .tree-view li.child,\n" - "#server-view .tree-view li.child,\n" - ".reconfigure-modal .tree-view li.child {\n" - " position: relative;\n" - "}\n" - "#database-view .tree-view li.child .tree-node,\n" - "#table-view .tree-view li.child .tree-node,\n" - "#datacenter-view .tree-view li.child .tree-node,\n" - "#server-view .tree-view li.child .tree-node,\n" - ".reconfigure-modal .tree-view li.child .tree-node {\n" - " position: absolute;\n" - " width: 22px;\n" - " top: 0;\n" - " bottom: 0;\n" - " background-image: url('images/list-square-bullet.png'), url('images/list-h" - "oriz-dash.png');\n" - " background-position: right center, 2.5px center;\n" - " background-repeat: no-repeat, repeat-x;\n" - "}\n" - "#database-view .tree-view li.child .child-info,\n" - "#table-view .tree-view li.child .child-info,\n" - "#datacenter-view .tree-view li.child .child-info,\n" - "#server-view .tree-view li.child .child-info,\n" - ".reconfigure-modal .tree-view li.child .child-info {\n" - " margin-left: 34px;\n" - " border-bottom: thin solid #f0f0f0;\n" - " height: 40px;\n" - " position: relative;\n" - "}\n" - "#database-view .tree-view li.child .child-info p,\n" - "#table-view .tree-view li.child .child-info p,\n" - "#datacenter-view .tree-view li.child .child-info p,\n" - "#server-view .tree-view li.child .child-info p,\n" - ".reconfigure-modal .tree-view li.child .child-info p {\n" - " color: #5c6169;\n" - " position: absolute;\n" - " text-overflow: ellipsis;\n" - " overflow: hidden;\n" - " line-height: 40px;\n" - " margin: 0;\n" - " font-family: \"Open Sans\";\n" - " font-size: 16px;\n" - "}\n" - "#database-view .tree-view li.child .child-info p .highlight,\n" - "#table-view .tree-view li.child .child-info p .highlight,\n" - "#datacenter-view .tree-view li.child .child-info p .highlight,\n" - "#server-view .tree-view li.child .child-info p .highlight,\n" - ".reconfigure-modal .tree-view li.child .child-info p .highlight {\n" - " color: #f7ae41;\n" - "}\n" - "#database-view .tree-view li.child:first-child .child,\n" - "#table-view .tree-view li.child:first-child .child,\n" - "#datacenter-view .tree-view li.child:first-child .child,\n" - "#server-view .tree-view li.child:first-child .child,\n" - ".reconfigure-modal .tree-view li.child:first-child .child {\n" - " border-top: thin solid #f0f0f0;\n" - "}\n" - "#database-view .section.list-view.table-list li.table {\n" - " background: left center no-repeat url('images/server-icon.png');\n" - " padding-left: 30px;\n" - "}\n" - "#database-view .section.list-view.table-list li.table .name {\n" - " width: 375px;\n" - " height: 40px;\n" - "}\n" - "#database-view .section.list-view.table-list li.table .info {\n" - " right: 200px;\n" - " width: 200px;\n" - " padding-left: 30px;\n" - " background: left center no-repeat url('images/graph-icon.png');\n" - "}\n" - "#database-view .section.list-view.table-list li.table .status {\n" - " right: 0;\n" - "}\n" - "#database-view .section.list-view.table-list li.table .status .label {\n" - " padding-left: 23px;\n" - "}\n" - "#database-view .section.list-view.table-list li.table .status .label-success" - " {\n" - " background: left center no-repeat url('images/green-light_glow_small.png')" - ";\n" - "}\n" - "#database-view .section.list-view.table-list li.table .status .label-failure" - " {\n" - " background: left center no-repeat url('images/red-light_glow_small.png');\n" - "}\n" - "#table-view .section.tree-view.server-assignments li.server .server-info,\n" - ".reconfigure-modal .section.tree-view.server-assignments li.server .server-i" - "nfo,\n" - "#table-view .tree-view li.server .server-info,\n" - ".reconfigure-modal .tree-view li.server .server-info {\n" - " background: left center no-repeat url('images/server-icon.png');\n" - " padding-left: 30px;\n" - "}\n" - "#table-view .section.tree-view.server-assignments li.server .server-info.add" - "ed,\n" - ".reconfigure-modal .section.tree-view.server-assignments li.server .server-i" - "nfo.added,\n" - "#table-view .tree-view li.server .server-info.added,\n" - ".reconfigure-modal .tree-view li.server .server-info.added {\n" - " background: left center no-repeat url('images/server-icon-added.png');\n" - "}\n" - "#table-view .section.tree-view.server-assignments li.server .server-info.del" - "eted,\n" - ".reconfigure-modal .section.tree-view.server-assignments li.server .server-i" - "nfo.deleted,\n" - "#table-view .tree-view li.server .server-info.deleted,\n" - ".reconfigure-modal .tree-view li.server .server-info.deleted {\n" - " background: left center no-repeat url('images/server-icon-removed.png');\n" - "}\n" - "#table-view .section.tree-view.server-assignments li.server .server-info .na" - "me,\n" - ".reconfigure-modal .section.tree-view.server-assignments li.server .server-i" - "nfo .name,\n" - "#table-view .tree-view li.server .server-info .name,\n" - ".reconfigure-modal .tree-view li.server .server-info .name {\n" - " width: 300px;\n" - " height: 40px;\n" - "}\n" - "#table-view .section.tree-view.server-assignments li.server .server-info .pr" - "imary,\n" - ".reconfigure-modal .section.tree-view.server-assignments li.server .server-i" - "nfo .primary,\n" - "#table-view .tree-view li.server .server-info .primary,\n" - ".reconfigure-modal .tree-view li.server .server-info .primary,\n" - "#table-view .section.tree-view.server-assignments li.server .server-info .se" - "condary,\n" - ".reconfigure-modal .section.tree-view.server-assignments li.server .server-i" - "nfo .secondary,\n" - "#table-view .tree-view li.server .server-info .secondary,\n" - ".reconfigure-modal .tree-view li.server .server-info .secondary {\n" - " width: 250px;\n" - " right: 0px;\n" - " text-align: left;\n" - " padding-left: 30px;\n" - "}\n" - "#table-view .section.tree-view.server-assignments li.server .server-info .pr" - "imary,\n" - ".reconfigure-modal .section.tree-view.server-assignments li.server .server-i" - "nfo .primary,\n" - "#table-view .tree-view li.server .server-info .primary,\n" - ".reconfigure-modal .tree-view li.server .server-info .primary {\n" - " background: left center no-repeat url('images/layers-icon.png');\n" - "}\n" - "#table-view .section.tree-view.server-assignments li.server .server-info .se" - "condary,\n" - ".reconfigure-modal .section.tree-view.server-assignments li.server .server-i" - "nfo .secondary,\n" - "#table-view .tree-view li.server .server-info .secondary,\n" - ".reconfigure-modal .tree-view li.server .server-info .secondary {\n" - " background: left center no-repeat url('images/grid-icon.png');\n" - "}\n" - "#table-view .section.tree-view.server-assignments .num-keys,\n" - ".reconfigure-modal .section.tree-view.server-assignments .num-keys,\n" - "#table-view .tree-view .num-keys,\n" - ".reconfigure-modal .tree-view .num-keys {\n" - " position: relative;\n" - " float: right;\n" - " right: 160px;\n" - " width: 150px;\n" - " padding-left: 30px;\n" - " background: left center no-repeat url('images/bars-icon_server-assignments" - ".png');\n" - "}\n" - "#table-view .section.replication h2.title,\n" - ".reconfigure-modal .section.replication h2.title {\n" - " border-bottom: none;\n" - " padding-bottom: 5px;\n" - "}\n" - "#table-view .section.replication .nav-tabs,\n" - ".reconfigure-modal .section.replication .nav-tabs {\n" - " padding-left: 0;\n" - "}\n" - "#table-view .section.replication p,\n" - ".reconfigure-modal .section.replication p,\n" - "#table-view .section.replication a,\n" - ".reconfigure-modal .section.replication a {\n" - " font-family: \"Open Sans\";\n" - "}\n" - "#table-view .section.replication .cluster_container,\n" - ".reconfigure-modal .section.replication .cluster_container {\n" - " overflow: auto;\n" - " border-bottom: 1px solid #efefef;\n" - " padding: 10px 0;\n" - " margin-bottom: 20px;\n" - "}\n" - "#table-view .section.replication .cluster_container .replica_container .clus" - "ter-acks,\n" - ".reconfigure-modal .section.replication .cluster_container .replica_containe" - "r .cluster-acks,\n" - "#table-view .section.replication .cluster_container .replica_container .clus" - "ter-replicas,\n" - ".reconfigure-modal .section.replication .cluster_container .replica_containe" - "r .cluster-replicas {\n" - " display: inline-block;\n" - "}\n" - "#table-view .section.replication .cluster_container .replica_container p,\n" - ".reconfigure-modal .section.replication .cluster_container .replica_containe" - "r p {\n" - " font-family: \"Open Sans\";\n" - " margin: 0 60px 0 0;\n" - "}\n" - "#table-view .section.replication .cluster_container .replica_container p spa" - "n.value,\n" - ".reconfigure-modal .section.replication .cluster_container .replica_containe" - "r p span.value {\n" - " padding: 0px 6px 0px 0px;\n" - " color: #f7ae41;\n" - " font-size: 24px;\n" - " line-height: 48px;\n" - "}\n" - "#table-view .section.replication .cluster_container .replica_container p spa" - "n.caption,\n" - ".reconfigure-modal .section.replication .cluster_container .replica_containe" - "r p span.caption {\n" - " color: #a9a9a9;\n" - " font-size: 14px;\n" - "}\n" - "#table-view .section.replication .cluster_container .replica_container p.max" - "-value,\n" - ".reconfigure-modal .section.replication .cluster_container .replica_containe" - "r p.max-value {\n" - " color: #a9a9a9;\n" - " font-size: 11px;\n" - "}\n" - "#table-view .section.replication .cluster_container .replica_container .inli" - "ne_edit_input,\n" - ".reconfigure-modal .section.replication .cluster_container .replica_containe" - "r .inline_edit_input {\n" - " margin: 4px 6px 3px 0px;\n" - " width: 30px;\n" - " padding: 3px 4px;\n" - " height: 28px;\n" - " font-size: 20px;\n" - " font-family: \"Open Sans\";\n" - "}\n" - "#table-view .section.replication .cluster_container .replica_container .btn," - "\n" - ".reconfigure-modal .section.replication .cluster_container .replica_containe" - "r .btn {\n" - " margin-top: 6px;\n" - " float: right;\n" - " margin-left: 15px;\n" - "}\n" - "#table-view .section.replication .hide-mdc,\n" - ".reconfigure-modal .section.replication .hide-mdc {\n" - " display: none;\n" - "}\n" - "#table-view .section.replication .mdc-options,\n" - ".reconfigure-modal .section.replication .mdc-options {\n" - " margin-top: 20px;\n" - "}\n" - "#table-view .section.replication .mdc-options.hidden,\n" - ".reconfigure-modal .section.replication .mdc-options.hidden {\n" - " display: none;\n" - "}\n" - "#table-view .section.replication .mdc-options .primary-dc,\n" - ".reconfigure-modal .section.replication .mdc-options .primary-dc {\n" - " border-top: thin solid #efefef;\n" - " padding-top: 15px;\n" - "}\n" - "#table-view .section.replication .mdc-options .primary-dc .content,\n" - ".reconfigure-modal .section.replication .mdc-options .primary-dc .content {\n" - " padding: 5px 0px 15px 0px;\n" - "}\n" - "#table-view .section.replication .mdc-options .primary-dc .content p,\n" - ".reconfigure-modal .section.replication .mdc-options .primary-dc .content p " - "{\n" - " margin: 7px 0px;\n" - "}\n" - "#table-view .section.replication .mdc-options .primary-dc .btn,\n" - ".reconfigure-modal .section.replication .mdc-options .primary-dc .btn {\n" - " float: right;\n" - " margin-left: 15px;\n" - "}\n" - "#table-view .section.replication .mdc-options .primary-dc p,\n" - ".reconfigure-modal .section.replication .mdc-options .primary-dc p {\n" - " margin: 0;\n" - "}\n" - "#table-view .section.replication .mdc-options .primary-dc p.caption,\n" - ".reconfigure-modal .section.replication .mdc-options .primary-dc p.caption {" - "\n" - " line-height: 24px;\n" - " font-size: 14px;\n" - "}\n" - "#table-view .section.replication .mdc-options .primary-dc .toggle-primary,\n" - ".reconfigure-modal .section.replication .mdc-options .primary-dc .toggle-pri" - "mary {\n" - " margin-bottom: 10px;\n" - "}\n" - "#table-view .section.replication .mdc-options .primary-dc .toggle-primary .s" - "witch,\n" - ".reconfigure-modal .section.replication .mdc-options .primary-dc .toggle-pri" - "mary .switch {\n" - " float: left;\n" - " margin-right: 20px;\n" - "}\n" - "#table-view .section.replication .mdc-options .primary-dc form.change-primar" - "y select,\n" - ".reconfigure-modal .section.replication .mdc-options .primary-dc form.change" - "-primary select {\n" - " margin-top: 4px;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_list_container,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_list_contai" - "ner {\n" - " padding: 0px;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_list_container .da" - "tacenter_tab,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_list_contai" - "ner .datacenter_tab {\n" - " position: relative;\n" - " width: 100px;\n" - " margin: 10px 0;\n" - " background: #efefef;\n" - " height: 40px;\n" - " line-height: 40px;\n" - " padding: 0 10px;\n" - " -webkit-border-radius: 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px;\n" - " background-clip: padding-box;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_list_container .da" - "tacenter_tab a,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_list_contai" - "ner .datacenter_tab a {\n" - " display: block;\n" - " cursor: pointer;\n" - " color: #7e7e7e;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_list_container .da" - "tacenter_tab a:hover,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_list_contai" - "ner .datacenter_tab a:hover {\n" - " color: #7e7e7e;\n" - " text-decoration: none;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_list_container .da" - "tacenter_tab.active,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_list_contai" - "ner .datacenter_tab.active {\n" - " background-color: #23a5df;\n" - " border-color: #23a5df;\n" - " -webkit-border-radius: 3px 0 0 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px 0 0 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px 0 0 3px;\n" - " background-clip: padding-box;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_list_container .da" - "tacenter_tab.active a,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_list_contai" - "ner .datacenter_tab.active a {\n" - " color: #fff;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_list_container .da" - "tacenter_tab.active a:hover,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_list_contai" - "ner .datacenter_tab.active a:hover {\n" - " color: #fff;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_list_container .da" - "tacenter_tab.active:after,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_list_contai" - "ner .datacenter_tab.active:after {\n" - " content: \" \";\n" - " font-size: 0;\n" - " height: 0;\n" - " line-height: 0;\n" - " border-style: solid;\n" - " border-color: transparent;\n" - " border-width: 20px 0 20px 20px;\n" - " border-left-color: #23a5df;\n" - " position: absolute;\n" - " right: -20px;\n" - " top: 0;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_list_container .da" - "tacenter_tab.universe,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_list_contai" - "ner .datacenter_tab.universe {\n" - " background: #f7ae41 url('images/globe-icon_white.png') 8px center no-repea" - "t;\n" - " border-color: #f7ae41;\n" - " color: #fcfcfc;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_list_container .da" - "tacenter_tab.universe a,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_list_contai" - "ner .datacenter_tab.universe a {\n" - " color: #fcfcfc;\n" - " padding-left: 24px;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_list_container .da" - "tacenter_tab.universe a:hover,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_list_contai" - "ner .datacenter_tab.universe a:hover {\n" - " color: #fcfcfc;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_list_container .da" - "tacenter_tab.universe.active:after,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_list_contai" - "ner .datacenter_tab.universe.active:after {\n" - " border-left-color: #f7ae41;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content {\n" - " margin: 0px;\n" - " padding-left: 10px;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .datacente" - "r_view,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .da" - "tacenter_view {\n" - " margin: 0px 0px 0px 40px;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .progress_" - "bar_full_container,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .pr" - "ogress_bar_full_container {\n" - " display: none;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .progress_" - "bar_full_container .progress_bar_container,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .pr" - "ogress_bar_full_container .progress_bar_container {\n" - " padding: 11px 0px 10px 0px;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .progress_" - "bar_full_container .progress_bar_container .details,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .pr" - "ogress_bar_full_container .progress_bar_container .details {\n" - " font-family: \"Open Sans\";\n" - " margin: 0px;\n" - " font-size: 14px;\n" - " line-height: 20px;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .progress_" - "bar_full_container .progress_bar_container .replicating,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .pr" - "ogress_bar_full_container .progress_bar_container .replicating {\n" - " font-family: \"Open Sans\";\n" - " margin: 0px;\n" - " color: #7ec4e3;\n" - " font-size: 14px;\n" - " line-height: 20px;\n" - " font-weight: bold;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .progress_" - "bar_full_container .progress_bar_container .full_bar,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .pr" - "ogress_bar_full_container .progress_bar_container .full_bar {\n" - " -webkit-border-radius: 5px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 5px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 5px;\n" - " background-clip: padding-box;\n" - " height: 10px;\n" - " background: #eee;\n" - " -webkit-box-shadow: inset 1px 1px 2px #ddd;\n" - " -moz-box-shadow: inset 1px 1px 2px #ddd;\n" - " box-shadow: inset 1px 1px 2px #ddd;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .progress_" - "bar_full_container .progress_bar_container .full_bar .bar,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .pr" - "ogress_bar_full_container .progress_bar_container .full_bar .bar {\n" - " height: 10px;\n" - " -webkit-border-radius: 5px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 5px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 5px;\n" - " background-clip: padding-box;\n" - " background: #f7ae41;\n" - " -webkit-box-shadow: inset 1px -2px 4px #DB8812;\n" - " -moz-box-shadow: inset 1px -2px 4px #DB8812;\n" - " box-shadow: inset 1px -2px 4px #DB8812;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .dc-info,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .dc" - "-info {\n" - " padding-bottom: 14px;\n" - " overflow: hidden;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .dc-info b" - "utton,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .dc" - "-info button {\n" - " float: right;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .dc-info:f" - "irst-child,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .dc" - "-info:first-child {\n" - " border-bottom: thin solid #f0f0f0;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .dc-info p" - ",\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .dc" - "-info p {\n" - " font-family: \"Open Sans\";\n" - " margin: 0;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .dc-info p" - ".caption,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .dc" - "-info p.caption {\n" - " color: #7ec4e3;\n" - " font-size: 14px;\n" - " line-height: 20px;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .dc-info p" - ".value,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .dc" - "-info p.value {\n" - " color: #5c6169;\n" - " font-size: 24px;\n" - " line-height: 48px;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .dc-info p" - ".max-value,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .dc" - "-info p.max-value {\n" - " color: #a9a9a9;\n" - " font-size: 11px;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .dc-info ." - "replica_value,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .dc" - "-info .replica_value,\n" - "#table-view .section.replication .mdc-options .datacenter_content .dc-info ." - "ack_value,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .dc" - "-info .ack_value {\n" - " float: left;\n" - " width: 90px;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .dc-info ." - "inline_edit_input,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .dc" - "-info .inline_edit_input {\n" - " margin: 8px 0px 4px 0px;\n" - " width: 40px;\n" - " padding: 0px 4px;\n" - " height: 34px;\n" - " font-size: 20px;\n" - " font-family: \"Open Sans\";\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .dc-info ." - "btn,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .dc" - "-info .btn {\n" - " margin-top: 26px;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .dc-info ." - "save_replicas_and_acks,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .dc" - "-info .save_replicas_and_acks {\n" - " margin: 30px 0px 0px 0px;\n" - "}\n" - "#table-view .section.replication .mdc-options .datacenter_content .dc-info ." - "make-primary,\n" - ".reconfigure-modal .section.replication .mdc-options .datacenter_content .dc" - "-info .make-primary {\n" - " margin: 10px 0px 0px 0px;\n" - "}\n" - "#table-view .section.sharding .edit-shards,\n" - ".reconfigure-modal .section.sharding .edit-shards {\n" - " margin: 20px 0;\n" - " min-height: 39px;\n" - "}\n" - "#table-view .section.sharding .edit-shards button,\n" - ".reconfigure-modal .section.sharding .edit-shards button {\n" - " float: right;\n" - "}\n" - "#table-view .section.sharding .edit-shards .alert-error,\n" - ".reconfigure-modal .section.sharding .edit-shards .alert-error {\n" - " margin-bottom: 25px;\n" - "}\n" - "#table-view .section.sharding .edit-shards .icon_paragraph,\n" - ".reconfigure-modal .section.sharding .edit-shards .icon_paragraph {\n" - " font-family: \"Open Sans\";\n" - " margin: 9px 0px 0px 0px;\n" - " font-size: 15px;\n" - " color: #a9a9a9;\n" - " padding-left: 40px;\n" - " background: left 9px no-repeat url('images/bars-icon.png');\n" - "}\n" - "#table-view .section.sharding .edit-shards .icon_paragraph span.big,\n" - ".reconfigure-modal .section.sharding .edit-shards .icon_paragraph span.big {" - "\n" - " padding: 0px 6px 0px 0px;\n" - " font-size: 24px;\n" - " color: #f7ae41;\n" - "}\n" - "#table-view .section.sharding .edit-shards .icon_paragraph span.shard_text,\n" - ".reconfigure-modal .section.sharding .edit-shards .icon_paragraph span.shard" - "_text {\n" - " display: inline-block;\n" - " padding-top: 10px;\n" - "}\n" - "#table-view .section.sharding .edit-shards input.num-shards,\n" - ".reconfigure-modal .section.sharding .edit-shards input.num-shards {\n" - " margin: -5px 6px 3px 0px;\n" - " width: 30px;\n" - " padding: 3px 4px;\n" - " height: 28px;\n" - " font-size: 20px;\n" - " font-family: \"Open Sans\";\n" - "}\n" - "#table-view .section.sharding .edit-shards .edit,\n" - ".reconfigure-modal .section.sharding .edit-shards .edit,\n" - "#table-view .section.sharding .edit-shards .cancel,\n" - ".reconfigure-modal .section.sharding .edit-shards .cancel,\n" - "#table-view .section.sharding .edit-shards .rebalance,\n" - ".reconfigure-modal .section.sharding .edit-shards .rebalance {\n" - " margin: 0px 0px 0px 10px;\n" - "}\n" - "#table-view .section.sharding .edit-shards p.max-value,\n" - ".reconfigure-modal .section.sharding .edit-shards p.max-value {\n" - " color: #a9a9a9;\n" - " font-size: 11px;\n" - " padding-left: 40px;\n" - " margin: 0;\n" - " line-height: 20px;\n" - "}\n" - "#table-view .section.sharding .shard-graph line,\n" - ".reconfigure-modal .section.sharding .shard-graph line {\n" - " stroke: #dcdcdc;\n" - "}\n" - "#table-view .section.sharding .shard-graph .rect,\n" - ".reconfigure-modal .section.sharding .shard-graph .rect {\n" - " fill: #9ad7f2;\n" - "}\n" - "#table-view .section.sharding .shard-graph text,\n" - ".reconfigure-modal .section.sharding .shard-graph text {\n" - " fill: #a3a3a3;\n" - "}\n" - "#table-view .section.sharding .shard-graph .shard-diagram,\n" - ".reconfigure-modal .section.sharding .shard-graph .shard-diagram {\n" - " display: none;\n" - " width: 100%;\n" - " height: 100%;\n" - " padding: 15px 4px 4px 4px;\n" - "}\n" - "#table-view .section.secondary_indexes h2.title,\n" - ".reconfigure-modal .section.secondary_indexes h2.title {\n" - " margin-bottom: 10px;\n" - "}\n" - "#table-view .section.secondary_indexes .list_secondary_indexes,\n" - ".reconfigure-modal .section.secondary_indexes .list_secondary_indexes,\n" - "#table-view .section.secondary_indexes .list_meta,\n" - ".reconfigure-modal .section.secondary_indexes .list_meta {\n" - " list-style: none;\n" - " margin: 10px 0px 0px 0px;\n" - " padding: 0px;\n" - "}\n" - "#table-view .section.secondary_indexes .list_secondary_indexes .square_li,\n" - ".reconfigure-modal .section.secondary_indexes .list_secondary_indexes .squar" - "e_li,\n" - "#table-view .section.secondary_indexes .list_meta .square_li,\n" - ".reconfigure-modal .section.secondary_indexes .list_meta .square_li {\n" - " background-image: url('images/list-square-bullet.png');\n" - " background-position: left center;\n" - " background-repeat: no-repeat;\n" - " padding: 5px 0px 5px 20px;\n" - " font-family: \"Open Sans\";\n" - " font-size: 15px;\n" - " color: #a3a3a3;\n" - "}\n" - "#table-view .section.secondary_indexes .list_secondary_indexes .square_li .n" - "ame,\n" - ".reconfigure-modal .section.secondary_indexes .list_secondary_indexes .squar" - "e_li .name,\n" - "#table-view .section.secondary_indexes .list_meta .square_li .name,\n" - ".reconfigure-modal .section.secondary_indexes .list_meta .square_li .name {\n" - " font-weight: bold;\n" - "}\n" - "#table-view .section.secondary_indexes .list_secondary_indexes .square_li .n" - "ew_index_name,\n" - ".reconfigure-modal .section.secondary_indexes .list_secondary_indexes .squar" - "e_li .new_index_name,\n" - "#table-view .section.secondary_indexes .list_meta .square_li .new_index_name" - ",\n" - ".reconfigure-modal .section.secondary_indexes .list_meta .square_li .new_ind" - "ex_name {\n" - " margin-bottom: -1px;\n" - "}\n" - "#table-view .section.secondary_indexes .list_secondary_indexes .input_li,\n" - ".reconfigure-modal .section.secondary_indexes .list_secondary_indexes .input" - "_li,\n" - "#table-view .section.secondary_indexes .list_meta .input_li,\n" - ".reconfigure-modal .section.secondary_indexes .list_meta .input_li {\n" - " padding: 0px 0px 0px 20px;\n" - "}\n" - "#table-view .section.secondary_indexes .list_secondary_indexes .alert_li .al" - "ert,\n" - ".reconfigure-modal .section.secondary_indexes .list_secondary_indexes .alert" - "_li .alert,\n" - "#table-view .section.secondary_indexes .list_meta .alert_li .alert,\n" - ".reconfigure-modal .section.secondary_indexes .list_meta .alert_li .alert {\n" - " margin-top: 5px;\n" - " margin-bottom: 5px;\n" - "}\n" - "#table-view .section.secondary_indexes .list_secondary_indexes .add_index_li" - ",\n" - ".reconfigure-modal .section.secondary_indexes .list_secondary_indexes .add_i" - "ndex_li,\n" - "#table-view .section.secondary_indexes .list_meta .add_index_li,\n" - ".reconfigure-modal .section.secondary_indexes .list_meta .add_index_li {\n" - " clear: both;\n" - " display: none;\n" - "}\n" - "#table-view .section.secondary_indexes .create_container,\n" - ".reconfigure-modal .section.secondary_indexes .create_container {\n" - " margin: 20px 0px 0px 0px;\n" - " font-size: 13px;\n" - "}\n" - "#table-view .section.secondary_indexes .alert,\n" - ".reconfigure-modal .section.secondary_indexes .alert {\n" - " margin-bottom: 0px;\n" - "}\n" - "#table-view .section.secondary_indexes .alert-on_create,\n" - ".reconfigure-modal .section.secondary_indexes .alert-on_create {\n" - " margin-top: 7px;\n" - "}\n" - "#table-view .section.secondary_indexes .alert-on_create .error_detail,\n" - ".reconfigure-modal .section.secondary_indexes .alert-on_create .error_detail" - " {\n" - " font-family: monospace;\n" - " font-size: 11px;\n" - "}\n" - "#table-view .section.secondary_indexes .cancel_delete_btn,\n" - ".reconfigure-modal .section.secondary_indexes .cancel_delete_btn,\n" - "#table-view .section.secondary_indexes .delete_index_btn,\n" - ".reconfigure-modal .section.secondary_indexes .delete_index_btn {\n" - " margin: 7px 7px 0px 0px;\n" - "}\n" - "#datacenter-view .section.list-view.server-list li.server {\n" - " background: left center no-repeat url('images/server-icon.png');\n" - " padding-left: 30px;\n" - "}\n" - "#datacenter-view .section.list-view.server-list li.server .name {\n" - " width: 375px;\n" - " height: 40px;\n" - "}\n" - "#datacenter-view .section.list-view.server-list li.server .info {\n" - " right: 200px;\n" - " width: 200px;\n" - " padding-left: 30px;\n" - " background: left center no-repeat url('images/graph-icon.png');\n" - "}\n" - "#datacenter-view .section.list-view.server-list li.server .status {\n" - " right: 0;\n" - "}\n" - "#datacenter-view .section.list-view.server-list li.server .status .label {\n" - " padding-left: 23px;\n" - "}\n" - "#datacenter-view .section.list-view.server-list li.server .status .label-suc" - "cess {\n" - " background: left center no-repeat url('images/green-light_glow_small.png')" - ";\n" - "}\n" - "#datacenter-view .section.list-view.server-list li.server .status .label-fai" - "lure {\n" - " background: left center no-repeat url('images/red-light_glow_small.png');\n" - "}\n" - "#server-view .section.tree-view.responsibilities li.shard .shard-info {\n" - " background: left center no-repeat url('images/server-icon.png');\n" - " padding-left: 30px;\n" - "}\n" - "#server-view .section.tree-view.responsibilities li.shard .shard-info .name " - "{\n" - " width: 455px;\n" - " height: 40px;\n" - "}\n" - "#server-view .section.tree-view.responsibilities li.shard .shard-info .role " - "{\n" - " right: 175px;\n" - " width: 145px;\n" - " padding-left: 30px;\n" - " background: left center no-repeat url('images/grid-icon.png');\n" - "}\n" - "#server-view .section.tree-view.responsibilities li.shard .shard-info .num-k" - "eys {\n" - " right: 0;\n" - " padding-left: 35px;\n" - " background: left center no-repeat url('images/bars-icon_server-assignments" - ".png');\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Toggle switches courtesy of Thibaut Courouble (webinterfacelab.com).\n" - "\n" - " License for this toggle switch snippet (.switch class) follows:\n" - " Copyright (c) 2012 Thibaut Courouble\n" - "\n" - " Permission is hereby granted, free of charge, to any person obtaining\n" - " a copy of this software and associated documentation files (the\n" - " \"Software\"), to deal in the Software without restriction, including\n" - " without limitation the rights to use, copy, modify, merge, publish,\n" - " distribute, sublicense, and/or sell copies of the Software, and to\n" - " permit persons to whom the Software is furnished to do so, subject to\n" - " the following conditions:\n" - "\n" - " The above copyright notice and this permission notice shall be\n" - " included in all copies or substantial portions of the Software.\n" - "\n" - " THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n" - " EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n" - " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n" - " NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n" - " LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n" - " OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n" - " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" - " ----------------------------\n" - "*/\n" - ".switch {\n" - " float: left;\n" - " position: relative;\n" - " height: 24px;\n" - " width: 100px;\n" - " background: rgba(0, 0, 0, 0.1);\n" - " -webkit-border-radius: 3px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 3px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 3px;\n" - " background-clip: padding-box;\n" - " -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3), 0 1px rgba(255, 25" - "5, 255, 0.1);\n" - " -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3), 0 1px rgba(255, 255, " - "255, 0.1);\n" - " box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3), 0 1px rgba(255, 255, 255, " - "0.1);\n" - "}\n" - ".switch .switch-label {\n" - " position: relative;\n" - " z-index: 2;\n" - " float: left;\n" - " width: 48px;\n" - " line-height: 24px;\n" - " font-size: 11px;\n" - " color: rgba(255, 255, 255, 0.15);\n" - " text-align: center;\n" - " text-shadow: 0 1px 1px rgba(0, 0, 0, 0.55);\n" - " text-transform: uppercase;\n" - " cursor: pointer;\n" - "}\n" - ".switch .switch-label:active {\n" - " font-weight: bold;\n" - "}\n" - ".switch .switch-label.switch-label-off {\n" - " padding-left: 2px;\n" - "}\n" - ".switch .switch-label.switch-label-on {\n" - " padding-right: 2px;\n" - "}\n" - ".switch .switch-input {\n" - " display: none;\n" - "}\n" - ".switch .switch-input:checked + .switch-label {\n" - " font-weight: bold;\n" - " color: rgba(0, 0, 0, 0.65);\n" - " text-shadow: 0 1px rgba(255, 255, 255, 0.25);\n" - " -webkit-transition: 0.15s ease-out;\n" - " -moz-transition: 0.15s ease-out;\n" - " -ms-transition: 0.15s ease-out;\n" - " -o-transition: 0.15s ease-out;\n" - " transition: 0.15s ease-out;\n" - " -webkit-transition: 0 .15s ease-out;\n" - " -moz-transition: 0 .15s ease-out;\n" - " -o-transition: 0 .15s ease-out;\n" - " -ms-transition: 0 .15s ease-out;\n" - " transition: 0 .15s ease-out;\n" - " -webkit-transition-property: color,text-shadow;\n" - " -moz-transition-property: color,text-shadow;\n" - " -o-transition-property: color,text-shadow;\n" - " -ms-transition-property: color,text-shadow;\n" - " transition-property: color,text-shadow;\n" - "}\n" - ".switch .switch-input:checked + .switch-label-on ~ .switch-selection {\n" - " left: 50px;\n" - " /* Note: left: 50%; doesn't transition in WebKit */\n" - "}\n" - ".switch .switch-selection {\n" - " position: absolute;\n" - " z-index: 1;\n" - " top: 2px;\n" - " left: 2px;\n" - " display: block;\n" - " width: 48px;\n" - " height: 20px;\n" - " -webkit-border-radius: 2px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 2px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 2px;\n" - " background-clip: padding-box;\n" - " -webkit-box-shadow: inset 0 1px rgba(255, 255, 255, 0.5), 0 0 2px rgba(0, " - "0, 0, 0.2);\n" - " -moz-box-shadow: inset 0 1px rgba(255, 255, 255, 0.5), 0 0 2px rgba(0, 0, " - "0, 0.2);\n" - " box-shadow: inset 0 1px rgba(255, 255, 255, 0.5), 0 0 2px rgba(0, 0, 0, 0." - "2);\n" - " -webkit-transition: left 0.15s ease-out;\n" - " -moz-transition: left 0.15s ease-out;\n" - " -o-transition: left 0.15s ease-out;\n" - " -ms-transition: left 0.15s ease-out;\n" - " transition: left 0.15s ease-out;\n" - " background-color: #F7AE41;\n" - "}\n" - ".switch.switch-green {\n" - " background-color: #65bd63;\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjAlIiB5Mj0iMTAwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9IiM5ZGQ5OTMiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZn" - "NldD0iIiBzdG9wLWNvbG9yPSIjNjViZDYzIiBzdG9wLW9wYWNpdHk9IjEiLz48L2xpbmVhckdyYW" - "RpZW50PjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9InVybCgjZ3" - "JhZC11Y2dnLWdlbmVyYXRlZCkiIC8+PC9zdmc+);\n" - " background-image: -moz-linear-gradient(top, #9dd993, #65bd63);\n" - " background-image: -webkit-gradient(linear,left top,left bottom,color-stop(" - ",#9dd993),color-stop(,#65bd63));\n" - " background-image: -webkit-linear-gradient(top, #9dd993, #65bd63);\n" - " background-image: -o-linear-gradient(top, #9dd993, #65bd63);\n" - " background-image: linear-gradient(top, #9dd993, #65bd63);\n" - "}\n" - ".switch.switch-blue .switch-selection {\n" - " background-color: #3aa2d0;\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjAlIiB5Mj0iMTAwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9IiM0ZmM5ZWUiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZn" - "NldD0iIiBzdG9wLWNvbG9yPSIjM2FhMmQwIiBzdG9wLW9wYWNpdHk9IjEiLz48L2xpbmVhckdyYW" - "RpZW50PjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9InVybCgjZ3" - "JhZC11Y2dnLWdlbmVyYXRlZCkiIC8+PC9zdmc+);\n" - " background-image: -moz-linear-gradient(top, #4fc9ee, #3aa2d0);\n" - " background-image: -webkit-gradient(linear,left top,left bottom,color-stop(" - ",#4fc9ee),color-stop(,#3aa2d0));\n" - " background-image: -webkit-linear-gradient(top, #4fc9ee, #3aa2d0);\n" - " background-image: -o-linear-gradient(top, #4fc9ee, #3aa2d0);\n" - " background-image: linear-gradient(top, #4fc9ee, #3aa2d0);\n" - "}\n" - ".switch.switch-yellow .switch-selection {\n" - " background-color: #c4bb61;\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjAlIiB5Mj0iMTAwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9IiNlMGRkOTQiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZn" - "NldD0iIiBzdG9wLWNvbG9yPSIjYzRiYjYxIiBzdG9wLW9wYWNpdHk9IjEiLz48L2xpbmVhckdyYW" - "RpZW50PjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9InVybCgjZ3" - "JhZC11Y2dnLWdlbmVyYXRlZCkiIC8+PC9zdmc+);\n" - " background-image: -moz-linear-gradient(top, #e0dd94, #c4bb61);\n" - " background-image: -webkit-gradient(linear,left top,left bottom,color-stop(" - ",#e0dd94),color-stop(,#c4bb61));\n" - " background-image: -webkit-linear-gradient(top, #e0dd94, #c4bb61);\n" - " background-image: -o-linear-gradient(top, #e0dd94, #c4bb61);\n" - " background-image: linear-gradient(top, #e0dd94, #c4bb61);\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Progress bar UIComponent\n" - " ----------------------------\n" - "*/\n" - ".backfill-progress p {\n" - " margin: 0;\n" - " line-height: 48px;\n" - "}\n" - ".backfill-progress .progress-status p {\n" - " margin-bottom: 10px;\n" - "}\n" - ".secondary_indexes .progress-status {\n" - " margin: 5px 0px -9px 0px;\n" - "}\n" - ".secondary_indexes .progress-status p {\n" - " margin: 0px 0px -5px 0px;\n" - "}\n" - ".secondary_indexes .progress_li {\n" - " color: #a9a9a9;\n" - " margin: 0px 0px 8px 20px;\n" - "}\n" - ".edit-shards {\n" - " border-bottom: 1px solid #efefef;\n" - " margin-bottom: 20px;\n" - "}\n" - ".secondary_indexes {\n" - " font-family: \"Open Sans\";\n" - " font-size: 15px;\n" - " color: #72BDDF;\n" - " padding: 10px 0;\n" - " border-bottom: 1px solid #efefef;\n" - " margin-bottom: 20px;\n" - "}\n" - ".secondary_indexes span.big {\n" - " font-size: 24px;\n" - " color: #5C6169;\n" - "}\n" - ".progress-status {\n" - " padding: 10px 0;\n" - "}\n" - ".progress-status p {\n" - " color: #a9a9a9;\n" - " line-height: normal;\n" - "}\n" - ".progress-status .progress {\n" - " height: 12px;\n" - " -webkit-border-radius: 12px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 12px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 12px;\n" - " background-clip: padding-box;\n" - " width: 65%;\n" - "}\n" - ".progress-status .progress .bar {\n" - " width: 100%;\n" - " background-color: #f7ad41;\n" - " background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIi" - "A/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaG" - "VpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj" - "48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQtdWNnZy1nZW5lcmF0ZWQiIGdyYWRpZW50VW5pdHM9In" - "VzZXJTcGFjZU9uVXNlIiB4MT0iMCUiIHkxPSIxMDAlIiB4Mj0iMCUiIHkyPSIwJSI+PHN0b3Agb2" - "Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYigyNDcsMTc1LDY3KSIgc3RvcC1vcGFjaXR5PSIxIi8+PH" - "N0b3Agb2Zmc2V0PSIiIHN0b3AtY29sb3I9InJnYigyNDksMTkwLDEwMykiIHN0b3Atb3BhY2l0eT" - "0iMSIvPjwvbGluZWFyR3JhZGllbnQ+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD" - "0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz48L3N2Zz4=);\n" - " background-image: -moz-linear-gradient(bottom, rgba(247,175,67,1), rgba(24" - "9,190,103, 1));\n" - " background-image: -webkit-gradient(linear,left bottom,left top,color-stop(" - ",rgba(247,175,67,1)),color-stop(,rgba(249,190,103, 1)));\n" - " background-image: -webkit-linear-gradient(bottom, rgba(247,175,67,1), rgba" - "(249,190,103, 1));\n" - " background-image: -o-linear-gradient(bottom, rgba(247,175,67,1), rgba(249," - "190,103, 1));\n" - " background-image: linear-gradient(bottom, rgba(247,175,67,1), rgba(249,190" - ",103, 1));\n" - " -webkit-box-shadow: 0 2px 1px rgba(0,0,0,.04), inset 0 -1px 2px rgba(0,0,0" - ",.05);\n" - " -moz-box-shadow: 0 2px 1px rgba(0,0,0,.04), inset 0 -1px 2px rgba(0,0,0,.0" - "5);\n" - " box-shadow: 0 2px 1px rgba(0,0,0,.04), inset 0 -1px 2px rgba(0,0,0,.05);\n" - "}\n" - ".progress-status .stages {\n" - " width: 30%;\n" - " float: right;\n" - "}\n" - ".progress-status .stages p {\n" - " text-align: right;\n" - " font-size: 13px;\n" - " margin: -3px 0px 15px 0px;\n" - "}\n" - ".progress-status .stages p.completed:before {\n" - " content: '\\2714';\n" - " display: block;\n" - " float: left;\n" - " font-size: 13px;\n" - " color: #4E9258;\n" - " padding-left: 10px;\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Progress bars from Bootstrap 2.2.0\n" - " -- note: we did not want to upgrade all of\n" - " our dependencies to the new version of\n" - " Bootstrap, but we needed some critical fixes\n" - " for progress bars. To that end, the following\n" - " styles are taken from progress-bars.less of\n" - " v2.2.0. Remove on upgrade! TODO\n" - " ----------------------------\n" - "*/\n" - "@-webkit-keyframes progress-bar-stripes {\n" - " from {\n" - " background-position: 40px 0;\n" - " }\n" - " to {\n" - " background-position: 0 0;\n" - " }\n" - "}\n" - "@-moz-keyframes progress-bar-stripes {\n" - " from {\n" - " background-position: 40px 0;\n" - " }\n" - " to {\n" - " background-position: 0 0;\n" - " }\n" - "}\n" - "@-ms-keyframes progress-bar-stripes {\n" - " from {\n" - " background-position: 40px 0;\n" - " }\n" - " to {\n" - " background-position: 0 0;\n" - " }\n" - "}\n" - "@-o-keyframes progress-bar-stripes {\n" - " from {\n" - " background-position: 0 0;\n" - " }\n" - " to {\n" - " background-position: 40px 0;\n" - " }\n" - "}\n" - "@keyframes progress-bar-stripes {\n" - " from {\n" - " background-position: 40px 0;\n" - " }\n" - " to {\n" - " background-position: 0 0;\n" - " }\n" - "}\n" - ".animate-ellipsis-keyframes-1 0% {\n" - " opacity: 0.2;\n" - "}\n" - ".animate-ellipsis-keyframes-1 25% {\n" - " opacity: 1;\n" - "}\n" - ".animate-ellipsis-keyframes-1 50% {\n" - " opacity: 1;\n" - "}\n" - ".animate-ellipsis-keyframes-1 75% {\n" - " opacity: 1;\n" - "}\n" - ".animate-ellipsis-keyframes-1 100% {\n" - " opacity: 0.2;\n" - "}\n" - ".animate-ellipsis-keyframes-2 0% {\n" - " opacity: 0.2;\n" - "}\n" - ".animate-ellipsis-keyframes-2 25% {\n" - " opacity: 0.2;\n" - "}\n" - ".animate-ellipsis-keyframes-2 50% {\n" - " opacity: 1;\n" - "}\n" - ".animate-ellipsis-keyframes-2 75% {\n" - " opacity: 1;\n" - "}\n" - ".animate-ellipsis-keyframes-2 100% {\n" - " opacity: 0.2;\n" - "}\n" - ".animate-ellipsis-keyframes-3 0% {\n" - " opacity: 0.2;\n" - "}\n" - ".animate-ellipsis-keyframes-3 25% {\n" - " opacity: 0.2;\n" - "}\n" - ".animate-ellipsis-keyframes-3 50% {\n" - " opacity: 0.2;\n" - "}\n" - ".animate-ellipsis-keyframes-3 75% {\n" - " opacity: 1;\n" - "}\n" - ".animate-ellipsis-keyframes-3 100% {\n" - " opacity: 0.2;\n" - "}\n" - "@-webkit-keyframes animate-ellipsis-1 {\n" - " 0% {\n" - " opacity: 0.2;\n" - " }\n" - " 25% {\n" - " opacity: 1;\n" - " }\n" - " 50% {\n" - " opacity: 1;\n" - " }\n" - " 75% {\n" - " opacity: 1;\n" - " }\n" - " 100% {\n" - " opacity: 0.2;\n" - " }\n" - "}\n" - "@-moz-keyframes animate-ellipsis-1 {\n" - " 0% {\n" - " opacity: 0.2;\n" - " }\n" - " 25% {\n" - " opacity: 1;\n" - " }\n" - " 50% {\n" - " opacity: 1;\n" - " }\n" - " 75% {\n" - " opacity: 1;\n" - " }\n" - " 100% {\n" - " opacity: 0.2;\n" - " }\n" - "}\n" - "@-o-keyframes animate-ellipsis-1 {\n" - " 0% {\n" - " opacity: 0.2;\n" - " }\n" - " 25% {\n" - " opacity: 1;\n" - " }\n" - " 50% {\n" - " opacity: 1;\n" - " }\n" - " 75% {\n" - " opacity: 1;\n" - " }\n" - " 100% {\n" - " opacity: 0.2;\n" - " }\n" - "}\n" - "@keyframes animate-ellipsis-1 {\n" - " 0% {\n" - " opacity: 0.2;\n" - " }\n" - " 25% {\n" - " opacity: 1;\n" - " }\n" - " 50% {\n" - " opacity: 1;\n" - " }\n" - " 75% {\n" - " opacity: 1;\n" - " }\n" - " 100% {\n" - " opacity: 0.2;\n" - " }\n" - "}\n" - "@-webkit-keyframes animate-ellipsis-2 {\n" - " 0% {\n" - " opacity: 0.2;\n" - " }\n" - " 25% {\n" - " opacity: 0.2;\n" - " }\n" - " 50% {\n" - " opacity: 1;\n" - " }\n" - " 75% {\n" - " opacity: 1;\n" - " }\n" - " 100% {\n" - " opacity: 0.2;\n" - " }\n" - "}\n" - "@-moz-keyframes animate-ellipsis-2 {\n" - " 0% {\n" - " opacity: 0.2;\n" - " }\n" - " 25% {\n" - " opacity: 0.2;\n" - " }\n" - " 50% {\n" - " opacity: 1;\n" - " }\n" - " 75% {\n" - " opacity: 1;\n" - " }\n" - " 100% {\n" - " opacity: 0.2;\n" - " }\n" - "}\n" - "@-o-keyframes animate-ellipsis-2 {\n" - " 0% {\n" - " opacity: 0.2;\n" - " }\n" - " 25% {\n" - " opacity: 0.2;\n" - " }\n" - " 50% {\n" - " opacity: 1;\n" - " }\n" - " 75% {\n" - " opacity: 1;\n" - " }\n" - " 100% {\n" - " opacity: 0.2;\n" - " }\n" - "}\n" - "@keyframes animate-ellipsis-2 {\n" - " 0% {\n" - " opacity: 0.2;\n" - " }\n" - " 25% {\n" - " opacity: 0.2;\n" - " }\n" - " 50% {\n" - " opacity: 1;\n" - " }\n" - " 75% {\n" - " opacity: 1;\n" - " }\n" - " 100% {\n" - " opacity: 0.2;\n" - " }\n" - "}\n" - "@-webkit-keyframes animate-ellipsis-3 {\n" - " 0% {\n" - " opacity: 0.2;\n" - " }\n" - " 25% {\n" - " opacity: 0.2;\n" - " }\n" - " 50% {\n" - " opacity: 0.2;\n" - " }\n" - " 75% {\n" - " opacity: 1;\n" - " }\n" - " 100% {\n" - " opacity: 0.2;\n" - " }\n" - "}\n" - "@-moz-keyframes animate-ellipsis-3 {\n" - " 0% {\n" - " opacity: 0.2;\n" - " }\n" - " 25% {\n" - " opacity: 0.2;\n" - " }\n" - " 50% {\n" - " opacity: 0.2;\n" - " }\n" - " 75% {\n" - " opacity: 1;\n" - " }\n" - " 100% {\n" - " opacity: 0.2;\n" - " }\n" - "}\n" - "@-o-keyframes animate-ellipsis-3 {\n" - " 0% {\n" - " opacity: 0.2;\n" - " }\n" - " 25% {\n" - " opacity: 0.2;\n" - " }\n" - " 50% {\n" - " opacity: 0.2;\n" - " }\n" - " 75% {\n" - " opacity: 1;\n" - " }\n" - " 100% {\n" - " opacity: 0.2;\n" - " }\n" - "}\n" - "@keyframes animate-ellipsis-3 {\n" - " 0% {\n" - " opacity: 0.2;\n" - " }\n" - " 25% {\n" - " opacity: 0.2;\n" - " }\n" - " 50% {\n" - " opacity: 0.2;\n" - " }\n" - " 75% {\n" - " opacity: 1;\n" - " }\n" - " 100% {\n" - " opacity: 0.2;\n" - " }\n" - "}\n" - ".animated-ellipsis span:nth-child(1) {\n" - " -webkit-animation: animate-ellipsis-1 3s infinite cubic-bezier(0,1,0,1);\n" - " -moz-animation: animate-ellipsis-1 3s infinite cubic-bezier(0,1,0,1);\n" - " -o-animation: animate-ellipsis-1 3s infinite cubic-bezier(0,1,0,1);\n" - " -ms-animation: animate-ellipsis-1 3s infinite cubic-bezier(0,1,0,1);\n" - " animation: animate-ellipsis-1 3s infinite cubic-bezier(0,1,0,1);\n" - "}\n" - ".animated-ellipsis span:nth-child(2) {\n" - " -webkit-animation: animate-ellipsis-2 3s infinite cubic-bezier(0,1,0,1);\n" - " -moz-animation: animate-ellipsis-2 3s infinite cubic-bezier(0,1,0,1);\n" - " -o-animation: animate-ellipsis-2 3s infinite cubic-bezier(0,1,0,1);\n" - " -ms-animation: animate-ellipsis-2 3s infinite cubic-bezier(0,1,0,1);\n" - " animation: animate-ellipsis-2 3s infinite cubic-bezier(0,1,0,1);\n" - "}\n" - ".animated-ellipsis span:nth-child(3) {\n" - " -webkit-animation: animate-ellipsis-3 3s infinite cubic-bezier(0,1,0,1);\n" - " -moz-animation: animate-ellipsis-3 3s infinite cubic-bezier(0,1,0,1);\n" - " -o-animation: animate-ellipsis-3 3s infinite cubic-bezier(0,1,0,1);\n" - " -ms-animation: animate-ellipsis-3 3s infinite cubic-bezier(0,1,0,1);\n" - " animation: animate-ellipsis-3 3s infinite cubic-bezier(0,1,0,1);\n" - "}\n" - "@-webkit-keyframes add_row {\n" - " from {\n" - " background-color: #f1f1fa;\n" - " }\n" - " to {\n" - " background-color: #fbfbfb;\n" - " }\n" - "}\n" - "@-moz-keyframes add_row {\n" - " from {\n" - " background-color: #f1f1fa;\n" - " }\n" - " to {\n" - " background-color: #fbfbfb;\n" - " }\n" - "}\n" - "@-o-keyframes add_row {\n" - " from {\n" - " background-color: #f1f1fa;\n" - " }\n" - " to {\n" - " background-color: #fbfbfb;\n" - " }\n" - "}\n" - "@keyframes add_row {\n" - " from {\n" - " background-color: #f1f1fa;\n" - " }\n" - " to {\n" - " background-color: #fbfbfb;\n" - " }\n" - "}\n" - ".progress {\n" - " overflow: hidden;\n" - " height: 18px;\n" - " margin-bottom: 18px;\n" - " background-color: #f7f7f7;\n" - " background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);\n" - " background-image: -ms-linear-gradient(top, #f5f5f5, #f9f9f9);\n" - " background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(" - "#f9f9f9));\n" - " background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);\n" - " background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);\n" - " background-image: linear-gradient(top, #f5f5f5, #f9f9f9);\n" - " background-repeat: repeat-x;\n" - " filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5'" - ", endColorstr='#f9f9f9', GradientType=0);\n" - " -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1);\n" - " -moz-box-shadow: inset 0 1px 2px rgba(0,0,0,.1);\n" - " box-shadow: inset 0 1px 2px rgba(0,0,0,.1);\n" - " -webkit-border-radius: 4px;\n" - " -webkit-background-clip: padding-box;\n" - " -moz-border-radius: 4px;\n" - " -moz-background-clip: padding;\n" - " border-radius: 4px;\n" - " background-clip: padding-box;\n" - "}\n" - ".progress .bar {\n" - " width: 0%;\n" - " height: 100%;\n" - " color: #fff;\n" - " float: left;\n" - " font-size: 12px;\n" - " text-align: center;\n" - " text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n" - " -webkit-box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);\n" - " -moz-box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);\n" - " box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);\n" - " -ms-box-sizing: border-box;\n" - " -webkit-box-sizing: border-box;\n" - " -moz-box-sizing: border-box;\n" - " box-sizing: border-box;\n" - " -webkit-transition: width .6s ease;\n" - " -moz-transition: width .6s ease;\n" - " -o-transition: width .6s ease;\n" - " -ms-transition: width .6s ease;\n" - " transition: width .6s ease;\n" - "}\n" - ".progress .bar + .bar {\n" - " -webkit-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0" - ",0,.15);\n" - " -moz-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0," - ".15);\n" - " box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15);" - "\n" - "}\n" - ".progress-striped .bar {\n" - " background-color: #149bdf;\n" - " background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25" - ", rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5," - " transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, " - "rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));" - "\n" - " background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15)" - " 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(" - "255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n" - " background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25" - "%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255" - ", 255, 255, 0.15) 75%, transparent 75%, transparent);\n" - " background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%," - " transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, " - "255, 255, 0.15) 75%, transparent 75%, transparent);\n" - " background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, tr" - "ansparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255" - ", 255, 0.15) 75%, transparent 75%, transparent);\n" - " -o-background-size: 40px 40px;\n" - " -webkit-background-size: 40px 40px;\n" - " -moz-background-size: 40px 40px;\n" - " background-size: 40px 40px;\n" - "}\n" - ".progress.active .bar {\n" - " -webkit-animation: progress-bar-stripes 2s linear infinite;\n" - " -moz-animation: progress-bar-stripes 2s linear infinite;\n" - " -ms-animation: progress-bar-stripes 2s linear infinite;\n" - " -o-animation: progress-bar-stripes 2s linear infinite;\n" - " animation: progress-bar-stripes 2s linear infinite;\n" - "}\n" - ".progress-danger .bar,\n" - ".progress .bar-danger {\n" - " background-color: #dd514c;\n" - " background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);\n" - " background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);\n" - " background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(" - "#c43c35));\n" - " background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);\n" - " background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);\n" - " background-image: linear-gradient(top, #ee5f5b, #c43c35);\n" - " background-repeat: repeat-x;\n" - " filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b'" - ", endColorstr='#c43c35', GradientType=0);\n" - "}\n" - ".progress-danger.progress-striped .bar,\n" - ".progress-striped .bar-danger {\n" - " background-color: #ee5f5b;\n" - " background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25" - ", rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5," - " transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, " - "rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));" - "\n" - " background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15)" - " 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(" - "255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n" - " background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25" - "%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255" - ", 255, 255, 0.15) 75%, transparent 75%, transparent);\n" - " background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%," - " transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, " - "255, 255, 0.15) 75%, transparent 75%, transparent);\n" - " background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, tr" - "ansparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255" - ", 255, 0.15) 75%, transparent 75%, transparent);\n" - "}\n" - ".progress-success .bar,\n" - ".progress .bar-success {\n" - " background-color: #5eb95e;\n" - " background-image: -moz-linear-gradient(top, #62c462, #57a957);\n" - " background-image: -ms-linear-gradient(top, #62c462, #57a957);\n" - " background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(" - "#57a957));\n" - " background-image: -webkit-linear-gradient(top, #62c462, #57a957);\n" - " background-image: -o-linear-gradient(top, #62c462, #57a957);\n" - " background-image: linear-gradient(top, #62c462, #57a957);\n" - " background-repeat: repeat-x;\n" - " filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462'" - ", endColorstr='#57a957', GradientType=0);\n" - "}\n" - ".progress-success.progress-striped .bar,\n" - ".progress-striped .bar-success {\n" - " background-color: #62c462;\n" - " background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25" - ", rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5," - " transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, " - "rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));" - "\n" - " background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15)" - " 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(" - "255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n" - " background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25" - "%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255" - ", 255, 255, 0.15) 75%, transparent 75%, transparent);\n" - " background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%," - " transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, " - "255, 255, 0.15) 75%, transparent 75%, transparent);\n" - " background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, tr" - "ansparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255" - ", 255, 0.15) 75%, transparent 75%, transparent);\n" - "}\n" - ".progress-info .bar,\n" - ".progress .bar-info {\n" - " background-color: #4bb1cf;\n" - " background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);\n" - " background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);\n" - " background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(" - "#339bb9));\n" - " background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);\n" - " background-image: -o-linear-gradient(top, #5bc0de, #339bb9);\n" - " background-image: linear-gradient(top, #5bc0de, #339bb9);\n" - " background-repeat: repeat-x;\n" - " filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de'" - ", endColorstr='#339bb9', GradientType=0);\n" - "}\n" - ".progress-info.progress-striped .bar,\n" - ".progress-striped .bar-info {\n" - " background-color: #5bc0de;\n" - " background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25" - ", rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5," - " transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, " - "rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));" - "\n" - " background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15)" - " 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(" - "255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n" - " background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25" - "%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255" - ", 255, 255, 0.15) 75%, transparent 75%, transparent);\n" - " background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%," - " transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, " - "255, 255, 0.15) 75%, transparent 75%, transparent);\n" - " background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, tr" - "ansparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255" - ", 255, 0.15) 75%, transparent 75%, transparent);\n" - "}\n" - ".progress-warning .bar,\n" - ".progress .bar-warning {\n" - " background-color: #faa732;\n" - " background-image: -moz-linear-gradient(top, #fbb450, #f89406);\n" - " background-image: -ms-linear-gradient(top, #fbb450, #f89406);\n" - " background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(" - "#f89406));\n" - " background-image: -webkit-linear-gradient(top, #fbb450, #f89406);\n" - " background-image: -o-linear-gradient(top, #fbb450, #f89406);\n" - " background-image: linear-gradient(top, #fbb450, #f89406);\n" - " background-repeat: repeat-x;\n" - " filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450'" - ", endColorstr='#f89406', GradientType=0);\n" - "}\n" - ".progress-warning.progress-striped .bar,\n" - ".progress-striped .bar-warning {\n" - " background-color: #fbb450;\n" - " background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25" - ", rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5," - " transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, " - "rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));" - "\n" - " background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15)" - " 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(" - "255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n" - " background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25" - "%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255" - ", 255, 255, 0.15) 75%, transparent 75%, transparent);\n" - " background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%," - " transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, " - "255, 255, 0.15) 75%, transparent 75%, transparent);\n" - " background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, tr" - "ansparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255" - ", 255, 0.15) 75%, transparent 75%, transparent);\n" - "}\n" - "/*\n" - " ----------------------------\n" - " Updates\n" - " ----------------------------\n" - "*/\n" - "#updates_container {\n" - " width: 940px;\n" - " margin: 15px auto 0px auto;\n" - "}\n" - "#updates_container .settings {\n" - " display: none;\n" - " width: 895px;\n" - " margin: 20px auto 0px auto;\n" - " padding: 7px 35px 7px 10px;\n" - "}\n" - "#updates_container .settings .updates {\n" - " margin-top: 6px;\n" - " float: left;\n" - "}\n" - "#updates_container .settings .options_container {\n" - " float: right;\n" - "}\n" - "#updates_container .settings .close {\n" - " float: right;\n" - "}\n" - "#updates_container .settings .btn {\n" - " float: right;\n" - " padding: 4px 14px 4px;\n" - " margin-right: 20px;\n" - "}\n" - "#updates_container .settings .release_link a {\n" - " font-weight: normal;\n" - " color: #0088cc;\n" - "}\n" - "#updates_container .changeul {\n" - " padding: 0px;\n" - " margin: 0px;\n" - " list-style: none;\n" - "}\n" - "#updates_container .changeul .change {\n" - " padding: 0px;\n" - " margin: 0px 0px 0px 20px;\n" - "}\n" - ".reconfigure-panel {\n" - " font-family: \"Open Sans\";\n" - "}\n" - ".reconfigure-panel .info {\n" - " display: -webkit-box;\n" - " display: -moz-box;\n" - " display: -webkit-flex;\n" - " display: -ms-flexbox;\n" - " display: flex;\n" - " -ms-flex-align: center;\n" - " -webkit-align-items: center;\n" - " align-items: center;\n" - " -ms-flex-pack: justify;\n" - " -webkit-justify-content: space-between;\n" - " justify-content: space-between;\n" - "}\n" - ".reconfigure-panel .info div {\n" - " vertical-align: middle;\n" - "}\n" - ".reconfigure-panel button {\n" - " height: 75%;\n" - " margin: 0px 5px;\n" - "}\n" - ".reconfigure-panel span.value {\n" - " padding: 0px 6px 0px 6px;\n" - " color: #f7ae41;\n" - " font-size: 24px;\n" - " line-height: 48px;\n" - "}\n" - ".reconfigure-panel span.caption {\n" - " color: #a9a9a9;\n" - " font-size: 16px;\n" - "}\n" - "#modal-dialog {\n" - " position: fixed;\n" - " top: 0px;\n" - " left: 0px;\n" - " width: 100%;\n" - " height: 100%;\n" - " display: none;\n" - " z-index: 1050;\n" - " -ms-flex-pack: center;\n" - " -webkit-justify-content: center;\n" - " justify-content: center;\n" - " -ms-flex-align: center;\n" - " -webkit-align-items: center;\n" - " align-items: center;\n" - "}\n" - "#modal-dialog.visible {\n" - " display: -webkit-box;\n" - " display: -moz-box;\n" - " display: -webkit-flex;\n" - " display: -ms-flexbox;\n" - " display: flex;\n" - "}\n" - "#modal-dialog .modal {\n" - " position: static;\n" - " margin: 0px;\n" - " top: auto;\n" - " left: auto;\n" - "}\n" - ".reconfigure-modal {\n" - " display: -webkit-box;\n" - " display: -moz-box;\n" - " display: -webkit-flex;\n" - " display: -ms-flexbox;\n" - " display: flex;\n" - " -ms-flex-direction: column;\n" - " -webkit-flex-direction: column;\n" - " flex-direction: column;\n" - " -ms-flex-pack: justify;\n" - " -webkit-justify-content: space-between;\n" - " justify-content: space-between;\n" - "}\n" - ".reconfigure-modal .modal-body .expandbox {\n" - " min-height: 0px;\n" - " max-height: 300px;\n" - " transition: min-height 0.2s cubic-bezier(0.6, 0.04, 0.98, 0.335);\n" - "}\n" - "@media (min-height: 582px) {\n" - " .reconfigure-modal .modal-body .expandbox.expanded {\n" - " min-height: 323px;\n" - " }\n" - "}\n" - "@media (max-height: 582px) {\n" - " .reconfigure-modal .modal-body .expandbox.expanded {\n" - " min-height: 173px;\n" - " }\n" - "}\n" - ".reconfigure-modal .reconfigure-diff {\n" - " padding: 0px 6px;\n" - " background-image: url('images/thinker_on_hill.svg');\n" - " background-repeat: no-repeat;\n" - " background-position: calc(50% + 173px) calc(50% + 105px);\n" - " background-size: auto 336px;\n" - " background-attachment: fixed;\n" - " border: 1px solid #fff;\n" - " border-radius: 3px;\n" - " background-color: #fcfcfc;\n" - " height: 0;\n" - " transition: border-color 0.2s, height 0.2s cubic-bezier(0.6, 0.04, 0.98, 0" - ".335);\n" - "}\n" - ".reconfigure-modal .reconfigure-diff.expanded {\n" - " border-color: #d8d8d8;\n" - "}\n" - "@media (min-height: 582px) {\n" - " .reconfigure-modal .reconfigure-diff.expanded {\n" - " height: 288px;\n" - " }\n" - "}\n" - "@media (max-height: 582px) {\n" - " .reconfigure-modal .reconfigure-diff.expanded {\n" - " height: 138px;\n" - " }\n" - "}\n" - ".reconfigure-modal .reconfigure-diff:first-child {\n" - " margin-top: 6px;\n" - "}\n" - ".reconfigure-modal .reconfigure-diff:last-child {\n" - " margin-bottom: 6px;\n" - "}\n" - ".reconfigure-modal .alert_modal {\n" - " display: none;\n" - "}\n" - ".reconfigure-modal .alert.error {\n" - " border: 1px solid #D8C8C8;\n" - " text-align: left;\n" - " background-color: #F2E4E4;\n" - " font-family: \"Open Sans\";\n" - " font-size: 13px;\n" - " border-radius: 3px;\n" - " color: #4d4d4d;\n" - " position: relative;\n" - " padding: 0px;\n" - " display: none;\n" - "}\n" - ".reconfigure-modal .alert.error p {\n" - " margin: 10px 10px 10px 33px !important;\n" - "}\n" - ".reconfigure-modal .alert.error::before {\n" - " content: \"\\f100\";\n" - " font-family: 'Ionicons';\n" - " font-size: 18px;\n" - " color: #df8181;\n" - " position: absolute;\n" - " top: 10px;\n" - " left: 10px;\n" - " height: 100%;\n" - "}\n" - ".reconfigure-modal .alert.error.shown {\n" - " display: block;\n" - "}\n" - ".reconfigure-modal .alert.error p {\n" - " display: none;\n" - "}\n" - ".reconfigure-modal .alert.error p.shown {\n" - " display: block;\n" - "}\n" - ".reconfigure-modal .modal-body {\n" - " max-height: 100%;\n" - " overflow-y: hidden;\n" - " padding-bottom: 0px;\n" - "}\n" - ".reconfigure-modal .modal-footer {\n" - " display: block;\n" - "}\n" - ".reconfigure-modal .modal-footer:after {\n" - " content: none;\n" - " display: none;\n" - " clear: none;\n" - "}\n" - ".reconfigure-modal .intro {\n" - " display: -webkit-box;\n" - " display: -moz-box;\n" - " display: -webkit-flex;\n" - " display: -ms-flexbox;\n" - " display: flex;\n" - " -ms-flex-direction: column;\n" - " -webkit-flex-direction: column;\n" - " flex-direction: column;\n" - " -ms-flex-align: stretch;\n" - " -webkit-align-items: stretch;\n" - " align-items: stretch;\n" - "}\n" - ".reconfigure-modal .intro .infobox {\n" - " border: 1px solid #C9D9E1;\n" - " text-align: left;\n" - " background-color: #eaf5fa;\n" - " font-family: \"Open Sans\";\n" - " font-size: 13px;\n" - " border-radius: 3px;\n" - " color: #4d4d4d;\n" - " position: relative;\n" - " padding: 0px;\n" - " margin-top: 10px;\n" - "}\n" - ".reconfigure-modal .intro .infobox p {\n" - " margin: 10px 10px 10px 33px !important;\n" - "}\n" - ".reconfigure-modal .intro .infobox::before {\n" - " content: \"\\f149\";\n" - " font-family: 'Ionicons';\n" - " font-size: 18px;\n" - " color: #70b8db;\n" - " position: absolute;\n" - " top: 10px;\n" - " left: 10px;\n" - " height: 100%;\n" - "}\n" - ".reconfigure-modal .intro .reconfigure-form {\n" - " display: -webkit-box;\n" - " display: -moz-box;\n" - " display: -webkit-flex;\n" - " display: -ms-flexbox;\n" - " display: flex;\n" - " -ms-flex-align: flex-start;\n" - " -webkit-align-items: flex-start;\n" - " align-items: flex-start;\n" - " -ms-flex-pack: distribute;\n" - " -webkit-justify-content: space-around;\n" - " justify-content: space-around;\n" - " margin: 10px 0px;\n" - "}\n" - ".reconfigure-modal .intro .reconfigure-form span.caption {\n" - " color: #555;\n" - " font-size: 14px;\n" - "}\n" - ".reconfigure-modal .intro .reconfigure-form .inline-edit {\n" - " margin: 4px 6px 3px 0px;\n" - " width: 30px;\n" - " padding: 3px 4px;\n" - " height: 28px;\n" - " font-size: 20px;\n" - " font-family: \"Open Sans\";\n" - "}\n" - ".reconfigure-modal .intro .reconfigure-form .max.caption {\n" - " padding: 0px 1px;\n" - " color: #a9a9a9;\n" - " font-size: 10px;\n" - "}\n" - ".reconfigure-modal a.expand-tree {\n" - " padding-bottom: 5px;\n" - " display: block;\n" - " text-align: left;\n" - "}\n" - ".reconfigure-modal a.expand-tree:hover {\n" - " text-decoration: none;\n" - "}\n" - ".reconfigure-modal a.expand-tree::before {\n" - " content: url('images/arrow_right.png');\n" - " padding: 0 10px 0 0;\n" - "}\n" - ".reconfigure-modal a.expand-tree.expanded::before {\n" - " content: url('images/arrow_down.png');\n" - " padding: 0 10px 0 0;\n" - "}\n" - ".reconfigure-modal .reconfigure-diff {\n" - " overflow-y: auto;\n" - " margin-bottom: 10px;\n" - "}\n" - ".reconfigure-modal .reconfigure-diff p {\n" - " font-size: 12px !important;\n", - 65463) + std::string( - " line-height: 25px !important;\n" - "}\n" - ".reconfigure-modal .reconfigure-diff li.parent .parent-info p.name {\n" - " width: 100%;\n" - "}\n" - ".reconfigure-modal .reconfigure-diff li.parent.deleted {\n" - " text-decoration: line-through;\n" - "}\n" - ".reconfigure-modal .reconfigure-diff li.invalid {\n" - " width: 100%;\n" - " border-radius: 3px;\n" - " margin-top: 5px;\n" - " padding: 10px;\n" - " background-image: none;\n" - "}\n" - ".reconfigure-modal .reconfigure-diff .tree-line {\n" - " top: 12px;\n" - "}\n" - ".reconfigure-modal .reconfigure-diff.tree-view li p.role {\n" - " position: static;\n" - " width: 55%;\n" - " float: right;\n" - " padding-left: 20px;\n" - " background-size: 14px 14px;\n" - "}\n" - ".reconfigure-modal .reconfigure-diff.tree-view li .role-secondary {\n" - " font-weight: normal;\n" - " background: left center no-repeat url('images/grid-icon.png');\n" - "}\n" - ".reconfigure-modal .reconfigure-diff.tree-view li .role-primary {\n" - " font-weight: bold;\n" - " background: left center no-repeat url('images/layers-icon.png');\n" - "}\n" - ".reconfigure-modal .reconfigure-diff div.server-info.child-info {\n" - " height: 25px;\n" - " margin-left: 30px;\n" - " padding-left: 25px;\n" - " background-size: 20px 15px !important;\n" - " border-bottom: 0px;\n" - "}\n" - ".reconfigure-modal .reconfigure-diff li.deleted {\n" - " background-color: rgba(255, 0, 0, 0.05);\n" - "}\n" - ".reconfigure-modal .reconfigure-diff li.deleted .role {\n" - " text-decoration: line-through;\n" - "}\n" - ".reconfigure-modal .reconfigure-diff li.added {\n" - " background-color: rgba(0, 255, 0, 0.1);\n" - "}\n" - ".reconfigure-modal .reconfigure-diff li.role-change {\n" - " background-color: rgba(255, 255, 0, 0.1);\n" - "}\n" - ".reconfigure-modal .reconfigure-diff .change .deleted {\n" - " text-decoration: line-through;\n" - "}\n" - ".reconfigure-modal .reconfigure-diff .change .added {\n" - " background-color: transparent;\n" - " color: #f7ae41;\n" - "}\n", - 1671 ) }, - { "/cluster-min.js", std::string( - "(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=" - "=\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new" - " Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f" - "}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e]" - ";return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof requ" - "ire==\"function\"&&require;for(var o=0;o\":t.name}),this.timer=driver.run(e,5e3,func" - "tion(t){return function(e,i){var r,s,n,o,a,d,h,p;if(null!=e)return console.l" - "og(e);for(a=i.tables,r=0,n=a.length;rl[r])return 1}return 0}" - ",e.prototype.render=function(){return this},e.prototype.deactivate_update=fu" - "nction(){if(this.$el.slideUp(\"fast\"),null!=window.localStorage)return wind" - "ow.localStorage.check_updates=JSON.stringify(!1)},e}(Backbone.View),IsDiscon" - "nected=function(t){function e(){return this.display_fail=bind(this.display_f" - "ail,this),this.animate_loading=bind(this.animate_loading,this),this.render=b" - "ind(this.render,this),this.initialize=bind(this.initialize,this),e.__super__" - ".constructor.apply(this,arguments)}return extend(e,t),e.prototype.el=\"body\"" - ",e.prototype.className=\"is_disconnected_view\",e.prototype.template=require" - "(\"../handlebars/is_disconnected.hbs\"),e.prototype.message=require(\"../han" - "dlebars/is_disconnected_message.hbs\"),e.prototype.initialize=function(){ret" - "urn this.render(),setInterval(function(){return driver.run_once(r.expr(1))}," - "2e3)},e.prototype.render=function(){return this.$(\"#modal-dialog > .modal\"" - ").css(\"z-index\",\"1\"),this.$(\".modal-backdrop\").remove(),this.$el.appen" - "d(this.template()),this.$(\".is_disconnected\").modal({show:!0,backdrop:\"st" - "atic\"}),this.animate_loading()},e.prototype.animate_loading=function(){if(t" - "his.$(\".three_dots_connecting\"))return\"...\"===this.$(\".three_dots_conne" - "cting\").html()?this.$(\".three_dots_connecting\").html(\"\"):this.$(\".thre" - "e_dots_connecting\").append(\".\"),setTimeout(this.animate_loading,300)},e.p" - "rototype.display_fail=function(){return this.$(\".animation_state\").fadeOut" - "(\"slow\",function(t){return function(){return $(\".reconnecting_state\").ht" - "ml(t.message),$(\".animation_state\").fadeIn(\"slow\")}}(this))},e}(Backbone" - ".View),exports.MainContainer=MainContainer,exports.OptionsView=OptionsView,e" - "xports.AlertUpdates=AlertUpdates,exports.IsDisconnected=IsDisconnected;\n" - "},{\"../handlebars/body-structure.hbs\":\"/rethinkdb/admin/static/handlebars" - "/body-structure.hbs\",\"../handlebars/has_update.hbs\":\"/rethinkdb/admin/st" - "atic/handlebars/has_update.hbs\",\"../handlebars/is_disconnected.hbs\":\"/re" - "thinkdb/admin/static/handlebars/is_disconnected.hbs\",\"../handlebars/is_dis" - "connected_message.hbs\":\"/rethinkdb/admin/static/handlebars/is_disconnected" - "_message.hbs\",\"../handlebars/options_view.hbs\":\"/rethinkdb/admin/static/" - "handlebars/options_view.hbs\",\"./app.coffee\":\"/rethinkdb/admin/static/cof" - "fee/app.coffee\",\"./modals.coffee\":\"/rethinkdb/admin/static/coffee/modals" - ".coffee\",\"./models.coffee\":\"/rethinkdb/admin/static/coffee/models.coffee" - "\",\"./navbar.coffee\":\"/rethinkdb/admin/static/coffee/navbar.coffee\",\"./" - "router.coffee\":\"/rethinkdb/admin/static/coffee/router.coffee\",\"./topbar." - "coffee\":\"/rethinkdb/admin/static/coffee/topbar.coffee\",\"rethinkdb\":\"re" - "thinkdb\",\"rethinkdb-version\":\"rethinkdb-version\"}],\"/rethinkdb/admin/s" - "tatic/coffee/dashboard/dashboard.coffee\":[function(require,module,exports){" - "\n" - "var View,app,driver,h,log_view,models,panels,r,system_db,util,vdom,vis,bind=" - "function(e,r){return function(){return e.apply(r,arguments)}},extend=functio" - "n(e,r){function t(){this.constructor=e}for(var n in r)hasProp.call(r,n)&&(e[" - "n]=r[n]);return t.prototype=r.prototype,e.prototype=new t,e.__super__=r.prot" - "otype,e},hasProp={}.hasOwnProperty;vdom=require(\"../vdom_util.coffee\"),h=r" - "equire(\"virtual-dom/h\"),r=require(\"rethinkdb\"),panels=require(\"./panels" - ".coffee\"),util=require(\"../util.coffee\"),app=require(\"../app.coffee\"),d" - "river=app.driver,system_db=app.system_db,models=require(\"../models.coffee\"" - "),log_view=require(\"../log_view.coffee\"),vis=require(\"../vis.coffee\"),Vi" - "ew=function(e){function t(){return this.render_vdom=bind(this.render_vdom,th" - "is),this.cleanup=bind(this.cleanup,this),t.__super__.constructor.apply(this," - "arguments)}return extend(t,e),t.prototype.events={\"click .view-logs\":\"sho" - "w_all_logs\"},t.prototype.fetch={5e3:{servers:panels.ServersModel,tables:pan" - "els.TablesModel,indexes:panels.IndexesModel,resources:panels.ResourcesModel}" - "},t.prototype.init=function(e){return this.stats=new models.Stats,this.stats" - "_timer=driver.run(r.db(system_db).table(\"stats\").get([\"cluster\"]).do(fun" - "ction(e){return{keys_read:e(\"query_engine\")(\"read_docs_per_sec\"),keys_se" - "t:e(\"query_engine\")(\"written_docs_per_sec\")}}),1e3,this.stats.on_result)" - "},t.prototype.cleanup=function(){return driver.stop_timer(this.stats_timer)}" - ",t.prototype.show_all_logs=function(){return app.main.router.navigate(\"#log" - "s\",{trigger:!0})},t.prototype.render_vdom=function(){return null!=this.quer" - "y_error?(console.log(this.query_error.msg),h(\"div.section\",[h(\"h2.title\"" - ",\"Error\"),h(\"p\",[\"The request to retrieve data failed. You can try to \"" - ",h(\"a\",{href:\"#\"},\"refresh\")]),h(\"p\",\"Error:\"),null!=this.query_er" - "ror.msg?h(\"pre.main_query_error\",this.query_error.msg):void 0])):h(\"div.d" - "ashboard-container\",[h(\"div.section.cluster-status\",[h(\"div.panel.server" - "s\",new vdom.BackboneViewWidget(function(e){return function(){return new pan" - "els.ServersView({model:e.submodels.servers})}}(this))),h(\"div.panel.tables\"" - ",new vdom.BackboneViewWidget(function(e){return function(){return new panels" - ".TablesView({model:e.submodels.tables})}}(this))),h(\"div.panel.indexes\",ne" - "w vdom.BackboneViewWidget(function(e){return function(){return new panels.In" - "dexesView({model:e.submodels.indexes})}}(this))),h(\"div.panel.resources\",n" - "ew vdom.BackboneViewWidget(function(e){return function(){return new panels.R" - "esourcesView({model:e.submodels.resources})}}(this)))]),h(\"div.section#clus" - "ter_performance_container\",new vdom.BackboneViewWidget(function(e){return f" - "unction(){return new vis.OpsPlot(e.stats.get_stats,{width:833,height:300,sec" - "onds:119,type:\"cluster\"})}}(this))),h(\"div.section.recent-logs\",[h(\"but" - "ton.btn.btn-primary.view-logs\",\"View All\"),h(\"h2.title\",\"Recent log en" - "tries\"),h(\"div.recent-log-entries-container\",new vdom.BackboneViewWidget(" - "function(e){return function(){return new log_view.LogsContainer({limit:5,que" - "ry:driver.queries.all_logs})}}(this)))])])},t}(vdom.VirtualDomView),exports." - "View=View;\n" - "},{\"../app.coffee\":\"/rethinkdb/admin/static/coffee/app.coffee\",\"../log_" - "view.coffee\":\"/rethinkdb/admin/static/coffee/log_view.coffee\",\"../models" - ".coffee\":\"/rethinkdb/admin/static/coffee/models.coffee\",\"../util.coffee\"" - ":\"/rethinkdb/admin/static/coffee/util.coffee\",\"../vdom_util.coffee\":\"/r" - "ethinkdb/admin/static/coffee/vdom_util.coffee\",\"../vis.coffee\":\"/rethink" - "db/admin/static/coffee/vis.coffee\",\"./panels.coffee\":\"/rethinkdb/admin/s" - "tatic/coffee/dashboard/panels.coffee\",\"rethinkdb\":\"rethinkdb\",\"virtual" - "-dom/h\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb" - "-webui/node_modules/virtual-dom/h.js\"}],\"/rethinkdb/admin/static/coffee/da" - "shboard/panels.coffee\":[function(require,module,exports){\n" - "var IndexesModel,IndexesView,ResourcesModel,ResourcesView,ServersModel,Serve" - "rsView,TablesModel,TablesView,app,h,r,render_panel,systable,system_db,util,v" - "dom,extend=function(e,t){function r(){this.constructor=e}for(var n in t)hasP" - "rop.call(t,n)&&(e[n]=t[n]);return r.prototype=t.prototype,e.prototype=new r," - "e.__super__=t.prototype,e},hasProp={}.hasOwnProperty;r=require(\"rethinkdb\"" - "),h=require(\"virtual-dom/h\"),vdom=require(\"../vdom_util.coffee\"),util=re" - "quire(\"../util.coffee\"),app=require(\"../app.coffee\"),system_db=app.syste" - "m_db,systable=function(e){return function(e){return r.db(system_db).table(e)" - "}}(this),ServersModel=function(e){function t(){return t.__super__.constructo" - "r.apply(this,arguments)}return extend(t,e),t.query=function(e,t){return null" - "==e&&(e=systable(\"server_status\")),null==t&&(t=systable(\"table_config\"))" - ",r.do(e(\"name\").coerceTo(\"array\"),t.concatMap(function(e){return e(\"sha" - "rds\").default([])}).concatMap(function(e){return e(\"replicas\")}).distinct" - "(),function(e,r){return{servers_connected:e.count(),servers_missing:r.setDif" - "ference(e),unknown_missing:t.filter(function(e){return e.hasFields(\"shards\"" - ").not()})(\"name\").isEmpty().not()}})},t.prototype.defaults={servers_connec" - "ted:0,servers_missing:[],unknown_missing:!1},t}(Backbone.Model),ServersView=" - "function(e){function t(){return t.__super__.constructor.apply(this,arguments" - ")}return extend(t,e),t.prototype.render_vdom=function(){var e,t,r,n,s,o;retu" - "rn t=this.model.toJSON(),r=t.servers_missing.length,o=t.unknown_missing,s=o&" - "&r>0?r+\"+\":o&&r<=0?\"1+\":\"\"+r,n=o||0!==r,e=[[h(\"span.highlight\",t.ser" - "vers_connected+\" \"),util.pluralize_noun(\"server\",t.servers_connected),\"" - " connected\"],[h(\"span.highlight\",s+\" \"),util.pluralize_noun(\"server\"," - "t.servers_missing.length),\" missing\"]],render_panel(\"Servers\",n,e)},t}(v" - "dom.VirtualDomView),TablesModel=function(e){function t(){return t.__super__." - "constructor.apply(this,arguments)}return extend(t,e),t.query=function(e){ret" - "urn null==e&&(e=systable(\"table_status\")),{tables_ready:e.count(function(e" - "){return e(\"status\")(\"all_replicas_ready\")}),tables_unready:e.count(func" - "tion(e){return e(\"status\")(\"all_replicas_ready\").not()})}},t.prototype.d" - "efaults={tables_ready:0,tables_unready:0},t}(Backbone.Model),TablesView=func" - "tion(e){function t(){return t.__super__.constructor.apply(this,arguments)}re" - "turn extend(t,e),t.prototype.render_vdom=function(){var e,t,r;return t=this." - "model.toJSON(),r=t.tables_unready>0,e=[[h(\"span.highlight\",t.tables_ready+" - "\" \"),util.pluralize_noun(\"table\",t.tables_ready),\" ready\"],[h(\"span.h" - "ighlight\",t.tables_unready+\" \"),util.pluralize_noun(\"table\",t.tables_un" - "ready),\" with issues\"]],render_panel(\"Tables\",r,e)},t}(vdom.VirtualDomVi" - "ew),IndexesModel=function(e){function t(){return t.__super__.constructor.app" - "ly(this,arguments)}return extend(t,e),t.query=function(e,t,r,n){return null=" - "=e&&(e=systable(\"table_config\")),null==t&&(t=systable(\"table_status\")),n" - "ull==r&&(r=systable(\"jobs\")),null==n&&(n=systable(\"current_issues\")),{nu" - "m_sindexes:e.sum(function(e){return e(\"indexes\").count()}),sindexes_constr" - "ucting:r.filter({type:\"index_construction\"})(\"info\").map(function(e){ret" - "urn{db:e(\"db\"),table:e(\"table\"),index:e(\"index\"),progress:e(\"progress" - "\")}}).coerceTo(\"array\")}},t.prototype.defaults={num_sindexes:0,sindexes_c" - "onstructing:[]},t}(Backbone.Model),IndexesView=function(e){function t(){retu" - "rn t.__super__.constructor.apply(this,arguments)}return extend(t,e),t.protot" - "ype.render_vdom=function(){var e,t,r;return t=this.model.toJSON(),r=!1,e=[[h" - "(\"span.highlight\",\"\"+t.num_sindexes),\" secondary indexes\"],[h(\"span.h" - "ighlight\",\"\"+t.sindexes_constructing.length),\" indexes building\"]],rend" - "er_panel(\"Indexes\",r,e)},t}(vdom.VirtualDomView),ResourcesModel=function(e" - "){function t(){return t.__super__.constructor.apply(this,arguments)}return e" - "xtend(t,e),t.query=function(e,t){return null==e&&(e=systable(\"stats\")),nul" - "l==t&&(t=systable(\"server_status\")),{cache_used:e.filter(function(e){retur" - "n e(\"id\").contains(\"table_server\")})(\"storage_engine\")(\"cache\")(\"in" - "_use_bytes\").sum(),cache_total:t(\"process\")(\"cache_size_mb\").map(functi" - "on(e){return e.mul(1048576)}).sum(),disk_used:e.filter(function(e){return e(" - "\"id\").contains(\"table_server\")})(\"storage_engine\")(\"disk\")(\"space_u" - "sage\").map(function(e){return e(\"data_bytes\").add(e(\"garbage_bytes\")).a" - "dd(e(\"metadata_bytes\")).add(e(\"preallocated_bytes\"))}).sum()}},t.prototy" - "pe.defaults={cache_used:0,cache_total:0,disk_used:0},t}(Backbone.Model),Reso" - "urcesView=function(e){function t(){return t.__super__.constructor.apply(this" - ",arguments)}return extend(t,e),t.prototype.render_vdom=function(){var e,t,r," - "n,s,o,u;return o=this.model.toJSON(),u=!1,e=Math.ceil(o.cache_used/o.cache_t" - "otal*100),r=util.format_bytes(o.cache_used),t=util.format_bytes(o.cache_tota" - "l),n=util.format_bytes(o.disk_used),s=[[h(\"span.highlight\",e+\"%\"),\" cac" - "he used\"],[h(\"span.highlight\",\"\"+n),\" disk used\"]],render_panel(\"Res" - "ources\",u,s)},t}(vdom.VirtualDomView),render_panel=function(e){return funct" - "ion(e,t,r){var n;return n=t?\"problems-detected\":\"no-problems-detected\",h" - "(\"div.dashboard-panel.servers\",[h(\"div.status.\"+n,[h(\"h3\",e),h(\"ul\"," - "r.map(function(e){return h(\"li\",e)}))])])}}(this),exports.ServersView=Serv" - "ersView,exports.ServersModel=ServersModel,exports.TablesView=TablesView,expo" - "rts.TablesModel=TablesModel,exports.IndexesView=IndexesView,exports.IndexesM" - "odel=IndexesModel,exports.ResourcesView=ResourcesView,exports.ResourcesModel" - "=ResourcesModel;\n" - "},{\"../app.coffee\":\"/rethinkdb/admin/static/coffee/app.coffee\",\"../util" - ".coffee\":\"/rethinkdb/admin/static/coffee/util.coffee\",\"../vdom_util.coff" - "ee\":\"/rethinkdb/admin/static/coffee/vdom_util.coffee\",\"rethinkdb\":\"ret" - "hinkdb\",\"virtual-dom/h\":\"/rethinkdb/build/external/admin-deps_2.0.4/node" - "_modules/rethinkdb-webui/node_modules/virtual-dom/h.js\"}],\"/rethinkdb/admi" - "n/static/coffee/dataexplorer.coffee\":[function(require,module,exports){\n" - "var Container,DEFAULTS,DriverHandler,HistoryView,OptionsView,PagedResultView" - ",ProfileView,QueryResult,RawView,ResultView,ResultViewWrapper,TableView,Tabl" - "eViewerView,TreeView,app,dataexplorer_state,driver,r,system_db,util,bind=fun" - "ction(t,e){return function(){return t.apply(e,arguments)}},hasProp={}.hasOwn" - "Property,extend=function(t,e){function i(){this.constructor=t}for(var r in e" - ")hasProp.call(e,r)&&(t[r]=e[r]);return i.prototype=e.prototype,t.prototype=n" - "ew i,t.__super__=e.prototype,t};app=require(\"./app.coffee\"),system_db=app." - "system_db,driver=app.driver,util=require(\"./util.coffee\"),r=require(\"reth" - "inkdb\"),DEFAULTS={current_query:null,query_result:null,cursor_timed_out:!0," - "view:\"tree\",history_state:\"hidden\",last_keys:[],last_columns_size:{},opt" - "ions_state:\"hidden\",options:{suggestions:!0,electric_punctuation:!1,profil" - "er:!1,query_limit:40},history:[],focus_on_codemirror:!0},Object.freeze(DEFAU" - "LTS),Object.freeze(DEFAULTS.options),dataexplorer_state=_.extend({},DEFAULTS" - "),QueryResult=function(){function t(t){this.at_beginning=bind(this.at_beginn" - "ing,this),this.slice=bind(this.slice,this),this.drop_before=bind(this.drop_b" - "efore,this),this.force_end_gracefully=bind(this.force_end_gracefully,this),t" - "his.size=bind(this.size,this),this.set_error=bind(this.set_error,this),this." - "fetch_next=bind(this.fetch_next,this),this.fetch_next_many=bind(this.fetch_n" - "ext_many,this),this.discard=bind(this.discard,this),this.set=bind(this.set,t" - "his);var e,i,r;if(this.has_profile=t.has_profile,this.current_query=t.curren" - "t_query,this.raw_query=t.raw_query,this.driver_handler=t.driver_handler,this" - ".ready=!1,this.position=0,this.server_duration=null,null!=t.events){r=t.even" - "ts;for(e in r)hasProp.call(r,e)&&(i=r[e],this.on(e,i))}}return _.extend(t.pr" - "ototype,Backbone.Events),t.prototype.set=function(t,e){var i;return null!=t?" - "this.set_error(t):this.discard_results?void 0:(this.has_profile?(this.profil" - "e=e.profile,this.server_duration=e.profile.reduce(function(t,e){return t+(e[" - "\"duration(ms)\"]||e[\"mean_duration(ms)\"])},0),i=e.value):(this.profile=nu" - "ll,this.server_duration=null!=e?e.profile[0][\"duration(ms)\"]:void 0,i=e.va" - "lue),null==i||\"function\"!=typeof i._next||i instanceof Array?(this.type=\"" - "value\",this.value=i,this.ended=!0):(this.type=\"cursor\",this.results=[],th" - "is.results_offset=0,this.cursor=i,this.is_feed=this.cursor.toString().match(" - "/\\[object .*Feed\\]/),this.missing=0,this.ended=!1,this.server_duration=nul" - "l),this.ready=!0,this.trigger(\"ready\",this))},t.prototype.discard=function" - "(){var t,e;return this.trigger(\"discard\",this),this.off(),this.type=\"disc" - "arded\",this.discard_results=!0,delete this.profile,delete this.value,delete" - " this.results,delete this.results_offset,null!=(e=this.cursor)&&\"function\"" - "==typeof(t=e.close()).catch&&t.catch(function(){return null}),delete this.cu" - "rsor},t.prototype.fetch_next_many=function(t){var e;if(!this.ended)try{retur" - "n this.driver_handler.cursor_next_many(this.cursor,t,function(e){return func" - "tion(i,r){var s,n,o;if(!e.discard_results){for(s=0,n=r.length;s0&&e.trigger(\"add\",e),i&&!e.ended&&e.set_er" - "ror(i),r.lengththis.results_offset)return this.results=this.results.slice(t-this." - "results_offset),this.results_offset=t},t.prototype.slice=function(t,e){retur" - "n t<0?t=this.results.length+t:t-=this.results_offset,t=Math.max(0,t),null!=e" - "?(e<0?e=this.results.length+e:e-=this.results_offset,e=Math.min(this.results" - ".length,e),this.results.slice(t,e)):this.results.slice(t)},t.prototype.at_be" - "ginning=function(){return null==this.results_offset||0===this.results_offset" - "},t}(),Container=function(superClass){function Container(){return this.remov" - "e=bind(this.remove,this),this.display_full=bind(this.display_full,this),this" - ".display_normal=bind(this.display_normal,this),this.toggle_size=bind(this.to" - "ggle_size,this),this.handle_gutter_click=bind(this.handle_gutter_click,this)" - ",this.error_on_connect=bind(this.error_on_connect,this),this.load_query_text" - "=bind(this.load_query_text,this),this.clear_query=bind(this.clear_query,this" - "),this.separate_queries=bind(this.separate_queries,this),this.evaluate=bind(" - "this.evaluate,this),this.execute_portion=bind(this.execute_portion,this),thi" - "s.toggle_executing=bind(this.toggle_executing,this),this.execute_query=bind(" - "this.execute_query,this),this.abort_query=bind(this.abort_query,this),this.e" - "xtract_database_used=bind(this.extract_database_used,this),this.extend_descr" - "iption=bind(this.extend_description,this),this.mouseout_suggestion=bind(this" - ".mouseout_suggestion,this),this.mouseover_suggestion=bind(this.mouseover_sug" - "gestion,this),this.select_suggestion=bind(this.select_suggestion,this),this." - "write_suggestion=bind(this.write_suggestion,this),this.remove_highlight_sugg" - "estion=bind(this.remove_highlight_suggestion,this),this.highlight_suggestion" - "=bind(this.highlight_suggestion,this),this.move_suggestion=bind(this.move_su" - "ggestion,this),this.show_or_hide_arrow=bind(this.show_or_hide_arrow,this),th" - "is.hide_suggestion_and_description=bind(this.hide_suggestion_and_description" - ",this),this.hide_description=bind(this.hide_description,this),this.hide_sugg" - "estion=bind(this.hide_suggestion,this),this.show_description=bind(this.show_" - "description,this),this.show_suggestion_without_moving=bind(this.show_suggest" - "ion_without_moving,this),this.show_suggestion=bind(this.show_suggestion,this" - "),this.create_safe_regex=bind(this.create_safe_regex,this),this.create_sugge" - "stion=bind(this.create_suggestion,this),this.count_group_level=bind(this.cou" - "nt_group_level,this),this.extract_data_from_query=bind(this.extract_data_fro" - "m_query,this),this.get_last_key=bind(this.get_last_key,this),this.last_eleme" - "nt_type_if_incomplete=bind(this.last_element_type_if_incomplete,this),this.h" - "andle_keypress=bind(this.handle_keypress,this),this.count_not_closed_bracket" - "s=bind(this.count_not_closed_brackets,this),this.count_char=bind(this.count_" - "char,this),this.move_cursor=bind(this.move_cursor,this),this.remove_next=bin" - "d(this.remove_next,this),this.insert_next=bind(this.insert_next,this),this.g" - "et_previous_char=bind(this.get_previous_char,this),this.get_next_char=bind(t" - "his.get_next_char,this),this.pair_char=bind(this.pair_char,this),this.handle" - "_click=bind(this.handle_click,this),this.on_blur=bind(this.on_blur,this),thi" - "s.init_after_dom_rendered=bind(this.init_after_dom_rendered,this),this.rende" - "r=bind(this.render,this),this.handle_mousedown=bind(this.handle_mousedown,th" - "is),this.handle_mouseup=bind(this.handle_mouseup,this),this.handle_mousemove" - "=bind(this.handle_mousemove,this),this.fetch_data=bind(this.fetch_data,this)" - ",this.initialize=bind(this.initialize,this),this.save_current_query=bind(thi" - "s.save_current_query,this),this.clear_history=bind(this.clear_history,this)," - "this.save_query=bind(this.save_query,this),this.set_docs=bind(this.set_docs," - "this),this.set_doc_description=bind(this.set_doc_description,this),this.expa" - "nd_types=bind(this.expand_types,this),this.convert_type=bind(this.convert_ty" - "pe,this),this.activate_overflow=bind(this.activate_overflow,this),this.deact" - "ivate_overflow=bind(this.deactivate_overflow,this),this.adjust_collapsible_p" - "anel_height=bind(this.adjust_collapsible_panel_height,this),this.move_arrow=" - "bind(this.move_arrow,this),this.hide_collapsible_panel=bind(this.hide_collap" - "sible_panel,this),this.toggle_options=bind(this.toggle_options,this),this.to" - "ggle_history=bind(this.toggle_history,this),this.toggle_pressed_buttons=bind" - "(this.toggle_pressed_buttons,this),this.clear_history_view=bind(this.clear_h" - "istory_view,this),this.start_resize_history=bind(this.start_resize_history,t" - "his),this.mouse_up_description=bind(this.mouse_up_description,this),this.sto" - "p_propagation=bind(this.stop_propagation,this),this.mouse_down_description=b" - "ind(this.mouse_down_description,this),Container.__super__.constructor.apply(" - "this,arguments)}return extend(Container,superClass),Container.prototype.id=\"" - "dataexplorer\",Container.prototype.template=require(\"../handlebars/dataexpl" - "orer_view.hbs\"),Container.prototype.input_query_template=require(\"../handl" - "ebars/dataexplorer_input_query.hbs\"),Container.prototype.description_templa" - "te=require(\"../handlebars/dataexplorer-description.hbs\"),Container.prototy" - "pe.template_suggestion_name=require(\"../handlebars/dataexplorer_suggestion_" - "name_li.hbs\"),Container.prototype.description_with_example_template=require" - "(\"../handlebars/dataexplorer-description_with_example.hbs\"),Container.prot" - "otype.alert_connection_fail_template=require(\"../handlebars/alert-connectio" - "n_fail.hbs\"),Container.prototype.databases_suggestions_template=require(\"." - "./handlebars/dataexplorer-databases_suggestions.hbs\"),Container.prototype.t" - "ables_suggestions_template=require(\"../handlebars/dataexplorer-tables_sugge" - "stions.hbs\"),Container.prototype.reason_dataexplorer_broken_template=requir" - "e(\"../handlebars/dataexplorer-reason_broken.hbs\"),Container.prototype.quer" - "y_error_template=require(\"../handlebars/dataexplorer-query_error.hbs\"),Con" - "tainer.prototype.line_height=13,Container.prototype.size_history=50,Containe" - "r.prototype.max_size_stack=100,Container.prototype.max_size_query=1e3,Contai" - "ner.prototype.delay_toggle_abort=70,Container.prototype.events={\"mouseup .C" - "odeMirror\":\"handle_click\",\"mousedown .suggestion_name_li\":\"select_sugg" - "estion\",\"mouseover .suggestion_name_li\":\"mouseover_suggestion\",\"mouseo" - "ut .suggestion_name_li\":\"mouseout_suggestion\",\"click .clear_query\":\"cl" - "ear_query\",\"click .execute_query\":\"execute_query\",\"click .abort_query\"" - ":\"abort_query\",\"click .change_size\":\"toggle_size\",\"click #rerun_query" - "\":\"execute_query\",\"click .close\":\"close_alert\",\"click .clear_queries" - "_link\":\"clear_history_view\",\"click .close_queries_link\":\"toggle_histor" - "y\",\"click .toggle_options_link\":\"toggle_options\",\"mousedown .nano_bord" - "er_bottom\":\"start_resize_history\",\"mousedown .suggestion_description\":\"" - "mouse_down_description\",\"click .suggestion_description\":\"stop_propagatio" - "n\",\"mouseup .suggestion_description\":\"mouse_up_description\",\"mousedown" - " .suggestion_full_container\":\"mouse_down_description\",\"click .suggestion" - "_full_container\":\"stop_propagation\",\"mousedown .CodeMirror\":\"mouse_dow" - "n_description\",\"click .CodeMirror\":\"stop_propagation\"},Container.protot" - "ype.mouse_down_description=function(t){return this.keep_suggestions_on_blur=" - "!0,this.stop_propagation(t)},Container.prototype.stop_propagation=function(t" - "){return t.stopPropagation()},Container.prototype.mouse_up_description=funct" - "ion(t){return this.keep_suggestions_on_blur=!1,this.stop_propagation(t)},Con" - "tainer.prototype.start_resize_history=function(t){return this.history_view.s" - "tart_resize(t)},Container.prototype.clear_history_view=function(t){var e;ret" - "urn e=this,this.clear_history(),this.history_view.clear_history(t)},Containe" - "r.prototype.toggle_pressed_buttons=function(){return\"visible\"===this.histo" - "ry_view.state?(dataexplorer_state.history_state=\"visible\",this.$(\".clear_" - "queries_link\").fadeIn(\"fast\"),this.$(\".close_queries_link\").addClass(\"" - "active\")):(dataexplorer_state.history_state=\"hidden\",this.$(\".clear_quer" - "ies_link\").fadeOut(\"fast\"),this.$(\".close_queries_link\").removeClass(\"" - "active\")),\"visible\"===this.options_view.state?(dataexplorer_state.options" - "_state=\"visible\",this.$(\".toggle_options_link\").addClass(\"active\")):(d" - "ataexplorer_state.options_state=\"hidden\",this.$(\".toggle_options_link\")." - "removeClass(\"active\"))},Container.prototype.toggle_history=function(t){var" - " e;return e=this,this.deactivate_overflow(),t.no_animation===!0?(this.histor" - "y_view.state=\"visible\",this.$(\".content\").html(this.history_view.render(" - "!0).$el),this.move_arrow({type:\"history\",move_arrow:\"show\"}),this.adjust" - "_collapsible_panel_height({no_animation:!0,is_at_bottom:!0})):\"visible\"===" - "this.options_view.state?(this.options_view.state=\"hidden\",this.move_arrow(" - "{type:\"history\",move_arrow:\"animate\"}),this.options_view.$el.fadeOut(\"f" - "ast\",function(){return e.$(\".content\").html(e.history_view.render(!1).$el" - "),e.history_view.state=\"visible\",e.history_view.$el.fadeIn(\"fast\"),e.adj" - "ust_collapsible_panel_height({is_at_bottom:!0}),e.toggle_pressed_buttons()})" - "):\"hidden\"===this.history_view.state?(this.history_view.state=\"visible\"," - "this.$(\".content\").html(this.history_view.render(!0).$el),this.history_vie" - "w.delegateEvents(),this.move_arrow({type:\"history\",move_arrow:\"show\"}),t" - "his.adjust_collapsible_panel_height({is_at_bottom:!0})):\"visible\"===this.h" - "istory_view.state&&(this.history_view.state=\"hidden\",this.hide_collapsible" - "_panel(\"history\")),this.toggle_pressed_buttons()},Container.prototype.togg" - "le_options=function(t){var e;return e=this,this.deactivate_overflow(),(null!" - "=t?t.no_animation:void 0)===!0?(this.options_view.state=\"visible\",this.$(\"" - ".content\").html(this.options_view.render(!0).$el),this.options_view.delegat" - "eEvents(),this.move_arrow({type:\"options\",move_arrow:\"show\"}),this.adjus" - "t_collapsible_panel_height({no_animation:!0,is_at_bottom:!0})):\"visible\"==" - "=this.history_view.state?(this.history_view.state=\"hidden\",this.move_arrow" - "({type:\"options\",move_arrow:\"animate\"}),this.history_view.$el.fadeOut(\"" - "fast\",function(){return e.$(\".content\").html(e.options_view.render(!1).$e" - "l),e.options_view.state=\"visible\",e.options_view.$el.fadeIn(\"fast\"),e.ad" - "just_collapsible_panel_height(),e.toggle_pressed_buttons(),e.$(\".profiler_e" - "nabled\").css(\"visibility\",\"hidden\"),e.$(\".profiler_enabled\").hide()})" - "):\"hidden\"===this.options_view.state?(this.options_view.state=\"visible\"," - "this.$(\".content\").html(this.options_view.render(!0).$el),this.options_vie" - "w.delegateEvents(),this.move_arrow({type:\"options\",move_arrow:\"show\"}),t" - "his.adjust_collapsible_panel_height({cb:null!=t?t.cb:void 0})):\"visible\"==" - "=this.options_view.state&&(this.options_view.state=\"hidden\",this.hide_coll" - "apsible_panel(\"options\")),this.toggle_pressed_buttons()},Container.prototy" - "pe.hide_collapsible_panel=function(t){var e;return e=this,this.deactivate_ov" - "erflow(),this.$(\".nano\").animate({height:0},200,function(){if(e.activate_o" - "verflow(),\"history\"===t&&\"hidden\"===e.history_view.state||\"options\"===" - "t&&\"hidden\"===e.options_view.state)return e.$(\".nano_border\").hide(),e.$" - "(\".arrow_dataexplorer\").hide(),e.$(this).css(\"visibility\",\"hidden\")})," - "this.$(\".nano_border\").slideUp(\"fast\"),this.$(\".arrow_dataexplorer\").s" - "lideUp(\"fast\")},Container.prototype.move_arrow=function(t){var e;return\"o" - "ptions\"===t.type?e=74:\"history\"===t.type&&(e=154),\"show\"===t.move_arrow" - "?(this.$(\".arrow_dataexplorer\").css(\"margin-right\",e),this.$(\".arrow_da" - "taexplorer\").show()):\"animate\"===t.move_arrow&&this.$(\".arrow_dataexplor" - "er\").animate({\"margin-right\":e},200),this.$(\".nano_border\").show()},Con" - "tainer.prototype.adjust_collapsible_panel_height=function(t){var e,i,r;retur" - "n r=this,i=null!=(null!=t?t.size:void 0)?t.size:null!=(null!=t?t.extra:void " - "0)?Math.min(this.$(\".content > div\").height()+t.extra,this.history_view.he" - "ight_history):Math.min(this.$(\".content > div\").height(),this.history_view" - ".height_history),this.deactivate_overflow(),e=Math.max(150,i),e=Math.min(e,2" - "50),this.$(\".nano\").css(\"visibility\",\"visible\"),(null!=t?t.no_animatio" - "n:void 0)===!0?(this.$(\".nano\").height(i),this.$(\".nano > .content\").scr" - "ollTop(this.$(\".nano > .content > div\").height()),this.$(\".nano\").css(\"" - "visibility\",\"visible\"),this.$(\".arrow_dataexplorer\").show(),this.$(\".n" - "ano_border\").show(),(null!=t?t.no_animation:void 0)===!0&&this.$(\".nano\")" - ".nanoScroller({preventPageScrolling:!0}),this.activate_overflow()):(this.$(\"" - ".nano\").animate({height:i},e,function(){if(r.$(this).css(\"visibility\",\"v" - "isible\"),r.$(\".arrow_dataexplorer\").show(),r.$(\".nano_border\").show(),r" - ".$(this).nanoScroller({preventPageScrolling:!0}),r.activate_overflow(),null!" - "=t&&t.delay_scroll===!0&&t.is_at_bottom===!0&&r.$(\".nano > .content\").anim" - "ate({scrollTop:r.$(\".nano > .content > div\").height()},200),null!=(null!=t" - "?t.cb:void 0))return t.cb()}),null!=t&&t.delay_scroll!==!0&&t.is_at_bottom==" - "=!0?r.$(\".nano > .content\").animate({scrollTop:r.$(\".nano > .content > di" - "v\").height()},200):void 0)},Container.prototype.deactivate_overflow=functio" - "n(){if($(window).height()>=$(document).height())return $(\"body\").css(\"ove" - "rflow\",\"hidden\")},Container.prototype.activate_overflow=function(){return" - " $(\"body\").css(\"overflow\",\"auto\")},Container.prototype.displaying_full" - "_view=!1,Container.prototype.close_alert=function(t){return t.preventDefault" - "(),$(t.currentTarget).parent().slideUp(\"fast\",function(){return $(this).re" - "move()})},Container.prototype.map_state={\"\":\"\"},Container.prototype.desc" - "riptions={},Container.prototype.suggestions={},Container.prototype.types={va" - "lue:[\"number\",\"bool\",\"string\",\"array\",\"object\",\"time\",\"binary\"" - ",\"line\",\"point\",\"polygon\"],any:[\"number\",\"bool\",\"string\",\"array" - "\",\"object\",\"stream\",\"selection\",\"table\",\"db\",\"r\",\"error\",\"bi" - "nary\",\"line\",\"point\",\"polygon\"],geometry:[\"line\",\"point\",\"polygo" - "n\"],sequence:[\"table\",\"selection\",\"stream\",\"array\"],stream:[\"table" - "\",\"selection\"],grouped_stream:[\"stream\",\"array\"]},Container.prototype" - ".convert_type=function(t){return null!=this.types[t]?this.types[t]:[t]},Cont" - "ainer.prototype.expand_types=function(t){var e,i,r,s;if(s=[],_.isArray(t))fo" - "r(i=0,r=t.length;ithis.size_his" - "tory?window.localStorage.rethinkdb_history=JSON.stringify(dataexplorer_state" - ".history.slice(dataexplorer_state.history.length-this.size_history)):window." - "localStorage.rethinkdb_history=JSON.stringify(dataexplorer_state.history),th" - "is.history_view.add_query({query:i,broken_query:e})},Container.prototype.cle" - "ar_history=function(){return dataexplorer_state.history.length=0,window.loca" - "lStorage.rethinkdb_history=JSON.stringify(dataexplorer_state.history)},Conta" - "iner.prototype.save_current_query=function(){if(null!=window.localStorage)re" - "turn window.localStorage.current_query=JSON.stringify(this.codemirror.getVal" - "ue())},Container.prototype.initialize=function(t){var e,i,s,n;if(this.TermBa" - "seConstructor=r.expr(1).constructor.__super__.constructor.__super__.construc" - "tor,this.state=t.state,this.executing=!1,null!=window.localStorage){try{this" - ".state.options=JSON.parse(window.localStorage.options),_.defaults(this.state" - ".options,DEFAULTS.options)}catch(t){e=t,window.localStorage.removeItem(\"opt" - "ions\")}window.localStorage.options=JSON.stringify(this.state.options)}if(\"" - "string\"==typeof(null!=(i=window.localStorage)?i.current_query:void 0))try{d" - "ataexplorer_state.current_query=JSON.parse(window.localStorage.current_query" - ")}catch(t){e=t,window.localStorage.removeItem(\"current_query\")}if(null!=(n" - "ull!=(s=window.localStorage)?s.rethinkdb_history:void 0))try{dataexplorer_st" - "ate.history=JSON.parse(window.localStorage.rethinkdb_history)}catch(t){e=t,w" - "indow.localStorage.removeItem(\"rethinkdb_history\")}return this.query_has_c" - "hanged=(null!=(n=dataexplorer_state.query_result)?n.current_query:void 0)!==" - "dataexplorer_state.current_query,this.history_displayed_id=0,this.unsafe_to_" - "safe_regexstr=[[/\\\\/g,\"\\\\\\\\\"],[/\\(/g,\"\\\\(\"],[/\\)/g,\"\\\\)\"]," - "[/\\^/g,\"\\\\^\"],[/\\$/g,\"\\\\$\"],[/\\*/g,\"\\\\*\"],[/\\+/g,\"\\\\+\"]," - "[/\\?/g,\"\\\\?\"],[/\\./g,\"\\\\.\"],[/\\|/g,\"\\\\|\"],[/\\{/g,\"\\\\{\"]," - "[/\\}/g,\"\\\\}\"],[/\\[/g,\"\\\\[\"]],this.results_view_wrapper=new ResultV" - "iewWrapper({container:this,view:dataexplorer_state.view}),this.options_view=" - "new OptionsView({container:this,options:dataexplorer_state.options}),this.hi" - "story_view=new HistoryView({container:this,history:dataexplorer_state.histor" - "y}),this.driver_handler=new DriverHandler({container:this}),$(window).mousem" - "ove(this.handle_mousemove),$(window).mouseup(this.handle_mouseup),$(window)." - "mousedown(this.handle_mousedown),this.keep_suggestions_on_blur=!1,this.datab" - "ases_available={},this.fetch_data()},Container.prototype.fetch_data=function" - "(){var t;return t=r.db(system_db).table(\"table_config\").pluck(\"db\",\"nam" - "e\").group(\"db\").ungroup().map(function(t){return[t(\"group\"),t(\"reducti" - "on\")(\"name\").orderBy(function(t){return t})]}).coerceTo(\"OBJECT\").merge" - "(r.object(system_db,r.db(system_db).tableList().coerceTo(\"ARRAY\"))),this.t" - "imer=driver.run(t,5e3,function(t){return function(e,i){return null!=e?(conso" - "le.log(\"Error: Could not fetch databases and tables\"),console.log(e)):t.da" - "tabases_available=i}}(this))},Container.prototype.handle_mousemove=function(" - "t){return this.results_view_wrapper.handle_mousemove(t),this.history_view.ha" - "ndle_mousemove(t)},Container.prototype.handle_mouseup=function(t){return thi" - "s.results_view_wrapper.handle_mouseup(t),this.history_view.handle_mouseup(t)" - "},Container.prototype.handle_mousedown=function(t){return this.keep_suggesti" - "ons_on_blur=!1,this.hide_suggestion_and_description()},Container.prototype.r" - "ender=function(){return this.$el.html(this.template()),this.$(\".input_query" - "_full_container\").html(this.input_query_template()),\"Microsoft Internet Ex" - "plorer\"===(\"undefined\"!=typeof navigator&&null!==navigator?navigator.appN" - "ame:void 0)?(this.$(\".reason_dataexplorer_broken\").html(this.reason_dataex" - "plorer_broken_template({is_internet_explorer:!0})),this.$(\".reason_dataexpl" - "orer_broken\").slideDown(\"fast\"),this.$(\".button_query\").prop(\"disabled" - "\",!0)):\"undefined\"==typeof DataView||null===DataView||\"undefined\"==type" - "of Uint8Array||null===Uint8Array?(this.$(\".reason_dataexplorer_broken\").ht" - "ml(this.reason_dataexplorer_broken_template),this.$(\".reason_dataexplorer_b" - "roken\").slideDown(\"fast\"),this.$(\".button_query\").prop(\"disabled\",!0)" - "):null==r&&(this.$(\".reason_dataexplorer_broken\").html(this.reason_dataexp" - "lorer_broken_template({no_driver:!0})),this.$(\".reason_dataexplorer_broken\"" - ").slideDown(\"fast\"),this.$(\".button_query\").prop(\"disabled\",!0)),null!" - "=(null!=dataexplorer_state?dataexplorer_state.query_result:void 0)&&this.res" - "ults_view_wrapper.set_query_result({query_result:dataexplorer_state.query_re" - "sult}),this.$(\".results_container\").html(this.results_view_wrapper.render(" - "{query_has_changed:this.query_has_changed}).$el),this},Container.prototype.i" - "nit_after_dom_rendered=function(){if(this.codemirror=CodeMirror.fromTextArea" - "(document.getElementById(\"input_query\"),{mode:{name:\"javascript\"},onKeyE" - "vent:this.handle_keypress,lineNumbers:!0,lineWrapping:!0,matchBrackets:!0,ta" - "bSize:2}),this.codemirror.on(\"blur\",this.on_blur),this.codemirror.on(\"gut" - "terClick\",this.handle_gutter_click),this.codemirror.setSize(\"100%\",\"auto" - "\"),null!=dataexplorer_state.current_query&&this.codemirror.setValue(dataexp" - "lorer_state.current_query),this.codemirror.focus(),dataexplorer_state.focus_" - "on_codemirror=!0,this.codemirror.setCursor(this.codemirror.lineCount(),0),\"" - "\"===this.codemirror.getValue()&&this.handle_keypress(),this.results_view_wr" - "apper.init_after_dom_rendered(),this.draft=this.codemirror.getValue(),\"visi" - "ble\"===dataexplorer_state.history_state&&this.toggle_history({no_animation:" - "!0}),\"visible\"===dataexplorer_state.options_state)return this.toggle_optio" - "ns({no_animation:!0})},Container.prototype.on_blur=function(){if(dataexplore" - "r_state.focus_on_codemirror=!1,this.keep_suggestions_on_blur===!1)return thi" - "s.hide_suggestion_and_description()},Container.prototype.current_suggestions" - "=[],Container.prototype.current_highlighted_suggestion=-1,Container.prototyp" - "e.current_conpleted_query=\"\",Container.prototype.query_first_part=\"\",Con" - "tainer.prototype.query_last_part=\"\",Container.prototype.mouse_type_event={" - "click:!0,dblclick:!0,mousedown:!0,mouseup:!0,mouseover:!0,mouseout:!0,mousem" - "ove:!0},Container.prototype.char_breakers={\".\":!0,\"}\":!0,\")\":!0,\",\":" - "!0,\";\":!0,\"]\":!0},Container.prototype.handle_click=function(t){return th" - "is.handle_keypress(null,t)},Container.prototype.pair_char=function(t,e){var " - "i,r,s,n,o,a,l,h;if(null!=(null!=t?t.which:void 0)){if(\"\"!==this.codemirror" - ".getSelection()&&\"keypress\"===t.type&&(i=String.fromCharCode(t.which),null" - "!=i&&'\"'===i||\"'\"===i))return this.codemirror.replaceSelection(i+this.cod" - "emirror.getSelection()+i),t.preventDefault(),!0;if(8===t.which){if(\"keydown" - "\"!==t.type)return!0;if(h=this.get_previous_char(),null===h)return!0;if(h in" - " this.matching_opening_bracket){if(n=this.get_next_char(),n===this.matching_" - "opening_bracket[h]&&(o=this.count_not_closed_brackets(h),o<=0))return this.r" - "emove_next(),!0}else if(('\"'===h||\"'\"===h)&&(n=this.get_next_char(),n===h" - "&&\"\\\\\"!==this.get_previous_char(2)&&(a=this.count_char(i),a%2===0)))retu" - "rn this.remove_next(),!0;return!0}if(\"keypress\"!==t.type)return!0;if(i=Str" - "ing.fromCharCode(t.which),null!=i){if(\"\"!==this.codemirror.getSelection())" - "{if(!(i in this.matching_opening_bracket||i in this.matching_closing_bracket" - "))return!0;this.codemirror.replaceSelection(\"\")}if(r=this.last_element_typ" - "e_if_incomplete(e),'\"'===i||\"'\"===i){if(a=this.count_char(i),n=this.get_n" - "ext_char(),n===i)return a%2!==0||(\"string\"!==r&&\"object_key\"!==r||(this." - "move_cursor(1),t.preventDefault(),!0));if(a%2!==0)return!0;if(s=this.get_las" - "t_key(e),\"string\"===r)return!0;if(\"object_key\"===r&&\"\"!==s&&this.creat" - "e_safe_regex(i).test(s)===!0)return!0;this.insert_next(i)}else if(\"string\"" - "!==r){if(n=this.get_next_char(),i in this.matching_opening_bracket)return o=" - "this.count_not_closed_brackets(i),!(o>=0)||(this.insert_next(this.matching_o" - "pening_bracket[i]),!0);if(i in this.matching_closing_bracket&&(l=this.matchi" - "ng_closing_bracket[i],o=this.count_not_closed_brackets(l),n===i))return o<=0" - "&&(this.move_cursor(1),t.preventDefault()),!0}}}return!1},Container.prototyp" - "e.get_next_char=function(){var t;return t=this.codemirror.getCursor(),t.ch++" - ",this.codemirror.getRange(this.codemirror.getCursor(),t)},Container.prototyp" - "e.get_previous_char=function(t){var e,i;return i=this.codemirror.getCursor()" - ",e=this.codemirror.getCursor(),null!=t?(i.ch-=t,e.ch-=t-1):i.ch--,i.ch<0?nul" - "l:this.codemirror.getRange(i,e)},Container.prototype.insert_next=function(t)" - "{return this.codemirror.replaceRange(t,this.codemirror.getCursor()),this.mov" - "e_cursor(-1)},Container.prototype.remove_next=function(){var t;return t=this" - ".codemirror.getCursor(),t.ch++,this.codemirror.replaceRange(\"\",this.codemi" - "rror.getCursor(),t)},Container.prototype.move_cursor=function(t){var e;retur" - "n e=this.codemirror.getCursor(),e.ch+=t,e.ch<0&&(e.ch=0),this.codemirror.set" - "Cursor(e)},Container.prototype.count_char=function(t){var e,i,r,s,n,o,a,l,h," - "_,u;for(o=this.codemirror.getValue(),r=!1,u=0,a=0,i=s=0,n=o.length;s0)u--;else if(r===!0)e===_&&null!=o[i-1]&&\"\\\\\"!==o[i-1]&&(r" - "=!1,e===t&&a++);else{if(e===t&&a++,\"'\"===e||'\"'===e){r=!0,_=e;continue}if" - "(l=this.regex.inline_comment.exec(o.slice(i)),null!=l){u=l[0].length-1;conti" - "nue}if(h=this.regex.multiple_line_comment.exec(o.slice(i)),null!=h){u=h[0].l" - "ength-1;continue}}return a},Container.prototype.matching_opening_bracket={\"" - "(\":\")\",\"{\":\"}\",\"[\":\"]\"},Container.prototype.matching_closing_brac" - "ket={\")\":\"(\",\"}\":\"{\",\"]\":\"[\"},Container.prototype.count_not_clos" - "ed_brackets=function(t){var e,i,r,s,n,o,a,l,h,_,u;for(o=this.codemirror.getV" - "alue(),r=!1,u=0,a=0,i=s=0,n=o.length;s0)u--;else if(r==" - "=!0)e===_&&null!=o[i-1]&&\"\\\\\"!==o[i-1]&&(r=!1);else{if(e===t?a++:e===thi" - "s.matching_opening_bracket[t]&&a--,\"'\"===e||'\"'===e){r=!0,_=e;continue}if" - "(l=this.regex.inline_comment.exec(o.slice(i)),null!=l){u=l[0].length-1;conti" - "nue}if(h=this.regex.multiple_line_comment.exec(o.slice(i)),null!=h){u=h[0].l" - "ength-1;continue}}return a},Container.prototype.handle_keypress=function(t,e" - "){var i,r,s,n,o,a,l,h,_,u,c,p,d,g,y,f,m,b,w,v,x,q,k,$,C,j,z,V,S,T,R,O,D,A,N," - "K,E,M,P,I,J,U,L,B,F,H,W,Q,Z,Y;if(this.ignored_next_keyup===!0)return\"keyup\"" - "===(null!=e?e.type:void 0)&&9!==(null!=e?e.which:void 0)&&(this.ignored_next" - "_keyup=!1),!0;if(dataexplorer_state.focus_on_codemirror=!0,\"mouseup\"===(nu" - "ll!=e?e.type:void 0)&&this.hide_suggestion_and_description(),dataexplorer_st" - "ate.current_query=this.codemirror.getValue(),this.save_current_query(),null!" - "=(null!=e?e.which:void 0)){if(\"keydown\"!==e.type&&(e.ctrlKey===!0||e.metaK" - "ey===!0)&&32===e.which)return!0;if(27===e.which||!(\"keydown\"!==e.type||e.c" - "trlKey!==!0&&e.metaKey!==!0||32!==e.which||\"none\"===this.$(\".suggestion_d" - "escription\").css(\"display\")&&\"none\"===this.$(\".suggestion_name_list\")" - ".css(\"display\"))){if(e.preventDefault(),this.hide_suggestion_and_descripti" - "on(),j=this.codemirror.getRange({line:0,ch:0},this.codemirror.getCursor()),C" - "=this.codemirror.getRange(this.codemirror.getCursor(),{line:this.codemirror." - "lineCount()+1,ch:0}),W=this.extract_data_from_query({size_stack:0,query:j,po" - "sition:0}),null===W)return this.ignore_tab_keyup=!1,this.hide_suggestion_and" - "_description(),!1;if(this.current_highlighted_suggestion=-1,this.current_hig" - "hlighted_extra_suggestion=-1,this.$(\".suggestion_name_list\").empty(),this." - "query_last_part=C,this.current_suggestions=[],this.current_element=\"\",this" - ".current_extra_suggestion=\"\",this.written_suggestion=null,this.cursor_for_" - "auto_completion=this.codemirror.getCursor(),this.description=null,L={status:" - "null},this.create_suggestion({stack:W,query:j,result:L}),L.suggestions=this." - "uniq(L.suggestions),this.grouped_data=this.count_group_level(W).count_group>" - "0,(null!=(z=L.suggestions)?z.length:void 0)>0)for(V=L.suggestions,o=l=0,u=V." - "length;l-1)return e.preventDefault(),this.handle_key" - "press(),!0;if(k=this.get_previous_char(),k in this.matching_opening_bracket&" - "&(x=this.get_next_char(),this.matching_opening_bracket[k]===x))return s=this" - ".codemirror.getCursor(),this.insert_next(\"\\n\"),\n" - "this.codemirror.indentLine(s.line+1,\"smart\"),this.codemirror.setCursor(s)," - "!1}}else if(9===e.which&&e.ctrlKey===!1||\"keydown\"===e.type&&(e.ctrlKey===" - "!0||e.metaKey===!0)&&32===e.which&&\"none\"===this.$(\".suggestion_descripti" - "on\").css(\"display\")&&\"none\"===this.$(\".suggestion_name_list\").css(\"d" - "isplay\")){if(e.preventDefault(),\"keydown\"!==e.type)return!1;if((null!=(A=" - "this.current_suggestions)?A.length:void 0)>0){if(\"none\"===this.$(\".sugges" - "tion_name_list\").css(\"display\"))return this.show_suggestion(),!0;if(i=nul" - "l===this.written_suggestion?this.query_first_part+this.current_element+this." - "query_last_part:this.query_first_part+this.written_suggestion+this.query_las" - "t_part,i!==this.codemirror.getValue()){for(this.current_element=this.codemir" - "ror.getValue().slice(this.query_first_part.length,this.codemirror.getValue()" - ".length-this.query_last_part.length),U=this.create_safe_regex(this.current_e" - "lement),v=[],w=-1,N=this.current_suggestions,a=h=0,c=N.length;h0){for(this.$(\".suggestion_name_list\").empty(" - "),K=this.current_suggestions,o=_=0,p=K.length;_=this.current_suggestions.length)return this.show_suggestion_witho" - "ut_moving(),this.remove_highlight_suggestion(),this.write_suggestion({sugges" - "tion_to_write:this.current_element}),this.ignore_tab_keyup=!0,this.current_h" - "ighlighted_suggestion=-1,!0;if(null!=this.current_suggestions[this.current_h" - "ighlighted_suggestion])return this.show_suggestion_without_moving(),this.hig" - "hlight_suggestion(this.current_highlighted_suggestion),this.write_suggestion" - "({suggestion_to_write:this.current_suggestions[this.current_highlighted_sugg" - "estion]}),this.ignore_tab_keyup=!0,!0}else if(null!=this.description){if(\"n" - "one\"===this.$(\".suggestion_description\").css(\"display\"))return this.sho", - 65494) + std::string( - "w_description(),!0;if(null!=this.extra_suggestions&&this.extra_suggestions.l" - "ength>0&&this.extra_suggestion.start_body===this.extra_suggestion.start_body" - "){if(\"string\"===(null!=(E=this.extra_suggestion)&&null!=(M=E.body)&&null!=" - "(P=M[0])?P.type:void 0))if(this.extra_suggestion.body[0].complete===!0)this." - "extra_suggestions=[];else{for(r=this.extra_suggestion.body[0].name.replace(/" - "^\\s*('|\")/,\"\").replace(/('|\")\\s*$/,\"\"),U=this.create_safe_regex(r),b" - "=[],I=this.extra_suggestions,f=0,d=I.length;f0&&($=this.c" - "odemirror.getValue(),Q=this.extra_suggestion.start_body,null!=(null!=(J=this" - ".extra_suggestion.body)&&null!=(S=J[0])?S.name.length:void 0)&&(Q+=this.extr" - "a_suggestion.body[0].name.length),n=$.indexOf(\")\",Q),this.query_last_part=" - "\"\",n!==-1&&(this.query_last_part=$.slice(n)),this.query_first_part=$.slice" - "(0,this.extra_suggestion.start_body),y=this.query_first_part.split(\"\\n\")," - "e.shiftKey===!0?this.current_highlighted_extra_suggestion--:this.current_hig" - "hlighted_extra_suggestion++,this.current_highlighted_extra_suggestion>=this." - "extra_suggestions.length?this.current_highlighted_extra_suggestion=-1:this.c" - "urrent_highlighted_extra_suggestion<-1&&(this.current_highlighted_extra_sugg" - "estion=this.extra_suggestions.length-1),Y=\"\",this.current_highlighted_extr" - "a_suggestion===-1?null!=this.current_extra_suggestion&&(/^\\s*'/.test(this.c" - "urrent_extra_suggestion)===!0?Y=this.current_extra_suggestion+\"'\":/^\\s*\"" - "/.test(this.current_extra_suggestion)===!0&&(Y=this.current_extra_suggestion" - "+'\"')):(dataexplorer_state.options.electric_punctuation===!1&&(m=!0),/^\\s*" - "'/.test(this.current_extra_suggestion)===!0?Z=\"'\":/^\\s*\"/.test(this.curr" - "ent_extra_suggestion)===!0?Z='\"':(Z=\"'\",m=!0),Y=Z+this.extra_suggestions[" - "this.current_highlighted_extra_suggestion]+Z),this.write_suggestion({move_ou" - "tside:m,suggestion_to_write:Y}),this.ignore_tab_keyup=!0)}}}if(13===e.which&" - "&(e.shiftKey||e.ctrlKey||e.metaKey))return this.hide_suggestion_and_descript" - "ion(),e.preventDefault(),\"keydown\"!==e.type||(this.execute_query(),!0);if(" - "(e.ctrlKey||e.metaKey)&&86===e.which&&\"keydown\"===e.type)return this.last_" - "action_is_paste=!0,this.num_released_keys=0,e.metaKey&&this.num_released_key" - "s++,this.hide_suggestion_and_description(),!0;if(\"keyup\"===e.type&&this.la" - "st_action_is_paste===!0&&(17===e.which||91===e.which))return this.num_releas" - "ed_keys++,2===this.num_released_keys&&(this.last_action_is_paste=!1),this.hi" - "de_suggestion_and_description(),!0;if(\"keyup\"===e.type&&this.last_action_i" - "s_paste===!0&&86===e.which)return this.num_released_keys++,2===this.num_rele" - "ased_keys&&(this.last_action_is_paste=!1),this.hide_suggestion_and_descripti" - "on(),!0;if(\"keyup\"===e.type&&e.altKey&&38===e.which){if(this.history_displ" - "ayed_id1)ret" - "urn this.history_displayed_id--,this.codemirror.setValue(dataexplorer_state." - "history[dataexplorer_state.history.length-this.history_displayed_id].query)," - "e.preventDefault(),!0;1===this.history_displayed_id&&(this.history_displayed" - "_id--,this.codemirror.setValue(this.draft),this.codemirror.setCursor(this.co" - "demirror.lineCount(),0))}else{if(\"keyup\"===e.type&&e.altKey&&33===e.which)" - "return this.history_displayed_id=dataexplorer_state.history.length,this.code" - "mirror.setValue(dataexplorer_state.history[dataexplorer_state.history.length" - "-this.history_displayed_id].query),e.preventDefault(),!0;if(\"keyup\"===e.ty" - "pe&&e.altKey&&34===e.which)return this.history_displayed_id=this.history.len" - "gth,this.codemirror.setValue(dataexplorer_state.history[dataexplorer_state.h" - "istory.length-this.history_displayed_id].query),this.codemirror.setCursor(th" - "is.codemirror.lineCount(),0),e.preventDefault(),!0}}if(this.$(\".suggestion_" - "name_li_hl\").length>0&&13===(null!=e?e.which:void 0))return e.preventDefaul" - "t(),this.handle_keypress(),!0;if(0!==this.history_displayed_id&&null!=e&&(e." - "ctrlKey||e.shiftKey||e.altKey||16===e.which||17===e.which||18===e.which||20=" - "==e.which||91===e.which&&\"keypress\"!==e.type||92===e.which||e.type in this" - ".mouse_type_event))return!1;if(null!=e&&(16===e.which||17===e.which||18===e." - "which||20===e.which||91===e.which&&\"keypress\"!==e.type||92===e.which))retu" - "rn!1;if((null==e||37!==e.which&&38!==e.which&&39!==e.which&&40!==e.which&&33" - "!==e.which&&34!==e.which&&35!==e.which&&36!==e.which&&0!==e.which)&&(this.hi" - "story_displayed_id=0,this.draft=this.codemirror.getValue()),!(this.codemirro" - "r.getValue().length>this.max_size_query)){if(j=this.codemirror.getRange({lin" - "e:0,ch:0},this.codemirror.getCursor()),C=this.codemirror.getRange(this.codem" - "irror.getCursor(),{line:this.codemirror.lineCount()+1,ch:0}),W=this.extract_" - "data_from_query({size_stack:0,query:j,position:0}),null===W)return this.igno" - "re_tab_keyup=!1,this.hide_suggestion_and_description(),!1;if(dataexplorer_st" - "ate.options.electric_punctuation===!0&&this.pair_char(e,W),null!=(null!=e?e." - "type:void 0)&&\"keyup\"!==e.type&&9!==e.which&&\"mouseup\"!==e.type)return!1" - ";if(16===(null!=e?e.which:void 0))return!1;if(this.ignore_tab_keyup===!0&&9=" - "==(null!=e?e.which:void 0))return\"keyup\"===e.type&&(this.ignore_tab_keyup=" - "!1),!0;if(this.current_highlighted_suggestion=-1,this.current_highlighted_ex" - "tra_suggestion=-1,this.$(\".suggestion_name_list\").empty(),this.query_last_" - "part=C,\"\"!==this.codemirror.getSelection())return this.hide_suggestion_and" - "_description(),null!=e&&13===e.which&&(e.shiftKey||e.ctrlKey||e.metaKey)?(th" - "is.hide_suggestion_and_description(),\"keydown\"!==e.type||(this.execute_que" - "ry(),!0)):\"mouseup\"===(null!=e?e.type:void 0);if(this.current_suggestions=" - "[],this.current_element=\"\",this.current_extra_suggestion=\"\",this.written" - "_suggestion=null,this.cursor_for_auto_completion=this.codemirror.getCursor()" - ",this.description=null,L={status:null},F=this.regex.get_first_non_white_char" - ".exec(C),null===F||(null!=(T=F[1])?T[0]:void 0)in this.char_breakers||null!=" - "=(null!=(R=F[1])?R.match(/^((\\/\\/)|(\\/\\*))/):void 0)?(B=this.regex.last_" - "char_is_white.exec(j[j.length-1]),null!==B&&(L.status=\"break_and_look_for_d" - "escription\",this.hide_suggestion())):(L.status=\"break_and_look_for_descrip" - "tion\",this.hide_suggestion()),this.create_suggestion({stack:W,query:j,resul" - "t:L}),L.suggestions=this.uniq(L.suggestions),this.grouped_data=this.count_gr" - "oup_level(W).count_group>0,(null!=(O=L.suggestions)?O.length:void 0)>0){for(" - "H=!1,D=L.suggestions,o=q=0,g=D.length;q0)A--;else if(h=" - "==!0){if(s===D&&null!=v[l-1]&&\"\\\\\"!==v[l-1]&&(h=!1,\"string\"===o.type))" - "{if(o.name=v.slice(O,l+1),o.complete=!0,T.push(o),S++,S>this.max_size_stack)" - "return null;o={type:null,context:n,complete:!1},O=l+1}}else{if(\"'\"===s||'\"" - "'===s){h=!0,D=s,null===o.type&&(o.type=\"string\",O=l);continue}if(null===o." - "type){if($=this.regex.inline_comment.exec(v.slice(l)),null!=$){A=$[0].length" - "-1,O+=$[0].length;continue}if(C=this.regex.multiple_line_comment.exec(v.slic" - "e(l)),null!=C){A=C[0].length-1,O+=C[0].length;continue}if(O===l){if(V=this.r" - "egex.white_start.exec(v.slice(l)),null!=V){A=V[0].length-1,O+=V[0].length;co" - "ntinue}if(j=this.regex.anonymous.exec(v.slice(l)),null!==j){for(o.type=\"ano" - "nymous_function\",g=null!=(x=j[2])?x.split(\",\"):void 0,o.args=[],y=util.de" - "ep_copy(n),u=0,d=g.length;uthis.max_size_stack)return null;o={type:null,context" - ":n,complete:!1},O=l+j[0].length;continue}if(j=this.regex.object.exec(v.slice" - "(l)),null!==j){o.type=\"object\",o.next_key=null,o.body=[],o.current_key_sta" - "rt=l+j[0].length,A=j[0].length-1,R=[\"{\"];continue}if(j=this.regex.array.ex" - "ec(v.slice(l)),null!==j){o.type=\"array\",o.next_key=null,o.body=[],a=l+j[0]" - ".length,A=j[0].length-1,R=[\"[\"];continue}if(m=\".\"===s?l+1:l,j=this.regex" - ".method.exec(v.slice(m)),null!==j){if(z=this.regex.row.exec(v.slice(m)),null" - "!==z){if(w=z[0].indexOf(\"(\"),o.type=\"function\",o.name=\"row\",T.push(o)," - "S++,S>this.max_size_stack)return null;o={type:\"function\",name:\"(\",positi" - "on:b+3+1,context:n,complete:\"false\"},R=[\"(\"],O+=w,A=j[0].length-1+m-l;co" - "ntinue}if(\"function\"===(null!=(q=T[T.length-1])?q.type:void 0)||\"var\"===" - "(null!=(k=T[T.length-1])?k.type:void 0)){o.type=\"function\",o.name=j[0],o.p" - "osition=b+m,O+=m-l,A=j[0].length-1+m-l,R=[\"(\"];continue}if(w=j[0].indexOf(" - "\"(\"),w!==-1&&j[0].slice(0,w)in n){if(o.real_type=this.types.value,o.type=\"" - "var\",o.name=j[0].slice(0,w),T.push(o),S++,S>this.max_size_stack)return null" - ";o={type:\"function\",name:\"(\",position:b+w+1,context:n,complete:\"false\"" - "},R=[\"(\"],O=w,A=j[0].length-1;continue}}if(j=this.regex.method_var.exec(v." - "slice(m)),null!==j){if(j[0].slice(0,j[0].length-1)in n?(o.type=\"var\",o.rea" - "l_type=this.types.value):o.type=\"function\",o.position=b+m,o.name=j[0].slic" - "e(0,j[0].length-1).replace(/\\s/,\"\"),o.complete=!0,A=o.name.length-1+m-l,T" - ".push(o),S++,S>this.max_size_stack)return null;o={type:null,context:n,comple" - "te:!1},O=m+A+1,O-=m-l;continue}if(j=this.regex.comma.exec(v.slice(l)),null!=" - "=j){if(o.complete=!0,T.push({type:\"separator\",complete:!0,name:v.slice(l,j" - "[0].length)}),S++,S>this.max_size_stack)return null;o={type:null,context:n,c" - "omplete:!1},O=l+j[0].length-1+1,A=j[0].length-1;continue}if(j=this.regex.sem" - "icolon.exec(v.slice(l)),null!==j){if(o.complete=!0,T.push({type:\"separator\"" - ",complete:!0,name:v.slice(l,j[0].length)}),S++,S>this.max_size_stack)return " - "null;o={type:null,context:n,complete:!1},O=l+j[0].length-1+1,A=j[0].length-1" - ";continue}}else\";\"===s&&(O=l+1)}else{if(j=this.regex.comma.exec(v.slice(l)" - "),null!==j&&R.length<1){if(T.push({type:\"separator\",complete:!0,name:v.sli" - "ce(l,j[0].length),position:b+l}),S++,S>this.max_size_stack)return null;o={ty" - "pe:null,context:n,complete:!1},O=l+j[0].length-1,A=j[0].length-1;continue}if" - "(\"anonymous_function\"===o.type){if(s in this.stop_char.opening)R.push(s);e" - "lse if(s in this.stop_char.closing&&R[R.length-1]===this.stop_char.closing[s" - "]&&(R.pop(),0===R.length)){if(o.body=this.extract_data_from_query({size_stac" - "k:S,query:v.slice(r,l),context:o.context,position:b+r}),null===o.body)return" - " null;if(o.complete=!0,T.push(o),S++,S>this.max_size_stack)return null;o={ty" - "pe:null,context:n},O=l+1}}else if(\"loop\"===o.type){if(s in this.stop_char." - "opening)R.push(s);else if(s in this.stop_char.closing&&R[R.length-1]===this." - "stop_char.closing[s]&&(R.pop(),0===R.length)){if(o.body=this.extract_data_fr" - "om_query({size_stack:S,query:v.slice(r,l),context:o.context,position:b+r}),o" - ".body)return null;if(o.complete=!0,T.push(o),S++,S>this.max_size_stack)retur" - "n null;o={type:null,context:n},O=l+1}}else if(\"function\"===o.type){if(s in" - " this.stop_char.opening)R.push(s);else if(s in this.stop_char.closing&&R[R.l" - "ength-1]===this.stop_char.closing[s]&&(R.pop(),0===R.length)){if(o.body=this" - ".extract_data_from_query({size_stack:S,query:v.slice(O+o.name.length,l),cont" - "ext:o.context,position:b+O+o.name.length}),null===o.body)return null;if(o.co" - "mplete=!0,T.push(o),S++,S>this.max_size_stack)return null;o={type:null,conte" - "xt:n},O=l+1}}else if(\"object\"===o.type){if(c=[],s in this.stop_char.openin" - "g)R.push(s);else if(s in this.stop_char.closing&&R[R.length-1]===this.stop_c" - "har.closing[s]&&(R.pop(),0===R.length)){if(null!=o.next_key){if(i=this.extra" - "ct_data_from_query({size_stack:S,query:v.slice(o.current_value_start,l),cont" - "ext:o.context,position:b+o.current_value_start}),null===i)return null;f={typ" - "e:\"object_key\",key:o.next_key,key_complete:!0,complete:!1,body:i},o.body[o" - ".body.length-1]=f}if(o.next_key=null,o.complete=!0,T.push(o),S++,S>this.max_" - "size_stack)return null;o={type:null,context:n},O=l+1;continue}if(null==o.nex" - "t_key){if(1===R.length&&\":\"===s){if(f={type:\"object_key\",key:v.slice(o.c" - "urrent_key_start,l),key_complete:!0},0===o.body.length){if(o.body.push(f),S+" - "+,S>this.max_size_stack)return null}else o.body[o.body.length-1]=f;o.next_ke" - "y=v.slice(o.current_key_start,l),o.current_value_start=l+1}}else if(j=this.r" - "egex.comma.exec(v.slice(l)),1===R.length&&null!==j){if(i=this.extract_data_f" - "rom_query({size_stack:S,query:v.slice(o.current_value_start,l),context:o.con" - "text,position:o.current_value_start}),null===i)return null;f={type:\"object_" - "key\",key:o.next_key,key_complete:!0,body:i},o.body[o.body.length-1]=f,A=j[0" - "].length-1,o.next_key=null,o.current_key_start=l+j[0].length}}else if(\"arra" - "y\"===o.type){if(s in this.stop_char.opening)R.push(s);else if(s in this.sto" - "p_char.closing&&R[R.length-1]===this.stop_char.closing[s]&&(R.pop(),0===R.le" - "ngth)){if(i=this.extract_data_from_query({size_stack:S,query:v.slice(a,l),co" - "ntext:o.context,position:b+a}),null===i)return null;if(f={type:\"array_entry" - "\",complete:!0,body:i},f.body.length>0&&(o.body.push(f),S++,S>this.max_size_" - "stack))return null;continue}if(1===R.length&&\",\"===s){if(i=this.extract_da" - "ta_from_query({size_stack:S,query:v.slice(a,l),context:o.context,position:b+" - "a}),null===i)return null;if(f={type:\"array_entry\",complete:!0,body:i},f.bo" - "dy.length>0&&(o.body.push(f),S++,S>this.max_size_stack))return null;a=l+1}}}" - "}if(null!==o.type){if(o.complete=!1,\"function\"===o.type){if(o.body=this.ex" - "tract_data_from_query({size_stack:S,query:v.slice(O+o.name.length),context:o" - ".context,position:b+O+o.name.length}),null===o.body)return null}else if(\"an" - "onymous_function\"===o.type){if(o.body=this.extract_data_from_query({size_st" - "ack:S,query:v.slice(r),context:o.context,position:b+r}),null===o.body)return" - " null}else if(\"loop\"===o.type){if(o.body=this.extract_data_from_query({siz" - "e_stack:S,query:v.slice(r),context:o.context,position:b+r}),null===o.body)re" - "turn null}else if(\"string\"===o.type)o.name=v.slice(O);else if(\"object\"==" - "=o.type)if(null==o.next_key){if(f={type:\"object_key\",key:v.slice(o.current" - "_key_start),key_complete:!1,complete:!1},o.body.push(f),S++,S>this.max_size_" - "stack)return null;o.next_key=v.slice(o.current_key_start)}else{if(i=this.ext" - "ract_data_from_query({size_stack:S,query:v.slice(o.current_value_start),cont" - "ext:o.context,position:b+o.current_value_start}),null===i)return null;f={typ" - "e:\"object_key\",key:o.next_key,key_complete:!0,complete:!1,body:i},o.body[o" - ".body.length-1]=f,o.next_key=null}else if(\"array\"===o.type){if(i=this.extr" - "act_data_from_query({size_stack:S,query:v.slice(a),context:o.context,positio" - "n:b+a}),null===i)return null;if(f={type:\"array_entry\",complete:!1,body:i}," - "f.body.length>0&&(o.body.push(f),S++,S>this.max_size_stack))return null}if(T" - ".push(o),S++,S>this.max_size_stack)return null}else if(O!==l&&(v.slice(O)in " - "o.context?(o.name=v.slice(O),o.type=\"var\",o.real_type=this.types.value,o.c" - "omplete=!0):this.regex.number.test(v.slice(O))===!0?(o.type=\"number\",o.nam" - "e=v.slice(O),o.complete=!0):\".\"===v[O]?(o.type=\"function\",o.position=b+O" - ",o.name=v.slice(O+1),o.complete=!1):(o.name=v.slice(O),o.position=b+O,o.comp" - "lete=!1),T.push(o),S++,S>this.max_size_stack))return null;return T},Containe" - "r.prototype.count_group_level=function(t){var e,i,r,s,n,o,a;if(e=0,t.length>" - "0&&(o=!0,i=t[t.length-1],null!=i.body&&i.body.length>0&&i.complete===!1&&(n=" - "this.count_group_level(i.body),e+=n.count_group,o=n.parse_level,\"return\"==" - "=i.body[0].type&&(o=!1),\"function\"===i.body[i.body.length-1].type&&(o=!1))" - ",o===!0))for(r=s=a=t.length-1;s>=0;r=s+=-1)\"function\"===t[r].type&&\"ungro" - "up(\"===t[r].name?e-=1:\"function\"===t[r].type&&\"group(\"===t[r].name&&(e+" - "=1);return{count_group:e,parse_level:o}},Container.prototype.create_suggesti" - "on=function(t){var e,i,r,s,n,o,a,l,h,_,u,c,p,d,g,y,f,m,b,w,v,x,q,k,$,C,j,z,V" - ",S,T,R,O,D,A,N,K,E;if(A=t.stack,d=t.query,O=t.result,null===O.status&&0===A." - "length&&(O.suggestions=[],O.status=\"done\",this.query_first_part=\"\",null!" - "=this.suggestions[\"\"]))for(g=this.suggestions[\"\"],r=0,o=g.length;r=0;i=s+=-1)if(e=A[" - "i],null!=e.body&&e.body.length>0&&e.complete===!1&&this.create_suggestion({s" - "tack:e.body,query:null!=t?t.query:void 0,result:t.result}),\"done\"!==O.stat" - "us){if(null===O.status)if(e.complete===!0){if(\"function\"===e.type){if(e.co" - "mplete===!0||\"\"===e.name){O.suggestions=null,O.status=\"look_for_descripti" - "on\";break}O.suggestions=null,O.description=e.name,this.extra_suggestion={st" - "art_body:e.position+e.name.length,body:e.body},null!=(null!=($=e.body)&&null" - "!=(C=$[0])&&null!=(j=C.name)?j.length:void 0)&&(this.cursor_for_auto_complet" - "ion.ch-=e.body[0].name.length,this.current_extra_suggestion=e.body[0].name)," - "O.status=\"done\"}else if(\"anonymous_function\"===e.type||\"separator\"===e" - ".type||\"object\"===e.type||\"object_key\"===e.type||\"return\"===e.type){O." - "suggestions=null,O.status=\"look_for_description\";break}}else if(\"function" - "\"===e.type){if(null!=e.body){O.suggestions=null,O.description=e.name,this.e" - "xtra_suggestion={start_body:e.position+e.name.length,body:e.body},null!=(nul" - "l!=(z=e.body)&&null!=(V=z[0])&&null!=(S=V.name)?S.length:void 0)&&(this.curs" - "or_for_auto_completion.ch-=e.body[0].name.length,this.current_extra_suggesti" - "on=e.body[0].name),O.status=\"done\";break}O.suggestions=[],O.suggestions_re" - "gex=this.create_safe_regex(e.name),O.description=null,this.query_first_part=" - "d.slice(0,e.position+1),this.current_element=e.name,this.cursor_for_auto_com" - "pletion.ch-=e.name.length,this.current_query,0!==i?O.status=\"look_for_state" - "\":O.state=\"\"}else{if(\"anonymous_function\"===e.type||\"object_key\"===e." - "type||\"string\"===e.type||\"separator\"===e.type||\"array\"===e.type){O.sug" - "gestions=null,O.status=\"look_for_description\";break}if(null===e.type){O.su" - "ggestions=[],O.status=\"look_for_description\";break}}else if(\"look_for_des" - "cription\"===O.status){if(\"function\"!==e.type)break;O.description=e.name,t" - "his.extra_suggestion={start_body:e.position+e.name.length,body:e.body},null!" - "=(null!=(T=e.body)&&null!=(R=T[0])&&null!=(f=R.name)?f.length:void 0)&&(this" - ".cursor_for_auto_completion.ch-=e.body[0].name.length,this.current_extra_sug" - "gestion=e.body[0].name),O.suggestions=null,O.status=\"done\"}if(\"break_and_" - "look_for_description\"===O.status){if(\"function\"!==e.type||e.complete!==!1" - "||e.name.indexOf(\"(\")===-1){if(\"function\"!==e.type)break;O.status=\"look" - "_for_description\";break}O.description=e.name,this.extra_suggestion={start_b" - "ody:e.position+e.name.length,body:e.body},null!=(null!=(m=e.body)&&null!=(b=" - "m[0])&&null!=(w=b.name)?w.length:void 0)&&(this.cursor_for_auto_completion.c" - "h-=e.body[0].name.length,this.current_extra_suggestion=e.body[0].name),O.sug" - "gestions=null,D.push(O.status=\"done\")}else if(\"look_for_state\"===O.statu" - "s)if(\"function\"===e.type&&e.complete===!0){if(O.state=e.name,null!=this.ma" - "p_state[e.name])for(v=this.map_state[e.name],n=0,a=v.length;ni+418-150-23?" - "this.$(\".suggestion_full_container\").css(\"left\",i-34+\"px\"):e>i-150-23?" - "this.$(\".suggestion_full_container\").css(\"left\",i-34-150+\"px\"):this.$(" - "\".suggestion_full_container\").css(\"left\",e-100+\"px\"))},Container.proto" - "type.highlight_suggestion=function(t){return this.remove_highlight_suggestio" - "n(),this.$(\".suggestion_name_li\").eq(t).addClass(\"suggestion_name_li_hl\"" - "),this.$(\".suggestion_description\").html(this.description_template(this.ex" - "tend_description(this.current_suggestions[t]))),this.$(\".suggestion_descrip" - "tion\").show()},Container.prototype.remove_highlight_suggestion=function(){r" - "eturn this.$(\".suggestion_name_li\").removeClass(\"suggestion_name_li_hl\")" - "},Container.prototype.write_suggestion=function(t){var e,i,r;return r=t.sugg" - "estion_to_write,i=t.move_outside===!0,e=this.cursor_for_auto_completion.ch+r" - ".length,dataexplorer_state.options.electric_punctuation===!0?(\"(\"===r[r.le" - "ngth-1]&&this.count_not_closed_brackets(\"(\")>=0?(this.codemirror.setValue(" - "this.query_first_part+r+\")\"+this.query_last_part),this.written_suggestion=" - "r+\")\"):(this.codemirror.setValue(this.query_first_part+r+this.query_last_p" - "art),this.written_suggestion=r,i!==!1||'\"'!==r[r.length-1]&&\"'\"!==r[r.len" - "gth-1]||e--),this.codemirror.focus(),this.codemirror.setCursor({line:this.cu" - "rsor_for_auto_completion.line,ch:e})):(this.codemirror.setValue(this.query_f" - "irst_part+r+this.query_last_part),this.written_suggestion=r,i!==!1||'\"'!==r" - "[r.length-1]&&\"'\"!==r[r.length-1]||e--,this.codemirror.focus(),this.codemi" - "rror.setCursor({line:this.cursor_for_auto_completion.line,ch:e}))},Container" - ".prototype.select_suggestion=function(t){var e;return e=this.$(t.target).htm" - "l(),this.write_suggestion({suggestion_to_write:e}),this.hide_suggestion(),se" - "tTimeout(function(t){return function(){return t.handle_keypress(),t.codemirr" - "or.focus()}}(this),0)},Container.prototype.mouseover_suggestion=function(t){" - "return this.highlight_suggestion(t.target.dataset.id)},Container.prototype.m" - "ouseout_suggestion=function(t){return this.hide_description()},Container.pro" - "totype.extend_description=function(t){var e,i,r,s;return\"db(\"===t||\"dbDro" - "p(\"===t?(s=_.extend({},this.descriptions[t]()),0===_.keys(this.databases_av" - "ailable).length?e={no_database:!0}:(r=_.keys(this.databases_available),e={no" - "_database:!1,databases_available:r}),s.description=this.databases_suggestion" - "s_template(e)+s.description,this.extra_suggestions=r):\"table(\"===t||\"tabl" - "eDrop(\"===t?(i=this.extract_database_used(),s=_.extend({},this.descriptions" - "[t]()),i.error===!1?(e={tables_available:this.databases_available[i.name],no" - "_table:0===this.databases_available[i.name].length},null!=i.name&&(e.databas" - "e_name=i.name)):e={error:i.error},s.description=this.tables_suggestions_temp" - "late(e)+s.description,this.extra_suggestions=this.databases_available[i.name" - "]):(s=this.descriptions[t](this.grouped_data),this.extra_suggestions=null),s" - "},Container.prototype.extract_database_used=function(){var t,e,i,r,s,n,o;ret" - "urn o=this.codemirror.getRange({line:0,ch:0},this.codemirror.getCursor()),n=" - "o.lastIndexOf(\".db(\"),n===-1?(s=!1,null!=this.databases_available.test?{db" - "_found:!0,error:!1,name:\"test\"}:{db_found:!1,error:!0}):(t=o.slice(n+5),e=" - "o.slice(n+4,n+5),r=t.indexOf(e),r===-1?{db_found:!1,error:!0}:(i=t.slice(0,r" - "),null!=this.databases_available[i]?{db_found:!0,error:!1,name:i}:{db_found:" - "!1,error:!0}))},Container.prototype.abort_query=function(){var t;return this" - ".disable_toggle_executing=!1,this.toggle_executing(!1),null!=(t=dataexplorer" - "_state.query_result)&&t.force_end_gracefully(),this.driver_handler.close_con" - "nection()},Container.prototype.execute_query=function(){var t,e,i;this.execu" - "ting===!0&&this.abort_query,this.$(\".profiler_enabled\").slideUp(\"fast\")," - "dataexplorer_state.cursor_timed_out=!1,dataexplorer_state.query_has_changed=" - "!1,this.raw_query=this.codemirror.getSelection()||this.codemirror.getValue()" - ",this.query=this.clean_query(this.raw_query);try{return null!=(i=dataexplore" - "r_state.query_result)&&i.discard(),this.non_rethinkdb_query=\"\",this.index=" - "0,this.raw_queries=this.separate_queries(this.raw_query),this.queries=this.s" - "eparate_queries(this.query),0===this.queries.length?(e=this.query_error_temp" - "late({no_query:!0}),this.results_view_wrapper.render_error(null,e,!0)):this." - "execute_portion()}catch(e){return t=e,this.results_view_wrapper.render_error" - "(this.query,t,!0),this.save_query({query:this.raw_query,broken_query:!0})}}," - "Container.prototype.toggle_executing=function(t){var e;if(t===this.executing" - ")return void(t&&(null!=(e=dataexplorer_state.query_result)?e.is_feed:void 0)" - "&&this.$(\".loading_query_img\").hide());if(!this.disable_toggle_executing)r" - "eturn this.executing=t,null!=this.timeout_toggle_abort&&clearTimeout(this.ti" - "meout_toggle_abort),t?this.timeout_toggle_abort=setTimeout(function(t){retur" - "n function(){var e;return t.timeout_toggle_abort=null,(null!=(e=dataexplorer" - "_state.query_result)?e.is_feed:void 0)||t.$(\".loading_query_img\").show(),t" - ".$(\".execute_query\").hide(),t.$(\".abort_query\").show()}}(this),this.dela" - "y_toggle_abort):this.timeout_toggle_abort=setTimeout(function(t){return func" - "tion(){return t.timeout_toggle_abort=null,t.$(\".loading_query_img\").hide()" - ",t.$(\".execute_query\").show(),t.$(\".abort_query\").hide()}}(this),this.de" - "lay_toggle_abort)},Container.prototype.execute_portion=function(){var t,e,i," - "r,s,n;for(dataexplorer_state.query_result=null;null!=this.queries[this.index" - "];){r=this.non_rethinkdb_query,r+=this.queries[this.index];try{n=this.evalua" - "te(r)}catch(e){return t=e,this.queries.length>1?this.results_view_wrapper.re" - "nder_error(this.raw_queries[this.index],t,!0):this.results_view_wrapper.rend" - "er_error(null,t,!0),this.save_query({query:this.raw_query,broken_query:!0})," - "!1}if(this.index++,n instanceof this.TermBaseConstructor)return i=this.index" - "===this.queries.length,this.start_time=new Date,i&&(s=new QueryResult({has_p" - "rofile:dataexplorer_state.options.profiler,current_query:this.raw_query,\n" - "raw_query:this.raw_queries[this.index],driver_handler:this.driver_handler,ev" - "ents:{error:function(t){return function(e,i){return t.results_view_wrapper.r" - "ender_error(t.query,i)}}(this),ready:function(t){return function(e){var i,r," - "s,n,o;if(dataexplorer_state.pause_at=null,e.is_feed){for(t.toggle_executing(" - "!0),t.disable_toggle_executing=!0,n=[\"end\",\"discard\",\"error\"],o=[],r=0" - ",s=n.length;r0)u--;else if(r===!0){if(e===_&&null!=t[i-1]&&\"\\\\\"!==t[i-1]){l+=" - "t.slice(h,i+1).replace(/\\n/g,\"\\\\n\"),h=i+1,r=!1;continue}}else{if(\"'\"=" - "==e||'\"'===e){l+=t.slice(h,i).replace(/\\n/g,\"\"),h=i,r=!0,_=e;continue}if" - "(o=this.regex.inline_comment.exec(t.slice(i)),null!=o){l+=t.slice(h,i).repla" - "ce(/\\n/g,\"\"),h=i,u=o[0].length-1,h+=o[0].length;continue}if(a=this.regex." - "multiple_line_comment.exec(t.slice(i)),null!=a){l+=t.slice(h,i).replace(/\\n" - "/g,\"\"),h=i,u=a[0].length-1,h+=a[0].length;continue}}return l+=r?t.slice(h," - "i).replace(/\\n/g,\"\\\\\\\\n\"):t.slice(h,i).replace(/\\n/g,\"\")},Containe" - "r.prototype.separate_queries=function(t){var e,i,r,s,n,o,a,l,h,_,u,c,p,d;for" - "(l=[],r=!1,u=[],c=0,a={char:0,line:1},i=s=0,o=t.length;s0)d--;else if(r===!0){if(e===p&&nul" - "l!=t[i-1]&&\"\\\\\"!==t[i-1]){r=!1;continue}}else{if(\"'\"===e||'\"'===e){r=" - "!0,p=e;continue}if(h=this.regex.inline_comment.exec(t.slice(i)),null!=h){d=h" - "[0].length-1;continue}if(_=this.regex.multiple_line_comment.exec(t.slice(i))" - ",null!=_){d=_[0].length-1;continue}if(e in this.stop_char.opening)u.push(e);" - "else if(e in this.stop_char.closing){if(u[u.length-1]!==this.stop_char.closi" - "ng[e])throw this.query_error_template({syntax_error:!0,bracket:e,line:a.line" - ",position:a.char});u.pop()}else\";\"===e&&0===u.length&&(l.push(t.slice(c,i+" - "1)),c=i+1)}return cthis.max_datum_threshold},e.prototype.json_to_tree=function(t){return this." - "has_too_many_datums(t)?this.tree_large_container_template({json_data:JSON.st" - "ringify(t,null,4)}):this.tree_container_template({tree:util.json_to_node(t)}" - ")},e.prototype.has_too_many_datums_helper=function(t){var e,i,r,s,n;if(\"[ob" - "ject Object]\"===Object.prototype.toString.call(t)){e=0;for(s in t)if(e+=thi" - "s.has_too_many_datums_helper(t[s]),e>this.max_datum_threshold)return e;retur" - "n e}if(Array.isArray(t)){for(e=0,r=0,n=t.length;rthis.max_datum_threshold)return e;return e}ret" - "urn 1},e.prototype.toggle_collapse=function(t){return this.$(t.target).nextA" - "ll(\".jt_collapsible\").toggleClass(\"jt_collapsed\"),this.$(t.target).nextA" - "ll(\".jt_points\").toggleClass(\"jt_points_collapsed\"),this.$(t.target).nex" - "tAll(\".jt_b\").toggleClass(\"jt_b_collapsed\"),this.$(t.target).toggleClass" - "(\"jt_arrow_hidden\"),this.parent.set_scrollbar()},e.prototype.expand_tree_i" - "n_table=function(t){var e,i,r,s;return r=this.$(t.target).parent(),this.$(t." - "target).remove(),i=r.data(\"json_data\"),s=this.json_to_tree(i),r.html(s),e=" - "r.parent().attr(\"class\").split(\" \")[0],$(\".\"+e).css(\"max-width\",\"no" - "ne\"),e=r.parent().parent().attr(\"class\"),$(\".\"+e).css(\"max-width\",\"n" - "one\"),r.css(\"max-width\",\"none\"),this.parent.set_scrollbar()},e.prototyp" - "e.parent_pause_feed=function(t){return this.parent.pause_feed()},e.prototype" - ".pause_feed=function(){if(null==this.parent.container.state.pause_at)return " - "this.parent.container.state.pause_at=this.query_result.size()},e.prototype.u" - "npause_feed=function(){if(null!=this.parent.container.state.pause_at)return " - "this.parent.container.state.pause_at=null,this.render()},e.prototype.setStac" - "kSize=function(){var t,e,i,r,s;if(!this._patched_already)return t=null!=(e=t" - "his.query_result.cursor)&&null!=(i=e.__proto__)&&null!=(r=i.__proto__)&&null" - "!=(s=r.constructor)?s.prototype:void 0,(null!=t?t.stackSize:void 0)>12?(cons" - "ole.log(\"Patching stack limit on cursors to 12\"),t.stackSize=12,this._patc" - "hed_already=!0):void 0},e}(Backbone.View),PagedResultView=function(t){functi" - "on e(){return this.add_row=bind(this.add_row,this),this.show_next_batch=bind" - "(this.show_next_batch,this),this.rerenderTableViewerView=bind(this.rerenderT" - "ableViewerView,this),this.fetch_batch_rows=bind(this.fetch_batch_rows,this)," - "this.initialize=bind(this.initialize,this),e.__super__.constructor.apply(thi" - "s,arguments)}return extend(e,t),e.prototype.initialize=function(t){return e." - "__super__.initialize.call(this,t)},e.prototype.fetch_batch_rows=function(){i" - "f(this.query_result.type!==!1)return this.setStackSize(),this.query_result.i" - "s_feed||this.query_result.size()this.parent.container." - "state.options.query_limit)return t.last().remove()},e}(PagedResultView),Tabl" - "eView=function(t){function e(){return this.render=bind(this.render,this),thi" - "s.tag_record=bind(this.tag_record,this),this.json_to_table=bind(this.json_to" - "_table,this),this.join_table=bind(this.join_table,this),this.compute_data_fo" - "r_type=bind(this.compute_data_for_type,this),this.json_to_table_get_td_value" - "=bind(this.json_to_table_get_td_value,this),this.json_to_table_get_values=bi" - "nd(this.json_to_table_get_values,this),this.json_to_table_get_attr=bind(this" - ".json_to_table_get_attr,this),this.get_all_attr=bind(this.get_all_attr,this)" - ",this.order_keys=bind(this.order_keys,this),this.compute_occurrence=bind(thi" - "s.compute_occurrence,this),this.build_map_keys=bind(this.build_map_keys,this" - "),this.handle_mouseup=bind(this.handle_mouseup,this),this.resize_column=bind" - "(this.resize_column,this),this.handle_mousemove=bind(this.handle_mousemove,t" - "his),this.handle_mousedown=bind(this.handle_mousedown,this),this.initialize=" - "bind(this.initialize,this),e.__super__.constructor.apply(this,arguments)}ret" - "urn extend(e,t),e.prototype.className=\"results table_view_container\",e.pro" - "totype.templates={wrapper:require(\"../handlebars/dataexplorer_result_table." - "hbs\"),container:require(\"../handlebars/dataexplorer_result_json_table_cont" - "ainer.hbs\"),tr_attr:require(\"../handlebars/dataexplorer_result_json_table_" - "tr_attr.hbs\"),td_attr:require(\"../handlebars/dataexplorer_result_json_tabl" - "e_td_attr.hbs\"),tr_value:require(\"../handlebars/dataexplorer_result_json_t" - "able_tr_value.hbs\"),td_value:require(\"../handlebars/dataexplorer_result_js" - "on_table_td_value.hbs\"),td_value_content:require(\"../handlebars/dataexplor" - "er_result_json_table_td_value_content.hbs\"),data_inline:require(\"../handle" - "bars/dataexplorer_result_json_table_data_inline.hbs\"),no_result:require(\"." - "./handlebars/dataexplorer_result_empty.hbs\")},e.prototype.default_size_colu" - "mn=310,e.prototype.mouse_down=!1,e.prototype.events=function(){return _.exte" - "nd(e.__super__.events.call(this),{\"mousedown .click_detector\":\"handle_mou" - "sedown\"})},e.prototype.initialize=function(t){return e.__super__.initialize" - ".call(this,t),this.last_keys=this.parent.container.state.last_keys,this.last" - "_columns_size=this.parent.container.state.last_columns_size,this.listenTo(th" - "is.query_result,\"end\",function(t){return function(){if(0===t.current_batch" - "_size())return t.render()}}(this))},e.prototype.handle_mousedown=function(t)" - "{var e;if(\"click_detector\"===(null!=t&&null!=(e=t.target)?e.className:void" - " 0))return this.col_resizing=this.$(t.target).parent().data(\"col\"),this.st" - "art_width=this.$(t.target).parent().width(),this.start_x=t.pageX,this.mouse_" - "down=!0,$(\"body\").toggleClass(\"resizing\",!0)},e.prototype.handle_mousemo" - "ve=function(t){if(this.mouse_down)return this.parent.container.state.last_co" - "lumns_size[this.col_resizing]=Math.max(5,this.start_width-this.start_x+t.pag" - "eX),this.resize_column(this.col_resizing,this.parent.container.state.last_co" - "lumns_size[this.col_resizing])},e.prototype.resize_column=function(t,e){retu" - "rn this.$(\".col-\"+t).css(\"max-width\",e),this.$(\".value-\"+t).css(\"max-" - "width\",e-20),this.$(\".col-\"+t).css(\"width\",e),this.$(\".value-\"+t).css" - "(\"width\",e-20),e<20?(this.$(\".value-\"+t).css(\"padding-left\",e-5+\"px\"" - "),this.$(\".value-\"+t).css(\"visibility\",\"hidden\")):(this.$(\".value-\"+" - "t).css(\"padding-left\",\"15px\"),this.$(\".value-\"+t).css(\"visibility\",\"" - "visible\"))},e.prototype.handle_mouseup=function(t){if(this.mouse_down===!0)" - "return this.mouse_down=!1,$(\"body\").toggleClass(\"resizing\",!1),this.pare" - "nt.set_scrollbar()},e.prototype.build_map_keys=function(t){var e,i,r,s,n;if(" - "i=t.keys_count,r=t.result,jQuery.isPlainObject(r)){if(\"TIME\"===r.$reql_typ" - "e$)return i.primitive_value_count++;if(\"BINARY\"===r.$reql_type$)return i.p" - "rimitive_value_count++;s=[];for(e in r)n=r[e],null==i.object&&(i.object={})," - "null==i.object[e]&&(i.object[e]={primitive_value_count:0}),s.push(this.build" - "_map_keys({keys_count:i.object[e],result:n}));return s}return i.primitive_va" - "lue_count++},e.prototype.compute_occurrence=function(t){var e,i,r,s,n;if(nul" - "l==t.object)return t.occurrence=t.primitive_value_count;e=t.primitive_value_" - "count>0?1:0,i=t.primitive_value_count,s=t.object;for(r in s)n=s[r],e++,this." - "compute_occurrence(n),i+=n.occurrence;return t.occurrence=i/e},e.prototype.o" - "rder_keys=function(t){var e,i,r,s;if(e=[],null!=t.object){r=t.object;for(i i" - "n r)s=r[i],jQuery.isPlainObject(s)&&this.order_keys(s),e.push({key:i,value:s" - ".occurrence});e.sort(function(t,e){return e.value-t.value?e.value-t.value:t." - "key>e.key?1:-1})}if(t.sorted_keys=_.map(e,function(t){return t.key}),t.primi" - "tive_value_count>0)return t.sorted_keys.unshift(this.primitive_key)},e.proto" - "type.get_all_attr=function(t){var e,i,r,s,n,o,a,l,h,_,u;for(s=t.keys_count,e" - "=t.attr,l=t.prefix,h=t.prefix_str,_=s.sorted_keys,u=[],i=0,n=_.length;i0&&(a=a.slice(0,-1)),u.push(e." - "push({prefix:l,prefix_str:a,is_primitive:!0}))):null!=s.object[r].object?(o=" - "util.deep_copy(l),o.push(r),u.push(this.get_all_attr({keys_count:s.object[r]" - ",attr:e,prefix:o,prefix_str:(null!=h?h:\"\")+r+\".\"}))):u.push(e.push({pref" - "ix:l,prefix_str:h,key:r}));return u},e.prototype.json_to_table_get_attr=func" - "tion(t){return this.templates.tr_attr({attr:t})},e.prototype.json_to_table_g" - "et_values=function(t){var e,i,r,s,n,o,a,l,h,_,u,c,p,d,g,y,f,m,b;for(f=t.resu" - "lt,s=t.flatten_attr,r=[],n=a=0,u=f.length;a0){for(r=[],o=l=0,m=this" - ".last_keys.length-1;0<=m?l<=m:l>=m;o=0<=m?++l:--l)g=0,this.$(\".col-\"+o).ch" - "ildren().children().children().each(function(t,e){var i;if(i=$(e),gthis.default_size_column&&r.push({c" - "ol:o,size:g+20});for(;r.length>0;)if(r.sort(function(t,e){return t.size-e.si" - "ze}),r[0].size-this.$(\".col-\"+r[0].col).width()0)return this.$(\".raw_view_textarea\"" - ").height(t)},e.prototype.render=function(){return this.$el.html(this.templat" - "e(JSON.stringify(this.current_batch()))),this.adjust_height(),this},e}(Paged" - "ResultView),ProfileView=function(t){function e(){return this.render=bind(thi" - "s.render,this),this.initialize=bind(this.initialize,this),e.__super__.constr" - "uctor.apply(this,arguments)}return extend(e,t),e.prototype.className=\"resul" - "ts profile_view_container\",e.prototype.template=require(\"../handlebars/dat" - "aexplorer_result_profile.hbs\"),e.prototype.initialize=function(t){return Ze" - "roClipboard.setDefaults({moviePath:\"js/ZeroClipboard.swf\",forceHandCursor:" - "!0}),this.clip=new ZeroClipboard,e.__super__.initialize.call(this,t)},e.prot" - "otype.compute_total_duration=function(t){return t.reduce(function(t,e){retur" - "n t+(e[\"duration(ms)\"]||e[\"mean_duration(ms)\"])},0)},e.prototype.compute" - "_num_shard_accesses=function(t){var e,i,r,s;for(r=0,e=0,i=t.length;ei&&(this.floating_m" - "etadata=!1,this.$(\".metadata\").removeClass(\"floating_metadata\"),null!=th" - "is.container.state.pause_at&&this.unpause_feed(\"automatic\")),!this.floatin" - "g_metadata&&t .co" - "ntent\").scrollTop()+$(\".nano\").height(),this.$(\"#query_history_\"+e).sli" - "deUp(\"fast\",function(t){return function(){return r.$(t).remove(),r.render(" - "),r.container.adjust_collapsible_panel_height({is_at_bottom:i})}}(this))},e." - "prototype.add_query=function(t){var e,i,r,s;return r=t.query,e=t.broken_quer" - "y,s=this,i=this.$(\".history_list\").height()===$(\".nano > .content\").scro" - "llTop()+$(\".nano\").height(),this.$(\".history_list\").append(this.dataexpl" - "orer_query_li_template({query:r,broken_query:e,id:this.history.length-1,num:" - "this.history.length})),this.$(\".no_history\").length>0?this.$(\".no_history" - "\").slideUp(\"fast\",function(){if($(this).remove(),\"visible\"===s.state)re" - "turn s.container.adjust_collapsible_panel_height({is_at_bottom:i})}):\"visib" - "le\"===s.state?this.container.adjust_collapsible_panel_height({delay_scroll:" - "!0,is_at_bottom:i}):void 0},e.prototype.clear_history=function(t){var e;retu" - "rn e=this,t.preventDefault(),this.container.clear_history(),this.history=thi" - "s.container.state.history,this.$(\".query_history\").slideUp(\"fast\",functi" - "on(){if($(this).remove(),0===e.$(\".no_history\").length)return e.$(\".histo" - "ry_list\").append(e.dataexplorer_query_li_template({no_query:!0,displayed_cl" - "ass:\"hidden\"})),e.$(\".no_history\").slideDown(\"fast\")}),e.container.adj" - "ust_collapsible_panel_height({size:40,move_arrow:\"show\",is_at_bottom:\"tru" - "e\"})},e}(Backbone.View),DriverHandler=function(){function t(t){this.remove=" - "bind(this.remove,this),this.cursor_next=bind(this.cursor_next,this),this.cur" - "sor_next_many=bind(this.cursor_next_many,this),this.run_with_new_connection=" - "bind(this.run_with_new_connection,this),this.close_connection=bind(this.clos" - "e_connection,this),this._end=bind(this._end,this),this._begin=bind(this._beg" - "in,this),this.container=t.container,this.concurrent=0,this.total_duration=0}" - "return t.prototype._begin=function(){return 0===this.concurrent&&(this.conta" - "iner.toggle_executing(!0),this.begin_time=new Date),this.concurrent++},t.pro" - "totype._end=function(){var t;if(this.concurrent>0&&(this.concurrent--,t=new " - "Date,this.total_duration+=t-this.begin_time,this.begin_time=t),0===this.conc" - "urrent)return this.container.toggle_executing(!1)},t.prototype.close_connect" - "ion=function(){var t;if((null!=(t=this.connection)?t.open:void 0)===!0)retur" - "n driver.close(this.connection),this.connection=null,this._end()},t.prototyp" - "e.run_with_new_connection=function(t,e){var i,r,s;return i=e.callback,r=e.co" - "nnection_error,s=e.optargs,this.close_connection(),this.total_duration=0,thi" - "s.concurrent=0,driver.connect(function(e){return function(n,o){return null!=" - "n&&r(n),o.removeAllListeners(\"error\"),o.on(\"error\",function(t){return r(" - "t)}),e.connection=o,e._begin(),t.private_run(o,s,function(t,r){return e._end" - "(),i(t,r)})}}(this))},t.prototype.cursor_next_many=function(t,e,i){var r,s;r" - "eturn null==this.connection&&end(),this._begin(),s=[],r=null,(r=function(n){" - "return function(){return s.length===e?(n._end(),void setTimeout(i,0,null,s))" - ":t.next(function(t,e){return t?(n._end(),\"No more rows in the cursor.\"===t" - ".message?void setTimeout(i,0,null,s):void setTimeout(i,0,t,s)):(s.push(e),r(" - "))})}}(this))()},t.prototype.cursor_next=function(t,e){var i,r,s;return r=e." - "error,s=e.row,i=e.end,null==this.connection&&i(),this._begin(),t.next(functi" - "on(t){return function(e,n){return t._end(),null!=e?\"No more rows in the cur" - "sor.\"===e.message?i():r(e):s(n)}}(this))},t.prototype.remove=function(){ret" - "urn this.close_connection()},t}(),exports.QueryResult=QueryResult,exports.Co" - "ntainer=Container,exports.ResultView=ResultView,exports.PagedResultView=Page" - "dResultView,exports.TreeView=TreeView,exports.TableViewerView=TableViewerVie" - "w,exports.TableView=TableView,exports.RawView=RawView,exports.ProfileView=Pr" - "ofileView,exports.ResultViewWrapper=ResultViewWrapper,exports.OptionsView=Op" - "tionsView,exports.HistoryView=HistoryView,exports.DriverHandler=DriverHandle" - "r,exports.dataexplorer_state=dataexplorer_state;\n" - "},{\"../handlebars/alert-connection_fail.hbs\":\"/rethinkdb/admin/static/han" - "dlebars/alert-connection_fail.hbs\",\"../handlebars/dataexplorer-cursor_time" - "d_out.hbs\":\"/rethinkdb/admin/static/handlebars/dataexplorer-cursor_timed_o" - "ut.hbs\",\"../handlebars/dataexplorer-databases_suggestions.hbs\":\"/rethink" - "db/admin/static/handlebars/dataexplorer-databases_suggestions.hbs\",\"../han" - "dlebars/dataexplorer-description.hbs\":\"/rethinkdb/admin/static/handlebars/" - "dataexplorer-description.hbs\",\"../handlebars/dataexplorer-description_with" - "_example.hbs\":\"/rethinkdb/admin/static/handlebars/dataexplorer-description" - "_with_example.hbs\",\"../handlebars/dataexplorer-error.hbs\":\"/rethinkdb/ad" - "min/static/handlebars/dataexplorer-error.hbs\",\"../handlebars/dataexplorer-" - "history.hbs\":\"/rethinkdb/admin/static/handlebars/dataexplorer-history.hbs\"" - ",\"../handlebars/dataexplorer-option_page.hbs\":\"/rethinkdb/admin/static/ha" - "ndlebars/dataexplorer-option_page.hbs\",\"../handlebars/dataexplorer-options" - ".hbs\":\"/rethinkdb/admin/static/handlebars/dataexplorer-options.hbs\",\"../" - "handlebars/dataexplorer-query_error.hbs\":\"/rethinkdb/admin/static/handleba" - "rs/dataexplorer-query_error.hbs\",\"../handlebars/dataexplorer-query_li.hbs\"" - ":\"/rethinkdb/admin/static/handlebars/dataexplorer-query_li.hbs\",\"../handl" - "ebars/dataexplorer-reason_broken.hbs\":\"/rethinkdb/admin/static/handlebars/" - "dataexplorer-reason_broken.hbs\",\"../handlebars/dataexplorer-tables_suggest" - "ions.hbs\":\"/rethinkdb/admin/static/handlebars/dataexplorer-tables_suggesti" - "ons.hbs\",\"../handlebars/dataexplorer_input_query.hbs\":\"/rethinkdb/admin/" - "static/handlebars/dataexplorer_input_query.hbs\",\"../handlebars/dataexplore" - "r_large_result_json_tree_container.hbs\":\"/rethinkdb/admin/static/handlebar" - "s/dataexplorer_large_result_json_tree_container.hbs\",\"../handlebars/dataex" - "plorer_result_container.hbs\":\"/rethinkdb/admin/static/handlebars/dataexplo" - "rer_result_container.hbs\",\"../handlebars/dataexplorer_result_empty.hbs\":\"" - "/rethinkdb/admin/static/handlebars/dataexplorer_result_empty.hbs\",\"../hand" - "lebars/dataexplorer_result_json_table_container.hbs\":\"/rethinkdb/admin/sta" - "tic/handlebars/dataexplorer_result_json_table_container.hbs\",\"../handlebar" - "s/dataexplorer_result_json_table_data_inline.hbs\":\"/rethinkdb/admin/static" - "/handlebars/dataexplorer_result_json_table_data_inline.hbs\",\"../handlebars" - "/dataexplorer_result_json_table_td_attr.hbs\":\"/rethinkdb/admin/static/hand" - "lebars/dataexplorer_result_json_table_td_attr.hbs\",\"../handlebars/dataexpl" - "orer_result_json_table_td_value.hbs\":\"/rethinkdb/admin/static/handlebars/d" - "ataexplorer_result_json_table_td_value.hbs\",\"../handlebars/dataexplorer_re" - "sult_json_table_td_value_content.hbs\":\"/rethinkdb/admin/static/handlebars/" - "dataexplorer_result_json_table_td_value_content.hbs\",\"../handlebars/dataex" - "plorer_result_json_table_tr_attr.hbs\":\"/rethinkdb/admin/static/handlebars/" - "dataexplorer_result_json_table_tr_attr.hbs\",\"../handlebars/dataexplorer_re" - "sult_json_table_tr_value.hbs\":\"/rethinkdb/admin/static/handlebars/dataexpl" - "orer_result_json_table_tr_value.hbs\",\"../handlebars/dataexplorer_result_js" - "on_tree_container.hbs\":\"/rethinkdb/admin/static/handlebars/dataexplorer_re" - "sult_json_tree_container.hbs\",\"../handlebars/dataexplorer_result_profile.h" - "bs\":\"/rethinkdb/admin/static/handlebars/dataexplorer_result_profile.hbs\"," - "\"../handlebars/dataexplorer_result_raw.hbs\":\"/rethinkdb/admin/static/hand" - "lebars/dataexplorer_result_raw.hbs\",\"../handlebars/dataexplorer_result_tab" - "le.hbs\":\"/rethinkdb/admin/static/handlebars/dataexplorer_result_table.hbs\"" - ",\"../handlebars/dataexplorer_result_tree.hbs\":\"/rethinkdb/admin/static/ha" - "ndlebars/dataexplorer_result_tree.hbs\",\"../handlebars/dataexplorer_suggest" - "ion_name_li.hbs\":\"/rethinkdb/admin/static/handlebars/dataexplorer_suggesti" - "on_name_li.hbs\",\"../handlebars/dataexplorer_view.hbs\":\"/rethinkdb/admin/" - "static/handlebars/dataexplorer_view.hbs\",\"./app.coffee\":\"/rethinkdb/admi" - "n/static/coffee/app.coffee\",\"./util.coffee\":\"/rethinkdb/admin/static/cof" - "fee/util.coffee\",\"rethinkdb\":\"rethinkdb\"}],\"/rethinkdb/admin/static/co" - "ffee/issues.coffee\":[function(require,module,exports){\n" - "var IssuesBanner,availability_description,availability_string,createElement," - "diff,h,patch,rename_modal,render_issue,render_issues,render_issues_message,r" - "ender_log_write_error,render_memory_error,render_name_collision,render_non_t" - "ransitive_error,render_outdated_index,render_resolve_button,render_table_ava" - "ilability,render_unknown_issue,ui_modals,util,bind=function(e,r){return func" - "tion(){return e.apply(r,arguments)}},extend=function(e,r){function t(){this." - "constructor=e}for(var i in r)hasProp.call(r,i)&&(e[i]=r[i]);return t.prototy" - "pe=r.prototype,e.prototype=new t,e.__super__=r.prototype,e},hasProp={}.hasOw" - "nProperty;h=require(\"virtual-dom/h\"),diff=require(\"virtual-dom/diff\"),pa" - "tch=require(\"virtual-dom/patch\"),createElement=require(\"virtual-dom/creat" - "e-element\"),util=require(\"./util.coffee\"),ui_modals=require(\"./ui_compon" - "ents/modals.coffee\"),IssuesBanner=function(e){function r(){return this.rend" - "er=bind(this.render,this),this.toggle_display=bind(this.toggle_display,this)" - ",this.initialize=bind(this.initialize,this),r.__super__.constructor.apply(th" - "is,arguments)}return extend(r,e),r.prototype.events={\"click .btn-resolve-is" - "sues\":\"toggle_display\",\"click .change-route\":\"toggle_display\"},r.prot" - "otype.initialize=function(e){return this.collection=e.collection,this.show_r" - "esolve=!0,this.current_vdom_tree=render_issues(this.collection.toJSON(),this" - ".show_resolve),this.listenTo(this.model,\"change\",this.render),this.listenT" - "o(this.collection,\"change\",this.render),this.rename_modal=null,this.setEle" - "ment(createElement(this.current_vdom_tree))},r.prototype.toggle_display=func" - "tion(){return this.show_resolve?this.show_resolve=!1:this.collection.length>" - "0&&(this.show_resolve=!0),this.show_resolve?this.$(\".all-issues\").slideUp(" - "300,\"swing\",this.render):this.$(\".all-issues\").slideDown(300,\"swing\",t" - "his.render)},r.prototype.render=function(){var e,r;return e=render_issues(th" - "is.collection.toJSON(),this.show_resolve),r=diff(this.current_vdom_tree,e),p" - "atch(this.$el.get(0),r),this.current_vdom_tree=e,0===this.collection.length&" - "&(this.show_resolve=!0),this},r}(Backbone.View),rename_modal=function(e){ret" - "urn function(e){var r;return r=new ui_modals.RenameItemModal({item_type:e.it" - "emType,model:new Backbone.Model({id:e.id,name:e.name})}),r.render()}}(this)," - "render_unknown_issue=function(e){return{title:\"Unknown issue\",subtitle:e.t" - "ype,details:[h(\"p\",\"An unknown issue was sent by the server. Please conta" - "ct support.\"),h(\"p\",\"Raw data:\"),h(\"code\",e.description)]}},availabil" - "ity_string=function(e){return e.all_replicas_ready?\"is ready\":e.ready_for_" - "writes?\"has some replicas that are not ready\":e.ready_for_reads?\"is read " - "only\":e.ready_for_outdated_reads?\"is ready for outdated reads only\":\"is " - "not available\"},availability_description=function(e){return e.all_replicas_" - "ready?\" is ready.\":e.ready_for_writes?\" is available for all operations, " - "but some replicas are not ready.\":e.ready_for_reads?\" is available for out" - "dated and up-to-date reads, but not writes.\":e.ready_for_outdated_reads?\" " - "is available for outdated reads, but not up-to-date reads or writes.\":\" is" - " not available.\"},render_table_availability=function(e){var r;return r=e.in" - "fo,{title:\"Table availability\",subtitle:r.db+\".\"+r.table+\" \"+availabil" - "ity_string(r.status),details:[h(\"p\",[\"Table \",h(\"a.change-route\",{href" - ":\"#/tables/\"+r.table_id},r.db+\".\"+r.table),availability_description(r.st" - "atus)]),r.missing_servers.length>0?h(\"p\",[\"The following servers are disc" - "onnected:\",h(\"ul\",r.missing_servers.map(function(e){return h(\"li\",h(\"c" - "ode\",e))}))]):void 0,0===r.missing_servers.length?h(\"p\",[\"None of the re" - "plicas for this table are reachable \",\"(the servers for these replicas may" - " be down or disconnected). \",h(\"br\"),\"No operations can be performed on " - "this table until at least \",\"one replica is reachable.\"]):void 0]}},rende" - "r_name_collision=function(e,r){return{title:util.capitalize(e)+\" name confl" - "ict\",subtitle:r.info.name+\" is the name of more than one \"+e,details:[h(\"" - "p\",[util.pluralize_noun(e,r.info.ids.length,!0),\" with the name \"+r.info." - "name+\":\"]),h(\"ul\",r.info.ids.map(function(t){var i,s;return s=util.plura" - "lize_noun(e,2),i=\"database\"!==e?h(\"a.change-route\",{href:\"#\"+s+\"/\"+t" - "},h(\"span.uuid\",util.humanize_uuid(t))):h(\"span.uuid\",util.humanize_uuid" - "(t)),h(\"li\",[i,\" (\",h(\"a.rename\",{href:\"#\",onclick:function(e){retur" - "n function(e){return e.preventDefault(),rename_modal(e.target.dataset)}}(thi" - "s),dataset:{itemType:e,id:t,name:r.info.name}},\"Rename\"),\")\"])}))]}},ren" - "der_outdated_index=function(e){var r,t;return t=\"http://www.rethinkdb.com/d" - "ocs/troubleshooting/#my-secondary-index-is-outdated\",r=\"rethinkdb index-re" - "build\",{title:\"Outdated indexes\",subtitle:\"Some secondary indexes need t" - "o be updated\",details:[h(\"p\",[\"This cluster contains outdated indexes th" - "at were created with \",\"a previous version of RethinKDB that contained som" - "e bugs.\",h(\"br\"),\"Use \",h(\"code\",r),\" to apply the latest bug fixes " - "and improvements.\",\" See \",h(\"a\",{href:t},\"the troubleshooting page\")" - ",\" for more details.\"]),h(\"ul\",e.info.tables.map(function(e){return h(\"" - "li\",[\"The table \",h(\"a\",{href:\"#tables/\"+e.table_id},e.db+\".\"+e.tab" - "le),\" has \"+e.indexes.length+\" outdated \",util.pluralize_noun(\"index\"," - "e.indexes.length),\": \",e.indexes.join(\", \")])}))]}},render_memory_error=" - "function(e){return{title:\"Memory issue\",subtitle:[\"A server is using swap" - " memory.\"],details:[h(\"p\",[\"The following \",util.pluralize_noun(\"serve" - "r\",e.info.servers.length),\" encountered a memory problem:\"]),h(\"ul\",e.i" - "nfo.servers.map(function(e){return h(\"li\",h(\"a\",{href:\"/#servers/\"+e.s" - "erver_id},e.server))})),h(\"p\",[\"The issue reported is: \",h(\"code\",e.in" - "fo.message)]),h(\"p\",[\"Please fix the problem that is causing the \",util." - "pluralize_noun(\"server\",e.info.servers.length),\" to use swap memory. This" - " issue will go away \",\"after ten minutes have passed since a significant a" - "mount \",\"of swap memory was used,\",\" or after you restart RethinkDB.\"])" - "]}},render_non_transitive_error=function(e){return{title:\"Connectivity issu" - "e\",subtitle:[\"Some servers are only partially connected to the cluster.\"]" - ",details:[h(\"p\",[\"The following servers are not fully connected:\"]),h(\"" - "ul\",e.info.servers.map(function(e){return h(\"li\",h(\"a\",{href:\"/#server" - "s/\"+e.server_id},e.server))})),h(\"p\",[\"Partial connectivity can cause ta" - "bles to remain unavailable\",\" and queries to fail. Please check your netwo" - "rk configuration\",\" if this issue persists for more than a few seconds.\"]" - ")]}},render_log_write_error=function(e){return{title:\"Cannot write logs\",s" - "ubtitle:[\"Log \",util.pluralize_noun(\"file\",e.info.servers.length),\" can" - "not be written to\"],details:[h(\"p\",[\"The following \",util.pluralize_nou" - "n(\"server\",e.info.servers.length),\" encountered an error while writing lo" - "g statements:\"]),h(\"ul\",e.info.servers.map(function(e){return h(\"li\",h(" - "\"a\",{href:\"/#servers/\"+e.server_id},e.server))})),h(\"p\",[\"The error m" - "essage reported is: \",h(\"code\",e.info.message)]),h(\"p\",[\"Please fix th" - "e problem that is preventing the \",util.pluralize_noun(\"server\",e.info.se" - "rvers.length),\" from writing to their log file. This issue will go away \"," - "\"the next time the server successfully writes to the log file.\"])]}},rende" - "r_issue=function(e){var r,t;return t=function(){switch(e.type){case\"log_wri" - "te_error\":return render_log_write_error(e);case\"memory_error\":return rend" - "er_memory_error(e);case\"non_transitive_error\":return render_non_transitive" - "_error(e);case\"outdated_index\":return render_outdated_index(e);case\"table" - "_availability\":return render_table_availability(e);case\"db_name_collision\"" - ":return render_name_collision(\"database\",e);case\"server_name_collision\":" - "return render_name_collision(\"server\",e);case\"table_name_collision\":retu" - "rn render_name_collision(\"table\",e);default:return render_unknown_issue(e)" - "}}(),r=e.critical?\".critical\":\"\",h(\"div.issue-container\",h(\"div.issue" - "\"+r,[h(\"div.issue-header\",[h(\"p.issue-type\",t.title),h(\"p.message\",t." - "subtitle)]),h(\"hr\"),h(\"div.issue-details\",t.details)]))},render_issues=f" - "unction(e,r){var t;return t=e.length>0?\"\":\".no-issues\",h(\"div.issues-ba" - "nner\",e.length>0?[h(\"div.show-issues-banner\"+t,h(\"div.gradient-overlay\"" - "+t,h(\"div.container\",[render_resolve_button(r),h(\"p.message\",render_issu" - "es_message(e.length))]))),h(\"div#resolve-issues.all-issues\",h(\"div.contai" - "ner\",[h(\"div#issue-alerts\"),h(\"div.issues_list\",e.map(render_issue))]))" - "]:void 0)},render_issues_message=function(e){return 0===e?\"All issues have " - "been successfully resolved.\":[h(\"strong\",[e+\" \"+util.pluralize_noun(\"i" - "ssue\",e)]),\" \"+util.pluralize_verb(\"need\",e)+\" to be resolved\"]},rend" - "er_resolve_button=function(e){return e?h(\"button.btn.btn-resolve-issues.sho" - "w-issues\",\"Show issues\"):h(\"button.btn.btn-resolve-issues.hide-issues\"," - "\"Hide issues\")},exports.IssuesBanner=IssuesBanner;\n" - "},{\"./ui_components/modals.coffee\":\"/rethinkdb/admin/static/coffee/ui_com" - "ponents/modals.coffee\",\"./util.coffee\":\"/rethinkdb/admin/static/coffee/u" - "til.coffee\",\"virtual-dom/create-element\":\"/rethinkdb/build/external/admi" - "n-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/virtual-dom/create-el" - "ement.js\",\"virtual-dom/diff\":\"/rethinkdb/build/external/admin-deps_2.0.4" - "/node_modules/rethinkdb-webui/node_modules/virtual-dom/diff.js\",\"virtual-d" - "om/h\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-w" - "ebui/node_modules/virtual-dom/h.js\",\"virtual-dom/patch\":\"/rethinkdb/buil" - "d/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/virtua" - "l-dom/patch.js\"}],\"/rethinkdb/admin/static/coffee/log_view.coffee\":[funct" - "ion(require,module,exports){\n" - "var LogView,LogsContainer,LogsListView,app,driver,models,bind=function(e,t){" - "return function(){return e.apply(t,arguments)}},extend=function(e,t){functio" - "n i(){this.constructor=e}for(var r in t)hasProp.call(t,r)&&(e[r]=t[r]);retur" - "n i.prototype=t.prototype,e.prototype=new i,e.__super__=t.prototype,e},hasPr" - "op={}.hasOwnProperty;app=require(\"./app.coffee\"),driver=app.driver,models=" - "require(\"./models.coffee\"),LogsContainer=function(e){function t(){return t" - "his.remove=bind(this.remove,this),this.render=bind(this.render,this),this.fe" - "tch_data=bind(this.fetch_data,this),this.increase_limit=bind(this.increase_l" - "imit,this),this.initialize=bind(this.initialize,this),t.__super__.constructo" - "r.apply(this,arguments)}return extend(t,e),t.prototype.template={main:requir" - "e(\"../handlebars/logs_container.hbs\"),error:require(\"../handlebars/error-" - "query.hbs\"),alert_message:require(\"../handlebars/alert_message.hbs\")},t.p" - "rototype.events={\"click .next-log-entries\":\"increase_limit\"},t.prototype" - ".initialize=function(e){return null==e&&(e={}),this.limit=e.limit||20,this.c" - "urrent_limit=this.limit,this.server_id=e.server_id,this.container_rendered=!" - "1,this.query=e.query||driver.queries.all_logs,this.logs=new models.Logs,this" - ".logs_list=new LogsListView({collection:this.logs}),this.fetch_data()},t.pro" - "totype.increase_limit=function(e){return e.preventDefault(),this.current_lim" - "it+=this.limit,driver.stop_timer(this.timer),this.fetch_data()},t.prototype." - "fetch_data=function(){return this.timer=driver.run(this.query(this.current_l" - "imit,this.server_id),5e3,function(e){return function(t,i){var r,n,o,s,l;if(n" - "ull==t){for(n=r=0,o=i.length;r0){for(this.$(\".btn.btn" - "-primary\").prop({disabled:!0}),this.$(\".alert.error\").addClass(\"shown\")" - ",i=[],r=0,s=t.length;re?t.push(\"too-many-shards-hard-li" - "mit\"):i>r&&(i>n?t.push(\"too-many-shards\"):t.push(\"not-enough-defaults-sh" - "ard\")),0===s?t.push(\"zero-replicas\"):isNaN(s)?t.push(\"no-replicas\"):s>r" - "&&(s>n?t.push(\"too-many-replicas\"):t.push(\"not-enough-defaults-replica\")" - "),null!=this.model.get(\"server_error\")&&t.push(\"server-error\"),this.mode" - "l.set({errors:t}),t.length>0&&this.model.set({shards:[]}),t.length>0},t.prot" - "otype.fetch_dryrun=function(){var e,t;if(!this.get_errors())return e=functio" - "n(e){return e},t=r.db(this.model.get(\"db\")).table(this.model.get(\"name\")" - ").reconfigure({shards:this.model.get(\"num_shards\"),replicas:this.model.get" - "(\"num_replicas_per_shard\"),dryRun:!0})(\"config_changes\")(0).do(function(" - "t){return r.object(r.args(t.keys().map(function(e){return[e,t(e)(\"shards\")" - "]}).concatMap(e)))}).do(function(t){return t.merge({lookups:r.object(r.args(" - "t(\"new_val\")(\"replicas\").concatMap(e).setUnion(t(\"old_val\")(\"replicas" - "\").concatMap(e)).concatMap(function(e){return[e,r.db(system_db).table(\"ser" - "ver_status\").filter({name:e})(0)(\"id\").default(null)]})))})}),driver.run_" - "once(t,function(e){return function(t,r){return null!=t?e.model.set({server_e" - "rror:t.msg,shards:[]}):(e.model.set({server_error:null}),e.model.set({shards" - ":e.fixup_shards(r.old_val,r.new_val,r.lookups)}))}}(this))},t.prototype.shar" - "d_sort=function(e,t){return\"primary\"===e.role?-1:\"primary\"===t.role||\"p" - "rimary\"===t.old_role?1:e.role===t.role?e.name===t.name?0:e.name=t.length?(" - "m={primary_replica:null,replicas:[]},M=!0):(m=t[s],M=!1),k={replicas:[],chan" - "ge:M?\"deleted\":null},g=b.replicas,i=0,d=g.length;i=0||k.replicas.push({name:x,id:r" - "[x],change:\"added\",role:this.role(x===m.primary_replica),old_role:null,rol" - "e_change:!1});k.replicas.sort(this.shard_sort),D.push(k)}for(v=t.slice(e.len" - "gth),u=0,_=v.length;u<_;u++){for(m=v[u],k={replicas:[],change:\"added\"},w=m" - ".replicas,c=0,h=w.length;ce.get(\"shard_id\")?1:t.get(\"start_shard\")===!0?-1:e.get(\"start" - "_shard\")===!0?1:t.get(\"end_shard\")===!0?1:e.get(\"end_shard\")===!0?-1:t." - "get(\"primary\")===!0&&e.get(\"replica\")===!0?-1:t.get(\"replica\")===!0&&e" - ".get(\"primary\")===!0?1:t.get(\"replica\")===!0&&e.get(\"replica\")===!0?t." - "get(\"replica_position\")e.get(\"replica_position\")?1:0:void 0},e}(Backbone.Collection),Sha" - "rdAssignment=function(t){function e(){return e.__super__.constructor.apply(t" - "his,arguments)}return extend(e,t),e}(Backbone.Model),Responsibilities=functi" - "on(t){function e(){return e.__super__.constructor.apply(this,arguments)}retu" - "rn extend(e,t),e.prototype.model=Responsibility,e.prototype.name=\"Responsib" - "ility\",e.prototype.comparator=function(t,e){return t.get(\"db\")e.get(\"db\")?1:t.get(\"table\")e.get(\"table\")?1:t.get(\"is_table\")===!0?-1:e.get(\"is_tab" - "le\")===!0?1:t.get(\"index\")e.get(\"i" - "ndex\")?1:0},e}(Backbone.Collection),Responsibility=function(t){function e()" - "{return e.__super__.constructor.apply(this,arguments)}return extend(e,t),e}(" - "Backbone.Model),Dashboard=function(t){function e(){return e.__super__.constr" - "uctor.apply(this,arguments)}return extend(e,t),e}(Backbone.Model),Issue=func" - "tion(t){function e(){return e.__super__.constructor.apply(this,arguments)}re" - "turn extend(e,t),e}(Backbone.Model),Issues=function(t){function e(){return e" - ".__super__.constructor.apply(this,arguments)}return extend(e,t),e.prototype." - "model=Issue,e.prototype.name=\"Issues\",e}(Backbone.Collection),Logs=functio" - "n(t){function e(){return e.__super__.constructor.apply(this,arguments)}retur" - "n extend(e,t),e.prototype.model=Log,e.prototype.name=\"Logs\",e}(Backbone.Co" - "llection),Log=function(t){function e(){return e.__super__.constructor.apply(" - "this,arguments)}return extend(e,t),e}(Backbone.Model),Stats=function(t){func" - "tion e(){return this.get_stats=bind(this.get_stats,this),this.on_result=bind" - "(this.on_result,this),this.initialize=bind(this.initialize,this),e.__super__" - ".constructor.apply(this,arguments)}return extend(e,t),e.prototype.initialize" - "=function(t){return this.set({keys_read:0,keys_set:0})},e.prototype.on_resul" - "t=function(t,e){return null!=t?console.log(t):this.set(e)},e.prototype.get_s" - "tats=function(){return this.toJSON()},e}(Backbone.Model),exports.Servers=Ser" - "vers,exports.Server=Server,exports.Tables=Tables,exports.Table=Table,exports" - ".Reconfigure=Reconfigure,exports.Databases=Databases,exports.Database=Databa" - "se,exports.Indexes=Indexes,exports.Index=Index,exports.Distribution=Distribu" - "tion,exports.Shard=Shard,exports.ShardAssignments=ShardAssignments,exports.S" - "hardAssignment=ShardAssignment,exports.Responsibilities=Responsibilities,exp" - "orts.Responsibility=Responsibility,exports.Dashboard=Dashboard,exports.Issue" - "=Issue,exports.Issues=Issues,exports.Logs=Logs,exports.Log=Log,exports.Stats" - "=Stats;\n" - "},{}],\"/rethinkdb/admin/static/coffee/navbar.coffee\":[function(require,mod" - "ule,exports){\n" - "var NavBarView,app,bind=function(t,e){return function(){return t.apply(e,arg" - "uments)}},extend=function(t,e){function a(){this.constructor=t}for(var i in " - "e)hasProp.call(e,i)&&(t[i]=e[i]);return a.prototype=e.prototype,t.prototype=" - "new a,t.__super__=e.prototype,t},hasProp={}.hasOwnProperty;app=require(\"./a" - "pp.coffee\"),NavBarView=function(t){function e(){return this.update_cog_icon" - "=bind(this.update_cog_icon,this),this.set_active_tab=bind(this.set_active_ta" - "b,this),this.render=bind(this.render,this),this.init_typeahead=bind(this.ini" - "t_typeahead,this),this.initialize=bind(this.initialize,this),e.__super__.con" - "structor.apply(this,arguments)}return extend(e,t),e.prototype.id=\"navbar\"," - "e.prototype.className=\"container\",e.prototype.template=require(\"../handle" - "bars/navbar_view.hbs\"),e.prototype.events={\"click .options_link\":\"update" - "_cog_icon\"},e.prototype.initialize=function(t){return this.databases=t.data" - "bases,this.tables=t.tables,this.servers=t.servers,this.container=t.container" - ",this.options_state=\"hidden\"},e.prototype.init_typeahead=function(){return" - " this.$(\"input.search-box\").typeahead({source:function(t){return function(" - "e,a){var i,s,r;return s=_.map(t.servers.models,function(t){return{id:t.get(\"" - "id\"),name:t.get(\"name\")+\" (server)\",type:\"servers\"}}),r=_.map(t.table" - "s.models,function(t){return{id:t.get(\"id\"),name:t.get(\"name\")+\" (table)" - "\",type:\"tables\"}}),i=_.map(t.databases.models,function(t){return{id:t.get" - "(\"id\"),name:t.get(\"name\")+\" (database)\",type:\"databases\"}}),s.concat" - "(r).concat(i)}}(this),property:\"name\",onselect:function(t){return app.main" - ".router.navigate(\"#\"+t.type+\"/\"+t.id,{trigger:!0})}})},e.prototype.rende" - "r=function(t){return this.$el.html(this.template()),this.set_active_tab(t),t" - "his},e.prototype.set_active_tab=function(t){if(null!=t)switch(t){case\"dashb" - "oard\":return this.$(\"ul.nav-left li\").removeClass(\"active\"),this.$(\"li" - "#nav-dashboard\").addClass(\"active\");case\"index_tables\":return this.$(\"" - "ul.nav-left li\").removeClass(\"active\"),this.$(\"li#nav-tables\").addClass" - "(\"active\");case\"table\":return this.$(\"ul.nav-left li\").removeClass(\"a" - "ctive\"),this.$(\"li#nav-tables\").addClass(\"active\");case\"database\":ret" - "urn this.$(\"ul.nav-left li\").removeClass(\"active\"),this.$(\"li#nav-table" - "s\").addClass(\"active\");case\"index_servers\":return this.$(\"ul.nav-left " - "li\").removeClass(\"active\"),this.$(\"li#nav-servers\").addClass(\"active\"" - ");case\"server\":return this.$(\"ul.nav-left li\").removeClass(\"active\"),t" - "his.$(\"li#nav-servers\").addClass(\"active\");case\"datacenter\":return thi" - "s.$(\"ul.nav-left li\").removeClass(\"active\"),this.$(\"li#nav-servers\").a" - "ddClass(\"active\");case\"dataexplorer\":return this.$(\"ul.nav-left li\").r" - "emoveClass(\"active\"),this.$(\"li#nav-dataexplorer\").addClass(\"active\");" - "case\"logs\":return this.$(\"ul.nav-left li\").removeClass(\"active\"),this." - "$(\"li#nav-logs\").addClass(\"active\")}},e.prototype.update_cog_icon=functi" - "on(t){return this.$(\".cog_icon\").toggleClass(\"active\"),this.container.to" - "ggle_options(t)},e}(Backbone.View),exports.NavBarView=NavBarView;\n" - "},{\"../handlebars/navbar_view.hbs\":\"/rethinkdb/admin/static/handlebars/na" - "vbar_view.hbs\",\"./app.coffee\":\"/rethinkdb/admin/static/coffee/app.coffee" - "\"}],\"/rethinkdb/admin/static/coffee/router.coffee\":[function(require,modu" - "le,exports){\n" - "var BackboneCluster,app,dashboard,dataexplorer_view,log_view,server_view,ser" - "vers_index,table_view,tables_view,bind=function(e,t){return function(){retur" - "n e.apply(t,arguments)}},extend=function(e,t){function r(){this.constructor=" - "e}for(var i in t)hasProp.call(t,i)&&(e[i]=t[i]);return r.prototype=t.prototy" - "pe,e.prototype=new r,e.__super__=t.prototype,e},hasProp={}.hasOwnProperty;da" - "shboard=require(\"./dashboard/dashboard.coffee\"),log_view=require(\"./log_v" - "iew.coffee\"),dataexplorer_view=require(\"./dataexplorer.coffee\"),tables_vi" - "ew=require(\"./tables/index.coffee\"),table_view=require(\"./tables/table.co" - "ffee\"),servers_index=require(\"./servers/index.coffee\"),server_view=requir" - "e(\"./servers/server.coffee\"),app=require(\"./app.coffee\"),BackboneCluster" - "=function(e){function t(){return this.update_active_tab=bind(this.update_act" - "ive_tab,this),t.__super__.constructor.apply(this,arguments)}return extend(t," - "e),t.prototype.routes={\"\":\"dashboard\",tables:\"index_tables\",\"tables/\"" - ":\"index_tables\",\"tables/:id\":\"table\",\"tables/:id/\":\"table\",servers" - ":\"index_servers\",\"servers/\":\"index_servers\",\"servers/:id\":\"server\"" - ",\"servers/:id/\":\"server\",dashboard:\"dashboard\",\"dashboard/\":\"dashbo" - "ard\",logs:\"logs\",\"logs/\":\"logs\",dataexplorer:\"dataexplorer\",\"datae" - "xplorer/\":\"dataexplorer\"},t.prototype.initialize=function(e){return t.__s" - "uper__.initialize.apply(this,arguments),this.navbar=e.navbar,this.container=" - "$(\"#cluster\"),this.current_view=new Backbone.View,this.bind(\"route\",this" - ".update_active_tab)},t.prototype.update_active_tab=function(e){return this.n" - "avbar.set_active_tab(e)},t.prototype.index_tables=function(){return this.cur" - "rent_view.remove(),this.current_view=new tables_view.DatabasesContainer,this" - ".container.html(this.current_view.render().$el)},t.prototype.index_servers=f" - "unction(e){return this.current_view.remove(),this.current_view=new servers_i" - "ndex.View,this.container.html(this.current_view.render().$el)},t.prototype.d" - "ashboard=function(){return this.current_view.remove(),this.current_view=new " - "dashboard.View,this.container.html(this.current_view.render().$el)},t.protot" - "ype.logs=function(){return this.current_view.remove(),this.current_view=new " - "log_view.LogsContainer,this.container.html(this.current_view.render().$el)}," - "t.prototype.dataexplorer=function(){return this.current_view.remove(),this.c" - "urrent_view=new dataexplorer_view.Container({state:dataexplorer_view.dataexp" - "lorer_state}),this.container.html(this.current_view.render().$el),this.curre" - "nt_view.init_after_dom_rendered(),this.current_view.results_view_wrapper.set" - "_scrollbar()},t.prototype.goto_explore_table=function(e){return this.update_" - "active_tab(\"table\"),this.navigate(\"#tables/\"+e,{replace:!1}),this.table(" - "e),setTimeout(function(e){return function(){return window.scrollTo(0,documen" - "t.body.scrollHeight)}}(this),0)},t.prototype.goto_explore_table_id=function(" - "e,t){return this.update_active_tab(\"table\"),this.navigate(\"#tables/\"+e,{" - "replace:!1}),this.table(e),this.current_view.table_view.table_viewer.initiat" - "eSeek(t),setTimeout(function(e){return function(){return window.scrollTo(0,d" - "ocument.body.scrollHeight)}}(this),0)},t.prototype.table=function(e){return " - "this.current_view.remove(),this.current_view=new table_view.TableContainer(e" - "),this.container.html(this.current_view.render().$el)},t.prototype.server=fu" - "nction(e){return this.current_view.remove(),this.current_view=new server_vie" - "w.View(e),this.container.html(this.current_view.render().$el)},t}(Backbone.R" - "outer),exports.BackboneCluster=BackboneCluster;\n" - "},{\"./app.coffee\":\"/rethinkdb/admin/static/coffee/app.coffee\",\"./dashbo" - "ard/dashboard.coffee\":\"/rethinkdb/admin/static/coffee/dashboard/dashboard." - "coffee\",\"./dataexplorer.coffee\":\"/rethinkdb/admin/static/coffee/dataexpl" - "orer.coffee\",\"./log_view.coffee\":\"/rethinkdb/admin/static/coffee/log_vie" - "w.coffee\",\"./servers/index.coffee\":\"/rethinkdb/admin/static/coffee/serve" - "rs/index.coffee\",\"./servers/server.coffee\":\"/rethinkdb/admin/static/coff" - "ee/servers/server.coffee\",\"./tables/index.coffee\":\"/rethinkdb/admin/stat" - "ic/coffee/tables/index.coffee\",\"./tables/table.coffee\":\"/rethinkdb/admin" - "/static/coffee/tables/table.coffee\"}],\"/rethinkdb/admin/static/coffee/serv" - "ers/index.coffee\":[function(require,module,exports){\n" - "var Model,View,app,createElement,diff,driver,h,models,moment,patch,r,render_" - "server,system_db,util,extend=function(e,r){function t(){this.constructor=e}f" - "or(var n in r)hasProp.call(r,n)&&(e[n]=r[n]);return t.prototype=r.prototype," - "e.prototype=new t,e.__super__=r.prototype,e},hasProp={}.hasOwnProperty;r=req" - "uire(\"rethinkdb\"),h=require(\"virtual-dom/h\"),diff=require(\"virtual-dom/" - "diff\"),patch=require(\"virtual-dom/patch\"),createElement=require(\"virtual" - "-dom/create-element\"),moment=require(\"moment\"),util=require(\"../util.cof" - "fee\"),app=require(\"../app.coffee\"),system_db=app.system_db,driver=app.dri" - "ver,models=require(\"../models.coffee\"),Model=function(e){function r(){retu" - "rn r.__super__.constructor.apply(this,arguments)}return extend(r,e),r.query=" - "function(e,r,t){return{servers:e.map(r,function(e,r){return{id:e(\"id\"),nam" - "e:e(\"name\"),tags:e(\"tags\"),time_started:r(\"process\")(\"time_started\")" - ",hostname:r(\"network\")(\"hostname\"),cache_size:e(\"cache_size_mb\"),prima" - "ry_count:t.map(function(r){return r(\"shards\").default([]).count(function(r" - "){return r(\"primary_replicas\").contains(e(\"name\"))})}).sum(),secondary_c" - "ount:t.map(function(r){return r(\"shards\").default([]).count(function(r){re" - "turn r(\"replicas\")(\"server\").contains(e(\"name\")).and(r(\"primary_repli" - "cas\").contains(e(\"name\")).not())})}).sum()}}).coerceTo(\"array\")}},r.pro" - "totype.defaults={servers:[]},r}(Backbone.Model),View=function(e){function t(" - "){return t.__super__.constructor.apply(this,arguments)}return extend(t,e),t." - "prototype.initialize=function(e){return null==app.view_data_backup.servers_v" - "iew?this.model=app.view_data_backup.servers_view=new Model:this.model=app.vi" - "ew_data_backup.servers_view,this.listenTo(this.model,\"change\",this.render)" - ",this.current_vdom_tree=this.render_vdom(),this.setElement(createElement(thi" - "s.current_vdom_tree)),this.fetch()},t.prototype.fetch=function(){var e,t;ret" - "urn e={server_config:r.db(system_db).table(\"server_config\").coerceTo(\"arr" - "ay\"),server_status:r.db(system_db).table(\"server_status\").coerceTo(\"arra" - "y\"),table_status:r.db(system_db).table(\"table_status\").coerceTo(\"array\"" - ")},t=r.expr(e).do(function(e){return Model.query(e(\"server_config\"),e(\"se" - "rver_status\"),e(\"table_status\"))}),this.timer=driver.run(t,5e3,function(e" - "){return function(r,t){return null!=r?console.log(r):e.model.set(t)}}(this))" - "},t.prototype.remove=function(){return driver.stop_timer(this.timer),t.__sup" - "er__.remove.apply(this,arguments)},t.prototype.render=function(){var e;retur" - "n e=this.render_vdom(),patch(this.el,diff(this.current_vdom_tree,e)),this.cu" - "rrent_vdom_tree=e,this},t.prototype.render_vdom=function(){return h(\"div.se" - "rvers-container\",[h(\"h1.title\",\"Servers connected to the cluster\"),h(\"" - "table.servers-list.section\",this.model.get(\"servers\").map(render_server))" - "])},t}(Backbone.View),render_server=function(e){return h(\"tr.server-contain" - "er\",[h(\"td.name\",[h(\"a\",{href:\"#servers/\"+e.id},e.name),h(\"span.tags" - "\",{title:e.tags.join(\", \")},e.tags.join(\", \"))]),h(\"td.quick-info\",[e" - ".primary_count+\" \",util.pluralize_noun(\"primary\",e.primary_count)+\", \"" - ",e.secondary_count+\" \",\"\"+util.pluralize_noun(\"secondary\",e.secondary_" - "count)]),h(\"td.quick-info\",[h(\"span.highlight\",e.hostname),\", up for \"" - ",moment(e.time_started).fromNow(!0)])])},exports.View=View,exports.Model=Mod" - "el;\n" - "},{\"../app.coffee\":\"/rethinkdb/admin/static/coffee/app.coffee\",\"../mode" - "ls.coffee\":\"/rethinkdb/admin/static/coffee/models.coffee\",\"../util.coffe" - "e\":\"/rethinkdb/admin/static/coffee/util.coffee\",\"moment\":\"/rethinkdb/b" - "uild/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/mom" - "ent/moment.js\",\"rethinkdb\":\"rethinkdb\",\"virtual-dom/create-element\":\"" - "/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node" - "_modules/virtual-dom/create-element.js\",\"virtual-dom/diff\":\"/rethinkdb/b" - "uild/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/vir" - "tual-dom/diff.js\",\"virtual-dom/h\":\"/rethinkdb/build/external/admin-deps_" - "2.0.4/node_modules/rethinkdb-webui/node_modules/virtual-dom/h.js\",\"virtual" - "-dom/patch\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethi" - "nkdb-webui/node_modules/virtual-dom/patch.js\"}],\"/rethinkdb/admin/static/c" - "offee/servers/profile.coffee\":[function(require,module,exports){\n" - "var Model,View,createElement,diff,h,moment,patch,r,render_profile_row,render" - "_tag_row,util,extend=function(e,t){function r(){this.constructor=e}for(var i" - " in t)hasProp.call(t,i)&&(e[i]=t[i]);return r.prototype=t.prototype,e.protot", - 65462) + std::string( - "ype=new r,e.__super__=t.prototype,e},hasProp={}.hasOwnProperty,bind=function" - "(e,t){return function(){return e.apply(t,arguments)}};h=require(\"virtual-do" - "m/h\"),r=require(\"rethinkdb\"),diff=require(\"virtual-dom/diff\"),patch=req" - "uire(\"virtual-dom/patch\"),createElement=require(\"virtual-dom/create-eleme" - "nt\"),moment=require(\"moment\"),util=require(\"../util.coffee\"),Model=func" - "tion(e){function t(){return t.__super__.constructor.apply(this,arguments)}re" - "turn extend(t,e),t.query=function(e,t){return{version:t(\"process\")(\"versi" - "on\"),time_started:t(\"process\")(\"time_started\"),hostname:t(\"network\")(" - "\"hostname\"),tags:e(\"tags\"),cache_size:t(\"process\")(\"cache_size_mb\")." - "mul(1048576)}},t.prototype.defaults={version:null,time_started:null,hostname" - ":\"Unknown\",tags:[],cache_size_mb:null},t}(Backbone.Model),View=function(e)" - "{function t(){return this.render=bind(this.render,this),this.initialize=bind" - "(this.initialize,this),t.__super__.constructor.apply(this,arguments)}return " - "extend(t,e),t.prototype.initialize=function(){return this.listenTo(this.mode" - "l,\"change\",this.render),this.current_vdom_tree=this.render_vdom(this.model" - ".toJSON()),this.setElement(createElement(this.current_vdom_tree))},t.prototy" - "pe.render=function(){var e,t;return e=this.render_vdom(this.model.toJSON())," - "t=diff(this.current_vdom_tree,e),patch(this.el,t),this},t.prototype.render_v" - "dom=function(e){var t,r,i;return i=null!=(r=e.version)?r.split(\" \")[1].spl" - "it(\"-\")[0]:void 0,t=util.format_bytes(e.cache_size),h(\"div.server-info-vi" - "ew\",h(\"div.summary\",[render_profile_row(\"hostname\",\"hostname\",e.hostn" - "ame||\"N/A\"),null!=i?render_profile_row(\"version\",\"version\",i):void 0,n" - "ull!=e.time_started?render_profile_row(\"uptime\",\"uptime\",moment(e.time_s" - "tarted).fromNow(!0),e.time_started.toString()):void 0,null!=e.cache_size?ren" - "der_profile_row(\"cache-size\",\"cache size\",\"\"+t):void 0,null!=e.tags?re" - "nder_tag_row(e.tags):void 0]))},t}(Backbone.View),render_profile_row=functio" - "n(e,t,r,i){return null==i&&(i=null),h(\"div.profile-row.\"+e,[h(\"p.\"+e,[h(" - "\"span.big\",{title:i},r),\" \"+t])])},render_tag_row=function(e){return h(\"" - "div.profile-row.tags\",[e.length>0?h(\"p\",[h(\"span.tags\",e.join(\", \"))," - "\" tags\"]):void 0,0===e.length?h(\"p.admonition\",\"This server has no tags" - ".\"):void 0])},exports.View=View,exports.Model=Model;\n" - "},{\"../util.coffee\":\"/rethinkdb/admin/static/coffee/util.coffee\",\"momen" - "t\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webu" - "i/node_modules/moment/moment.js\",\"rethinkdb\":\"rethinkdb\",\"virtual-dom/" - "create-element\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/r" - "ethinkdb-webui/node_modules/virtual-dom/create-element.js\",\"virtual-dom/di" - "ff\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-web" - "ui/node_modules/virtual-dom/diff.js\",\"virtual-dom/h\":\"/rethinkdb/build/e" - "xternal/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/virtual-d" - "om/h.js\",\"virtual-dom/patch\":\"/rethinkdb/build/external/admin-deps_2.0.4" - "/node_modules/rethinkdb-webui/node_modules/virtual-dom/patch.js\"}],\"/rethi" - "nkdb/admin/static/coffee/servers/responsibilities.coffee\":[function(require" - ",module,exports){\n" - "var Model,View,createElement,diff,h,patch,r,render_shard,render_table,util,e" - "xtend=function(r,e){function t(){this.constructor=r}for(var n in e)hasProp.c" - "all(e,n)&&(r[n]=e[n]);return t.prototype=e.prototype,r.prototype=new t,r.__s" - "uper__=e.prototype,r},hasProp={}.hasOwnProperty;r=require(\"rethinkdb\"),h=r" - "equire(\"virtual-dom/h\"),diff=require(\"virtual-dom/diff\"),patch=require(\"" - "virtual-dom/patch\"),createElement=require(\"virtual-dom/create-element\"),u" - "til=require(\"../util.coffee\"),Model=function(e){function t(){return t.__su" - "per__.constructor.apply(this,arguments)}return extend(t,e),t.query=function(" - "e,t,n){return{tables:e.filter(function(r){return r(\"shards\")(\"replicas\")" - ".concatMap(function(r){return r}).contains(n)}).map(function(e){var i;return" - " i=t.get(e(\"id\")),{db:e(\"db\"),name:e(\"name\"),id:e(\"id\"),shards:e(\"s" - "hards\").map(i(\"shards\"),r.range(),function(r,t,i){return{shard_id:i.add(1" - "),total_shards:e(\"shards\").count(),inshard:r(\"replicas\").contains(n),cur" - "rently_primary:t(\"primary_replicas\").contains(n),configured_primary:r(\"pr" - "imary_replica\").eq(n),nonvoting:r(\"nonvoting_replicas\").contains(n)}}).fi" - "lter({inshard:!0}).without(\"inshard\").coerceTo(\"array\")}}).coerceTo(\"ar" - "ray\")}},t.prototype.defaults={tables:[]},t}(Backbone.Model),View=function(r" - "){function e(){return e.__super__.constructor.apply(this,arguments)}return e" - "xtend(e,r),e.prototype.initialize=function(r){return this.listenTo(this.mode" - "l,\"change\",this.render),this.current_vdom_tree=this.render_vdom(),this.set" - "Element(createElement(this.current_vdom_tree))},e.prototype.render=function(" - "){var r;return r=this.render_vdom(),patch(this.el,diff(this.current_vdom_tre" - "e,r)),this.current_vdom_tree=r,this},e.prototype.render_vdom=function(){retu" - "rn h(\"div\",[h(\"h2.title\",\"Table shards on this server\"),h(\"ul.parents" - "\",this.model.get(\"tables\").map(render_table))])},e}(Backbone.View),render" - "_table=function(r){return h(\"li.parent\",[h(\"div.parent-heading\",h(\"span" - ".parent-title\",[\"Table \",h(\"a\",{href:\"#/tables/\"+r.id},r.db+\".\"+r.n" - "ame)])),h(\"ul.children\",r.shards.map(render_shard))])},render_shard=functi" - "on(r){return h(\"li.child\",[h(\"span.child-name\",[\"Shard \"+r.shard_id+\"" - "/\"+r.total_shards]),h(\"span.child-role.\"+util.replica_roleclass(r),util.r" - "eplica_rolename(r))])},exports.Model=Model,exports.View=View;\n" - "},{\"../util.coffee\":\"/rethinkdb/admin/static/coffee/util.coffee\",\"rethi" - "nkdb\":\"rethinkdb\",\"virtual-dom/create-element\":\"/rethinkdb/build/exter" - "nal/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/virtual-dom/c" - "reate-element.js\",\"virtual-dom/diff\":\"/rethinkdb/build/external/admin-de" - "ps_2.0.4/node_modules/rethinkdb-webui/node_modules/virtual-dom/diff.js\",\"v" - "irtual-dom/h\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/ret" - "hinkdb-webui/node_modules/virtual-dom/h.js\",\"virtual-dom/patch\":\"/rethin" - "kdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_module" - "s/virtual-dom/patch.js\"}],\"/rethinkdb/admin/static/coffee/servers/server.c" - "offee\":[function(require,module,exports){\n" - "var Model,View,app,createElement,diff,driver,h,log_view,models,patch,r,rende" - "r_not_found,server_profile,server_resp,system_db,ui_modals,vdom,vis,extend=f" - "unction(e,r){function t(){this.constructor=e}for(var i in r)hasProp.call(r,i" - ")&&(e[i]=r[i]);return t.prototype=r.prototype,e.prototype=new t,e.__super__=" - "r.prototype,e},hasProp={}.hasOwnProperty,bind=function(e,r){return function(" - "){return e.apply(r,arguments)}};r=require(\"rethinkdb\"),h=require(\"virtual" - "-dom/h\"),patch=require(\"virtual-dom/patch\"),diff=require(\"virtual-dom/di" - "ff\"),createElement=require(\"virtual-dom/create-element\"),vdom=require(\"." - "./vdom_util.coffee\"),models=require(\"../models.coffee\"),app=require(\"../" - "app.coffee\"),driver=app.driver,system_db=app.system_db,vis=require(\"../vis" - ".coffee\"),log_view=require(\"../log_view.coffee\"),ui_modals=require(\"../u" - "i_components/modals.coffee\"),server_profile=require(\"./profile.coffee\"),s" - "erver_resp=require(\"./responsibilities.coffee\"),Model=function(e){function" - " r(){return r.__super__.constructor.apply(this,arguments)}return extend(r,e)" - ",r.query=function(e){return{name:e(\"name\"),id:e(\"id\")}},r.prototype.defa" - "ults={name:null,id:null},r}(Backbone.Model),View=function(e){function t(){re" - "turn this.remove=bind(this.remove,this),this.render=bind(this.render,this),t" - "his.initialize=bind(this.initialize,this),this.fetch_stats=bind(this.fetch_s" - "tats,this),this.fetch_server=bind(this.fetch_server,this),this.rename_server" - "=bind(this.rename_server,this),t.__super__.constructor.apply(this,arguments)" - "}return extend(t,e),t.prototype.rename_server=function(e){return e.preventDe" - "fault(),null!=this.rename_modal&&this.rename_modal.remove(),this.rename_moda" - "l=new ui_modals.RenameItemModal({model:this.model,item_type:\"server\"}),thi" - "s.rename_modal.render()},t.prototype.fetch_server=function(){var e;return e=" - "r.do(r.db(system_db).table(\"server_config\").get(this.model.get(\"id\")),r." - "db(system_db).table(\"server_status\").get(this.model.get(\"id\")),function(" - "e,t){var i,n;return i=r.db(system_db).table(\"table_config\"),n=r.db(system_" - "db).table(\"table_status\"),{profile:server_profile.Model.query(e,t),main:Mo" - "del.query(t),resp:server_resp.Model.query(i,n,e(\"name\"))}}),this.timer=dri" - "ver.run(e,5e3,function(e){return function(r,t){return null!=r?(e.error=r,con" - "sole.log(\"Error:\",r.message)):(e.error=null,e.model.set(t.main),e.profile_" - "model.set(t.profile),e.resp_model.set(t.resp)),e.render()}}(this))},t.protot" - "ype.fetch_stats=function(){return this.stats_timer=driver.run(r.db(system_db" - ").table(\"stats\").get([\"server\",this.model.get(\"id\")]).do(function(e){r" - "eturn{keys_read:e(\"query_engine\")(\"read_docs_per_sec\"),keys_set:e(\"quer" - "y_engine\")(\"written_docs_per_sec\")}}),1e3,this.stats.on_result)},t.protot" - "ype.initialize=function(e){return this.model=new Model({id:e}),this.profile_" - "model=new server_profile.Model,this.resp_model=new server_resp.Model,this.st" - "ats=new models.Stats,this.timer=null,this.error=null,this.stats_timer=null,t" - "his.current_vdom_tree=this.render_vdom(),this.setElement(createElement(this." - "current_vdom_tree)),this.fetch_server(),this.fetch_stats()},t.prototype.rend" - "er=function(){var e;return e=this.render_vdom(),patch(this.el,diff(this.curr" - "ent_vdom_tree,e)),this.current_vdom_tree=e,this},t.prototype.remove=function" - "(){return driver.stop_timer(this.timer),driver.stop_timer(this.stats_timer)," - "null!=this.rename_modal&&this.rename_modal.remove(),this.cleanup_widgets()}," - "t.prototype.cleanup_widgets=function(){return patch(this.el,diff(this.curren" - "t_vdom_tree,h(\"div\")))},t.prototype.render_vdom=function(){return null!=th" - "is.error?render_not_found(this.model.get(\"id\")):h(\"div#server-view\",[h(\"" - "div.operations\",h(\"div.dropdown\",[h(\"button.btn.dropdown-toggle\",{datas" - "et:{toggle:\"dropdown\"}},\"Operations\"),h(\"ul.dropdown-menu\",h(\"li\",h(" - "\"a.rename\",{href:\"#\",onclick:this.rename_server},\"Rename server\")))]))" - ",h(\"div.main_title\",h(\"h1.title\",\"Server overview for \"+this.model.get" - "(\"name\"))),h(\"div.user-alert-space\"),h(\"div.section.statistics\",[h(\"d" - "iv.content.row-fluid\",[h(\"div.span4.profile\",new vdom.BackboneViewWidget(" - "function(e){return function(){return new server_profile.View({model:e.profil" - "e_model})}}(this))),h(\"div.span8.performance-graph\",new vdom.BackboneViewW" - "idget(function(e){return function(){return new vis.OpsPlot(e.stats.get_stats" - ",{width:564,height:210,seconds:73,type:\"server\"})}}(this)))])]),h(\"div.se" - "ction.responsibilities.tree-view2\",new vdom.BackboneViewWidget(function(e){" - "return function(){return new server_resp.View({model:e.resp_model})}}(this))" - "),h(\"div.section.recent-logs\",[h(\"h2.title\",\"Recent log entries\"),h(\"" - "div.recent-log-entries\",new vdom.BackboneViewWidget(function(e){return func" - "tion(){return new log_view.LogsContainer({server_id:e.model.get(\"id\"),limi" - "t:5,query:driver.queries.server_logs})}}(this)))])])},t}(Backbone.View),rend" - "er_not_found=function(e){return h(\"div.section\",[h(\"h2.title\",\"Error\")" - ",h(\"p\",\"Server \"+e+\" could not be found or is unavailable\"),h(\"p\",[\"" - "You can try to \",h(\"a\",{href:\"#/servers/\"+e},\"refresh\"),\" or see a l" - "ist of all \",h(\"a\",{href:\"#/servers\"},\"servers\"),\".\"])])},exports.M" - "odel=Model,exports.View=View;\n" - "},{\"../app.coffee\":\"/rethinkdb/admin/static/coffee/app.coffee\",\"../log_" - "view.coffee\":\"/rethinkdb/admin/static/coffee/log_view.coffee\",\"../models" - ".coffee\":\"/rethinkdb/admin/static/coffee/models.coffee\",\"../ui_component" - "s/modals.coffee\":\"/rethinkdb/admin/static/coffee/ui_components/modals.coff" - "ee\",\"../vdom_util.coffee\":\"/rethinkdb/admin/static/coffee/vdom_util.coff" - "ee\",\"../vis.coffee\":\"/rethinkdb/admin/static/coffee/vis.coffee\",\"./pro" - "file.coffee\":\"/rethinkdb/admin/static/coffee/servers/profile.coffee\",\"./" - "responsibilities.coffee\":\"/rethinkdb/admin/static/coffee/servers/responsib" - "ilities.coffee\",\"rethinkdb\":\"rethinkdb\",\"virtual-dom/create-element\":" - "\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/no" - "de_modules/virtual-dom/create-element.js\",\"virtual-dom/diff\":\"/rethinkdb" - "/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/v" - "irtual-dom/diff.js\",\"virtual-dom/h\":\"/rethinkdb/build/external/admin-dep" - "s_2.0.4/node_modules/rethinkdb-webui/node_modules/virtual-dom/h.js\",\"virtu" - "al-dom/patch\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/ret" - "hinkdb-webui/node_modules/virtual-dom/patch.js\"}],\"/rethinkdb/admin/static" - "/coffee/tables/index.coffee\":[function(require,module,exports){\n" - "var DatabaseView,DatabasesContainer,DatabasesListView,TableView,app,driver,m" - "odals,models,r,system_db,bind=function(e,t){return function(){return e.apply" - "(t,arguments)}},extend=function(e,t){function a(){this.constructor=e}for(var" - " i in t)hasProp.call(t,i)&&(e[i]=t[i]);return a.prototype=t.prototype,e.prot" - "otype=new a,e.__super__=t.prototype,e},hasProp={}.hasOwnProperty;app=require" - "(\"../app.coffee\"),driver=app.driver,system_db=app.system_db,models=require" - "(\"../models.coffee\"),modals=require(\"../modals.coffee\"),r=require(\"reth" - "inkdb\"),DatabasesContainer=function(e){function t(){return this.remove=bind" - "(this.remove,this),this.fetch_data=bind(this.fetch_data,this),this.fetch_dat" - "a_once=bind(this.fetch_data_once,this),this.render_message=bind(this.render_" - "message,this),this.render=bind(this.render,this),this.initialize=bind(this.i" - "nitialize,this),this.update_delete_tables_button=bind(this.update_delete_tab" - "les_button,this),this.delete_tables=bind(this.delete_tables,this),this.add_d" - "atabase=bind(this.add_database,this),this.query_callback=bind(this.query_cal" - "lback,this),t.__super__.constructor.apply(this,arguments)}return extend(t,e)" - ",t.prototype.id=\"databases_container\",t.prototype.template={main:require(\"" - "../../handlebars/databases_container.hbs\"),error:require(\"../../handlebars" - "/error-query.hbs\"),alert_message:require(\"../../handlebars/alert_message.h" - "bs\")},t.prototype.events={\"click .add_database\":\"add_database\",\"click " - ".remove-tables\":\"delete_tables\",\"click .checkbox-container\":\"update_de" - "lete_tables_button\",\"click .close\":\"remove_parent_alert\"},t.prototype.q" - "uery_callback=function(e,t){var a;return this.loading=!1,null==e?this.databa" - "ses.set(_.map(_.sortBy(t,function(e){return e.name}),function(e){return new " - "models.Database(e)}),{merge:!0}):(null!=(a=this.error)?a.msg:void 0)!==e.msg" - "?(this.error=e,this.$el.html(this.template.error({url:\"#tables\",error:e.me" - "ssage}))):void 0},t.prototype.add_database=function(){return this.add_databa" - "se_dialog.render()},t.prototype.delete_tables=function(e){var t;if(!$(e.curr" - "entTarget).is(\":disabled\"))return t=[],this.$(\".checkbox-table:checked\")" - ".each(function(){return t.push({table:JSON.parse($(this).data(\"table\")),da" - "tabase:JSON.parse($(this).data(\"database\"))})}),this.remove_tables_dialog." - "render(t)},t.prototype.remove_parent_alert=function(e){var t;return e.preven" - "tDefault(),t=$(e.target).parent(),t.slideUp(\"fast\",function(){return t.rem" - "ove()})},t.prototype.update_delete_tables_button=function(){return this.$(\"" - ".checkbox-table:checked\").length>0?this.$(\".remove-tables\").prop(\"disabl" - "ed\",!1):this.$(\".remove-tables\").prop(\"disabled\",!0)},t.prototype.initi" - "alize=function(){return null==app.view_data_backup.tables_view_databases?(ap" - "p.view_data_backup.tables_view_databases=new models.Databases,this.loading=!" - "0):this.loading=!1,this.databases=app.view_data_backup.tables_view_databases" - ",this.databases_list=new DatabasesListView({collection:this.databases,contai" - "ner:this}),this.query=r.db(system_db).table(\"table_status\").group(\"db\")." - "orderBy(\"name\").merge(function(e){return{shards:e(\"shards\").count().defa" - "ult(0),replicas:e(\"shards\").default([]).map(function(e){return e(\"replica" - "s\").count()}).sum(),replicas_ready:e(\"shards\").default([]).map(function(e" - "){return e(\"replicas\").filter(function(e){return e(\"state\").eq(\"ready\"" - ")}).count()}).sum(),status:e(\"status\"),id:e(\"id\")}}).ungroup().map(funct" - "ion(e){return[e(\"group\"),e(\"reduction\")]}).coerceTo(\"object\").do(funct" - "ion(e){return r.db(system_db).table(\"db_config\").map(function(t){return t." - "merge({tables:e(t(\"name\")).default([])})})}),this.fetch_data(),this.add_da" - "tabase_dialog=new modals.AddDatabaseModal(this.databases),this.remove_tables" - "_dialog=new modals.RemoveTableModal({collection:this.databases})},t.prototyp" - "e.render=function(){return this.$el.html(this.template.main({})),this.$(\".d" - "atabases_list\").html(this.databases_list.render().$el),this},t.prototype.re" - "nder_message=function(e){return this.$(\"#user-alert-space\").append(this.te" - "mplate.alert_message({message:e}))},t.prototype.fetch_data_once=function(){r" - "eturn this.timer=driver.run_once(this.query,this.query_callback)},t.prototyp" - "e.fetch_data=function(){return this.timer=driver.run(this.query,5e3,this.que" - "ry_callback)},t.prototype.remove=function(){return driver.stop_timer(this.ti" - "mer),this.add_database_dialog.remove(),this.remove_tables_dialog.remove(),t." - "__super__.remove.call(this)},t}(Backbone.View),DatabasesListView=function(e)" - "{function t(){return this.remove=bind(this.remove,this),this.render=bind(thi" - "s.render,this),this.initialize=bind(this.initialize,this),t.__super__.constr" - "uctor.apply(this,arguments)}return extend(t,e),t.prototype.className=\"datab" - "ase_list\",t.prototype.template={no_databases:require(\"../../handlebars/no_" - "databases.hbs\"),loading_databases:require(\"../../handlebars/loading_databa" - "ses.hbs\")},t.prototype.initialize=function(e){return this.container=e.conta" - "iner,this.databases_view=[],this.collection.each(function(e){return function" - "(t){var a;return a=new DatabaseView({model:t}),e.databases_view.push(a),e.$e" - "l.append(a.render().$el)}}(this)),0===this.collection.length?this.$el.html(t" - "his.template.no_databases()):this.container.loading&&this.$el.html(this.temp" - "late.loading_databases()),this.listenTo(this.collection,\"add\",function(e){" - "return function(t){var a,i,s,r,n,o,l;if(r=new DatabaseView({model:t}),0===e." - "databases_view.length)e.databases_view.push(r),e.$el.html(r.render().$el);el" - "se{for(a=!1,o=e.databases_view,n=i=0,s=o.length;it.get(\"name\")){a=!0,e.databases_view.splice(n,0,r),0===n?e.$" - "el.prepend(r.render().$el):e.$(\".database_container\").eq(n-1).after(r.rend" - "er().$el);break}a===!1&&(e.databases_view.push(r),e.$el.append(r.render().$e" - "l))}if(1===e.databases_view.length)return e.$(\".no-databases\").remove()}}(" - "this)),this.listenTo(this.collection,\"remove\",function(e){return function(" - "t){var a,i,s,r,n;for(r=e.databases_view,s=a=0,i=r.length;at.get(\"name\")){a=!0,e.tables_views.splice(n,0,r)," - "0===n?e.$(\".tables_container\").prepend(r.render().$el):e.$(\".table_contai" - "ner\").eq(n-1).after(r.render().$el);break}a===!1&&(e.tables_views.push(r),e" - ".$(\".tables_container\").append(r.render().$el))}if(1===e.tables_views.leng" - "th)return e.$(\".no_element\").remove()}}(this)),this.listenTo(this.collecti" - "on,\"remove\",function(e){return function(t){var a,i,s,r,n;for(r=e.tables_vi" - "ews,s=a=0,i=r.length;a1e9&&(t=(\"\"+t).slice(0,-9)+\"M\")," - "t>1e6&&t<1e9?t=(\"\"+t).slice(0,-6)+\"m\":t>1e3&&t<1e6?t=(\"\"+t).slice(0,-3" - ")+\"k\":t},n.prototype.display_outdated=function(){if(\"none\"===this.$(\".o" - "utdated_distribution\").css(\"display\"))return this.$(\".outdated_distribut" - "ion\").slideDown(\"fast\")},n.prototype.render_data_distribution=function(){" - "var t,n,i,e,r,o,a,s,u,l,c,d,h,p,f,_,y,m,b,x,g,k,v,w,S;return $(\".tooltip\")" - ".remove(),this.$(\".loading\").slideUp(\"fast\"),this.$(\".outdated_distribu" - "tion\").slideUp(\"fast\"),this.$(\".shard-diagram\").show(),h=null!=(y=d3.ma" - "x(this.collection.models,function(t){return t.get(\"num_keys\")}))?y:100,p=n" - "ull!=(m=d3.min(this.collection.models,function(t){return t.get(\"num_keys\")" - "}))?m:0,k=328,g=270,c=20,l=20,f=20,1===this.collection.length?(n=100,u=20):2" - "===this.collection.length?(n=80,u=20):(n=Math.floor(.8*(328-2*c-2*f)/this.co" - "llection.length),u=Math.floor(.2*(328-2*c-2*f)/this.collection.length)),w=n*" - "this.collection.length+u*(this.collection.length-1),d=Math.floor((k-2*c-w)/2" - "),S=d3.scale.linear().domain([0,1.05*h]).range([0,g-2.5*l]),x=d3.select(\".s" - "hard-diagram\").attr(\"width\",k).attr(\"height\",g),e=x.select(\".bars_cont" - "ainer\"),i=e.selectAll(\"rect\").data(this.collection.map(function(t,n){retu" - "rn{index:n,num_keys:t.get(\"num_keys\")}})),i.enter().append(\"rect\").attr(" - "\"class\",\"rect\").attr(\"x\",function(t,i){return c+d+n*i+u*i}).attr(\"y\"" - ",function(t){return g-S(t.num_keys)-l-1}).attr(\"width\",n).attr(\"height\"," - "function(t){return S(t.num_keys)}).attr(\"title\",function(t){return\"Shard " - "\"+(t.index+1)+\"
~\"+t.num_keys+\" keys\"}),i.transition().duration(60" - "0).attr(\"x\",function(t,i){return c+d+n*i+u*i}).attr(\"y\",function(t){retu" - "rn g-S(t.num_keys)-l-1}).attr(\"width\",n).attr(\"height\",function(t){retur" - "n S(t.num_keys)}),i.exit().remove(),o=[],o.push({x1:c,x2:c,y1:l,y2:g-l}),o.p" - "ush({x1:c,x2:k-c,y1:g-l,y2:g-l}),r=x.select(\".extra_container\"),s=r.select" - "All(\".line\").data(o),s.enter().append(\"line\").attr(\"class\",\"line\").a" - "ttr(\"x1\",function(t){return t.x1}).attr(\"x2\",function(t){return t.x2}).a" - "ttr(\"y1\",function(t){return t.y1}).attr(\"y2\",function(t){return t.y2}),s" - ".exit().remove(),t=[],t.push({x:c,y:Math.floor(l/2+2),string:\"Docs\",anchor" - ":\"middle\"}),t.push({x:Math.floor(k/2),y:g,string:\"Shards\",anchor:\"middl" - "e\"}),a=r.selectAll(\".legend\").data(t),a.enter().append(\"text\").attr(\"c" - "lass\",\"legend\").attr(\"x\",function(t){return t.x}).attr(\"y\",function(t" - "){return t.y}).attr(\"text-anchor\",function(t){return t.anchor}).text(funct" - "ion(t){return t.string}),a.exit().remove(),v=r.selectAll(\".cache\").data(S." - "ticks(5)),v.enter().append(\"rect\").attr(\"class\",\"cache\").attr(\"width\"" - ",function(t,n){return 0===n?0:4}).attr(\"height\",18).attr(\"x\",c-2).attr(\"" - "y\",function(t){return g-l-S(t)-14}).attr(\"fill\",\"#fff\"),v.transition()." - "duration(600).attr(\"y\",function(t){return g-l-S(t)-14}),v.exit().remove()," - "b=r.selectAll(\".rule\").data(S.ticks(5)),_=function(t){return function(n){r" - "eturn n.attr(\"y\",function(t){return g-l-S(t)}).attr(\"x\",c).attr(\"text-a" - "nchor\",\"middle\").attr(\"class\",\"rule\").text(function(n,i){return 0!==i" - "?t.prettify_num(n):\"\"})}}(this),b.enter().append(\"text\").call(_),b.trans" - "ition().duration(600).call(_),b.exit().remove(),this.$(\"rect\").tooltip({tr" - "igger:\"hover\"})},n}(Backbone.View),exports.ShardDistribution=ShardDistribu" - "tion;\n" - "},{\"../../handlebars/shard_distribution_container.hbs\":\"/rethinkdb/admin/" - "static/handlebars/shard_distribution_container.hbs\"}],\"/rethinkdb/admin/st" - "atic/coffee/tables/table.coffee\":[function(require,module,exports){\n" - "var Profile,ReconfigureDiffView,ReconfigurePanel,SecondaryIndexView,Secondar" - "yIndexesView,TableContainer,TableMainView,TableStatusIndicator,Title,app,dri" - "ver,modals,models,r,shard_assignments,shard_distribution,system_db,ui_modals" - ",ui_progress,vis,bind=function(e,t){return function(){return e.apply(t,argum" - "ents)}},extend=function(e,t){function i(){this.constructor=e}for(var r in t)" - "hasProp.call(t,r)&&(e[r]=t[r]);return i.prototype=t.prototype,e.prototype=ne" - "w i,e.__super__=t.prototype,e},hasProp={}.hasOwnProperty,indexOf=[].indexOf|" - "|function(e){for(var t=0,i=this.length;t0)return this.current_errors[" - "e]=[]},t.prototype.guaranteed_query=function(){return r.do(r.db(system_db).t" - "able(\"server_config\").coerceTo(\"array\"),r.db(system_db).table(\"table_st" - "atus\").get(this.id),r.db(system_db).table(\"table_config\").get(this.id),fu" - "nction(e,t,i){return r.branch(t.eq(null),null,t.merge({max_shards:64,num_sha" - "rds:i(\"shards\").count().default(0),num_servers:e.count(),num_default_serve" - "rs:e.filter(function(e){return e(\"tags\").contains(\"default\")}).count(),m" - "ax_num_shards:r.expr([64,e.filter(function(e){return e(\"tags\").contains(\"" - "default\")}).count()]).min(),num_primary_replicas:t(\"shards\").count(functi" - "on(e){return e(\"primary_replicas\").isEmpty().not()}).default(0),num_replic" - "as:t(\"shards\").default([]).map(function(e){return e(\"replicas\").count()}" - ").sum(),num_available_replicas:t(\"shards\").concatMap(function(e){return e(" - "\"replicas\").filter({state:\"ready\"})}).count().default(0),num_replicas_pe" - "r_shard:i(\"shards\").default([]).map(function(e){return e(\"replicas\").cou" - "nt()}).max().default(0),status:t(\"status\"),id:t(\"id\")}).without(\"shards" - "\"))})},t.prototype.failable_index_query=function(){return r.do(r.db(system_" - "db).table(\"table_config\").get(this.id),function(e){return r.db(e(\"db\"))." - "table(e(\"name\"),{read_mode:\"single\"}).indexStatus().pluck(\"index\",\"re" - "ady\",\"progress\").merge(function(t){return{id:t(\"index\"),db:e(\"db\"),ta" - "ble:e(\"name\")}})})},t.prototype.failable_misc_query=function(){return r.do" - "(r.db(system_db).table(\"table_status\").get(this.id),r.db(system_db).table(" - "\"table_config\").get(this.id),r.db(system_db).table(\"server_config\").map(" - "function(e){return[e(\"name\"),e(\"id\")]}).coerceTo(\"ARRAY\").coerceTo(\"O" - "BJECT\"),function(e,t,i){return e.merge({distribution:r.db(e(\"db\")).table(" - "e(\"name\"),{read_mode:\"single\"}).info()(\"doc_count_estimates\").map(r.ra" - "nge(),function(e,t){return{num_keys:e,id:t}}).coerceTo(\"array\"),total_keys" - ":r.db(e(\"db\")).table(e(\"name\"),{read_mode:\"single\"}).info()(\"doc_coun" - "t_estimates\").sum(),shard_assignments:e.merge(function(e){return e(\"shards" - "\").default([]).map(t(\"shards\").default([]),r.db(e(\"db\")).table(e(\"name" - "\"),{read_mode:\"single\"}).info()(\"doc_count_estimates\"),function(e,t,s){" - "return{num_keys:s,replicas:e(\"replicas\").merge(function(r){return{id:i(r(\"" - "server\")).default(null),configured_primary:t(\"primary_replica\").eq(r(\"se" - "rver\")),currently_primary:e(\"primary_replicas\").contains(r(\"server\")),n" - "onvoting:t(\"nonvoting_replicas\").contains(r(\"server\"))}}).orderBy(r.desc" - "(\"currently_primary\"),r.desc(\"configured_primary\"))}})})})})},t.prototyp" - "e.handle_failable_index_response=function(e,t){var i;return null!=e?(this.ad" - "d_error_message(\"index\",e.msg),void this.model.set(\"index_unavailable\",!" - "0)):(this.model.set(\"index_unavailable\",!1),this.clear_cached_error_messag" - "es(\"index\"),null!=this.indexes?this.indexes.set(_.map(t,function(e){return" - " new models.Index(e)})):this.indexes=new models.Indexes(_.map(t,function(e){" - "return new models.Index(e)})),null!=(i=this.table_view)&&i.set_indexes(this." - "indexes),this.model.set({indexes:t}),null==this.table_view?(this.table_view=" - "new TableMainView({model:this.model,indexes:this.indexes,distribution:this.d" - "istribution,shards_assignments:this.shards_assignments}),this.render()):void" - " 0)},t.prototype.handle_failable_misc_response=function(e,t){var i,r;return " - "null!=e?(this.add_error_message(\"misc\",e.msg),void this.model.set(\"info_u" - "navailable\",!0)):(this.model.set(\"info_unavailable\",!1),this.clear_cached" - "_error_messages(\"misc\"),null!=this.distribution?(this.distribution.set(_.m" - "ap(t.distribution,function(e){return new models.Shard(e)})),this.distributio" - "n.trigger(\"update\")):(this.distribution=new models.Distribution(_.map(t.di" - "stribution,function(e){return new models.Shard(e)})),null!=(i=this.table_vie" - "w)&&i.set_distribution(this.distribution)),null!=this.shards_assignments?thi" - "s.shards_assignments.set(_.map(t.shard_assignments,function(e){return new mo" - "dels.ShardAssignment(e)})):(this.shards_assignments=new models.ShardAssignme" - "nts(_.map(t.shard_assignments,function(e){return new models.ShardAssignment(" - "e)})),null!=(r=this.table_view)&&r.set_assignments(this.shards_assignments))" - ",this.model.set(t),null==this.table_view?(this.table_view=new TableMainView(" - "{model:this.model,indexes:this.indexes,distribution:this.distribution,shards" - "_assignments:this.shards_assignments}),this.render()):void 0)},t.prototype.h" - "andle_guaranteed_response=function(e,t){var i;return null!=e?(this.error=e,t" - "his.render()):(i=null!=this.error,this.error=null,null===t?(i=i||this.table_" - "found,this.table_found=!1,this.indexes=null,this.render()):(i=i||!this.table" - "_found,this.table_found=!0,this.model.set(t)),i?this.render():void 0)},t.pro" - "totype.fetch_data=function(){return this.failable_index_timer=driver.run(thi" - "s.failable_index_query(),1e3,this.handle_failable_index_response),this.faila" - "ble_misc_timer=driver.run(this.failable_misc_query(),1e4,this.handle_failabl" - "e_misc_response),this.guaranteed_timer=driver.run(this.guaranteed_query(),5e" - "3,this.handle_guaranteed_response)},t.prototype.render=function(){var e;retu" - "rn null!=this.error?this.$el.html(this.template.error({error:null!=(e=this.e" - "rror)?e.message:void 0,url:\"#tables/\"+this.id})):this.table_found?this.$el" - ".html(this.table_view.render().$el):this.$el.html(this.template.not_found({i" - "d:this.id,type:\"table\",type_url:\"tables\",type_all_url:\"tables\"})),this" - "},t.prototype.remove=function(){var e;return driver.stop_timer(this.guarante" - "ed_timer),driver.stop_timer(this.failable_index_timer),driver.stop_timer(thi" - "s.failable_misc_timer),null!=(e=this.table_view)&&e.remove(),t.__super__.rem" - "ove.call(this)},t}(Backbone.View),TableMainView=function(e){function t(){ret" - "urn this.remove=bind(this.remove,this),this.rename_table=bind(this.rename_ta" - "ble,this),this.render=bind(this.render,this),this.set_assignments=bind(this." - "set_assignments,this),this.set_distribution=bind(this.set_distribution,this)" - ",this.set_indexes=bind(this.set_indexes,this),this.initialize=bind(this.init" - "ialize,this),t.__super__.constructor.apply(this,arguments)}return extend(t,e" - "),t.prototype.className=\"namespace-view\",t.prototype.template={main:requir" - "e(\"../../handlebars/table_container.hbs\"),alert:null},t.prototype.events={" - "\"click .close\":\"close_alert\",\"click .operations .rename\":\"rename_tabl" - "e\",\"click .operations .delete\":\"delete_table\",\"click .explore-table\":" - "\"explore_table\",\"submit .table-lookup-by-id\":\"explore_table_document\"}" - ",t.prototype.initialize=function(e,t){return this.indexes=e.indexes,this.dis" - "tribution=e.distribution,this.shards_assignments=e.shards_assignments,this.t" - "itle=new Title({model:this.model}),this.profile=new Profile({model:this.mode" - "l}),this.secondary_indexes_view=new SecondaryIndexesView({collection:this.in" - "dexes,model:this.model}),this.shard_distribution=new shard_distribution.Shar" - "dDistribution({collection:this.distribution,model:this.model}),this.server_a" - "ssignments=new shard_assignments.ShardAssignmentsView({model:this.model,coll" - "ection:this.shards_assignments}),this.reconfigure=new ReconfigurePanel({mode" - "l:this.model}),this.stats=new models.Stats,this.stats_timer=driver.run(r.db(" - "system_db).table(\"stats\").get([\"table\",this.model.get(\"id\")]).do(funct" - "ion(e){return{keys_read:e(\"query_engine\")(\"read_docs_per_sec\"),keys_set:" - "e(\"query_engine\")(\"written_docs_per_sec\")}}),1e3,this.stats.on_result),t" - "his.performance_graph=new vis.OpsPlot(this.stats.get_stats,{width:564,height" - ":210,seconds:73,type:\"table\"})},t.prototype.set_indexes=function(e){if(nul" - "l==this.indexes)return this.indexes=e,this.secondary_indexes_view.set_indexe" - "s(this.indexes)},t.prototype.set_distribution=function(e){return this.distri" - "bution=e,this.shard_distribution.set_distribution(this.distribution)},t.prot" - "otype.set_assignments=function(e){return this.shards_assignments=e,this.serv" - "er_assignments.set_assignments(this.shards_assignments)},t.prototype.render=" - "function(){return this.$el.html(this.template.main({namespace_id:this.model." - "get(\"id\")})),this.$(\".main_title\").html(this.title.render().$el),this.$(" - "\".profile\").html(this.profile.render().$el),this.$(\".performance-graph\")" - ".html(this.performance_graph.render().$el),this.$(\".sharding\").html(this.s" - "hard_distribution.render().el),this.$(\".server-assignments\").html(this.ser" - "ver_assignments.render().el),this.$(\".secondary_indexes\").html(this.second" - "ary_indexes_view.render().el),this.$(\".reconfigure-panel\").html(this.recon" - "figure.render().el),this.table_viewer=TableViewer.makeWithTable(this.$(\".ta" - "ble_viewer\")[0],r,driver,this.model.get(\"id\")),this},t.prototype.close_al" - "ert=function(e){return e.preventDefault(),$(e.currentTarget).parent().slideU" - "p(\"fast\",function(){return $(this).remove()})},t.prototype.rename_table=fu" - "nction(e){return e.preventDefault(),null!=this.rename_modal&&this.rename_mod" - "al.remove(),this.rename_modal=new ui_modals.RenameItemModal({model:this.mode" - "l}),this.rename_modal.render()},t.prototype.delete_table=function(e){return " - "e.preventDefault(),null!=this.remove_table_dialog&&this.remove_table_dialog." - "remove(),this.remove_table_dialog=new modals.RemoveTableModal,this.remove_ta" - "ble_dialog.render([{table:this.model.get(\"name\"),database:this.model.get(\"" - "db\")}])},t.prototype.explore_table=function(e){return app.main.router.goto_" - "explore_table(this.model.get(\"id\"))},t.prototype.explore_table_document=fu" - "nction(e){var t;return t=this.$(\".table-lookup-box\").attr(\"value\"),app.m" - "ain.router.goto_explore_table_id(this.model.get(\"id\"),t)},t.prototype.remo" - "ve=function(){return this.title.remove(),this.profile.remove(),this.shard_di" - "stribution.remove(),this.server_assignments.remove(),this.performance_graph." - "remove(),this.secondary_indexes_view.remove(),this.reconfigure.remove(),driv" - "er.stop_timer(this.stats_timer),null!=this.remove_table_dialog&&this.remove_" - "table_dialog.remove(),null!=this.rename_modal&&this.rename_modal.remove(),t." - "__super__.remove.call(this)},t}(Backbone.View),ReconfigurePanel=function(e){" - "function t(){return this.render=bind(this.render,this),this.render_status=bi" - "nd(this.render_status,this),this.fetch_progress=bind(this.fetch_progress,thi" - "s),this.remove=bind(this.remove,this),this.launch_modal=bind(this.launch_mod" - "al,this),this.initialize=bind(this.initialize,this),t.__super__.constructor." - "apply(this,arguments)}return extend(t,e),t.prototype.className=\"reconfigure" - "-panel\",t.prototype.templates={main:require(\"../../handlebars/reconfigure." - "hbs\"),status:require(\"../../handlebars/replica_status.hbs\")},t.prototype." - "events={\"click .reconfigure.btn\":\"launch_modal\"},t.prototype.initialize=" - "function(e){return this.model=e.model,this.listenTo(this.model,\"change:num_" - "shards\",this.render),this.listenTo(this.model,\"change:num_replicas_per_sha" - "rd\",this.render),this.listenTo(this.model,\"change:num_available_replicas\"" - ",this.render_status),this.progress_bar=new ui_progress.OperationProgressBar(" - "this.templates.status),this.timer=null},t.prototype.launch_modal=function(){" - "return null!=this.reconfigure_modal&&this.reconfigure_modal.remove(),this.re" - "configure_modal=new modals.ReconfigureModal({model:new models.Reconfigure({p" - "arent:this,id:this.model.get(\"id\"),db:this.model.get(\"db\"),name:this.mod" - "el.get(\"name\"),total_keys:this.model.get(\"total_keys\"),shards:[],max_sha" - "rds:this.model.get(\"max_shards\"),num_shards:this.model.get(\"num_shards\")" - ",num_servers:this.model.get(\"num_servers\"),num_default_servers:this.model." - "get(\"num_default_servers\"),max_num_shards:this.model.get(\"max_num_shards\"" - "),num_replicas_per_shard:this.model.get(\"num_replicas_per_shard\")})}),this" - ".reconfigure_modal.render()},t.prototype.remove=function(){return null!=this" - ".reconfigure_modal&&this.reconfigure_modal.remove(),null!=this.timer&&(drive" - "r.stop_timer(this.timer),this.timer=null),this.progress_bar.remove(),t.__sup" - "er__.remove.call(this)},t.prototype.fetch_progress=function(){var e;return e" - "=r.db(system_db).table(\"table_status\").get(this.model.get(\"id\"))(\"shard" - "s\").default([])(\"replicas\").concatMap(function(e){return e}).do(function(" - "e){return{num_available_replicas:e.filter({state:\"ready\"}).count(),num_rep" - "licas:e.count()}}),null!=this.timer&&(driver.stop_timer(this.timer),this.tim" - "er=null),this.timer=driver.run(e,1e3,function(e){return function(t,i){return" - " null!=t?(console.log(\"Nothing bad - Could not fetch replicas statuses\"),c" - "onsole.log(t)):(e.model.set(i),e.render_status())}}(this))},t.prototype.rend" - "er_status=function(){if(this.model.get(\"num_available_replicas\")t.get(\"index\")){i=!0,e.indexes_view.splice(o,0,n" - "),0===o?e.$(\".list_secondary_indexes\").prepend(n.render().$el):e.$(\".inde" - "x_container\").eq(o-1).after(n.render().$el);break}i===!1&&(e.indexes_view.p" - "ush(n),e.$(\".list_secondary_indexes\").append(n.render().$el))}if(1===e.ind" - "exes_view.length)return e.$(\".no_index\").hide()}}(this)),this.listenTo(thi" - "s.collection,\"remove\",function(e){return function(t){var i,r,s,n;for(s=e.i" - "ndexes_view,i=0,r=s.length;i\")}):(e.collection.add(new models.Index({id:t,index:t,db:e.model.get(\"db\"" - "),table:e.model.get(\"name\")})),e.render_feedback({create_ok:!0,name:t}),e." - "hide_add_index())})}}(this)(e)},t.prototype.hide_alert=function(e){var t;ret" - "urn null!=e&&null!=(null!=(t=this.$(e.target))?t.data(\"name\"):void 0)&&(th" - "is.deleting_secondary_index=null),e.preventDefault(),$(e.target).parent().hi" - "de()},t.prototype.remove=function(){var e,i,r,s;for(this.stopListening(),r=t" - "his.indexes_view,e=0,i=r.length;e\")}):1===i.dropped?(e.container.r" - "ender_feedback({delete_ok:!0,name:e.model.get(\"index\")}),e.model.destroy()" - "):e.container.render_error({delete_fail:!0,message:\"Result was not {dropped" - ": 1}\"})}}(this))},t.prototype.cancel_delete=function(){return this.$(\".ale" - "rt_confirm_delete\").hide()},t.prototype.remove=function(){var e;return null" - "!=(e=this.progress_bar)&&e.remove(),t.__super__.remove.call(this)},t}(Backbo" - "ne.View),exports.TableContainer=TableContainer,exports.TableMainView=TableMa" - "inView,exports.ReconfigurePanel=ReconfigurePanel,exports.ReconfigureDiffView" - "=ReconfigureDiffView,exports.Title=Title,exports.Profile=Profile,exports.Tab" - "leStatusIndicator=TableStatusIndicator,exports.SecondaryIndexesView=Secondar" - "yIndexesView,exports.SecondaryIndexView=SecondaryIndexView;\n" - "},{\"../../handlebars/element_view-not_found.hbs\":\"/rethinkdb/admin/static" - "/handlebars/element_view-not_found.hbs\",\"../../handlebars/error-query.hbs\"" - ":\"/rethinkdb/admin/static/handlebars/error-query.hbs\",\"../../handlebars/r" - "econfigure-diff.hbs\":\"/rethinkdb/admin/static/handlebars/reconfigure-diff." - "hbs\",\"../../handlebars/reconfigure.hbs\":\"/rethinkdb/admin/static/handleb" - "ars/reconfigure.hbs\",\"../../handlebars/replica_status.hbs\":\"/rethinkdb/a" - "dmin/static/handlebars/replica_status.hbs\",\"../../handlebars/secondary_ind" - "exes-alert_msg.hbs\":\"/rethinkdb/admin/static/handlebars/secondary_indexes-" - "alert_msg.hbs\",\"../../handlebars/secondary_indexes-error.hbs\":\"/rethinkd" - "b/admin/static/handlebars/secondary_indexes-error.hbs\",\"../../handlebars/s" - "imple_progressbar.hbs\":\"/rethinkdb/admin/static/handlebars/simple_progress" - "bar.hbs\",\"../../handlebars/table-secondary_index.hbs\":\"/rethinkdb/admin/" - "static/handlebars/table-secondary_index.hbs\",\"../../handlebars/table-secon" - "dary_indexes.hbs\":\"/rethinkdb/admin/static/handlebars/table-secondary_inde" - "xes.hbs\",\"../../handlebars/table_container.hbs\":\"/rethinkdb/admin/static" - "/handlebars/table_container.hbs\",\"../../handlebars/table_profile.hbs\":\"/" - "rethinkdb/admin/static/handlebars/table_profile.hbs\",\"../../handlebars/tab" - "le_status_indicator.hbs\":\"/rethinkdb/admin/static/handlebars/table_status_" - "indicator.hbs\",\"../../handlebars/table_title.hbs\":\"/rethinkdb/admin/stat" - "ic/handlebars/table_title.hbs\",\"../app.coffee\":\"/rethinkdb/admin/static/" - "coffee/app.coffee\",\"../modals.coffee\":\"/rethinkdb/admin/static/coffee/mo" - "dals.coffee\",\"../models.coffee\":\"/rethinkdb/admin/static/coffee/models.c" - "offee\",\"../ui_components/modals.coffee\":\"/rethinkdb/admin/static/coffee/" - "ui_components/modals.coffee\",\"../ui_components/progressbar.coffee\":\"/ret" - "hinkdb/admin/static/coffee/ui_components/progressbar.coffee\",\"../vis.coffe" - "e\":\"/rethinkdb/admin/static/coffee/vis.coffee\",\"./shard_assignments.coff" - "ee\":\"/rethinkdb/admin/static/coffee/tables/shard_assignments.coffee\",\"./" - "shard_distribution.coffee\":\"/rethinkdb/admin/static/coffee/tables/shard_di" - "stribution.coffee\",\"rethinkdb\":\"rethinkdb\"}],\"/rethinkdb/admin/static/" - "coffee/topbar.coffee\":[function(require,module,exports){\n" - "var ClientConnectionStatus,Container,Issues,ServersConnected,TablesAvailable" - ",app,driver,issues,bind=function(e,t){return function(){return e.apply(t,arg" - "uments)}},extend=function(e,t){function i(){this.constructor=e}for(var s in " - "t)hasProp.call(t,s)&&(e[s]=t[s]);return i.prototype=t.prototype,e.prototype=" - "new i,e.__super__=t.prototype,e},hasProp={}.hasOwnProperty;issues=require(\"" - "./issues.coffee\"),app=require(\"./app.coffee\"),driver=app.driver,Container" - "=function(e){function t(){return this.remove=bind(this.remove,this),this.rem" - "ove_parent_alert=bind(this.remove_parent_alert,this),this.render=bind(this.r" - "ender,this),this.initialize=bind(this.initialize,this),t.__super__.construct" - "or.apply(this,arguments)}return extend(t,e),t.prototype.className=\"sidebar-" - "container\",t.prototype.template=require(\"../handlebars/sidebar-container.h" - "bs\"),t.prototype.initialize=function(e){return this.model=e.model,this.issu" - "es=e.issues,this.client_panel=new ClientConnectionStatus({model:this.model})" - ",this.servers_panel=new ServersConnected({model:this.model}),this.tables_pan" - "el=new TablesAvailable({model:this.model}),this.issues_panel=new Issues({mod" - "el:this.model}),this.issues_banner=new issues.IssuesBanner({model:this.model" - ",collection:this.issues})},t.prototype.render=function(){return this.$el.htm" - "l(this.template()),this.$(\".client-connection-status\").html(this.client_pa" - "nel.render().el),this.$(\".servers-connected\").html(this.servers_panel.rend" - "er().el),this.$(\".tables-available\").html(this.tables_panel.render().el),t" - "his.$(\".issues\").html(this.issues_panel.render().el),this.$el.append(this." - "issues_banner.render().el),this},t.prototype.remove_parent_alert=function(e)" - "{var t;return t=$(e.target).parent(),t.slideUp(\"fast\",function(e){return f" - "unction(){return t.remove(),e.issues_being_resolved(),e.issues_banner.render" - "()}}(this))},t.prototype.remove=function(){return this.client_panel.remove()" - ",this.servers_panel.remove(),this.tables_panel.remove(),this.issues_panel.re" - "move(),this.issues_banner.remove(),t.__super__.remove.call(this)},t}(Backbon" - "e.View),ClientConnectionStatus=function(e){function t(){return this.remove=b" - "ind(this.remove,this),this.render=bind(this.render,this),this.initialize=bin", - 65529) + std::string( - "d(this.initialize,this),t.__super__.constructor.apply(this,arguments)}return" - " extend(t,e),t.prototype.className=\"client-connection-status\",t.prototype." - "template=require(\"../handlebars/sidebar-client_connection_status.hbs\"),t.p" - "rototype.initialize=function(){return this.listenTo(this.model,\"change:me\"" - ",this.render)},t.prototype.render=function(){return this.$el.html(this.templ" - "ate({disconnected:!1,me:this.model.get(\"me\")})),this},t.prototype.remove=f" - "unction(){return this.stopListening(),t.__super__.remove.call(this)},t}(Back" - "bone.View),ServersConnected=function(e){function t(){return this.remove=bind" - "(this.remove,this),this.render=bind(this.render,this),this.initialize=bind(t" - "his.initialize,this),t.__super__.constructor.apply(this,arguments)}return ex" - "tend(t,e),t.prototype.template=require(\"../handlebars/sidebar-servers_conne" - "cted.hbs\"),t.prototype.initialize=function(){return this.listenTo(this.mode" - "l,\"change:num_servers\",this.render)},t.prototype.render=function(){return " - "this.$el.html(this.template({num_servers:this.model.get(\"num_servers\")}))," - "this},t.prototype.remove=function(){return this.stopListening(),t.__super__." - "remove.call(this)},t}(Backbone.View),TablesAvailable=function(e){function t(" - "){return this.remove=bind(this.remove,this),this.render=bind(this.render,thi" - "s),this.initialize=bind(this.initialize,this),t.__super__.constructor.apply(" - "this,arguments)}return extend(t,e),t.prototype.template=require(\"../handleb" - "ars/sidebar-tables_available.hbs\"),t.prototype.initialize=function(){return" - " this.listenTo(this.model,\"change:num_tables\",this.render),this.listenTo(t" - "his.model,\"change:num_available_tables\",this.render)},t.prototype.render=f" - "unction(){return this.$el.html(this.template({num_tables:this.model.get(\"nu" - "m_tables\"),num_available_tables:this.model.get(\"num_available_tables\"),er" - "ror:this.model.get(\"num_available_tables\")\"+e.main_text+\"\"),this" - ".$(\".custom_btn_placeholder > .\"+e.class_str).click(function(t){return fun" - "ction(t){return e.on_click(t)}}(this)),n.push(this.$(\".custom_btn_placehold" - "er > .\"+e.class_str).button());return n},e.prototype.hide_modal=function(){" - "if(this.$container.removeClass(\"visible\"),null!=this.$modal)return this.$m" - "odal.modal(\"hide\")},e.prototype.cancel_modal=function(t){return this.hide_" - "modal(),t.preventDefault()},e.prototype.reroute=function(t){return this.hide" - "_modal()},e.prototype.check_keypress_is_enter=function(t){if(13===t.which)re" - "turn t.preventDefault(),this.abstract_submit(t)},e.prototype.abstract_submit" - "=function(t){return t.preventDefault(),this.on_submit(t)},e.prototype.reset_" - "buttons=function(){return this.$(\".btn-primary\").button(\"reset\"),this.$(" - "\".cancel\").button(\"reset\")},e.prototype.on_success=function(t){return th" - "is.reset_buttons(),this.remove()},e.prototype.on_submit=function(t){return t" - "his.$(\".btn-primary\").button(\"loading\"),this.$(\".cancel\").button(\"loa" - "ding\")},e.prototype.on_error=function(t){return this.$(\".alert_modal\").ht" - "ml(this.error_template({ajax_fail:!0,error:null!=t&&\"\"!==t?t:void 0})),\"n" - "one\"===this.$(\".alert_modal_content\").css(\"display\")?this.$(\".alert_mo" - "dal_content\").slideDown(\"fast\"):(this.$(\".alert_modal_content\").css(\"d" - "isplay\",\"none\"),this.$(\".alert_modal_content\").fadeIn()),this.reset_but" - "tons()},e.prototype.add_custom_button=function(t,e,r,o){return this.custom_b" - "uttons.push({main_text:t,class_str:e,data_loading_text:r,on_click:o})},e.pro" - "totype.find_custom_button=function(t){return this.$(\".custom_btn_placeholde" - "r > .\"+t)},e.prototype.remove=function(){return this.hide_modal(),e.__super" - "__.remove.call(this)},e}(Backbone.View),ConfirmationDialogModal=function(t){" - "function e(){return e.__super__.constructor.apply(this,arguments)}return ext" - "end(e,t),e.prototype.template=require(\"../../handlebars/confirmation_dialog" - ".hbs\"),e.prototype.class=\"confirmation-modal\",e.prototype.render=function" - "(t,r,o,s){return this.url=r,this.data=o,this.on_user_success=s,e.__super__.r" - "ender.call(this,{message:t,modal_title:\"Confirmation\",btn_secondary_text:\"" - "No\",btn_primary_text:\"Yes\"}),this.$(\".btn-primary\").focus()},e.prototyp" - "e.on_submit=function(){return e.__super__.on_submit.apply(this,arguments),$." - "ajax({processData:!1,url:this.url,type:\"POST\",contentType:\"application/js" - "on\",data:this.data,success:this.on_success,error:this.on_error})},e.prototy" - "pe.on_success=function(t){return e.__super__.on_success.apply(this,arguments" - "),this.on_user_success(t)},e}(AbstractModal),RenameItemModal=function(t){fun" - "ction e(){return this.on_submit=bind(this.on_submit,this),this.initialize=bi" - "nd(this.initialize,this),e.__super__.constructor.apply(this,arguments)}retur" - "n extend(e,t),e.prototype.template=require(\"../../handlebars/rename_item-mo" - "dal.hbs\"),e.prototype.alert_tmpl=require(\"../../handlebars/renamed_item-al" - "ert.hbs\"),e.prototype.error_template=require(\"../../handlebars/error_input" - ".hbs\"),e.prototype.class=\"rename-item-modal\",e.prototype.initialize=funct" - "ion(t){if(e.__super__.initialize.apply(this,arguments),this.model instanceof" - " models.Table)return this.item_type=\"table\";if(this.model instanceof model" - "s.Database)return this.item_type=\"database\";if(this.model instanceof model" - "s.Server)return this.item_type=\"server\";if(null!=t.item_type)return this.i" - "tem_type=t.item_type;throw\"Rename *what* kind of item?\"},e.prototype.rende" - "r=function(){return e.__super__.render.call(this,{type:this.item_type,old_na" - "me:this.model.get(\"name\"),id:this.model.get(\"id\"),modal_title:\"Rename \"" - "+this.item_type,is_database:\"database\"===this.item_type,btn_primary_text:\"" - "Rename \"+this.item_type}),this.$(\"#focus_new_name\").focus()},e.prototype." - "on_submit=function(){var t,o;return e.__super__.on_submit.apply(this,argumen" - "ts),this.old_name=this.model.get(\"name\"),this.formdata=util.form_data_as_o" - "bject($(\"form\",this.$modal)),t=!0,\"\"===this.formdata.new_name?(t=!1,$(\"" - ".alert_modal\").html(this.error_template({empty_name:!0}))):/^[a-zA-Z0-9_-]+" - "$/.test(this.formdata.new_name)===!1&&(t=!1,$(\".alert_modal\").html(this.er" - "ror_template({special_char_detected:!0,type:this.item_type}))),t===!0?(\"tab" - "le\"===this.item_type?o=r.db(system_db).table(\"table_config\").get(this.mod" - "el.get(\"id\")).update({name:this.formdata.new_name}):\"database\"===this.it" - "em_type?o=r.db(system_db).table(\"db_config\").get(this.model.get(\"id\")).u" - "pdate({name:this.formdata.new_name}):\"server\"===this.item_type&&(o=r.db(sy" - "stem_db).table(\"server_config\").get(this.model.get(\"id\")).update({name:t" - "his.formdata.new_name})),driver.run_once(o,function(t){return function(e,r){" - "return null!=e?t.on_error(e):null!=(null!=r?r.first_error:void 0)?t.on_error" - "(new Error(r.first_error)):1===(null!=r?r.replaced:void 0)?t.on_success():t." - "on_error(new Error(\"The value returned for `replaced` was not 1.\"))}}(this" - "))):($(\".alert_modal_content\").slideDown(\"fast\"),this.reset_buttons())}," - "e.prototype.on_success=function(t){var r,o,s;if(e.__super__.on_success.apply" - "(this,arguments),r=this.model.get(\"name\"),this.model.set({name:this.formda" - "ta.new_name}),(null!=(o=this.options)?o.hide_alert:void 0)||$(\"#user-alert-" - "space\").html(this.alert_tmpl({type:this.item_type,old_name:r,new_name:this." - "model.get(\"name\")})),\"function\"==typeof(null!=(s=this.options)?s.on_succ" - "ess:void 0))return this.options.on_success(this.model)},e}(AbstractModal),ex" - "ports.AbstractModal=AbstractModal,exports.ConfirmationDialogModal=Confirmati" - "onDialogModal,exports.RenameItemModal=RenameItemModal;\n" - "},{\"../../handlebars/abstract-modal-outer.hbs\":\"/rethinkdb/admin/static/h" - "andlebars/abstract-modal-outer.hbs\",\"../../handlebars/confirmation_dialog." - "hbs\":\"/rethinkdb/admin/static/handlebars/confirmation_dialog.hbs\",\"../.." - "/handlebars/error_input.hbs\":\"/rethinkdb/admin/static/handlebars/error_inp" - "ut.hbs\",\"../../handlebars/rename_item-modal.hbs\":\"/rethinkdb/admin/stati" - "c/handlebars/rename_item-modal.hbs\",\"../../handlebars/renamed_item-alert.h" - "bs\":\"/rethinkdb/admin/static/handlebars/renamed_item-alert.hbs\",\"../app." - "coffee\":\"/rethinkdb/admin/static/coffee/app.coffee\",\"../models.coffee\":" - "\"/rethinkdb/admin/static/coffee/models.coffee\",\"../util.coffee\":\"/rethi" - "nkdb/admin/static/coffee/util.coffee\",\"rethinkdb\":\"rethinkdb\"}],\"/reth" - "inkdb/admin/static/coffee/ui_components/progressbar.coffee\":[function(requi" - "re,module,exports){\n" - "var OperationProgressBar,bind=function(t,e){return function(){return t.apply" - "(e,arguments)}},extend=function(t,e){function i(){this.constructor=t}for(var" - " n in e)hasProp.call(e,n)&&(t[n]=e[n]);return i.prototype=e.prototype,t.prot" - "otype=new i,t.__super__=e.prototype,t},hasProp={}.hasOwnProperty;OperationPr" - "ogressBar=function(t){function e(){return this.set_none_state=bind(this.set_" - "none_state,this),this.skip_to_processing=bind(this.skip_to_processing,this)," - "this.get_stage=bind(this.get_stage,this),this.render=bind(this.render,this)," - "this.reset_percent=bind(this.reset_percent,this),e.__super__.constructor.app" - "ly(this,arguments)}return extend(e,t),e.prototype.states=[\"none\",\"startin" - "g\",\"processing\",\"finished\"],e.prototype.initialize=function(t){return t" - "his.stage=\"none\",null!=t?this.template=t:this.template=require(\"../../han" - "dlebars/progressbar.hbs\"),this.timeout=null,this.percent=0},e.prototype.res" - "et_percent=function(){return this.percent=0},e.prototype.render=function(t,e" - ",i,n){var s,r;return t!==e&&null!=this.timeout&&(clearTimeout(this.timeout)," - "this.timeout=null,this.stage=\"processing\"),\"none\"===this.stage&&null!=i." - "new_value&&(this.stage=\"starting\",null!=this.timeout&&(clearTimeout(this.t" - "imeout),this.timeout=null)),\"starting\"===this.stage&&null!=i.got_response&" - "&(this.stage=\"processing\",null!=this.timeout&&(clearTimeout(this.timeout)," - "this.timeout=null)),\"processing\"===this.stage&&t===e&&(this.stage=\"finish" - "ed\",null!=this.timeout&&(clearTimeout(this.timeout),this.timeout=null)),s=_" - ".extend(i,{current_value:t,max_value:e}),\"starting\"===this.stage&&(s=_.ext" - "end(s,{operation_active:!0,starting:!0})),\"processing\"===this.stage&&(r=t=" - "==e?0:Math.floor(t/e*100),i.check===!0&&(ri;r=0<=i?++a:--a)n+=t(e[r]),r!==e.length-1&&(n+=\", \");return n}),Handleb" - "ars.registerHelper(\"comma_separated_simple\",function(e){var t,r,a,n;for(a=" - "\"\",t=r=0,n=e.length;0<=n?rn;t=0<=n?++r:--r)a+=e[t],t!==e.length-1&&(a" - "+=\", \");return a}),Handlebars.registerHelper(\"links_to_servers\",function" - "(e,t){var r,a,n,i;for(n=\"\",r=a=0,i=e.length;0<=i?ai;r=0<=i?++a:--a)n+" - "=e[r].exists?''+e[r].name+\"\":e[r].name,r!==e.length-1&&(n+=\", \");return null!=t&&" - "t===!1?n:new Handlebars.SafeString(n)}),Handlebars.registerHelper(\"links_to" - "_datacenters_inline_for_replica\",function(e){var t,r,a,n;for(a=\"\",t=r=0,n" - "=e.length;0<=n?rn;t=0<=n?++r:--r)a+=\"\"+e[t].name+\"\"" - ",t!==e.length-1&&(a+=\", \");return new Handlebars.SafeString(a)}),pluralize" - "_noun=function(e,t,r){var a,n;return null==e?\"NOUN_NULL\":(a=\"y\"===e.subs" - "tr(-1),n=1===t?e:a&&\"key\"!==e?e.slice(0,e.length-1)+\"ies\":\"s\"===e.subs" - "tr(-1)?e+\"es\":\"x\"===e.substr(-1)?e+\"es\":e+\"s\",r===!0&&(n=n.charAt(0)" - ".toUpperCase()+n.slice(1)),n)},Handlebars.registerHelper(\"pluralize_noun\"," - "pluralize_noun),Handlebars.registerHelper(\"pluralize_verb_to_have\",functio" - "n(e){return 1===e?\"has\":\"have\"}),pluralize_verb=function(e,t){return 1==" - "=t?e+\"s\":e},Handlebars.registerHelper(\"pluralize_verb\",pluralize_verb),c" - "apitalize=function(e){return null!=e?e.charAt(0).toUpperCase()+e.slice(1):\"" - "NULL\"},Handlebars.registerHelper(\"capitalize\",capitalize),humanize_uuid=f" - "unction(e){return null!=e?e.substr(0,6):\"NULL\"},Handlebars.registerHelper(" - "\"humanize_uuid\",humanize_uuid),Handlebars.registerHelper(\"humanize_server" - "_connectivity\",function(e){var t,r;return null==e&&(e=\"N/A\"),r=\"connecte" - "d\"===e?\"success\":\"failure\",t=\"\"+cap" - "italize(e)+\"\",new Handlebars.SafeString(t)}),humanize_table_status=" - "function(e){return e?e.all_replicas_ready||e.ready_for_writes?\"Ready\":e.re" - "ady_for_reads?\"Reads only\":e.ready_for_outdated_reads?\"Outdated reads\":\"" - "Unavailable\":\"\"},Handlebars.registerHelper(\"humanize_table_readiness\",f" - "unction(e,t,r){var a,n;return void 0===e?(a=\"failure\",n=\"unknown\"):e.all" - "_replicas_ready?(a=\"success\",n=humanize_table_status(e)+\" \"+t+\"/\"+r):e" - ".ready_for_writes?(a=\"partial-success\",n=humanize_table_status(e)+\" \"+t+" - "\"/\"+r):(a=\"failure\",n=humanize_table_status(e)),new Handlebars.SafeStrin" - "g(\"
\"+n+\"
\")}),Handlebars.re" - "gisterHelper(\"humanize_table_status\",humanize_table_status),approximate_co" - "unt=function(e){var t,r;return 0===e?\"0\":e<=5?\"5\":e<=10?\"10\":(t=Math.r" - "ound(e/Math.pow(10,e.toString().length-2))*Math.pow(10,e.toString().length-2" - "),t<100?(10*Math.floor(t/10)).toString():t<1e3?(100*Math.floor(t/100)).toStr" - "ing():t<1e6?(r=(t/1e3).toString(),1===r.length&&(r+=\".0\"),r+\"K\"):t<1e9?(" - "r=(t/1e6).toString(),1===r.length&&(r+=\".0\"),r+\"M\"):(r=(t/1e9).toString(" - "),1===r.length&&(r+=\".0\"),r+\"B\"))},Handlebars.registerHelper(\"approxima" - "te_count\",approximate_count),format_bytes=function(e,t){var r,a,n;return nu" - "ll==t&&(t=1),0===e?\"0 Bytes\":(a=1024,n=[\"Bytes\",\"KB\",\"MB\",\"GB\",\"T" - "B\",\"PB\",\"EB\",\"ZB\",\"YB\"],r=Math.floor(Math.log(e)/Math.log(a)),(e/Ma" - "th.pow(a,r)).toFixed(t)+\" \"+n[r])},Handlebars.registerHelper(\"print_safe\"" - ",function(e){return null!=e?new Handlebars.SafeString(e):\"\"}),Handlebars.r" - "egisterHelper(\"inc\",function(e){return e+1}),Handlebars.registerHelper(\"i" - "f_defined\",function(e,t){return\"undefined\"!=typeof e?t.fn(this):t.inverse" - "(this)}),form_data_as_object=function(e){var t,r,a,n,i;for(t=e.serializeArra" - "y(),r={},a=0,n=t.length;a=1073741824?(c/1073741824).toFixe" - "d(1)+\"GB\":c>=1048576?(c/1048576).toFixed(1)+\"MB\":c>=1024?(c/1024).toFixe" - "d(1)+\"KB\":1===c?c+\" byte\":c+\" bytes\",0===c)return\"\"" - ";for(f=atob(e.data.slice(0,8)),g=\"\",s=o=0,_=f.length;o<_;s=++o)n=f[s],u=f." - "charCodeAt(s).toString(16),1===u.length&&(u=\"0\"+u),g+=u,s!==f.length-1&&(g" - "+=\" \");return c>f.length&&(g+=\"...\"),\"'},jso" - "n_to_node=function(){var e;return e={span:require(\"../handlebars/dataexplor" - "er_result_json_tree_span.hbs\"),span_with_quotes:require(\"../handlebars/dat" - "aexplorer_result_json_tree_span_with_quotes.hbs\"),url:require(\"../handleba" - "rs/dataexplorer_result_json_tree_url.hbs\"),email:require(\"../handlebars/da" - "taexplorer_result_json_tree_email.hbs\"),object:require(\"../handlebars/data" - "explorer_result_json_tree_object.hbs\"),array:require(\"../handlebars/dataex" - "plorer_result_json_tree_array.hbs\")},json_to_node=function(t){var r,a,n,i,s" - ",o,l,_,u,p,c,d;if(d=typeof t,u=\"\",null===t)return e.span({classname:\"jt_n" - "ull\",value:\"null\"});if(\"[object Array]\"===Object.prototype.toString.cal" - "l(t)){if(0===t.length)return\"[ ]\";for(c=[],n=0,l=t.length;n=1?(e=\"\"+(t/1e9).toFixed(1),\".0\"===e.slice(-2" - ")&&(e=e.slice(0,-2)),e+=\"B\"):t/1e6>=1?(e=\"\"+(t/1e6).toFixed(1),\".0\"===" - "e.slice(-2)&&(e=e.slice(0,-2)),e+=\"M\"):t/1e3>=1?(e=\"\"+(t/1e3).toFixed(1)" - ",\".0\"===e.slice(-2)&&(e=e.slice(0,-2)),e+=\"K\"):e=\"\"+t.toFixed(0),e)},O" - "psPlotLegend=function(t){function e(){return this.remove=bind(this.remove,th" - "is),this.render=bind(this.render,this),this.initialize=bind(this.initialize," - "this),e.__super__.constructor.apply(this,arguments)}return extend(e,t),e.pro" - "totype.className=\"ops-plot-legend\",e.prototype.template=require(\"../handl" - "ebars/ops_plot_legend.hbs\"),e.prototype.initialize=function(t,e,s){return t" - "his.context=s,this.reads=null,this.writes=null,this.read_metric=t,this.read_" - "metric.on(\"change\",function(s){return function(){return s.reads=t.valueAt(" - "s.context.size()-1),s.writes=e.valueAt(s.context.size()-1),s.render()}}(this" - "))},e.prototype.render=function(){return this.$el.html(this.template({read_c" - "ount:null!=this.reads?num_formatter(this.reads):\"N/A\",write_count:null!=th" - "is.writes?num_formatter(this.writes):\"N/A\"})),this},e.prototype.remove=fun" - "ction(){return this.context.stop(),e.__super__.remove.call(this)},e}(Backbon" - "e.View),OpsPlot=function(t){function e(){return this.remove=bind(this.remove" - ",this),this.render=bind(this.render,this),this.make_metric=bind(this.make_me" - "tric,this),e.__super__.constructor.apply(this,arguments)}return extend(e,t)," - "e.prototype.className=\"ops-plot\",e.prototype.template=require(\"../handleb" - "ars/ops_plot.hbs\"),e.prototype.barebones_template=require(\"../handlebars/o" - "ps_plot.hbs\"),e.prototype.type=\"cluster\",e.prototype.HEIGHT_IN_PIXELS=200" - ",e.prototype.HEIGHT_IN_UNITS=20500,e.prototype.WIDTH_IN_PIXELS=300,e.prototy" - "pe.WIDTH_IN_SECONDS=60,e.prototype.HAXIS_TICK_INTERVAL_IN_SECS=15,e.prototyp" - "e.HAXIS_MINOR_SUBDIVISION_COUNT=3,e.prototype.VAXIS_TICK_SUBDIVISION_COUNT=5" - ",e.prototype.VAXIS_MINOR_SUBDIVISION_COUNT=2,e.prototype.make_metric=functio" - "n(t){var e,s;return s=new InterpolatingCache(this.WIDTH_IN_PIXELS,this.WIDTH" - "_IN_PIXELS/this.WIDTH_IN_SECONDS,function(e){return function(){return e.stat" - "s_fn()[t]}}(this)),e=function(t){return function(t,e,i,n){var r;return t=+t," - "e=+e,r=(e-t)/i,n(null,s.step(r))}}(this),this.context.on(\"focus\",function(" - "t){return function(e){return d3.selectAll(\".value\").style(\"right\",null==" - "=e?null:t.context.size()-e+\"px\")}}(this)),this.context.metric(e,t)},e.prot" - "otype.initialize=function(t,s){return null!=s&&(null!=s.height&&(this.HEIGHT" - "_IN_PIXELS=s.height),null!=s.height_in_units&&(this.HEIGHT_IN_UNITS=s.height" - "_in_units),null!=s.width&&(this.WIDTH_IN_PIXELS=s.width),null!=s.seconds&&(t" - "his.WIDTH_IN_SECONDS=s.seconds),null!=s.haxis&&(null!=s.haxis.seconds_per_ti" - "ck&&(this.HAXIS_TICK_INTERVAL_IN_SECS=s.haxis.seconds_per_tick),null!=s.haxi" - "s.ticks_per_label&&(this.HAXIS_MINOR_SUBDIVISION_COUNT=s.haxis.ticks_per_lab" - "el)),null!=s.vaxis&&(null!=s.vaxis.num_ticks&&(this.VAXIS_TICK_SUBDIVISION_C" - "OUNT=s.vaxis.num_ticks),null!=s.vaxis.ticks_per_label&&(this.VAXIS_MINOR_SUB" - "DIVISION_COUNT=s.vaxis.ticks_per_label)),null!=s.type&&(this.type=s.type)),e" - ".__super__.initialize.apply(this,arguments),this.stats_fn=t,this.context=cub" - "ism.context().serverDelay(0).clientDelay(0).step(1e3/(this.WIDTH_IN_PIXELS/t" - "his.WIDTH_IN_SECONDS)).size(this.WIDTH_IN_PIXELS),this.read_stats=this.make_" - "metric(\"keys_read\"),this.write_stats=this.make_metric(\"keys_set\"),this.l" - "egend=new OpsPlotLegend(this.read_stats,this.write_stats,this.context)},e.pr" - "ototype.render=function(){return this.$el.html(this.template({cluster:\"clus" - "ter\"===this.type,datacenter:\"datacenter\"===this.type,server:\"server\"===" - "this.type,database:\"database\"===this.type,table:\"table\"===this.type})),t" - "his.sensible_plot=this.context.sensible().height(this.HEIGHT_IN_PIXELS).colo" - "rs([\"#983434\",\"#729E51\"]).extent([0,this.HEIGHT_IN_UNITS]),d3.select(thi" - "s.$(\".plot\")[0]).call(function(t){return function(e){return e.data([[t.rea" - "d_stats,t.write_stats]]),t.selection=e.append(\"div\").attr(\"class\",\"char" - "t\"),t.selection.call(t.sensible_plot),e.append(\"div\").attr(\"class\",\"ha" - "xis\").call(t.context.axis().orient(\"bottom\").ticks(d3.time.seconds,t.HAXI" - "S_TICK_INTERVAL_IN_SECS).tickSubdivide(t.HAXIS_MINOR_SUBDIVISION_COUNT-1).ti" - "ckSize(6,3,0).tickFormat(d3.time.format(\"%I:%M:%S\"))),e.append(\"div\").at" - "tr(\"class\",\"hgrid\").call(t.context.axis().orient(\"bottom\").ticks(d3.ti" - "me.seconds,t.HAXIS_TICK_INTERVAL_IN_SECS).tickSize(t.HEIGHT_IN_PIXELS+4,0,0)" - ".tickFormat(\"\")),e.append(\"div\").attr(\"class\",\"vaxis\").call(t.contex" - "t.axis(t.HEIGHT_IN_PIXELS,[t.read_stats,t.write_stats],t.sensible_plot.scale" - "()).orient(\"left\").ticks(t.VAXIS_TICK_SUBDIVISION_COUNT).tickSubdivide(t.V" - "AXIS_MINOR_SUBDIVISION_COUNT-1).tickSize(6,3,0).tickFormat(num_formatter)),e" - ".append(\"div\").attr(\"class\",\"vgrid\").call(t.context.axis(t.HEIGHT_IN_P" - "IXELS,[t.read_stats,t.write_stats],t.sensible_plot.scale()).orient(\"left\")" - ".ticks(t.VAXIS_TICK_SUBDIVISION_COUNT).tickSize(-(t.WIDTH_IN_PIXELS+35),0,0)" - ".tickFormat(\"\")),t.$(\".legend-container\").html(t.legend.render().el)}}(t" - "his)),this},e.prototype.remove=function(){return this.sensible_plot.remove(t" - "his.selection),this.context.stop(),this.legend.remove(),e.__super__.remove.c" - "all(this)},e}(Backbone.View),SizeBoundedCache=function(){function t(t,e){thi" - "s.values=[],this.ndp=t,this.stat=e}return t.prototype.push=function(t){var e" - ",s,i,n,r;if(\"function\"==typeof this.stat?r=this.stat(t):null!=t&&(r=t[this" - ".stat]),!isNaN(r)){if(this.values.push(r),this.values.lengthn;e=i<=n?++s:--s)this.values." - "push(r);return this.values.length>this.ndp?this.values=this.values.slice(-th" - "is.ndp):void 0}},t.prototype.get=function(){return this.values},t.prototype." - "get_latest=function(){return this.values[this.values.length-1]},t}(),Interpo" - "latingCache=function(){function t(t,e,s){var i,n,r;for(this.ndp=t,this.nip=e" - ",this.get_data_fn=s,this.values=[],i=n=0,r=this.ndp-1;0<=r?n<=r:n>=r;i=0<=r?" - "++n:--n)this.values.push(0);this.next_value=null,this.last_date=null}return " - "t.prototype.step=function(t){return this.push_data(),this.values.slice(-t)}," - "t.prototype.push_data=function(){var t,e,s,i,n,r,a;if(null===this.last_date)" - "return this.last_date=Date.now(),this.values.push(this.get_data_fn()),!0;for" - "(t=this.get_data_fn(),this.next_value!==t&&(this.start_value=this.values[thi" - "s.values.length-1],this.next_value=t,this.interpolation_step=1),e=Date.now()" - "-this.last_date,n=Math.max(1,Math.round(e/1e3*this.nip)),s=i=1,r=n;1<=r?i<=r" - ":i>=r;s=1<=r?++i:--i)this.values[this.values.length-1]===this.next_value?a=t" - "his.next_value:(a=this.start_value+(this.next_value-this.start_value)/this.n" - "ip*this.interpolation_step,this.interpolation_step+=1,this.interpolation_ste" - "p>this.nip&&(a=this.next_value)),this.values.push(a);return this.last_date=D" - "ate.now(),this.values.length>this.ndp?this.values=this.values.slice(-this.nd" - "p):void 0},t}(),exports.OpsPlotLegend=OpsPlotLegend,exports.OpsPlot=OpsPlot," - "exports.SizeBoundedCache=SizeBoundedCache,exports.InterpolatingCache=Interpo" - "latingCache;\n" - "},{\"../handlebars/ops_plot.hbs\":\"/rethinkdb/admin/static/handlebars/ops_p" - "lot.hbs\",\"../handlebars/ops_plot_legend.hbs\":\"/rethinkdb/admin/static/ha" - "ndlebars/ops_plot_legend.hbs\"}],\"/rethinkdb/admin/static/handlebars/abstra" - "ct-modal-outer.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(a,n,t,l){var e;return this.escapeExpression((e=" - "null!=(e=n.btn_secondary_text||(null!=a?a.btn_secondary_text:a))?e:n.helperM" - "issing,\"function\"==typeof e?e.call(a,{name:\"btn_secondary_text\",hash:{}," - "data:l}):e))},3:function(a,n,t,l){return\"Cancel\"},5:function(a,n,t,l){var " - "e;return this.escapeExpression((e=null!=(e=n.btn_primary_text||(null!=a?a.bt" - "n_primary_text:a))?e:n.helperMissing,\"function\"==typeof e?e.call(a,{name:\"" - "btn_primary_text\",hash:{},data:l}):e))},7:function(a,n,t,l){return\"Commit\"" - "},compiler:[6,\">= 2.0.0-beta.1\"],main:function(a,n,t,l){var e,s,i=n.helper" - "Missing,r=\"function\",o=this.escapeExpression;return'\\n
\\n
\\n &ti" - "mes;\\n

'+o((s=null!=(s=n.modal_title||(null!=a?a.modal_t" - "itle:a))?s:i,typeof s===r?s.call(a,{name:\"modal_title\",hash:{},data:l}):s)" - ")+'

\\n
\\n
\\n " - "
\\n
\\" - "n
\\n \\n '+(null!=(e=n.if.ca" - "ll(a,null!=a?a.btn_secondary_text:a,{name:\"if\",hash:{},fn:this.program(1,l" - ",0),inverse:this.program(3,l,0),data:l}))?e:\"\")+'\\n \\" - "n \\n
\\n
\\n\\n\\n\"" - "},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/add_database-modal.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,n,a,l){re" - "turn' \\n
\\n
\\n
\\n \\n " - "
\\n \\n " - "
\\n
\\n
\\n
\\n\\n\\n" - "'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/add_table-modal.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,a,s,n){re" - "turn'
\\n \\n \\" - "n \\n

Hide optional settings

\\n\\n
\\n \\n \\n
\\n
\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/alert-connection_fail.hbs\":[function(require,module,exports){" - "\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,r,a,n){re" - "turn'\\n
\\n ×\\n

The " - "connection to the server failed. Try runnin" - "g the query again.

\\n
\\n\\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/alert_message.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,a,s,r){re" - "turn'\\n
\\n " - " ×\\n

'+this.escapeExp" - "ression((a.print_safe||e&&e.print_safe||a.helperMissing).call(e,null!=e?e.me" - "ssage:e,{name:\"print_safe\",hash:{},data:r}))+\"

\\n
\\n\\n\\n\"" - "},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/body-structure.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(i,n,t,e){re" - "turn'\\n
\\n
" - "
\\n
\\n
\\n
\\n
\\n " - "
\\n
\\n
\\n <" - "/div>\\n
\\n
\\n
\\n
\\n
\\n\\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/confirmation_dialog.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,r,a,n){re" - "turn' \\n
\\n

'" - "+this.escapeExpression((r.print_safe||e&&e.print_safe||r.helperMissing).call" - "(e,null!=e?e.message:e,{name:\"print_safe\",hash:{},data:n}))+\"

\\n\\n\\" - "n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/database.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(a,e,n,t){va" - "r l,s=e.helperMissing,i=\"function\",d=this.escapeExpression;return'
\\n
Database
\\n " - "

'+d((l=null!=(l=e.name||(null!=a?a.name:a))?l:s,typeof l=" - "==i?l.call(a,{name:\"name\",hash:{},data:t}):l))+'

\\n
\\n \\n Delete " - "Database\\n
\\n
\\n
\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/databases_container.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,a,t,s){re" - "turn'
\\n \\n \\n
" - "\\n

Tables in the cluster

\\n
\\n
\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer-cursor_timed_out.hbs\":[function(require,module,e" - "xports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,t,r,o){re" - "turn\"\\n No more data available, the cursor timed out. Re-execute the qu" - "ery to retrieve more results.\\n\\n\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer-databases_suggestions.hbs\":[function(require,mod" - "ule,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(a,e,s,n){return\" No database found. Use" - " dbCreate() to create a new database first.\\n\"},3:function(a,e,s,n){return" - "\" List of databases available: \"+this.escapeExpression((e.c" - "omma_separated_simple||a&&a.comma_separated_simple||e.helperMissing).call(a," - "null!=a?a.databases_available:a,{name:\"comma_separated_simple\",hash:{},dat" - "a:n}))+\"\\n\"},compiler:[6,\">= 2.0.0-beta.1\"],main:function(a,e," - "s,n){var r;return\"\\n

\\n\"+(null!=(r=e.if.call(a,null!=a?a.no_databa" - "se:a,{name:\"if\",hash:{},fn:this.program(1,n,0),inverse:this.program(3,n,0)" - ",data:n}))?r:\"\")+'

\\n
\\n\\n'}," - "useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer-description.hbs\":[function(require,module,export" - "s){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(a,n,e,s){var r;return''+this.escapeExpression((r=null!=(r=n.args||(null!=a?a.args:a))" - "?r:n.helperMissing,\"function\"==typeof r?r.call(a,{name:\"args\",hash:{},da" - "ta:s}):r))+\"\"},compiler:[6,\">= 2.0.0-beta.1\"],main:function(a,n,e" - ",s){var r,l,i=n.helperMissing,t=this.escapeExpression;return'\\n

'+t((l=null!=(l=n.name||(null!=a?a.name:a))?l:i,\"fu" - "nction\"==typeof l?l.call(a,{name:\"name\",hash:{},data:s}):l))+\"\"+" - "(null!=(r=n.if.call(a,null!=a?a.args:a,{name:\"if\",hash:{},fn:this.program(" - "1,s,0),inverse:this.noop,data:s}))?r:\"\")+\"

\\n\"+t((n.print_safe||a&&a" - ".print_safe||i).call(a,null!=a?a.description:a,{name:\"print_safe\",hash:{}," - "data:s}))+\"\\n\\n\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer-description_with_example.hbs\":[function(require," - "module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(a,e,n,r){return'

Note: Commands chained after group will operate on each " - "reduction. Call ungroup to operate on the array of groups.

'},compiler:[6" - ",\">= 2.0.0-beta.1\"],main:function(a,e,n,r){var t,i=e.helperMissing,s=this." - "escapeExpression;return\"\\n

\"+s((e.print_safe||a&&a.print_safe||i).c" - "all(a,null!=a?a.description:a,{name:\"print_safe\",hash:{},data:r}))+\"

\\" - "n \"+(null!=(t=e.if.call(a,null!=a?a.grouped_data:a,{name:\"if\",hash:{}," - "fn:this.program(1,r,0),inverse:this.noop,data:r}))?t:\"\")+'\\n
\\n
\\n " - " '+s((e.print_safe||a&&a.print_safe||i).call(a,null!=a?a.example:a,{name:\"p" - "rint_safe\",hash:{},data:r}))+\"\\n\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer-error.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(r,e,n,a){return\" JavaScript error:\\" - "n\"},3:function(r,e,n,a){return\" Server error:\\n\"},5:function(" - "r,e,n,a){var l;return'

The query that returned an " - "error is:

\\n
'+this.escapeExpr"
-      "ession((l=null!=(l=e.query||(null!=r?r.query:r))?l:e.helperMissing,\"functio"
-      "n\"==typeof l?l.call(r,{name:\"query\",hash:{},data:a}):l))+\"
\\n\"},c" - "ompiler:[6,\">= 2.0.0-beta.1\"],main:function(r,e,n,a){var l,s;return'\\n " - "

Error

\\n

\\n'+(null!=(" - "l=e.if.call(r,null!=r?r.js_error:r,{name:\"if\",hash:{},fn:this.program(1,a," - "0),inverse:this.program(3,a,0),data:a}))?l:\"\")+'

\\n
'+this.escapeExpression((s=null!=(s=e.error||(null!="
-      "r?r.error:r))?s:e.helperMissing,\"function\"==typeof s?s.call(r,{name:\"erro"
-      "r\",hash:{},data:a}):s))+\"
\\n\\n\"+(null!=(l=e.if.call(r,null!=r?r.qu" - "ery:r,{name:\"if\",hash:{},fn:this.program(5,a,0),inverse:this.noop,data:a})" - ")?l:\"\")+\"\\n\\n\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer-history.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,r,a,l){re" - "turn'\\n
    \\n\\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer-option_page.hbs\":[function(require,module,export" - "s){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(e,a,n,l){return'selected=\"selected\"'},compile" - "r:[6,\">= 2.0.0-beta.1\"],main:function(e,a,n,l){var t,s,p=a.helperMissing,i" - "=\"function\",r=this.escapeExpression;return'\\n \\n\\n\\n\"},useD" - "ata:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer-options.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(i,t,e,n){return\" checked=true\"},compiler:[6,\"" - ">= 2.0.0-beta.1\"],main:function(i,t,e,n){var a,o;return'
      \\n
    • \\n
      \\n " - "
      Enabl" - "e automatic suggestions and command descriptions
    • \\n
    • \\n
      \\n
      Auto-insert pair brackets, parentheses, and quo" - "tes
    • \\n
    • \\n
      Query profiler active: re-ru" - "n the query to see results.
      \\n
      \\n
      Enable query profiler
    • \\n <" - "li class=\"text-input\" data-option=\"query_limit\">\\n
      \\n
      Max results per query (Note: returning m" - "ore results will increase loading time)
      \\n
    \\n'},useData:" - "!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer-query_error.hbs\":[function(require,module,export" - "s){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(n,e,a,r){return\"You should remove .run() from " - "your queries when using the Data Explorer (as of RethinkDB 1.4). The query w" - "ill be built and sent by the Data Explorer itself.\"},3:function(n,e,a,r){re" - "turn\"Last query was not a RethinkDB query.\"},5:function(n,e,a,r){return\"N" - "o query found\"},7:function(n,e,a,r){var l,i=e.helperMissing,t=\"function\"," - "o=this.escapeExpression;return\"Syntax error, missing opening bracket for \"" - "+o((l=null!=(l=e.bracket||(null!=n?n.bracket:n))?l:i,typeof l===t?l.call(n,{" - "name:\"bracket\",hash:{},data:r}):l))+\" (line \"+o((l=null!=(l=e.line||(nul" - "l!=n?n.line:n))?l:i,typeof l===t?l.call(n,{name:\"line\",hash:{},data:r}):l)" - ")+\", position \"+o((l=null!=(l=e.position||(null!=n?n.position:n))?l:i,type" - "of l===t?l.call(n,{name:\"position\",hash:{},data:r}):l))+\").\"},compiler:[" - "6,\">= 2.0.0-beta.1\"],main:function(n,e,a,r){var l;return\"\\n\"+(null!=(l=" - "e.if.call(n,null!=n?n.found_run:n,{name:\"if\",hash:{},fn:this.program(1,r,0" - "),inverse:this.noop,data:r}))?l:\"\")+\"\\n\"+(null!=(l=e.if.call(n,null!=n?" - "n.last_non_query:n,{name:\"if\",hash:{},fn:this.program(3,r,0),inverse:this." - "noop,data:r}))?l:\"\")+\"\\n\"+(null!=(l=e.if.call(n,null!=n?n.no_query:n,{n" - "ame:\"if\",hash:{},fn:this.program(5,r,0),inverse:this.noop,data:r}))?l:\"\"" - ")+\"\\n\"+(null!=(l=e.if.call(n,null!=n?n.syntax_error:n,{name:\"if\",hash:{" - "},fn:this.program(7,r,0),inverse:this.noop,data:r}))?l:\"\")+\"\\n\\n\\n\\n\"" - "},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer-query_li.hbs\":[function(require,module,exports){" - "\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(a,l,n,e){var s;return'
  • No history available
  • \\n'},3:function(a" - ",l,n,e){var s,i,r=l.helperMissing,t=\"function\",u=this.escapeExpression;ret" - "urn'
  • \\n \\n
    ['+u((i=nul" - "l!=(i=l.num||(null!=a?a.num:a))?i:r,typeof i===t?i.call(a,{name:\"num\",hash" - ":{},data:e}):i))+']
    \\n
    '+u((i=null!=(i=l.query"
    -      "||(null!=a?a.query:a))?i:r,typeof i===t?i.call(a,{name:\"query\",hash:{},dat"
    -      "a:e}):i))+'
    \\n
    \\n'+(null!=(s=l.if.c" - "all(a,null!=a?a.broken_query:a,{name:\"if\",hash:{},fn:this.program(4,e,0),i" - "nverse:this.noop,data:e}))?s:\"\")+' \\n \\n \\n \\n
    \\n
  • \\n " - "
    \\n \\n
    \\n\\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_large_result_json_tree_container.hbs\":[function(" - "require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(a,n,s,e){return'
    \\n &tim" - "es;\\n

    Syntax highlighting is disabled because of the amount o" - "f data returned by the query.

    \\n
    \\n'},compiler:[6,\">= 2.0.0-b" - "eta.1\"],main:function(a,n,s,e){var t,l;return(null!=(t=n.if.call(a,null!=a?" - "a.too_many_datums:a,{name:\"if\",hash:{},fn:this.program(1,e,0),inverse:this" - ".noop,data:e}))?t:\"\")+'\\n
    \\n \\n \\n ...\\n
    \\n '+this.escapeExpression((l=null!=(l=n.json_data||" - "(null!=a?a.json_data:a))?l:n.helperMissing,\"function\"==typeof l?l.call(a,{" - "name:\"json_data\",hash:{},data:e}):l))+\"\\n
    \\n
    \\n\\" - "n\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_container.hbs\":[function(require,module,e" - "xports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(n,e,a,l){return'
  • Query profile
  • \\n'},3:func" - "tion(n,e,a,l){return\"floating_metadata\"},5:function(n,e,a,l){return\"feed_" - "paused\"},7:function(n,e,a,l){return\"feed_unpaused\"},9:function(n,e,a,l){v" - "ar s;return null!=(s=e.if.call(n,null!=(s=null!=n?n.feed:n)?s.ended:s,{name:" - "\"if\",hash:{},fn:this.program(10,l,0),inverse:this.program(12,l,0),data:l})" - ")?s:\"\"},10:function(n,e,a,l){return\" The changefeed is clo", - 65463) + std::string( - "sed.\\n\"},12:function(n,e,a,l){var s;return' \\n \\n Listening for eve" - "nts...\\n (paused: '+this.escapeExpression(this.lambda(null!=(s=null!=n?" - "n.feed:n)?s.upcoming:s,n))+\" more available).\\n\"},14:functi" - "on(n,e,a,l){var s;return\" \"+(null!=(s=e.if.call(n,null!=n?n.que" - "ry_has_changed:n,{name:\"if\",hash:{},fn:this.program(15,l,0),inverse:this.n" - "oop,data:l}))?s:\"\")+\"\\n\"+(null!=(s=e.if.call(n,null!=n?n.no_results:n,{" - "name:\"if\",hash:{},fn:this.program(17,l,0),inverse:this.program(19,l,0),dat" - "a:l}))?s:\"\")},15:function(n,e,a,l){return\"Results from the previous query" - ":\"},17:function(n,e,a,l){return\"No results returned.\\n\"},19:function(n,e" - ",a,l){var s;return null!=(s=e.if.call(n,null!=n?n.paginated:n,{name:\"if\",h" - "ash:{},fn:this.program(20,l,0),inverse:this.noop,data:l}))?s:\"\"},20:functi" - "on(n,e,a,l){var s,r,i=e.helperMissing,t=this.escapeExpression;return\" " - " \"+t((r=null!=(r=e.num_results||(null!=n?n.num_results:n))?r:i" - ",\"function\"==typeof r?r.call(n,{name:\"num_results\",hash:{},data:l}):r))+" - "\" \"+t((e.pluralize_noun||n&&n.pluralize_noun||i).call(n,\"row\",null!=n?n." - "num_results:n,{name:\"pluralize_noun\",hash:{},data:l}))+\" returned\"+(null" - "!=(s=e.if.call(n,null!=n?n.show_more_data:n,{name:\"if\",hash:{},fn:this.pro" - "gram(21,l,0),inverse:this.noop,data:l}))?s:\"\")+(null!=(s=e.if.call(n,null!" - "=n?n.execution_time_pretty:n,{name:\"if\",hash:{},fn:this.program(23,l,0),in" - "verse:this.noop,data:l}))?s:\"\")+\".\\n\"+(null!=(s=e.if.call(n,null!=n?n.r" - "ange_end:n,{name:\"if\",hash:{},fn:this.program(25,l,0),inverse:this.noop,da" - "ta:l}))?s:\"\")+(null!=(s=e.if.call(n,null!=n?n.show_more_data:n,{name:\"if\"" - ",hash:{},fn:this.program(27,l,0),inverse:this.noop,data:l}))?s:\"\")+\"\\n\"" - "},21:function(n,e,a,l){return\" so far\"},23:function(n,e,a,l){var s;return\"" - " in \"+this.escapeExpression((s=null!=(s=e.execution_" - "time_pretty||(null!=n?n.execution_time_pretty:n))?s:e.helperMissing,\"functi" - "on\"==typeof s?s.call(n,{name:\"execution_time_pretty\",hash:{},data:l}):s))" - "},25:function(n,e,a,l){var s,r=e.helperMissing,i=\"function\",t=this.escapeE" - "xpression;return\"Displaying rows \"+t((s=null!=(s=e.range_begin||(null!=n?n" - ".range_begin:n))?s:r,typeof s===i?s.call(n,{name:\"range_begin\",hash:{},dat" - "a:l}):s))+\"-\"+t((s=null!=(s=e.range_end||(null!=n?n.range_end:n))?s:r,type" - "of s===i?s.call(n,{name:\"range_end\",hash:{},data:l}):s))},27:function(n,e," - "a,l){return\", more available \"},29:function(n,e,a,l){var s;return' \\n Ol" - "der results have been discarded.\\n \\n'},30:function(n,e,a," - "l){return\"hidden\"},32:function(n,e,a,l){var s;return null!=(s=e.if.call(n," - "null!=n?n.show_more_data:n,{name:\"if\",hash:{},fn:this.program(33,l,0),inve" - "rse:this.program(35,l,0),data:l}))?s:\"\"},33:function(n,e,a,l){return' <" - "div class=\"info more_results\">\\n

    More data is available. Load th" - "e next batch →

    \\n \\n'},35:function(n,e,a,l){var s;ret" - "urn null!=(s=e.if.call(n,null!=n?n.cursor_timed_out_template:n,{name:\"if\"," - "hash:{},fn:this.program(36,l,0),inverse:this.noop,data:l}))?s:\"\"},36:funct" - "ion(n,e,a,l){return'

    '+this.escapeExpression((e.print_safe||n&&n.print_s" - "afe||e.helperMissing).call(n,null!=n?n.cursor_timed_out_template:n,{name:\"p" - "rint_safe\",hash:{},data:l}))+\"

    \\n\"},compiler:[6,\">= 2.0.0-beta" - ".1\"],main:function(n,e,a,l){var s;return'
    \\n " - " \\n \\n
    \\n
    \\n
    \\n'+(null!=(s=e.if.call(n,null!=n?n.feed:n,{name:\"if\",hash:{},fn:this." - "program(29,l,0),inverse:this.program(32,l,0),data:l}))?s:\"\")},useData:!0})" - ";\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_empty.hbs\":[function(require,module,expor" - "ts){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(r,e,n,t){var a;return null!=(a=e.if.call(r,null" - "!=r?r.at_beginning:r,{name:\"if\",hash:{},fn:this.program(2,t,0),inverse:thi" - "s.program(4,t,0),data:t}))?a:\"\"},2:function(r,e,n,t){return\"

    No" - " results were returned for this query

    \\n\"},4:function(r,e,n,t){return\"" - "

    No more results were returned for this query

    \\n\"},6:function" - "(r,e,n,t){return\"

    Waiting for more results

    \\n\"},compiler:[6,\">=" - " 2.0.0-beta.1\"],main:function(r,e,n,t){var a;return null!=(a=e.if.call(r,nu" - "ll!=r?r.ended:r,{name:\"if\",hash:{},fn:this.program(1,t,0),inverse:this.pro" - "gram(6,t,0),data:t}))?a:\"\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_json_table_container.hbs\":[function(requi" - "re,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(a,e,n,t){va" - "r s=e.helperMissing,l=this.escapeExpression;return'
    \\n \\n '+l((e.print_safe||a&&a.p" - "rint_safe||s).call(a,null!=a?a.table_attr:a,{name:\"print_safe\",hash:{},dat" - "a:t}))+\"\\n \"+l((e.print_safe||a&&a.print_safe||s).call(a,null!=a?a.tab" - "le_data:a,{name:\"print_safe\",hash:{},data:t}))+\"\\n
    \\n
    \\n" - "\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_json_table_data_inline.hbs\":[function(req" - "uire,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(a,n,s,e){var l,t,r=n.helperMissing,i=this.escap" - "eExpression;return' \\n " - " \\n '+(null!" - "=(l=n.unless.call(a,null!=a?a.is_inline:a,{name:\"unless\",hash:{},fn:this.p" - "rogram(2,e,0),inverse:this.noop,data:e}))?l:\"\")+\"\\n \"" - "+i((n.print_safe||a&&a.print_safe||r).call(a,null!=a?a.value:a,{name:\"print" - "_safe\",hash:{},data:e}))+\"\\n \"+(null!=(l=n.if.call(a" - ",null!=a?a.need_comma:a,{name:\"if\",hash:{},fn:this.program(4,e,0),inverse:" - "this.noop,data:e}))?l:\"\")+\"\\n \\n\"},2:function(a,n,s,e){r" - "eturn''},4:function(a,n,s,e){re" - "turn\", \"},6:function(a,n,s,e){var l,t,r=n.helperMissing,i=this.escapeExpre" - "ssion;return\" \"+i((n.print_safe||a&&a.print_safe||r).call(a,null!=a?a.va" - "lue:a,{name:\"print_safe\",hash:{},data:e}))+\"\"+(null!=(l=n.if.call" - "(a,null!=a?a.need_comma:a,{name:\"if\",hash:{},fn:this.program(4,e,0),invers" - "e:this.noop,data:e}))?l:\"\")+\"\\n\"},compiler:[6,\">= 2.0.0-beta.1\"],main" - ":function(a,n,s,e){var l;return\"\\n\"+(null!=(l=n.if.call(a,null!=a?a.data_" - "to_expand:a,{name:\"if\",hash:{},fn:this.program(1,e,0),inverse:this.program" - "(6,e,0),data:e}))?l:\"\")+\"\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_json_table_td_attr.hbs\":[function(require" - ",module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(l,a,e,t){va" - "r n,s=a.helperMissing,c=\"function\",o=this.escapeExpression;return'\\n <" - "td class=\"td_attr '+o((n=null!=(n=a.classtd||(null!=l?l.classtd:l))?n:s,typ" - "eof n===c?n.call(l,{name:\"classtd\",hash:{},data:t}):n))+'\" data-col='+o((" - "n=null!=(n=a.col||(null!=l?l.col:l))?n:s,typeof n===c?n.call(l,{name:\"col\"" - ",hash:{},data:t}):n))+'>\\n
    \\n " - "
    '+o((n=null!=" - "(n=a.key||(null!=l?l.key:l))?n:s,typeof n===c?n.call(l,{name:\"key\",hash:{}" - ",data:t}):n))+\"
    \\n
    \\n \\n\\n\\n\"},useData:!0})" - ";\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_json_table_td_value.hbs\":[function(requir" - "e,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(l,a,e,n){va" - "r t,o=a.helperMissing,s=\"function\",c=this.escapeExpression;return'\\n <" - "td class=\"col-'+c((t=null!=(t=a.col||(null!=l?l.col:l))?t:o,typeof t===s?t." - "call(l,{name:\"col\",hash:{},data:n}):t))+'\" data-col='+c((t=null!=(t=a.co" - "l||(null!=l?l.col:l))?t:o,typeof t===s?t.call(l,{name:\"col\",hash:{},data:n" - "}):t))+\">\\n \"+c((a.print_safe||l&&l.print_safe||o).call(l,null!=l?" - "l.cell_content:l,{name:\"print_safe\",hash:{},data:n}))+\"\\n \\n\\n" - "\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_json_table_td_value_content.hbs\":[functio" - "n(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(a,l,n,s){var e,t=l.helperMissing,u=\"function\"" - ",c=this.escapeExpression;return'
    \\n \\n " - "\\n '+c((l.print_saf" - "e||a&&a.print_safe||t).call(a,null!=a?a.value:a,{name:\"print_safe\",hash:{}" - ",data:s}))+\"\\n \\n
    \\n \"},3:function(a,l,n,s){v" - "ar e;return null!=(e=l.if.call(a,null!=a?a.is_object:a,{name:\"if\",hash:{}," - "fn:this.program(4,s,0),inverse:this.program(6,s,0),data:s}))?e:\"\"},4:funct" - "ion(a,l,n,s){var e,t=l.helperMissing,u=\"function\",c=this.escapeExpression;" - "return'\\n
    \\n" - " {...}\\n
    \\n \\n'},6:function(a,l,n,s){v" - "ar e,t=l.helperMissing,u=\"function\",c=this.escapeExpression;return' '+c((e=null!=(e=l.valu" - "e||(null!=a?a.value:a))?e:t,typeof e===u?e.call(a,{name:\"value\",hash:{},da" - "ta:s}):e))+\"
    \\n \"},compiler:[6,\">= 2.0.0-beta.1\"],main:f" - "unction(a,l,n,s){var e;return'\\n
    \\n'+(nul" - "l!=(e=l.if.call(a,null!=a?a.data_to_expand:a,{name:\"if\",hash:{},fn:this.pr" - "ogram(1,s,0),inverse:this.program(3,s,0),data:s}))?e:\"\")+\"\\n
    \\n\\n" - "\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_json_table_tr_attr.hbs\":[function(require" - ",module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(l,a,e,t){var n,r,s=a.helperMissing,c=\"function" - "\",i=this.escapeExpression;return' \\n \\n'+(null!=(n=a.if.call(l,null!=l?l.is_primitiv" - "e:l,{name:\"if\",hash:{},fn:this.program(2,t,0),inverse:this.program(4,t,0)," - "data:t}))?n:\"\")+\" \\n \\n\"},2:function(l,a," - "e,t){var n,r=a.helperMissing,s=\"function\",c=this.escapeExpression;return' " - "
    '+c((n=null!=(n=a.prefix_str||(null!=l?l.prefix_str:l))?n:r,typeof n===s?n." - "call(l,{name:\"prefix_str\",hash:{},data:t}):n))+\" value
    \\n\"" - "},4:function(l,a,e,t){var n,r=a.helperMissing,s=\"function\",c=this.escapeEx" - "pression;return'
    '+c((n=null!=(n=a.prefix_str||(null!=l?l.prefix_str:l))?n" - ":r,typeof n===s?n.call(l,{name:\"prefix_str\",hash:{},data:t}):n))+c((n=null" - "!=(n=a.key||(null!=l?l.key:l))?n:r,typeof n===s?n.call(l,{name:\"key\",hash:" - "{},data:t}):n))+\"
    \\n\"},compiler:[6,\">= 2.0.0-beta.1\"],main:functio" - "n(l,a,e,t){var n;return'\\n \\n \\n \\n'" - "+(null!=(n=a.each.call(l,null!=l?l.attr:l,{name:\"each\",hash:{},fn:this.pro" - "gram(1,t,0),inverse:this.noop,data:t}))?n:\"\")+\" \\n\\n\\n\"},useD" - "ata:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_json_table_tr_value.hbs\":[function(requir" - "e,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(e,a,n,r){var l,s;return' \\n
    '+this.escapeExpression((s=null!=(s=a.record||(null!=e?e" - ".record:e))?s:a.helperMissing,\"function\"==typeof s?s.call(e,{name:\"record" - "\",hash:{},data:r}):s))+\"
    \\n\"+(null!=(l=a.each.call(e,null!=e?e" - ".cells:e,{name:\"each\",hash:{},fn:this.program(2,r,0),inverse:this.noop,dat" - "a:r}))?l:\"\")+\" \\n\"},2:function(e,a,n,r){return\" \"+" - "this.escapeExpression((a.print_safe||e&&e.print_safe||a.helperMissing).call(" - "e,e,{name:\"print_safe\",hash:{},data:r}))+\"\\n\"},compiler:[6,\">= 2.0.0-b" - "eta.1\"],main:function(e,a,n,r){var l;return\"\\n\"+(null!=(l=a.each.call(e," - "null!=e?e.document:e,{name:\"each\",hash:{},fn:this.program(1,r,0),inverse:t" - "his.noop,data:r}))?l:\"\")+\"\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_json_tree_array.hbs\":[function(require,mo" - "dule,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(n,a,s,l){return\" [ ]\\n\"},3:function(n,a,s" - ",l){var e;return' \\n [\\n ...\\n
      \\n'+(null!=(e=a.each.call(n,null!=n?n.values:n,{na" - "me:\"each\",hash:{},fn:this.program(4,l,0),inverse:this.noop,data:l}))?e:\"\"" - ")+'
    \\n ]'},4:function(n,a,s" - ",l){var e;return\"
  • \\n \"+this.escapeExpression((a.pri" - "nt_safe||n&&n.print_safe||a.helperMissing).call(n,null!=n?n.value:n,{name:\"" - "print_safe\",hash:{},data:l}))+(null!=(e=a.unless.call(n,null!=n?n.no_comma:" - "n,{name:\"unless\",hash:{},fn:this.program(5,l,0),inverse:this.noop,data:l})" - ")?e:\"\")+\"\\n
  • \\n\"},5:function(n,a,s,l){return\", \"},compile" - "r:[6,\">= 2.0.0-beta.1\"],main:function(n,a,s,l){var e;return\"\\n\\n\"+(nul" - "l!=(e=a.if.call(n,null!=n?n.no_value:n,{name:\"if\",hash:{},fn:this.program(" - "1,l,0),inverse:this.program(3,l,0),data:l}))?e:\"\")+\"\\n\\n\"},useData:!0}" - ");\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_json_tree_container.hbs\":[function(requir" - "e,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,a,r,n){re" - "turn'\\n
    '+this.escapeExpression((a.print_safe||" - "e&&e.print_safe||a.helperMissing).call(e,null!=e?e.tree:e,{name:\"print_safe" - "\",hash:{},data:n}))+\"
    \\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_json_tree_email.hbs\":[function(require,mo" - "dule,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(a,e,l,n){va" - "r i,s=e.helperMissing,t=\"function\",m=this.escapeExpression;return'\\n <" - "span class=\"jt_editable jt_email\" >'+m((i=null!=(i=e.email||(null!=a?a.ema" - "il:a))?i:s,typeof i===t?i.call(a,{name:\"email\",hash:{},data:n}):i))+',\\n \xc2\xbb\\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_json_tree_object.hbs\":[function(require,m" - "odule,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(n,a,s,l){return\" { } \\n\"},3:function(" - "n,a,s,l){var e;return' \\n {
    \\n ...\\n" - "
      \\n'+(null!=(e=a.each.call(n," - "null!=n?n.values:n,{name:\"each\",hash:{},fn:this.program(4,l,0),inverse:thi" - "s.noop,data:l}))?e:\"\")+'
    \\n }\\n'},4:function(n,a,s,l){var e,r=this.escapeExpression;return' " - "
  • \\n \"'+r(this.lambda(null!=n?n.key:n,n))+" - "'\": '+r((a.print_safe||n&&n.print_safe||a.helperMissing).call(n,null!=n?n.v" - "alue:n,{name:\"print_safe\",hash:{},data:l}))+(null!=(e=a.unless.call(n,null" - "!=n?n.no_comma:n,{name:\"unless\",hash:{},fn:this.program(5,l,0),inverse:thi" - "s.noop,data:l}))?e:\"\")+\"\\n
  • \\n\"},5:function(n,a,s,l){re" - "turn\", \"},compiler:[6,\">= 2.0.0-beta.1\"],main:function(n,a,s,l){var e;re" - "turn\"\\n\"+(null!=(e=a.if.call(n,null!=n?n.no_value:n,{name:\"if\",hash:{}," - "fn:this.program(1,l,0),inverse:this.program(3,l,0),data:l}))?e:\"\")+\"\\n\\" - "n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_json_tree_span.hbs\":[function(require,mod" - "ule,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(a,e,l,n){va" - "r s,t=e.helperMissing,r=\"function\",u=this.escapeExpression;return'\\n <" - "span class=\"jt_editable '+u((s=null!=(s=e.classname||(null!=a?a.classname:a" - "))?s:t,typeof s===r?s.call(a,{name:\"classname\",hash:{},data:n}):s))+'\" >'" - "+u((s=null!=(s=e.value||(null!=a?a.value:a))?s:t,typeof s===r?s.call(a,{name" - ":\"value\",hash:{},data:n}):s))+\"\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_json_tree_span_with_quotes.hbs\":[function" - "(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(a,e,l,n){va" - "r s,t=e.helperMissing,r=\"function\",u=this.escapeExpression;return'\\n <" - "span class=\"jt_editable '+u((s=null!=(s=e.classname||(null!=a?a.classname:a" - "))?s:t,typeof s===r?s.call(a,{name:\"classname\",hash:{},data:n}):s))+'\">\"" - "'+u((s=null!=(s=e.value||(null!=a?a.value:a))?s:t,typeof s===r?s.call(a,{nam" - "e:\"value\",hash:{},data:n}):s))+'\"\\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_json_tree_url.hbs\":[function(require,modu" - "le,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(l,a,e,n){va" - "r r,s=a.helperMissing,t=\"function\",u=this.escapeExpression;return'\\n <" - "span class=\"jt_editable jt_url\" >'+u((r=null!=(r=a.url||(null!=l?l.url:l))" - "?r:s,typeof r===t?r.call(l,{name:\"url\",hash:{},data:n}):r))+',\\n " - " \xc2\xbb\\n" - "\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_profile.hbs\":[function(require,module,exp" - "orts){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(a,e,n,s){var l,r,t=e.helperMissing,i=\"function" - "\",o=this.escapeExpression;return'
    \\n
    \\n
    \\n" - "
    \\n

    '+o((r=null!=(r=e.total_duration||(null!=a?a.total_d" - "uration:a))?r:t,typeof r===i?r.call(a,{name:\"total_duration\",hash:{},data:" - "s}):r))+' round-trip time

    \\n
    \\n
    \\n

    " - "'+o((r=null!=(r=e.server_duration||(null!=a?a.server_duration:a))?r:t,typeof" - " r===i?r.call(a,{name:\"server_duration\",hash:{},data:s}):r))+' serv" - "er time

    \\n
    \\n
    \\n

    '+o((r=null!=(r=e.num_shard_access" - "es||(null!=a?a.num_shard_accesses:a))?r:t,typeof r===i?r.call(a,{name:\"num_" - "shard_accesses\",hash:{},data:s}):r))+' shard accesses

    \\n " - "
    \\n
    \\n
    \\n
    \\n \\n
    '+(null!=(r=null!=(r=e.tree||(null!=a?a.tree:a))?r:t,l=ty" - "peof r===i?r.call(a,{name:\"tree\",hash:{},data:s}):r)?l:\"\")+\"
    \\n\"" - "},3:function(a,e,n,s){return'
    \\n " - "

    Enable the query profile" - "r (developer preview) and re-run the query.

    \\n
    \\n'},compiler:[6," - "\">= 2.0.0-beta.1\"],main:function(a,e,n,s){var l;return'
    \\n'+(null!=(l=e.with.call(a,null!=a?a.profile:a,{name" - ":\"with\",hash:{},fn:this.program(1,s,0),inverse:this.noop,data:s}))?l:\"\")" - "+(null!=(l=e.unless.call(a,null!=a?a.profile:a,{name:\"unless\",hash:{},fn:t" - "his.program(3,s,0),inverse:this.noop,data:s}))?l:\"\")+\"
    \\n\"},useDat" - "a:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_raw.hbs\":[function(require,module,exports" - "){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,a,r,t){re" - "turn'
    \\n \\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_table.hbs\":[function(require,module,expor" - "ts){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,n,a,l){va" - "r t,r;return'
    \\n '+(null!=(r=null!=(" - "r=n.content||(null!=e?e.content:e))?r:n.helperMissing,t=\"function\"==typeof" - " r?r.call(e,{name:\"content\",hash:{},data:l}):r)?t:\"\")+\"\\n
    \\n\"}," - "useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_result_tree.hbs\":[function(require,module,export" - "s){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,n,r,a){va" - "r l,t;return'
    \\n
    \\n '+(null!=(t=null!=(t=n.tree||(null!=e?e.tree:e))?t:n.h" - "elperMissing,l=\"function\"==typeof t?t.call(e,{name:\"tree\",hash:{},data:a" - "}):t)?l:\"\")+\"\\n
    \\n
    \\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_suggestion_name_li.hbs\":[function(require,module" - ",exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(a,e,n,l){va" - "r s,t=e.helperMissing,i=\"function\",u=this.escapeExpression;return'\\n <" - "li class=\"suggestion_name_li\" data-has_argument=\"'+u((s=null!=(s=e.has_ar" - "gument||(null!=a?a.has_argument:a))?s:t,typeof s===i?s.call(a,{name:\"has_ar" - "gument\",hash:{},data:l}):s))+'\" data-id='+u((s=null!=(s=e.id||(null!=a?a.i" - "d:a))?s:t,typeof s===i?s.call(a,{name:\"id\",hash:{},data:l}):s))+\">\"+u((s" - "=null!=(s=e.suggestion||(null!=a?a.suggestion:a))?s:t,typeof s===i?s.call(a," - "{name:\"suggestion\",hash:{},data:l}):s))+\"\\n\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/dataexplorer_view.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(n,i,t,s){re" - "turn'\\n
    \\n
    \\n " - "\\n \\n <" - "button class=\"btn toggle_options_link\" title=\"Options\">
    \\n \\n
    \\n

    Data Explorer

    \\n
    \\n
    \\n
    \\n
    \\n
    \\" - "n
    \\n
    \\" - "n
    \\n
    \\n
    \\n
    \\n
    \\n
    \\n
    \\n\\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/declare_server_dead-modal.hbs\":[function(require,module,expor" - "ts){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,n,r,t){va" - "r i;return'\\n
    \\n\\n" - "\\n

    How can service availability be r" - "estored?

    \\n\\n

    \\n To restore service availability" - ", this server either needs to rejoin\\n the cluster or be permanently" - " removed.\\n

    \\n\\n

    What does removing a server do?

    \\n\\n

    The server will be dropped from the cluster, service a" - "vailability\\n will resume, and its data will be destroyed to maintai" - "n cluster\\n integrity.\\n

    \\n\\n
    \\n\\n
    \\n

    \\n Are you sure you want to permanently" - " remove '+this.escapeExpression((i=null!=(i=n.server_name||(null!=e?" - "e.server_name:e))?i:n.helperMissing,\"function\"==typeof i?i.call(e,{name:\"" - "server_name\",hash:{},data:t}):i))+'?\\n

    \\n\\n " - "
    \\n Warn" - "ing: Removing this server will destroy its\\n data, proc" - "eed with caution.\\n
    \\n\\n

    \\n

    \\n Please type \"remove\" to confirm" - "\\n
    \\n

    \\n \\n
    \\n\\n\\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/delete-database-modal.hbs\":[function(require,module,exports){" - "\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,a,t,n){re" - "turn'\\n
    \\n This name does" - "n\\'t match the name of the database you\\'re trying to delete. \\n \\n
    Deleting the databa" - "se will delete all the tables in it.
    This action cannot" - " be undone.
    \\n
    " - "\\n

    Please type in the name of the database to confirm.

    \\n \\n \\n
    \\n\\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/element_view-not_found.hbs\":[function(require,module,exports)" - "{\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(l,a,e,n){va" - "r t,i=a.helperMissing,r=this.escapeExpression,u=\"function\";return'\\n <" - "div class=\"section\">\\n

    Error

    \\n '+r((a.capitalize||l&&l.capitalize||i).call(l,null!=l?l.type:l,{name:\"capi" - "talize\",hash:{},data:n}))+\" \"+r((t=null!=(t=a.id||(null!=l?l.id:l))?t:i,t" - "ypeof t===u?t.call(l,{name:\"id\",hash:{},data:n}):t))+' could not be found " - "or is unavailable.

    \\n

    You can try to refresh or " - "see a list of all '+r((a.pluralize_noun||l&&l.pluralize_noun||i).call(l,null!=l?l." - "type:l,2,{name:\"pluralize_noun\",hash:{},data:n}))+\".

    \\n
    " - "\\n\\n\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/empty_list.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,n,a,l){va" - "r t,i=n.helperMissing,r=\"function\",s=this.escapeExpression;return'\\n <" - "div class=\"no_element\">\\n
    There " - "are no '+s((t=null!=(t=n.element||(null!=e?e.element:e))?t:i,typeof t===r?t." - "call(e,{name:\"element\",hash:{},data:l}):t))+\"s in this \"+s((t=null!=(t=n" - ".container||(null!=e?e.container:e))?t:i,typeof t===r?t.call(e,{name:\"conta" - "iner\",hash:{},data:l}):t))+\".
    \\n
    \\n\\n\\n\\n\"},useData:!0" - "});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/error-query.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(r,e,n,a){var l;return'
    '+this.escapeExpression((l=null!=(l=e.error||(null!=r?r.error:r))?l:e"
    -      ".helperMissing,\"function\"==typeof l?l.call(r,{name:\"error\",hash:{},data:"
    -      "a}):l))+\"
    \"},compiler:[6,\">= 2.0.0-beta.1\"],main:function(r,e,n,a){" - "var l,s;return'\\n
    \\n

    " - "Error

    \\n

    The request to retrieve data failed. You can try to " - "refresh.

    \\n

    Error:

    \\n '+(null!=(l=e.if" - ".call(r,null!=r?r.error:r,{name:\"if\",hash:{},fn:this.program(1,a,0),invers" - "e:this.noop,data:a}))?l:\"\")+\"\\n
    \\n\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/error_input.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(e,n,a,r){var t;return\" Sorry, something wen" - "t wrong with the request. Please try again.\\n \"+(null!=(t=n.if.call(e,n" - "ull!=e?e.error:e,{name:\"if\",hash:{},fn:this.program(2,r,0),inverse:this.no" - "op,data:r}))?t:\"\")+\"\\n\"},2:function(e,n,a,r){var t;return'
    Error: <" - "pre class=\"server_error\">'+this.escapeExpression(this.lambda(null!=(t=null" - "!=e?e.error:e)?t.message:t,e))+\"\"},4:function(e,n,a,r){return\" " - "

    You need to create a database before creating a table.

    \\n\"},6:func" - "tion(e,n,a,r){return\"

    Please enter an integer for the port.

    \\" - "n\"},8:function(e,n,a,r){return\"

    Please do not use an empty name " - "for a database.

    \\n\"},10:function(e,n,a,r){return\"

    The chosen" - " database's name is already being used.
    Please choose another one.

    \\" - "n\"},12:function(e,n,a,r){return\"

    Please do not use an empty name" - " for a table.

    \\n\"},14:function(e,n,a,r){return\"

    You have to " - "select a database.

    \\n\"},16:function(e,n,a,r){return\"

    The cho" - "sen table is already being used. Please choose another one.

    \\n\"},18:fun" - "ction(e,n,a,r){return\"

    Please do not use an empty name for a data" - "center.

    \\n\"},20:function(e,n,a,r){return\"

    The chosen datacen" - "ter's name is already being used. please choose another one.

    \\n\"},22:fu" - "nction(e,n,a,r){return\"

    Please do not use an empty name for a ser" - "ver.

    \\n\"},24:function(e,n,a,r){return\"

    The chosen server's n" - "ame is already being used. please choose another one.

    \\n\"},26:function(" - "e,n,a,r){var t;return\"

    You can only use alphanumeric characters, hyph" - "ens and underscores for a \"+this.escapeExpression((t=null!=(t=n.type||(null" - "!=e?e.type:e))?t:n.helperMissing,\"function\"==typeof t?t.call(e,{name:\"typ" - "e\",hash:{},data:r}):t))+\"'s name.

    \\n\"},28:function(e,n,a,r){var t;ret" - "urn\"

    The secondary index could not be created. Message returned by th" - "e driver:
    \\n \"+this.escapeExpression((t=null!=(t=n.message||(null!=" - "e?e.message:e))?t:n.helperMissing,\"function\"==typeof t?t.call(e,{name:\"me" - "ssage\",hash:{},data:r}):t))+\"

    \\n\"},compiler:[6,\">= 2.0.0-beta.1\"],m" - "ain:function(e,n,a,r){var t;return'\\n
    \\n ×\\n'+(null!=(t=n.if.call(e,null!=e?e.ajax_fail:e,{name:\"if\",ha" - "sh:{},fn:this.program(1,r,0),inverse:this.noop,data:r}))?t:\"\")+(null!=(t=n" - ".if.call(e,null!=e?e.create_db_first:e,{name:\"if\",hash:{},fn:this.program(" - "4,r,0),inverse:this.noop,data:r}))?t:\"\")+(null!=(t=n.if.call(e,null!=e?e.p" - "ort_isnt_integer:e,{name:\"if\",hash:{},fn:this.program(6,r,0),inverse:this." - "noop,data:r}))?t:\"\")+(null!=(t=n.if.call(e,null!=e?e.database_is_empty:e,{" - "name:\"if\",hash:{},fn:this.program(8,r,0),inverse:this.noop,data:r}))?t:\"\"" - ")+(null!=(t=n.if.call(e,null!=e?e.database_exists:e,{name:\"if\",hash:{},fn:" - "this.program(10,r,0),inverse:this.noop,data:r}))?t:\"\")+(null!=(t=n.if.call" - "(e,null!=e?e.table_name_empty:e,{name:\"if\",hash:{},fn:this.program(12,r,0)" - ",inverse:this.noop,data:r}))?t:\"\")+(null!=(t=n.if.call(e,null!=e?e.no_data" - "base:e,{name:\"if\",hash:{},fn:this.program(14,r,0),inverse:this.noop,data:r" - "}))?t:\"\")+(null!=(t=n.if.call(e,null!=e?e.table_exists:e,{name:\"if\",hash" - ":{},fn:this.program(16,r,0),inverse:this.noop,data:r}))?t:\"\")+(null!=(t=n." - "if.call(e,null!=e?e.datacenter_is_empty:e,{name:\"if\",hash:{},fn:this.progr" - "am(18,r,0),inverse:this.noop,data:r}))?t:\"\")+(null!=(t=n.if.call(e,null!=e" - "?e.datacenter_exists:e,{name:\"if\",hash:{},fn:this.program(20,r,0),inverse:" - "this.noop,data:r}))?t:\"\")+(null!=(t=n.if.call(e,null!=e?e.server_is_empty:" - "e,{name:\"if\",hash:{},fn:this.program(22,r,0),inverse:this.noop,data:r}))?t" - ":\"\")+(null!=(t=n.if.call(e,null!=e?e.server_exists:e,{name:\"if\",hash:{}," - "fn:this.program(24,r,0),inverse:this.noop,data:r}))?t:\"\")+(null!=(t=n.if.c" - "all(e,null!=e?e.special_char_detected:e,{name:\"if\",hash:{},fn:this.program" - "(26,r,0),inverse:this.noop,data:r}))?t:\"\")+(null!=(t=n.if.call(e,null!=e?e" - ".fail_create_secondary_index:e,{name:\"if\",hash:{},fn:this.program(28,r,0)," - "inverse:this.noop,data:r}))?t:\"\")+\"
    \\n\\n\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/fail_solve_issue.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,r,n,t){re" - "turn\"\\n Sorry, something went wrong. The issue might not be up to date," - " please refresh.\\n\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/has_update.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,a,n,l){va" - "r s,r=a.helperMissing,t=\"function\",i=this.escapeExpression;return'\\n <" - "a class=\"close\" href=\"#\">×\\n

    Update found: A new version of RethinkDB is available (version '+i((s=null!=(s=a.last" - "_version||(null!=e?e.last_version:e))?s:r,typeof s===t?s.call(e,{name:\"last" - "_version\",hash:{},data:l}):s))+'): see the release announcement for details.

    \\n\\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/is_disconnected.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,n,a,s){re" - "turn'\\n
    \\n
    \\n

    You have been disconnected from the server

    \\n " - "
    \\n
    \\n

    The connection to the" - " server has been lost. The server may be under heavy load.\\n

    \\n Try" - "ing to reconnect\\n " - "\\n

    \\n
    \\n
    \\n\\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/is_disconnected_message.hbs\":[function(require,module,exports" - "){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,n,r,a){re" - "turn\"\\n Failed to reconnect. Trying again \\n\\n\\n\\n\"},useData:!0}" - ");\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/loading_databases.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,n,a,i){re" - "turn'\\n
    \\n
    \\n
    Loading tables...<" - "/div>\\n
    \\n
    \\n\\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/log-entry.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,a,s,l){va" - "r n,t=a.helperMissing,i=\"function\",r=this.escapeExpression;return'
    \\n
    \\n '+r((n=null!=(n=a.message|" - "|(null!=e?e.message:e))?n:t,typeof n===i?n.call(e,{name:\"message\",hash:{}," - "data:l}):n))+'\\n

    Posted by\\n '+r((n=null!=(n=a.se" - "rver||(null!=e?e.server:e))?n:t,typeof n===i?n.call(e,{name:\"server\",hash:" - "{},data:l}):n))+'\\n

    \\n
    \\n
    \\n " - "\\n
    \\" - "n
    \\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/logs_container.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,n,s,o){re" - "turn'

    Log entries

    \\n
    \\n
    \\n
    \\n

    No log entries to s" - "how

    \\n

    No more log entries to show

    \\" - "n

    \\n Older log entries »\\n

    \\n
    \\n<" - "/div>\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/navbar_view.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(a,e,i,l){re" - "turn'\\n \\n
    \\n" - "
    \\n
    \\n " - " \\n
    \\n
    \\n\\n'},useData:!" - "0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/no_databases.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,a,n,t){re" - "turn'\\n
    \\n
    \\n You need to create a database before creating a t" - "able.\\n
    \\n
    \\n " - "
    There are no databases in this clust" - "er.
    \\n
    \\n
    \\n\\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/ops_plot.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(n,e,a,r){return'

    Cluster per" - "formance

    '},compiler:[6,\">= 2.0.0-beta.1\"],main:function(n,e,a,r){var " - "l;return'\\n
    \\n '+(null!=(l=e.i" - "f.call(n,null!=n?n.cluster:n,{name:\"if\",hash:{},fn:this.program(1,r,0),inv" - "erse:this.noop,data:r}))?l:\"\")+'\\n
    \\n " - "
    \\n
    \\n\\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/ops_plot_legend.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,n,a,l){va" - "r i,s=n.helperMissing,r=\"function\",t=this.escapeExpression;return'\\n <" - "div class=\"element reads\">\\n
    \\n

    Reads/sec: '+t((i=null!=(i=n.read_coun" - "t||(null!=e?e.read_count:e))?i:s,typeof i===r?i.call(e,{name:\"read_count\"," - "hash:{},data:l}):i))+'

    \\n
    \\n
    " - "\\n
    \\n " - "

    Writes/sec: '+t((i=null!=(i=n.write_count||(null!=e?e.write_count:" - "e))?i:s,typeof i===r?i.call(e,{name:\"write_count\",hash:{},data:l}):i))+\" " - "

    \\n
    \\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/options_view.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(s,n,e,a){return\"checked\"},compiler:[6,\">= 2." - "0.0-beta.1\"],main:function(s,n,e,a){var i,l;return'\\n
    \\n
    \\n " - "
    \\n
    \\n " - "
    RethinkDB '+this.escapeExpression((l=null!=(l=n.version||(null!=s?s.v" - "ersion:s))?l:n.helperMissing,\"function\"==typeof l?l.call(s,{name:\"version" - "\",hash:{},data:a}):l))+'
    \\n Web UI Settings\\n
    \\n
    \\n \\n
    \\n " - "
    \\n \\n " - "
    \\n " - "
    \\n
    Automatically look fo" - "r updates to RethinkDB
    \\n
    \\n
    \\n\\n'},useData:!" - "0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/progressbar.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(n,e,a,l){var t;return'
    \\" - "n
    \\n'+(null!=(t=e.if.call(n,null!=n?n.starting:n,{na" - "me:\"if\",hash:{},fn:this.program(2,l,0),inverse:this.noop,data:l}))?t:\"\")" - "+(null!=(t=e.if.call(n,null!=n?n.processing:n,{name:\"if\",hash:{},fn:this.p" - "rogram(4,l,0),inverse:this.noop,data:l}))?t:\"\")+(null!=(t=e.if.call(n,null" - "!=n?n.finished:n,{name:\"if\",hash:{},fn:this.program(6,l,0),inverse:this.no" - "op,data:l}))?t:\"\")+\"
    \\n\"+(null!=(t=e.if.call(n,null!=n?n.startin" - "g:n,{name:\"if\",hash:{},fn:this.program(8,l,0),inverse:this.program(10,l,0)" - ",data:l}))?t:\"\")+'
    <" - "/div>\\n
    \\n'},2:function(n,e,a,l){return\"

    Started

    \\n\"},4:f" - "unction(n,e,a,l){return'

    Started

    \\n

    Proc" - "essing

    \\n'},6:function(n,e,a,l){return'

    Starte" - "d

    \\n

    Processed

    \\n'},8:function(n,e,a,l){va" - "r t;return\"

    0/\"+this.escapeExpression((t=null!=(t=e.new_value||(null!=" - "n?n.new_value:n))?t:e.helperMissing,\"function\"==typeof t?t.call(n,{name:\"" - "new_value\",hash:{},data:l}):t))+\" completed

    \\n\"},10:function(n,e,a,l)" - "{var t,r=e.helperMissing,s=\"function\",i=this.escapeExpression;return\" \"+i((t=null!=(t=e.current_value||(null!=n?n.current_value:n))?t:r,typeof t" - "===s?t.call(n,{name:\"current_value\",hash:{},data:l}):t))+\"/\"+i((t=null!=" - "(t=e.max_value||(null!=n?n.max_value:n))?t:r,typeof t===s?t.call(n,{name:\"m" - "ax_value\",hash:{},data:l}):t))+\" completed

    \\n\"},12:function(n,e,a,l){" - "return\"0\"},14:function(n,e,a,l){var t;return this.escapeExpression((t=null" - "!=(t=e.percent_complete||(null!=n?n.percent_complete:n))?t:e.helperMissing,\"" - "function\"==typeof t?t.call(n,{name:\"percent_complete\",hash:{},data:l}):t)" - ")},16:function(n,e,a,l){var t,r=e.helperMissing,s=\"function\",i=this.escape" - "Expression;return'

    '+i((t=null!=(t=e.current_value||(n" - "ull!=n?n.current_value:n))?t:r,typeof t===s?t.call(n,{name:\"current_value\"" - ",hash:{},data:l}):t))+\"/\"+i((t=null!=(t=e.max_value||(null!=n?n.max_value:" - "n))?t:r,typeof t===s?t.call(n,{name:\"max_value\",hash:{},data:l}):t))+\" connected

    \\n\"},compiler:[6,\">= 2.0.0-beta.1\"],main:function(n,e," - "a,l){var t;return null!=(t=e.if.call(n,null!=n?n.operation_active:n,{name:\"" - "if\",hash:{},fn:this.program(1,l,0),inverse:this.program(16,l,0),data:l}))?t" - ":\"\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/reconfigure-diff.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(a,n,l,e){return'
  • \\n " - "Server changes not shown: Invalid configuration.\\n
  • \\n'},3:function(a" - ",n,l,e){var s;return'
  • \\n'+(null!=(s=n.each.ca" - "ll(a,null!=a?a.shards:a,{name:\"each\",hash:{},fn:this.program(4,e,0),invers" - "e:this.noop,data:e}))?s:\"\")},4:function(a,n,l,e){var s,i,r=n.helperMissing" - ",t=this.escapeExpression;return'
  • \\n
    \\n

    \\n Shard '+t((n.inc||a" - "&&a.inc||r).call(a,e&&e.index,{name:\"inc\",hash:{},data:e}))+\"\\n " - "

    \\n
    \\n
  • \\n\"+(null!=(s=n.each.call(a,null!=a?a.repli" - "cas:a,{name:\"each\",hash:{},fn:this.program(5,e,0),inverse:this.noop,data:e" - "}))?s:\"\")},5:function(a,n,l,e){var s,i,r=n.helperMissing,t=\"function\",c=" - "this.escapeExpression;return(null!=(s=n.unless.call(a,null!=a?a.role_change:" - "a,{name:\"unless\",hash:{},fn:this.program(6,e,0),inverse:this.program(8,e,0" - "),data:e}))?s:\"\")+'
    \\n
    \\n

    '+c((i=null!=(" - "i=n.name||(null!=a?a.name:a))?i:r,typeof i===t?i.call(a,{name:\"name\",hash:" - "{},data:e}):i))+\"

    \\n\"+(null!=(s=n.unless.call(a,null!=a?a.role_cha" - "nge:a,{name:\"unless\",hash:{},fn:this.program(10,e,0),inverse:this.program(" - "15,e,0),data:e}))?s:\"\")+\"
    \\n \\n\"},6:function(a,n,l,e){v" - "ar s;return'
  • \\n'},8:function(a,n,l,e){ret" - "urn'
  • \\n'},10:function(a,n,l,e){var " - "s;return null!=(s=n.if.call(a,null!=a?a.role:a,{name:\"if\",hash:{},fn:this." - "program(11,e,0),inverse:this.program(13,e,0),data:e}))?s:\"\"},11:function(a" - ",n,l,e){var s,i=n.helperMissing,r=this.escapeExpression;return'

    '+r((n.capitalize|" - "|a&&a.capitalize||i).call(a,null!=a?a.role:a,{name:\"capitalize\",hash:{},da" - "ta:e}))+\" replica

    \\n\"},13:function(a,n,l,e){var s,i=n.helperMissing,r=" - "this.escapeExpression;return'

    '+r((n.capitalize||a&&a.capitalize||i).c" - "all(a,null!=a?a.old_role:a,{name:\"capitalize\",hash:{},data:e}))+\" replica" - "

    \\n\"},15:function(a,n,l,e){var s,i=n.helperMissing,r=this.escapeExpress" - "ion;return'

    \\n \\n \\n '+r((n.capitalize||a&&a.capitalize||i).call(a,null!=a?a.old_" - "role:a,{name:\"capitalize\",hash:{},data:e}))+'\\n →" - "\\n '+r((n.capitalize||a&&a.capitalize||i" - ").call(a,null!=a?a.role:a,{name:\"capitalize\",hash:{},data:e}))+\" replica<" - "/span>\\n \\n

    \\n\"},compiler:[6,\">= 2.0.0-beta.1\"" - "],main:function(a,n,l,e){var s;return'
      \\n'+(n" - "ull!=(s=n.if.call(a,null!=a?a.errors:a,{name:\"if\",hash:{},fn:this.program(" - "1,e,0),inverse:this.program(3,e,0),data:e}))?s:\"\")+\"
    \\n\"},useData:!" - "0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/reconfigure-modal.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,a,r,n){va" - "r s,l=a.helperMissing,t=\"function\",o=this.escapeExpression;return'
    \\n
    \\n

    \\n Applying changes" - " may cause the table to become temporarily unavailable.\\n

    \\n \\n
    \\n
    \\n \\n shards\\n" - "
    \\n max: '+o((s=null!=(s=a.max_num_s" - "hards||(null!=e?e.max_num_shards:e))?s:l,typeof s===t?s.call(e,{name:\"max_n" - "um_shards\",hash:{},data:n}):s))+'\\n
    \\n
    \\n
    \\" - "n \\n replicas per shard\\n " - "
    \\n max: '+o((s=null!=(s=a.num_default_" - "servers||(null!=e?e.num_default_servers:e))?s:l,typeof s===t?s.call(e,{name:" - "\"num_default_servers\",hash:{},data:n}):s))+'\\n
    \\n
    \\" - "n
    \\n
    \\n
    \\n
    \\n

    \\n Each table mus" - "t have at least one shard.\\n

    \\n

    \\n" - " The number of shards is not set.\\n

    \\n

    \\n Each table must have at least one replica per shard.\\" - "n

    \\n

    \\n The number of replic" - "as per shard is not set.\\n

    \\n

    \\n A table may not have more shards than there are servers. Currently" - "\\n you have '+o((s=null!=(s=a.num_default_servers||(null!=e?e.num_defa" - "ult_servers:e))?s:l,typeof s===t?s.call(e,{name:\"num_default_servers\",hash" - ":{},data:n}):s))+' servers available for this table.\\n

    \\n

    \\n A table may not have more t" - "han '+o((s=null!=(s=a.max_shards||(null!=e?e.max_shards:e))?s:l,typeof s===t" - "?s.call(e,{name:\"max_shards\",hash:{},data:n}):s))+' shards.\\n

    \\n " - "

    \\n While you have enou" - "gh servers for this many shards, not enough of\\n them have the \"defau" - "lt\" tag. You can manage server tags through\\n the table \"rethinkdb." - "server_config\".\\n

    \\n

    \\n While you have enough servers for this many replicas, not enou" - "gh\\n of them have the \"default\" tag. You can manage server tags thro" - "ugh\\n the table \"rethinkdb.server_config\".\\n

    \\n

    \\n A table may not have more replicas per" - " shard than there are\\n servers. Currently you have '+o((s=null!=(s=a." - "num_default_servers||(null!=e?e.num_default_servers:e))?s:l,typeof s===t?s.c" - "all(e,{name:\"num_default_servers\",hash:{},data:n}):s))+' servers\\n a" - "vailable for this table.\\n

    \\n

    \\" - "n The server returned the following error:\\n \\n '+o((s=null!=(s=a.server_error||(null!=e?e.server_error:e)" - ")?s:l,typeof s===t?s.call(e,{name:\"server_error\",hash:{},data:n}):s))+'\\n" - " \\n

    \\n
    \\n " - "Where\\'s my data going?\\n
    \\n
    \\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/reconfigure.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(a,n,s,l){va" - "r e,r=n.helperMissing,i=\"function\",p=this.escapeExpression;return'

    Sharding and replication

    \\n
    \\n
    \\" - "n '+p((e=null!=(e=n.num_shards||(null!=a?a.n" - "um_shards:a))?e:r,typeof e===i?e.call(a,{name:\"num_shards\",hash:{},data:l}" - "):e))+'\\n '+p((n.pluralize_noun||a&&a.plu" - "ralize_noun||r).call(a,\"shard\",null!=a?a.num_shards:a,{name:\"pluralize_no" - "un\",hash:{},data:l}))+'\\n
    \\n
    \\n '+p((e=null!=(e=n.num_replicas_per_shard||(null!=a?a.num_repli" - "cas_per_shard:a))?e:r,typeof e===i?e.call(a,{name:\"num_replicas_per_shard\"" - ",hash:{},data:l}):e))+'\\n '+p((n.pluraliz" - "e_noun||a&&a.pluralize_noun||r).call(a,\"replica\",null!=a?a.num_replicas_pe" - "r_shard:a,{name:\"pluralize_noun\",hash:{},data:l}))+\" per shard\\n " - "
    \\n \\n\\n
    \\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/remove_table-modal.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(e,a,l,t){return\"

    Are you sure you w" - "ant to delete this table:

    \\n\"},3:function(e,a,l,t){return\" Are you sure you want to delete these tables:\\n\"},5:function(e,a,l,t" - "){var n,r=a.helperMissing,s=\"function\",i=this.escapeExpression;return' " - "
  • '+i((n=null!=(n=a.database||(null!=e?e.data" - "base:e))?n:r,typeof n===s?n.call(e,{name:\"database\",hash:{},data:t}):n))+\"" - ".\"+i((n=null!=(n=a.table||(null!=e?e.table:e))?n:r,typeof n===s?n.call(e,{n" - "ame:\"table\",hash:{},data:t}):n))+\"
  • \\n\"},compiler:[6,\">= 2.0.0-" - "beta.1\"],main:function(e,a,l,t){var n;return'\\n
    Deleting a table will delete all its data. This ac" - "tion cannot be reversed.
    \\n
    \\n'+(null!=(n=a.if.call(e,null!=e?e.single_delete:e,{name:\"if" - "\",hash:{},fn:this.program(1,t,0),inverse:this.program(3,t,0),data:t}))?n:\"" - "\")+'
      \\n'+(null!=(n=a.each.call(e,null!=e?e.tab" - "les:e,{name:\"each\",hash:{},fn:this.program(5,t,0),inverse:this.noop,data:t" - "}))?n:\"\")+\"
    \\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/rename_item-modal.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(e,a,n,l){var r;return\" \"+this.esca" - "peExpression((r=null!=(r=a.id||(null!=e?e.id:e))?r:a.helperMissing,\"functio" - "n\"==typeof r?r.call(e,{name:\"id\",hash:{},data:l}):r))+\"\\n\"},3:function" - "(e,a,n,l){var r,t=a.helperMissing,i=this.escapeExpression,s=\"function\";ret" - "urn' '+i((r=null!=(r=a" - ".id||(null!=e?e.id:e))?r:t,typeof r===s?r.call(e,{name:\"id\",hash:{},data:l" - "}):r))+\"\\n\"},compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,a,n,l){" - "var r,t,i=a.helperMissing,s=\"function\",o=this.escapeExpression;return'
    \\n
    \\n ", - 65483) + std::string( - "

    \\n '+o((t=null!=(t=a.type||(null!=e?e.type:e))?t" - ":i,typeof t===s?t.call(e,{name:\"type\",hash:{},data:l}):t))+\" id:" - "\\n\"+(null!=(r=a.if.call(e,null!=e?e.is_database:e,{name:\"if\",hash:{},fn:" - "this.program(1,l,0),inverse:this.program(3,l,0),data:l}))?r:\"\")+' <" - "/p>\\n \\n " - "

    \\n \\n
    \\n
    \\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/renamed_item-alert.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(a,e,l,n){va" - "r s,t=e.helperMissing,p=\"function\",r=this.escapeExpression;return'
    \\n ×\\n

    You successfully renamed '+r((s=null!=(s=e.t" - "ype||(null!=a?a.type:a))?s:t,typeof s===p?s.call(a,{name:\"type\",hash:{},da" - "ta:n}):s))+' '+r((s=null!=(s=e.old_name||(null!=a?a.old" - "_name:a))?s:t,typeof s===p?s.call(a,{name:\"old_name\",hash:{},data:n}):s))+" - "' to '+r((s=null!=(s=e.new_name||(null!=a?a.new_" - "name:a))?s:t,typeof s===p?s.call(a,{name:\"new_name\",hash:{},data:n}):s))+\"" - ".

    \\n
    \\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/replica_status.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(n,a,e,l){var t;return'
    \\n
    \\n'+(null!=(t=a.if.call(n,null!=n?n.starti" - "ng:n,{name:\"if\",hash:{},fn:this.program(2,l,0),inverse:this.noop,data:l}))" - "?t:\"\")+(null!=(t=a.if.call(n,null!=n?n.processing:n,{name:\"if\",hash:{},f" - "n:this.program(4,l,0),inverse:this.noop,data:l}))?t:\"\")+(null!=(t=a.if.cal" - "l(n,null!=n?n.finished:n,{name:\"if\",hash:{},fn:this.program(6,l,0),inverse" - ":this.noop,data:l}))?t:\"\")+\"
    \\n\"+(null!=(t=a.if.call(n,null!=n?n" - ".starting:n,{name:\"if\",hash:{},fn:this.program(8,l,0),inverse:this.program" - "(10,l,0),data:l}))?t:\"\")+'
    \\n
    \\n'},2:function(n,a,e,l){return\"

    Started

    \\" - "n\"},4:function(n,a,e,l){return\"

    Started

    \\n

    Table reconfigu" - "ring

    \\n\"},6:function(n,a,e,l){return'

    Started

    \\n

    Table reconfigured

    \\n'},8:function(n,a,e,l){var t;return\"" - "

    0/\"+this.escapeExpression((t=null!=(t=a.new_value||(null!=n?n.new_valu" - "e:n))?t:a.helperMissing,\"function\"==typeof t?t.call(n,{name:\"new_value\"," - "hash:{},data:l}):t))+\" replicas completed

    \\n\"},10:function(n,a,e,l){va" - "r t,r,i=a.helperMissing,s=\"function\",u=this.escapeExpression;return\"

    " - "\\n \"+u((r=null!=(r=a.current_value||(null!=n?n.current_value:n))?r:i,ty" - "peof r===s?r.call(n,{name:\"current_value\",hash:{},data:l}):r))+\"/\"+u((r=" - "null!=(r=a.max_value||(null!=n?n.max_value:n))?r:i,typeof r===s?r.call(n,{na" - "me:\"max_value\",hash:{},data:l}):r))+\" \"+u((a.pluralize_noun||n&&n.plural" - "ize_noun||i).call(n,\"replica\",null!=n?n.num_replicas:n,{name:\"pluralize_n" - "oun\",hash:{},data:l}))+(null!=(t=a.if.call(n,null!=n?n.total_blocks:n,{name" - ":\"if\",hash:{},fn:this.program(11,l,0),inverse:this.program(13,l,0),data:l}" - "))?t:\"\")+\"

    \\n\"},11:function(n,a,e,l){var t,r=a.helperMissing,i=\"f" - "unction\",s=this.escapeExpression;return\",\\n \"+s((t=null!=(t=a.replica" - "ted_blocks||(null!=n?n.replicated_blocks:n))?t:r,typeof t===i?t.call(n,{name" - ":\"replicated_blocks\",hash:{},data:l}):t))+\"/\"+s((t=null!=(t=a.total_bloc" - "ks||(null!=n?n.total_blocks:n))?t:r,typeof t===i?t.call(n,{name:\"total_bloc" - "ks\",hash:{},data:l}):t))+\" blocks copied\\n\"},13:function(n,a,e,l){return" - "\" up to date\\n\"},15:function(n,a,e,l){return\"0\"},17:function(n,a,e,l" - "){var t;return this.escapeExpression((t=null!=(t=a.percent_complete||(null!=" - "n?n.percent_complete:n))?t:a.helperMissing,\"function\"==typeof t?t.call(n,{" - "name:\"percent_complete\",hash:{},data:l}):t))},compiler:[6,\">= 2.0.0-beta." - "1\"],main:function(n,a,e,l){var t;return null!=(t=a.if.call(n,null!=n?n.oper" - "ation_active:n,{name:\"if\",hash:{},fn:this.program(1,l,0),inverse:this.noop" - ",data:l}))?t:\"\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/resolve_issues-resolved.hbs\":[function(require,module,exports" - "){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(e,a,n,s){return\"

    You successfully so" - "lved the name conflict.

    \\n\"},3:function(e,a,n,s){var r;return' <" - "p>You successfully removed the server '+this.escapeExpr" - "ession((r=null!=(r=a.server_name||(null!=e?e.server_name:e))?r:a.helperMissi" - "ng,\"function\"==typeof r?r.call(e,{name:\"server_name\",hash:{},data:s}):r)" - ")+\".

    \\n\"},compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,a,n," - "s){var r;return'
    \\n ×\\n'+(null!=(r=a.if.cal" - "l(e,null!=e?e.name_conflict:e,{name:\"if\",hash:{},fn:this.program(1,s,0),in" - "verse:this.noop,data:s}))?r:\"\")+(null!=(r=a.if.call(e,null!=e?e.server_dea" - "d:e,{name:\"if\",hash:{},fn:this.program(3,s,0),inverse:this.noop,data:s}))?" - "r:\"\")+\"
    \\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/secondary_indexes-alert_msg.hbs\":[function(require,module,exp" - "orts){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(e,n,a,l){var s;return\"

    You successfully " - "deleted the secondary index \"+this.escapeExpression((s=null!=(s=n.n" - "ame||(null!=e?e.name:e))?s:n.helperMissing,\"function\"==typeof s?s.call(e,{" - "name:\"name\",hash:{},data:l}):s))+\".

    \\n\"},3:function(e,n,a,l" - "){var s;return\"

    You successfully created the secondary index " - "\"+this.escapeExpression((s=null!=(s=n.name||(null!=e?e.name:e))?s:n.helperM" - "issing,\"function\"==typeof s?s.call(e,{name:\"name\",hash:{},data:l}):s))+\"" - ".

    \\n\"},compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,n,a,l)" - "{var s;return\"\\n\"+(null!=(s=n.if.call(e,null!=e?e.delete_ok:e,{name:\"if\"" - ",hash:{},fn:this.program(1,l,0),inverse:this.noop,data:l}))?s:\"\")+(null!=(" - "s=n.if.call(e,null!=e?e.create_ok:e,{name:\"if\",hash:{},fn:this.program(3,l" - ",0),inverse:this.noop,data:l}))?s:\"\")+\"\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/secondary_indexes-error.hbs\":[function(require,module,exports" - "){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(e,n,a,r){return'

    An error occurred: Could not" - " open a connection. Try again.\\n'},3:function(e,n,a,r){return'

    An error occurred: could not retrieve s" - "econdary indexes. Try again.

    \\" - "n'},5:function(e,n,a,r){return'

    The secondary index could not be created:<" - "/p>\\n

    \\n '+this.escapeExpression((n.print_safe" - "||e&&e.print_safe||n.helperMissing).call(e,null!=e?e.message:e,{name:\"print" - "_safe\",hash:{},data:r}))+\"\\n
    \\n\"},7:function(e,n,a,r){return'

    Th" - "e secondary index could not be deleted:

    \\n
    \\" - "n '+this.escapeExpression((n.print_safe||e&&e.print_safe||n.helperMissing)." - "call(e,null!=e?e.message:e,{name:\"print_safe\",hash:{},data:r}))+\"\\n\\n\"},compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,n,a,r){var i;return" - "(null!=(i=n.if.call(e,null!=e?e.connect_fail:e,{name:\"if\",hash:{},fn:this." - "program(1,r,0),inverse:this.noop,data:r}))?i:\"\")+(null!=(i=n.if.call(e,nul" - "l!=e?e.index_list_fail:e,{name:\"if\",hash:{},fn:this.program(3,r,0),inverse" - ":this.noop,data:r}))?i:\"\")+(null!=(i=n.if.call(e,null!=e?e.create_fail:e,{" - "name:\"if\",hash:{},fn:this.program(5,r,0),inverse:this.noop,data:r}))?i:\"\"" - ")+(null!=(i=n.if.call(e,null!=e?e.delete_fail:e,{name:\"if\",hash:{},fn:this" - ".program(7,r,0),inverse:this.noop,data:r}))?i:\"\")},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/shard_distribution_container.hbs\":[function(require,module,ex" - "ports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(a,i,n,t){re" - "turn'

    Data distribution

    \\n
    \\n

    \\n Distribution data cannot be updated while not enough r" - "eplicas are available.\\n

    \\n
    \\n
    \\n
    \\n The distribution may be slightly outdated
    \\n " - "
    \\n \\n \\n \\n \\n
    \\n
    \\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/sidebar-client_connection_status.hbs\":[function(require,modul" - "e,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(n,e,r,a){return\"error\"},3:function(n,e,r,a){r" - "eturn\"

    Disconnected

    \\n\"},5:function(n,e,r,a){var t;return\"" - "
    Connected to
    \\n

    \\n\"+(null!=(t=e.if.call(n,null" - "!=n?n.me:n,{name:\"if\",hash:{},fn:this.program(6,a,0),inverse:this.program(" - "8,a,0),data:a}))?t:\"\")+\"

    \\n\"},6:function(n,e,r,a){var t;ret" - "urn\" \"+this.escapeExpression((t=null!=(t=e.me||(null!=n?n.me:n))?" - "t:e.helperMissing,\"function\"==typeof t?t.call(n,{name:\"me\",hash:{},data:" - "a}):t))+\"\\n\"},8:function(n,e,r,a){return\" <Unknown>\\n\"}" - ",compiler:[6,\">= 2.0.0-beta.1\"],main:function(n,e,r,a){var t;return' \\" - "n'+(null!=(t=e.if.call(n,null!=n?n.disconnected:n,{name:\"if\",hash:{},fn:th" - "is.program(3,a,0),inverse:this.program(5,a,0),data:a}))?t:\"\")+\"
    " - "\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/sidebar-container.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(s,a,n,e){re" - "turn'
    \\n
    \\n
    \\n " - "
    \\n
    \\n
    \\n
    \\n
    \\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/sidebar-issues.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(n,s,e,a){return\"error\"},3:function(n,s,e,a){v" - "ar u,i=s.helperMissing,l=this.escapeExpression;return'
    \\n

    '+l((u=null!=(u=s.num_issues||(null!=n?n.num_iss" - "ues:n))?u:i,\"function\"==typeof u?u.call(n,{name:\"num_issues\",hash:{},dat" - "a:a}):u))+\" \"+l((s.pluralize_noun||n&&n.pluralize_noun||i).call(n,\"issue\"" - ",null!=n?n.num_issues:n,{name:\"pluralize_noun\",hash:{},data:a}))+\"

    \\" - "n\"},5:function(n,s,e,a){return\"

    No issues

    \\n\"},compiler:[" - "6,\">= 2.0.0-beta.1\"],main:function(n,s,e,a){var u;return'
    \\n
    Issues\\n'+(null!=(u=s.if.call(n,null!=n?n.num_issues:n,{name:\"if\",hash:{},fn" - ":this.program(3,a,0),inverse:this.program(5,a,0),data:a}))?u:\"\")+\"
    \\" - "n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/sidebar-servers_connected.hbs\":[function(require,module,expor" - "ts){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,n,r,s){va" - "r a;return'
    \\n
    Servers
    \\n

    '+this.escapeE" - "xpression((a=null!=(a=n.num_servers||(null!=e?e.num_servers:e))?a:n.helperMi" - "ssing,\"function\"==typeof a?a.call(e,{name:\"num_servers\",hash:{},data:s})" - ":a))+\" connected

    \\n
    \\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/sidebar-tables_available.hbs\":[function(require,module,export" - "s){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(a,e,l,n){return\"error\"},compiler:[6,\">= 2.0." - "0-beta.1\"],main:function(a,e,l,n){var r,s,t=e.helperMissing,i=\"function\"," - "u=this.escapeExpression;return'\\n
    \\n
    Tables
    \\n

    '+u(" - "(s=null!=(s=e.num_available_tables||(null!=a?a.num_available_tables:a))?s:t," - "typeof s===i?s.call(a,{name:\"num_available_tables\",hash:{},data:n}):s))+\"" - "/\"+u((s=null!=(s=e.num_tables||(null!=a?a.num_tables:a))?s:t,typeof s===i?s" - ".call(a,{name:\"num_tables\",hash:{},data:n}):s))+\" ready

    \\n
    " - "\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/simple_progressbar.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(n,e,a,r){var i;return'
    \\n
    \\n'+(null!=(i=e.if.call(n,null!=" - "n?n.starting:n,{name:\"if\",hash:{},fn:this.program(2,r,0),inverse:this.noop" - ",data:r}))?i:\"\")+(null!=(i=e.if.call(n,null!=n?n.processing:n,{name:\"if\"" - ",hash:{},fn:this.program(4,r,0),inverse:this.noop,data:r}))?i:\"\")+(null!=(" - "i=e.if.call(n,null!=n?n.finished:n,{name:\"if\",hash:{},fn:this.program(6,r," - "0),inverse:this.noop,data:r}))?i:\"\")+'
    \\n
    \\n
    \\n'},2:function" - "(n,e,a,r){return\"

    Started

    \\n\"},4:function(n,e,a,r){r" - "eturn\"

    Processing

    \\n\"},6:function(n,e,a,r){return' " - "

    Processed

    \\n'},8:function(n,e,a,r){" - "return\"0\"},10:function(n,e,a,r){var i;return this.escapeExpression((i=null" - "!=(i=e.percent_complete||(null!=n?n.percent_complete:n))?i:e.helperMissing,\"" - "function\"==typeof i?i.call(n,{name:\"percent_complete\",hash:{},data:r}):i)" - ")},compiler:[6,\">= 2.0.0-beta.1\"],main:function(n,e,a,r){var i;return\"\\n" - "\"+(null!=(i=e.if.call(n,null!=n?n.operation_active:n,{name:\"if\",hash:{},f" - "n:this.program(1,r,0),inverse:this.noop,data:r}))?i:\"\")+\"\\n\"}" - ",useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/table-secondary_index.hbs\":[function(require,module,exports){" - "\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(n,e,a,t){return\"[empty string]\"},3:f" - "unction(n,e,a,t){var l;return this.escapeExpression((l=null!=(l=e.name||(nul" - "l!=n?n.name:n))?l:e.helperMissing,\"function\"==typeof l?l.call(n,{name:\"na" - "me\",hash:{},data:t}):l))},5:function(n,e,a,t){return\"Ready to use\"},7:fun" - "ction(n,e,a,t){return\"building\"},9:function(n,e,a,t){var l;return\"\"+this.escapeExpression((l=null!=(l=e.name||(null!=n?n.name:n))?l:e.helper" - "Missing,\"function\"==typeof l?l.call(n,{name:\"name\",hash:{},data:t}):l))+" - "\"
    \"},compiler:[6,\">= 2.0.0-beta.1\"],main:function(n,e,a,t){var l" - ",s;return'\\n
    '+(null!=(l=e" - ".if.call(n,null!=n?n.is_empty:n,{name:\"if\",hash:{},fn:this.program(1,t,0)," - "inverse:this.program(3,t,0),data:t}))?l:\"\")+' \xe2\x80\x94 delete
    \\n
    \\n '+(null!=(l=e.if.call(n,null!=n?n.ready:n,{name:\"" - "if\",hash:{},fn:this.program(5,t,0),inverse:this.program(7,t,0),data:t}))?l:" - "\"\")+' \\n
    \\n
    \\n
    \\n
    \\n " - " Are you sure you want to delete the secondary index '+(null!=(l=e." - "if.call(n,null!=n?n.is_empty:n,{name:\"if\",hash:{},fn:this.program(1,t,0),i" - "nverse:this.program(9,t,0),data:t}))?l:\"\")+'?
    \\n Cancel\\n
    \\n
    \\n \\n\\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/table-secondary_indexes.hbs\":[function(require,module,exports" - "){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,a,n,l){re" - "turn'

    Secondary indexes

    \\n
    \\n ×\\n
    \\n
    \\n
    \\n ×\\n
    \\n
    \\n
    \\n
    \\n

    \\n Secondary indexes cann" - "ot be updated while a primary replica is unavailable.\\n

    \\n
    \\n\\n
      \\n
    • No secondary indexes found.
    • \\n
    \\n
      \\n
    \\n
    \\n'},useData:!0});" - "\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/table.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(a,l,e,n){va" - "r s,i=l.helperMissing,t=\"function\",u=this.escapeExpression;return'\\n <" - "div class=\"checkbox-container\">\\n \\n
    \\n
    \\n

    '+u((s=null!=(s=l.name||(null!=a?a.name:a))?s:i,typeof s===t" - "?s.call(a,{name:\"name\",hash:{},data:n}):s))+'

    \\n

    \\n '+u((s=null!=(s=l.shards||(null!=a?a.shards:a))?" - "s:i,typeof s===t?s.call(a,{name:\"shards\",hash:{},data:n}):s))+\" \"+u((l.p" - "luralize_noun||a&&a.pluralize_noun||i).call(a,\"shard\",null!=a?a.shards:a,{" - "name:\"pluralize_noun\",hash:{},data:n}))+\", \"+u((s=null!=(s=l.replicas||(" - "null!=a?a.replicas:a))?s:i,typeof s===t?s.call(a,{name:\"replicas\",hash:{}," - "data:n}):s))+\" \"+u((l.pluralize_noun||a&&a.pluralize_noun||i).call(a,\"rep" - "lica\",null!=a?a.replicas:a,{name:\"pluralize_noun\",hash:{},data:n}))+\"\\n" - "

    \\n \"+u((l.humanize_table_readiness||a&&a.humanize_table" - "_readiness||i).call(a,null!=a?a.status:a,null!=a?a.replicas_ready:a,null!=a?" - "a.replicas:a,{name:\"humanize_table_readiness\",hash:{},data:n}))+'\\n " - "
    \\n \\n
    " - "\\n
    \\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/table_container.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,n,s,i){re" - "turn'
    \\n
    \\n \\n
    \\n \\n
    \\n
    \\n
    \\n
    \\n \\n " - " \\n
    \\n \\n
    \\n
    \\n
    \\n

    Statistics

    \\n
    \\n " - "
    \\n
    \\n
    \\n
    \\n
    " - "\\n
    \\n
    \\n
    \\n
    \\n
    \\n
    \\n
    \\n
    \\n
    \\n
    \\n " - "

    Table Viewer

    \\n
    \\n
    \\n
    \\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/table_profile.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({1:function(a,s,n,l){return' (some replicas missing)\\n'},3:function(a,s,n,l){return\" " - " About \"+this.escapeExpression((s.approximate_count||a&&a.approximate_count" - "||s.helperMissing).call(a,null!=a?a.total_keys:a,{name:\"approximate_count\"" - ",hash:{},data:l}))+\"\\n\"},5:function(a,s,n,l){return\" Unknown\\n\"" - "},compiler:[6,\">= 2.0.0-beta.1\"],main:function(a,s,n,l){var e,i,t=s.helper" - "Missing,r=this.escapeExpression,p=\"function\";return'
    \\n
    \\n

    \\n " - " '+r((s.humanize_table_status||a&&a.humanize_table_stat" - "us||t).call(a,null!=a?a.status:a,{name:\"humanize_table_status\",hash:{},dat" - "a:l}))+\"\\n\"+(null!=(e=s.if.call(a,null!=a?a.parenthetical:a,{name:" - "\"if\",hash:{},fn:this.program(1,l,0),inverse:this.noop,data:l}))?e:\"\")+' " - "

    \\n
    \\n
    \\n

    \\n \\n'+(null!=(e=(s.if_defined||a&&a.if_defined|" - "|t).call(a,null!=a?a.total_keys:a,{name:\"if_defined\",hash:{},fn:this.progr" - "am(3,l,0),inverse:this.program(5,l,0),data:l}))?e:\"\")+' docume" - "nts

    \\n
    \\n
    \\n

    <" - "span class=\"big\">'+r((i=null!=(i=s.num_primary_replicas||(null!=a?a.num_pr" - "imary_replicas:a))?i:t,typeof i===p?i.call(a,{name:\"num_primary_replicas\"," - "hash:{},data:l}):i))+\"/\"+r((i=null!=(i=s.num_shards||(null!=a?a.num_shards" - ":a))?i:t,typeof i===p?i.call(a,{name:\"num_shards\",hash:{},data:l}):i))+' primary replicas

    \\n
    \\n
    \\n

    \\n '+r((i=null!=(i=s.num_availa" - "ble_replicas||(null!=a?a.num_available_replicas:a))?i:t,typeof i===p?i.call(" - "a,{name:\"num_available_replicas\",hash:{},data:l}):i))+\"/\"+r((i=null!=(i=" - "s.num_replicas||(null!=a?a.num_replicas:a))?i:t,typeof i===p?i.call(a,{name:" - "\"num_replicas\",hash:{},data:l}):i))+\" replicas available

    \\n " - "
    \\n
    \\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/table_status_indicator.hbs\":[function(require,module,exports)" - "{\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(n,c,e,l){re" - "turn'\\n \\n " - " \\n \\n <" - "path class=\"inner-ring\" d=\"M144,256c0-36.9,18.553-69.208,46.314-87.034l-2" - "3.141-24.512c-6.26,4.608-12.18,9.833-17.684,15.663\\n C125.314,18" - "5.729,112,219.781,112,256c0,36.219,13.314,70.271,37.49,95.883c5.504,5.829,11" - ".424,11.055,17.684,15.662\\n l23.141-24.511C162.553,325.208,144,2" - "92.9,144,256z\"/>\\n \\n \\n \\n \\n " - " \\n\\n'},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/admin/stat" - "ic/handlebars/table_title.hbs\":[function(require,module,exports){\n" - "var HandlebarsCompiler=require(\"hbsfy/runtime\");module.exports=HandlebarsC" - "ompiler.template({compiler:[6,\">= 2.0.0-beta.1\"],main:function(e,a,n,l){va" - "r r,t=a.helperMissing,s=\"function\",i=this.escapeExpression;return'\\n <" - "h1 class=\"title\">Table overview for '+i((r=null!=(r=a.db||(null!=e?e.db:e)" - ")?r:t,typeof r===s?r.call(e,{name:\"db\",hash:{},data:l}):r))+\".\"+i((r=nul" - "l!=(r=a.name||(null!=e?e.name:e))?r:t,typeof r===s?r.call(e,{name:\"name\",h" - "ash:{},data:l}):r))+\"\\n\\n\\n\"},useData:!0});\n" - "},{\"hbsfy/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/hbsfy/runtime.js\"}],\"/rethinkdb/build/exte" - "rnal/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/l" - "ib/_empty.js\":[function(require,module,exports){\n" - "\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/browserify/node_modules/browser-resolve/empty.js\":[functio" - "n(require,module,exports){\n" - "arguments[4][\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethi" - "nkdb-webui/node_modules/browserify/lib/_empty.js\"][0].apply(exports,argumen" - "ts)\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/browserify/node_modules/buffer/index.js\":[function(require" - ",module,exports){\n" - "(function (global){\n" - "\"use strict\";function typedArraySupport(){try{var t=new Uint8Array(1);retu" - "rn t.__proto__={__proto__:Uint8Array.prototype,foo:function(){return 42}},42" - "===t.foo()&&\"function\"==typeof t.subarray&&0===t.subarray(1,1).byteLength}" - "catch(t){return!1}}function kMaxLength(){return Buffer.TYPED_ARRAY_SUPPORT?2" - "147483647:1073741823}function createBuffer(t,e){if(kMaxLength()" - "=kMaxLength())throw new RangeError(\"Attempt to allocate Buffer larger than " - "maximum size: 0x\"+kMaxLength().toString(16)+\" bytes\");return 0|t}function" - " SlowBuffer(t){return+t!=t&&(t=0),Buffer.alloc(+t)}function byteLength(t,e){" - "if(Buffer.isBuffer(t))return t.length;if(\"undefined\"!=typeof ArrayBuffer&&" - "\"function\"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(t)||t instanceo" - "f ArrayBuffer))return t.byteLength;\"string\"!=typeof t&&(t=\"\"+t);var r=t." - "length;if(0===r)return 0;for(var n=!1;;)switch(e){case\"ascii\":case\"latin1" - "\":case\"binary\":return r;case\"utf8\":case\"utf-8\":case void 0:return utf" - "8ToBytes(t).length;case\"ucs2\":case\"ucs-2\":case\"utf16le\":case\"utf-16le" - "\":return 2*r;case\"hex\":return r>>>1;case\"base64\":return base64ToBytes(t" - ").length;default:if(n)return utf8ToBytes(t).length;e=(\"\"+e).toLowerCase()," - "n=!0}}function slowToString(t,e,r){var n=!1;if((void 0===e||e<0)&&(e=0),e>th" - "is.length)return\"\";if((void 0===r||r>this.length)&&(r=this.length),r<=0)re" - "turn\"\";if(r>>>=0,e>>>=0,r<=e)return\"\";for(t||(t=\"utf8\");;)switch(t){ca" - "se\"hex\":return hexSlice(this,e,r);case\"utf8\":case\"utf-8\":return utf8Sl" - "ice(this,e,r);case\"ascii\":return asciiSlice(this,e,r);case\"latin1\":case\"" - "binary\":return latin1Slice(this,e,r);case\"base64\":return base64Slice(this" - ",e,r);case\"ucs2\":case\"ucs-2\":case\"utf16le\":case\"utf-16le\":return utf" - "16leSlice(this,e,r);default:if(n)throw new TypeError(\"Unknown encoding: \"+" - "t);t=(t+\"\").toLowerCase(),n=!0}}function swap(t,e,r){var n=t[e];t[e]=t[r]," - "t[r]=n}function bidirectionalIndexOf(t,e,r,n,f){if(0===t.length)return-1;if(" - "\"string\"==typeof r?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-" - "2147483648),r=+r,isNaN(r)&&(r=f?0:t.length-1),r<0&&(r=t.length+r),r>=t.lengt" - "h){if(f)return-1;r=t.length-1}else if(r<0){if(!f)return-1;r=0}if(\"string\"=" - "=typeof e&&(e=Buffer.from(e,n)),Buffer.isBuffer(e))return 0===e.length?-1:ar" - "rayIndexOf(t,e,r,n,f);if(\"number\"==typeof e)return e=255&e,Buffer.TYPED_AR" - "RAY_SUPPORT&&\"function\"==typeof Uint8Array.prototype.indexOf?f?Uint8Array." - "prototype.indexOf.call(t,e,r):Uint8Array.prototype.lastIndexOf.call(t,e,r):a" - "rrayIndexOf(t,[e],r,n,f);throw new TypeError(\"val must be string, number or" - " Buffer\")}function arrayIndexOf(t,e,r,n,f){function i(t,e){return 1===o?t[e" - "]:t.readUInt16BE(e*o)}var o=1,u=t.length,s=e.length;if(void 0!==n&&(n=String" - "(n).toLowerCase(),\"ucs2\"===n||\"ucs-2\"===n||\"utf16le\"===n||\"utf-16le\"" - "===n)){if(t.length<2||e.length<2)return-1;o=2,u/=2,s/=2,r/=2}var a;if(f){var" - " h=-1;for(a=r;au&&(r=u-s),a=r;a>=0;a" - "--){for(var c=!0,l=0;lf&&(n=f)):n=f;var i=e.length;if(i%2!==0)throw new TypeError(\"Inval" - "id hex string\");n>i/2&&(n=i/2);for(var o=0;o239?4:i>223?3:i>191?2:1;if(f+u<=r){var s,a,h,c;switch(u){case 1:i" - "<128&&(o=i);break;case 2:s=t[f+1],128===(192&s)&&(c=(31&i)<<6|63&s,c>127&&(o" - "=c));break;case 3:s=t[f+1],a=t[f+2],128===(192&s)&&128===(192&a)&&(c=(15&i)<" - "<12|(63&s)<<6|63&a,c>2047&&(c<55296||c>57343)&&(o=c));break;case 4:s=t[f+1]," - "a=t[f+2],h=t[f+3],128===(192&s)&&128===(192&a)&&128===(192&h)&&(c=(15&i)<<18" - "|(63&s)<<12|(63&a)<<6|63&h,c>65535&&c<1114112&&(o=c))}}null===o?(o=65533,u=1" - "):o>65535&&(o-=65536,n.push(o>>>10&1023|55296),o=56320|1023&o),n.push(o),f+=" - "u}return decodeCodePointsArray(n)}function decodeCodePointsArray(t){var e=t." - "length;if(e<=MAX_ARGUMENTS_LENGTH)return String.fromCharCode.apply(String,t)" - ";for(var r=\"\",n=0;nn)&&(r=n);for(var f=\"\",i=e;ir)throw new RangeError(\"Trying to access beyond buffer l" - "ength\")}function checkInt(t,e,r,n,f,i){if(!Buffer.isBuffer(t))throw new Typ" - "eError('\"buffer\" argument must be a Buffer instance');if(e>f||et.length)throw ne" - "w RangeError(\"Index out of range\")}function objectWriteUInt16(t,e,r,n){e<0" - "&&(e=65535+e+1);for(var f=0,i=Math.min(t.length-r,2);f>>8*(n?f:1-f)}function objectWriteUInt32(t,e,r,n){e<0&&(e=42949" - "67295+e+1);for(var f=0,i=Math.min(t.length-r,4);f>>8*(n?f:3-" - "f)&255}function checkIEEE754(t,e,r,n,f,i){if(r+n>t.length)throw new RangeErr" - "or(\"Index out of range\");if(r<0)throw new RangeError(\"Index out of range\"" - ")}function writeFloat(t,e,r,n,f){return f||checkIEEE754(t,e,r,4,3.4028234663" - "852886e38,-3.4028234663852886e38),ieee754.write(t,e,r,n,23,4),r+4}function w" - "riteDouble(t,e,r,n,f){return f||checkIEEE754(t,e,r,8,1.7976931348623157e308," - "-1.7976931348623157e308),ieee754.write(t,e,r,n,52,8),r+8}function base64clea" - "n(t){if(t=stringtrim(t).replace(INVALID_BASE64_RE,\"\"),t.length<2)return\"\"" - ";for(;t.length%4!==0;)t+=\"=\";return t}function stringtrim(t){return t.trim" - "?t.trim():t.replace(/^\\s+|\\s+$/g,\"\")}function toHex(t){return t<16?\"0\"" - "+t.toString(16):t.toString(16)}function utf8ToBytes(t,e){e=e||1/0;for(var r," - "n=t.length,f=null,i=[],o=0;o55295&&r<57344){i" - "f(!f){if(r>56319){(e-=3)>-1&&i.push(239,191,189);continue}if(o+1===n){(e-=3)" - ">-1&&i.push(239,191,189);continue}f=r;continue}if(r<56320){(e-=3)>-1&&i.push" - "(239,191,189),f=r;continue}r=(f-55296<<10|r-56320)+65536}else f&&(e-=3)>-1&&" - "i.push(239,191,189);if(f=null,r<128){if((e-=1)<0)break;i.push(r)}else if(r<2" - "048){if((e-=2)<0)break;i.push(r>>6|192,63&r|128)}else if(r<65536){if((e-=3)<" - "0)break;i.push(r>>12|224,r>>6&63|128,63&r|128)}else{if(!(r<1114112))throw ne" - "w Error(\"Invalid code point\");if((e-=4)<0)break;i.push(r>>18|240,r>>12&63|" - "128,r>>6&63|128,63&r|128)}}return i}function asciiToBytes(t){for(var e=[],r=" - "0;r>8,f=r%256,i.push(f),i.push(n);return i}function base64ToBytes(t){return" - " base64.toByteArray(base64clean(t))}function blitBuffer(t,e,r,n){for(var f=0" - ";f=e.length||f>=t.length);++f)e[f+r]=t[f];return f}function isnan(" - "t){return t!==t}var base64=require(\"base64-js\"),ieee754=require(\"ieee754\"" - "),isArray=require(\"isarray\");exports.Buffer=Buffer,exports.SlowBuffer=Slow" - "Buffer,exports.INSPECT_MAX_BYTES=50,Buffer.TYPED_ARRAY_SUPPORT=void 0!==glob" - "al.TYPED_ARRAY_SUPPORT?global.TYPED_ARRAY_SUPPORT:typedArraySupport(),export" - "s.kMaxLength=kMaxLength(),Buffer.poolSize=8192,Buffer._augment=function(t){r" - "eturn t.__proto__=Buffer.prototype,t},Buffer.from=function(t,e,r){return fro" - "m(null,t,e,r)},Buffer.TYPED_ARRAY_SUPPORT&&(Buffer.prototype.__proto__=Uint8" - "Array.prototype,Buffer.__proto__=Uint8Array,\"undefined\"!=typeof Symbol&&Sy" - "mbol.species&&Buffer[Symbol.species]===Buffer&&Object.defineProperty(Buffer," - "Symbol.species,{value:null,configurable:!0})),Buffer.alloc=function(t,e,r){r" - "eturn alloc(null,t,e,r)},Buffer.allocUnsafe=function(t){return allocUnsafe(n" - "ull,t)},Buffer.allocUnsafeSlow=function(t){return allocUnsafe(null,t)},Buffe" - "r.isBuffer=function(t){return!(null==t||!t._isBuffer)},Buffer.compare=functi" - "on(t,e){if(!Buffer.isBuffer(t)||!Buffer.isBuffer(e))throw new TypeError(\"Ar" - "guments must be Buffers\");if(t===e)return 0;for(var r=t.length,n=e.length,f" - "=0,i=Math.min(r,n);f" - "0&&(t=this.toString(\"hex\",0,e).match(/.{2}/g).join(\" \"),this.length>e&&(" - "t+=\" ... \")),\"\"},Buffer.prototype.compare=function(t,e,r" - ",n,f){if(!Buffer.isBuffer(t))throw new TypeError(\"Argument must be a Buffer" - "\");if(void 0===e&&(e=0),void 0===r&&(r=t?t.length:0),void 0===n&&(n=0),void" - " 0===f&&(f=this.length),e<0||r>t.length||n<0||f>this.length)throw new RangeE" - "rror(\"out of range index\");if(n>=f&&e>=r)return 0;if(n>=f)return-1;if(e>=r" - ")return 1;if(e>>>=0,r>>>=0,n>>>=0,f>>>=0,this===t)return 0;for(var i=f-n,o=r" - "-e,u=Math.min(i,o),s=this.slice(n,f),a=t.slice(e,r),h=0;hf)&&(r=f),t.length>0&&(" - "r<0||e<0)||e>this.length)throw new RangeError(\"Attempt to write outside buf" - "fer bounds\");n||(n=\"utf8\");for(var i=!1;;)switch(n){case\"hex\":return he" - "xWrite(this,t,e,r);case\"utf8\":case\"utf-8\":return utf8Write(this,t,e,r);c" - "ase\"ascii\":return asciiWrite(this,t,e,r);case\"latin1\":case\"binary\":ret" - "urn latin1Write(this,t,e,r);case\"base64\":return base64Write(this,t,e,r);ca" - "se\"ucs2\":case\"ucs-2\":case\"utf16le\":case\"utf-16le\":return ucs2Write(t" - "his,t,e,r);default:if(i)throw new TypeError(\"Unknown encoding: \"+n);n=(\"\"" - "+n).toLowerCase(),i=!0}},Buffer.prototype.toJSON=function(){return{type:\"Bu" - "ffer\",data:Array.prototype.slice.call(this._arr||this,0)}};var MAX_ARGUMENT" - "S_LENGTH=4096;Buffer.prototype.slice=function(t,e){var r=this.length;t=~~t,e" - "=void 0===e?r:~~e,t<0?(t+=r,t<0&&(t=0)):t>r&&(t=r),e<0?(e+=r,e<0&&(e=0)):e>r" - "&&(e=r),e0&&(f*=256);)n+=this[t+--e]*f;return n},Buffer.prototype.readUInt8=funct" - "ion(t,e){return e||checkOffset(t,1,this.length),this[t]},Buffer.prototype.re" - "adUInt16LE=function(t,e){return e||checkOffset(t,2,this.length),this[t]|this" - "[t+1]<<8},Buffer.prototype.readUInt16BE=function(t,e){return e||checkOffset(" - "t,2,this.length),this[t]<<8|this[t+1]},Buffer.prototype.readUInt32LE=functio" - "n(t,e){return e||checkOffset(t,4,this.length),(this[t]|this[t+1]<<8|this[t+2" - "]<<16)+16777216*this[t+3]},Buffer.prototype.readUInt32BE=function(t,e){retur" - "n e||checkOffset(t,4,this.length),16777216*this[t]+(this[t+1]<<16|this[t+2]<" - "<8|this[t+3])},Buffer.prototype.readIntLE=function(t,e,r){t=0|t,e=0|e,r||che" - "ckOffset(t,e,this.length);for(var n=this[t],f=1,i=0;++i=f&&(n-=Math.pow(2,8*e)),n},Buffer.prototype.readInt" - "BE=function(t,e,r){t=0|t,e=0|e,r||checkOffset(t,e,this.length);for(var n=e,f" - "=1,i=this[t+--n];n>0&&(f*=256);)i+=this[t+--n]*f;return f*=128,i>=f&&(i-=Mat" - "h.pow(2,8*e)),i},Buffer.prototype.readInt8=function(t,e){return e||checkOffs" - "et(t,1,this.length),128&this[t]?(255-this[t]+1)*-1:this[t]},Buffer.prototype" - ".readInt16LE=function(t,e){e||checkOffset(t,2,this.length);var r=this[t]|thi" - "s[t+1]<<8;return 32768&r?4294901760|r:r},Buffer.prototype.readInt16BE=functi" - "on(t,e){e||checkOffset(t,2,this.length);var r=this[t+1]|this[t]<<8;return 32" - "768&r?4294901760|r:r},Buffer.prototype.readInt32LE=function(t,e){return e||c" - "heckOffset(t,4,this.length),this[t]|this[t+1]<<8|this[t+2]<<16|this[t+3]<<24" - "},Buffer.prototype.readInt32BE=function(t,e){return e||checkOffset(t,4,this." - "length),this[t]<<24|this[t+1]<<16|this[t+2]<<8|this[t+3]},Buffer.prototype.r" - "eadFloatLE=function(t,e){return e||checkOffset(t,4,this.length),ieee754.read" - "(this,t,!0,23,4)},Buffer.prototype.readFloatBE=function(t,e){return e||check" - "Offset(t,4,this.length),ieee754.read(this,t,!1,23,4)},Buffer.prototype.readD" - "oubleLE=function(t,e){return e||checkOffset(t,8,this.length),ieee754.read(th" - "is,t,!0,52,8)},Buffer.prototype.readDoubleBE=function(t,e){return e||checkOf" - "fset(t,8,this.length),ieee754.read(this,t,!1,52,8)},Buffer.prototype.writeUI" - "ntLE=function(t,e,r,n){if(t=+t,e=0|e,r=0|r,!n){var f=Math.pow(2,8*r)-1;check" - "Int(this,t,e,r,f,0)}var i=1,o=0;for(this[e]=255&t;++o=0&&(o*=256);)this[e+i]=t/o&255;return e+r},Buffe" - "r.prototype.writeUInt8=function(t,e,r){return t=+t,e=0|e,r||checkInt(this,t," - "e,1,255,0),Buffer.TYPED_ARRAY_SUPPORT||(t=Math.floor(t)),this[e]=255&t,e+1}," - "Buffer.prototype.writeUInt16LE=function(t,e,r){return t=+t,e=0|e,r||checkInt" - "(this,t,e,2,65535,0),Buffer.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>" - ">8):objectWriteUInt16(this,t,e,!0),e+2},Buffer.prototype.writeUInt16BE=funct" - "ion(t,e,r){return t=+t,e=0|e,r||checkInt(this,t,e,2,65535,0),Buffer.TYPED_AR" - "RAY_SUPPORT?(this[e]=t>>>8,this[e+1]=255&t):objectWriteUInt16(this,t,e,!1),e" - "+2},Buffer.prototype.writeUInt32LE=function(t,e,r){return t=+t,e=0|e,r||chec" - "kInt(this,t,e,4,4294967295,0),Buffer.TYPED_ARRAY_SUPPORT?(this[e+3]=t>>>24,t" - "his[e+2]=t>>>16,this[e+1]=t>>>8,this[e]=255&t):objectWriteUInt32(this,t,e,!0" - "),e+4},Buffer.prototype.writeUInt32BE=function(t,e,r){return t=+t,e=0|e,r||c" - "heckInt(this,t,e,4,4294967295,0),Buffer.TYPED_ARRAY_SUPPORT?(this[e]=t>>>24," - "this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t):objectWriteUInt32(this,t,e" - ",!1),e+4},Buffer.prototype.writeIntLE=function(t,e,r,n){if(t=+t,e=0|e,!n){va" - "r f=Math.pow(2,8*r-1);checkInt(this,t,e,r,f-1,-f)}var i=0,o=1,u=0;for(this[e" - "]=255&t;++i>" - "0)-u&255;return e+r},Buffer.prototype.writeIntBE=function(t,e,r,n){if(t=+t,e" - "=0|e,!n){var f=Math.pow(2,8*r-1);checkInt(this,t,e,r,f-1,-f)}var i=r-1,o=1,u" - "=0;for(this[e+i]=255&t;--i>=0&&(o*=256);)t<0&&0===u&&0!==this[e+i+1]&&(u=1)," - "this[e+i]=(t/o>>0)-u&255;return e+r},Buffer.prototype.writeInt8=function(t,e" - ",r){return t=+t,e=0|e,r||checkInt(this,t,e,1,127,-128),Buffer.TYPED_ARRAY_SU" - "PPORT||(t=Math.floor(t)),t<0&&(t=255+t+1),this[e]=255&t,e+1},Buffer.prototyp" - "e.writeInt16LE=function(t,e,r){return t=+t,e=0|e,r||checkInt(this,t,e,2,3276" - "7,-32768),Buffer.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8):objectW" - "riteUInt16(this,t,e,!0),e+2},Buffer.prototype.writeInt16BE=function(t,e,r){r" - "eturn t=+t,e=0|e,r||checkInt(this,t,e,2,32767,-32768),Buffer.TYPED_ARRAY_SUP" - "PORT?(this[e]=t>>>8,this[e+1]=255&t):objectWriteUInt16(this,t,e,!1),e+2},Buf" - "fer.prototype.writeInt32LE=function(t,e,r){return t=+t,e=0|e,r||checkInt(thi" - "s,t,e,4,2147483647,-2147483648),Buffer.TYPED_ARRAY_SUPPORT?(this[e]=255&t,th" - "is[e+1]=t>>>8,this[e+2]=t>>>16,this[e+3]=t>>>24):objectWriteUInt32(this,t,e," - "!0),e+4},Buffer.prototype.writeInt32BE=function(t,e,r){return t=+t,e=0|e,r||" - "checkInt(this,t,e,4,2147483647,-2147483648),t<0&&(t=4294967295+t+1),Buffer.T" - "YPED_ARRAY_SUPPORT?(this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3" - "]=255&t):objectWriteUInt32(this,t,e,!1),e+4},Buffer.prototype.writeFloatLE=f" - "unction(t,e,r){return writeFloat(this,t,e,!0,r)},Buffer.prototype.writeFloat" - "BE=function(t,e,r){return writeFloat(this,t,e,!1,r)},Buffer.prototype.writeD" - "oubleLE=function(t,e,r){return writeDouble(this,t,e,!0,r)},Buffer.prototype." - "writeDoubleBE=function(t,e,r){return writeDouble(this,t,e,!1,r)},Buffer.prot" - "otype.copy=function(t,e,r,n){if(r||(r=0),n||0===n||(n=this.length),e>=t.leng" - "th&&(e=t.length),e||(e=0),n>0&&n=this.length)throw new RangeError(\"sourceStart out of bound" - "s\");if(n<0)throw new RangeError(\"sourceEnd out of bounds\");n>this.length&" - "&(n=this.length),t.length-e=0;--f)t[f+e]=this[f+r];else if(i<1e3||!Buffer.TYPED_ARR" - "AY_SUPPORT)for(f=0;f>>=0,r=void 0===r?this.length:r>>>0,t||(t=0);" - "var i;if(\"number\"==typeof t)for(i=e;i0" - ")throw new Error(\"Invalid string. Length must be a multiple of 4\");n=\"=\"" - "===o[a-2]?2:\"=\"===o[a-1]?1:0,p=new Arr(3*a/4-n),t=n>0?a-4:a;var k=0;for(r=" - "0,e=0;r>16&255,p[k++]=u>>8&255,p[k++]=255&u;return 2===n?(u=revLookup[o.charCod" - "eAt(r)]<<2|revLookup[o.charCodeAt(r+1)]>>4,p[k++]=255&u):1===n&&(u=revLookup" - "[o.charCodeAt(r)]<<10|revLookup[o.charCodeAt(r+1)]<<4|revLookup[o.charCodeAt" - "(r+2)]>>2,p[k++]=u>>8&255,p[k++]=255&u),p}function tripletToBase64(o){return" - " lookup[o>>18&63]+lookup[o>>12&63]+lookup[o>>6&63]+lookup[63&o]}function enc" - "odeChunk(o,r,e){for(var t,u=[],n=r;nk?k:a+p));return 1===t?(r=o[e-1],u+=lookup[r>>2],u+=lookup[r<<" - "4&63],u+=\"==\"):2===t&&(r=(o[e-2]<<8)+o[e-1],u+=lookup[r>>10],u+=lookup[r>>" - "4&63],u+=lookup[r<<2&63],u+=\"=\"),n.push(u),n.join(\"\")}exports.toByteArra" - "y=toByteArray,exports.fromByteArray=fromByteArray;var lookup=[],revLookup=[]" - ",Arr=\"undefined\"!=typeof Uint8Array?Uint8Array:Array;init();\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/browserify/node_modules/buffer/node_modules/ieee754/index.j" - "s\":[function(require,module,exports){\n" - "exports.read=function(a,o,t,r,h){var M,p,w=8*h-r-1,f=(1<>1,i=-7,N=" - "t?h-1:0,n=t?-1:1,s=a[o+N];for(N+=n,M=s&(1<<-i)-1,s>>=-i,i+=w;i>0;M=256*M+a[o" - "+N],N+=n,i-=8);for(p=M&(1<<-i)-1,M>>=-i,i+=r;i>0;p=256*p+a[o+N],N+=n,i-=8);i" - "f(0===M)M=1-e;else{if(M===f)return p?NaN:(s?-1:1)*(1/0);p+=Math.pow(2,r),M-=" - "e}return(s?-1:1)*p*Math.pow(2,M-r)},exports.write=function(a,o,t,r,h,M){var " - "p,w,f,e=8*M-h-1,i=(1<>1,n=23===h?Math.pow(2,-24)-Math.pow(2,-77):0" - ",s=r?0:M-1,u=r?1:-1,l=o<0||0===o&&1/o<0?1:0;for(o=Math.abs(o),isNaN(o)||o===" - "1/0?(w=isNaN(o)?1:0,p=i):(p=Math.floor(Math.log(o)/Math.LN2),o*(f=Math.pow(2" - ",-p))<1&&(p--,f*=2),o+=p+N>=1?n/f:n*Math.pow(2,1-N),o*f>=2&&(p++,f/=2),p+N>=" - "i?(w=0,p=i):p+N>=1?(w=(o*f-1)*Math.pow(2,h),p+=N):(w=o*Math.pow(2,N-1)*Math." - "pow(2,h),p=0));h>=8;a[t+s]=255&w,s+=u,w/=256,h-=8);for(p=p<0;a[t" - "+s]=255&p,s+=u,p/=256,e-=8);a[t+s-u]|=128*l};\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/browserify/node_modules/buffer/node_modules/isarray/index.j" - "s\":[function(require,module,exports){\n" - "var toString={}.toString;module.exports=Array.isArray||function(r){return\"[" - "object Array]\"==toString.call(r)};\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/browserify/node_modules/crypto-browserify/index.js\":[funct" - "ion(require,module,exports){\n" - "\"use strict\";exports.randomBytes=exports.rng=exports.pseudoRandomBytes=exp" - "orts.prng=require(\"randombytes\"),exports.createHash=exports.Hash=require(\"" - "create-hash\"),exports.createHmac=exports.Hmac=require(\"create-hmac\");var " - "hashes=[\"sha1\",\"sha224\",\"sha256\",\"sha384\",\"sha512\",\"md5\",\"rmd16" - "0\"].concat(Object.keys(require(\"browserify-sign/algos\")));exports.getHash" - "es=function(){return hashes};var p=require(\"pbkdf2\");exports.pbkdf2=p.pbkd" - "f2,exports.pbkdf2Sync=p.pbkdf2Sync;var aes=require(\"browserify-cipher\");[\"" - "Cipher\",\"createCipher\",\"Cipheriv\",\"createCipheriv\",\"Decipher\",\"cre" - "ateDecipher\",\"Decipheriv\",\"createDecipheriv\",\"getCiphers\",\"listCiphe" - "rs\"].forEach(function(e){exports[e]=aes[e]});var dh=require(\"diffie-hellma" - "n\");[\"DiffieHellmanGroup\",\"createDiffieHellmanGroup\",\"getDiffieHellman" - "\",\"createDiffieHellman\",\"DiffieHellman\"].forEach(function(e){exports[e]" - "=dh[e]});var sign=require(\"browserify-sign\");[\"createSign\",\"Sign\",\"cr" - "eateVerify\",\"Verify\"].forEach(function(e){exports[e]=sign[e]}),exports.cr" - "eateECDH=require(\"create-ecdh\");var publicEncrypt=require(\"public-encrypt" - "\");[\"publicEncrypt\",\"privateEncrypt\",\"publicDecrypt\",\"privateDecrypt" - "\"].forEach(function(e){exports[e]=publicEncrypt[e]}),[\"createCredentials\"" - "].forEach(function(e){exports[e]=function(){throw new Error([\"sorry, \"+e+\"" - " is not implemented yet\",\"we accept pull requests\",\"https://github.com/c" - "rypto-browserify/crypto-browserify\"].join(\"\\n\"))}});\n" - "},{\"browserify-cipher\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_m" - "odules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserif" - "y/node_modules/browserify-cipher/browser.js\",\"browserify-sign\":\"/rethink" - "db/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules" - "/browserify/node_modules/crypto-browserify/node_modules/browserify-sign/brow" - "ser.js\",\"browserify-sign/algos\":\"/rethinkdb/build/external/admin-deps_2." - "0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/crypto" - "-browserify/node_modules/browserify-sign/algos.js\",\"create-ecdh\":\"/rethi" - "nkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modul" - "es/browserify/node_modules/crypto-browserify/node_modules/create-ecdh/browse" - "r.js\",\"create-hash\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_mod" - "ules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/" - "node_modules/create-hash/browser.js\",\"create-hmac\":\"/rethinkdb/build/ext" - "ernal/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/" - "node_modules/crypto-browserify/node_modules/create-hmac/browser.js\",\"diffi" - "e-hellman\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethin" - "kdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_module" - "s/diffie-hellman/browser.js\",\"pbkdf2\":\"/rethinkdb/build/external/admin-d" - "eps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/" - "crypto-browserify/node_modules/pbkdf2/browser.js\",\"public-encrypt\":\"/ret" - "hinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_mod" - "ules/browserify/node_modules/crypto-browserify/node_modules/public-encrypt/b" - "rowser.js\",\"randombytes\":\"/rethinkdb/build/external/admin-deps_2.0.4/nod" - "e_modules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browse" - "rify/node_modules/randombytes/browser.js\"}],\"/rethinkdb/build/external/adm" - "in-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modu" - "les/crypto-browserify/node_modules/bn.js/lib/bn.js\":[function(require,modul" - "e,exports){\n" - "!function(t,i){\"use strict\";function r(t,i){if(!t)throw new Error(i||\"Ass" - "ertion failed\")}function h(t,i){t.super_=i;var r=function(){};r.prototype=i" - ".prototype,t.prototype=new r,t.prototype.constructor=t}function n(t,i,r){ret" - "urn n.isBN(t)?t:(this.negative=0,this.words=null,this.length=0,this.red=null" - ",void(null!==t&&(\"le\"!==i&&\"be\"!==i||(r=i,i=10),this._init(t||0,i||10,r|" - "|\"be\"))))}function e(t,i,r){for(var h=0,n=Math.min(t.length,r),e=i;e=49&&o<=54?o-49+10:o>=17&&o<=22?o-17+1" - "0:15&o}return h}function o(t,i,r,h){for(var n=0,e=Math.min(t.length,r),o=i;o" - "=49?s-49+10:s>=17?s-17+10:s}retur" - "n n}function s(t){for(var i=new Array(t.bitLength()),r=0;r>>n}return i}function u(t,i,r){r.neg" - "ative=i.negative^t.negative;var h=t.length+i.length|0;r.length=h,h=h-1|0;var" - " n=0|t.words[0],e=0|i.words[0],o=n*e,s=67108863&o,u=o/67108864|0;r.words[0]=" - "s;for(var a=1;a>>26,m=67108863&u,f=Math.min(a,i.length-1" - "),d=Math.max(0,a-t.length+1);d<=f;d++){var p=a-d|0;n=0|t.words[p],e=0|i.word" - "s[d],o=n*e+m,l+=o/67108864|0,m=67108863&o}r.words[a]=0|m,u=0|l}return 0!==u?" - "r.words[a]=0|u:r.length--,r.strip()}function a(t,i,r){r.negative=i.negative^" - "t.negative,r.length=t.length+i.length;for(var h=0,n=0,e=0;e>>26)|0,n+=o>>>26,o&=6710" - "8863}r.words[e]=s,h=o,o=n}return 0!==h?r.words[e]=h:r.length--,r.strip()}fun" - "ction l(t,i,r){var h=new m;return h.mulp(t,i,r)}function m(t,i){this.x=t,thi" - "s.y=i}function f(t,i){this.name=t,this.p=new n(i,16),this.n=this.p.bitLength" - "(),this.k=new n(1).iushln(this.n).isub(this.p),this.tmp=this._tmp()}function" - " d(){f.call(this,\"k256\",\"ffffffff ffffffff ffffffff ffffffff ffffffff fff" - "fffff fffffffe fffffc2f\")}function p(){f.call(this,\"p224\",\"ffffffff ffff" - "ffff ffffffff ffffffff 00000000 00000000 00000001\")}function M(){f.call(thi" - "s,\"p192\",\"ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff\")}functi" - "on v(){f.call(this,\"25519\",\"7fffffffffffffff ffffffffffffffff fffffffffff" - "fffff ffffffffffffffed\")}function g(t){if(\"string\"==typeof t){var i=n._pr" - "ime(t);this.m=i.p,this.prime=i}else r(t.gtn(1),\"modulus must be greater tha" - "n 1\"),this.m=t,this.prime=null}function c(t){g.call(this,t),this.shift=this" - ".m.bitLength(),this.shift%26!==0&&(this.shift+=26-this.shift%26),this.r=new " - "n(1).iushln(this.shift),this.r2=this.imod(this.r.sqr()),this.rinv=this.r._in" - "vmp(this.m),this.minv=this.rinv.mul(this.r).isubn(1).div(this.m),this.minv=t" - "his.minv.umod(this.r),this.minv=this.r.sub(this.minv)}\"object\"==typeof t?t" - ".exports=n:i.BN=n,n.BN=n,n.wordSize=26;var w;try{w=require(\"buffer\").Buffe" - "r}catch(t){}n.isBN=function(t){return t instanceof n||null!==t&&\"object\"==" - "typeof t&&t.constructor.wordSize===n.wordSize&&Array.isArray(t.words)},n.max" - "=function(t,i){return t.cmp(i)>0?t:i},n.min=function(t,i){return t.cmp(i)<0?" - "t:i},n.prototype._init=function(t,i,h){if(\"number\"==typeof t)return this._" - "initNumber(t,i,h);if(\"object\"==typeof t)return this._initArray(t,i,h);\"he" - "x\"===i&&(i=16),r(i===(0|i)&&i>=2&&i<=36),t=t.toString().replace(/\\s+/g,\"\"" - ");var n=0;\"-\"===t[0]&&n++,16===i?this._parseHex(t,n):this._parseBase(t,i,n" - "),\"-\"===t[0]&&(this.negative=1),this.strip(),\"le\"===h&&this._initArray(t" - "his.toArray(),i,h)},n.prototype._initNumber=function(t,i,h){t<0&&(this.negat" - "ive=1,t=-t),t<67108864?(this.words=[67108863&t],this.length=1):t<45035996273" - "70496?(this.words=[67108863&t,t/67108864&67108863],this.length=2):(r(t<90071" - "99254740992),this.words=[67108863&t,t/67108864&67108863,1],this.length=3),\"" - "le\"===h&&this._initArray(this.toArray(),i,h)},n.prototype._initArray=functi" - "on(t,i,h){if(r(\"number\"==typeof t.length),t.length<=0)return this.words=[0" - "],this.length=1,this;this.length=Math.ceil(t.length/3),this.words=new Array(" - "this.length);for(var n=0;n=0;n-=3)o=t[n]|t[n-1]<<8|t[n-2]<<16,this.wor" - "ds[e]|=o<>>26-s&67108863,s+=24,s>=26&&(s-=26,e" - "++);else if(\"le\"===h)for(n=0,e=0;n>>26-s&67108863,s+=24,s>=2" - "6&&(s-=26,e++);return this.strip()},n.prototype._parseHex=function(t,i){this" - ".length=Math.ceil((t.length-i)/6),this.words=new Array(this.length);for(var " - "r=0;r=i;" - "r-=6)n=e(t,r,r+6),this.words[h]|=n<>>26-o&419" - "4303,o+=24,o>=26&&(o-=26,h++);r+6!==i&&(n=e(t,i,r+6),this.words[h]|=n<>>26-o&4194303),this.strip()},n.prototype._parseBas" - "e=function(t,i,r){this.words=[0],this.length=1;for(var h=0,n=1;n<=67108863;n" - "*=i)h++;h--,n=n/i|0;for(var e=t.length-r,s=e%h,u=Math.min(e,e-s)+r,a=0,l=r;l" - "1&&0===this.words[this.length-1];)this.length--;return this._normSign()}" - ",n.prototype._normSign=function(){return 1===this.length&&0===this.words[0]&" - "&(this.negative=0),this},n.prototype.inspect=function(){return(this.red?\"\"};var y=[\"\",\"0\",\"00\",\"000\"" - ",\"0000\",\"00000\",\"000000\",\"0000000\",\"00000000\",\"000000000\",\"0000", - 65489) + std::string( - "000000\",\"00000000000\",\"000000000000\",\"0000000000000\",\"00000000000000" - "\",\"000000000000000\",\"0000000000000000\",\"00000000000000000\",\"00000000" - "0000000000\",\"0000000000000000000\",\"00000000000000000000\",\"000000000000" - "000000000\",\"0000000000000000000000\",\"00000000000000000000000\",\"0000000" - "00000000000000000\",\"0000000000000000000000000\"],b=[0,0,25,16,12,11,10,9,8" - ",8,7,7,7,7,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5],_=[0,0,33554432,43" - "046721,16777216,48828125,60466176,40353607,16777216,43046721,1e7,19487171,35" - "831808,62748517,7529536,11390625,16777216,24137569,34012224,47045881,64e6,40" - "84101,5153632,6436343,7962624,9765625,11881376,14348907,17210368,20511149,24" - "3e5,28629151,33554432,39135393,45435424,52521875,60466176];n.prototype.toStr" - "ing=function(t,i){t=t||10,i=0|i||1;var h;if(16===t||\"hex\"===t){h=\"\";for(" - "var n=0,e=0,o=0;o>>24-n&16777215,h=0!==e||o!==this.length-1?y[6-u.length]+u" - "+h:u+h,n+=2,n>=26&&(n-=26,o--)}for(0!==e&&(h=e.toString(16)+h);h.length%i!==" - "0;)h=\"0\"+h;return 0!==this.negative&&(h=\"-\"+h),h}if(t===(0|t)&&t>=2&&t<=" - "36){var a=b[t],l=_[t];h=\"\";var m=this.clone();for(m.negative=0;!m.isZero()" - ";){var f=m.modn(l).toString(t);m=m.idivn(l),h=m.isZero()?f+h:y[a-f.length]+f" - "+h}for(this.isZero()&&(h=\"0\"+h);h.length%i!==0;)h=\"0\"+h;return 0!==this." - "negative&&(h=\"-\"+h),h}r(!1,\"Base should be between 2 and 36\")},n.prototy" - "pe.toNumber=function(){var t=this.words[0];return 2===this.length?t+=6710886" - "4*this.words[1]:3===this.length&&1===this.words[2]?t+=4503599627370496+67108" - "864*this.words[1]:this.length>2&&r(!1,\"Number can only safely store up to 5" - "3 bits\"),0!==this.negative?-t:t},n.prototype.toJSON=function(){return this." - "toString(16)},n.prototype.toBuffer=function(t,i){return r(\"undefined\"!=typ" - "eof w),this.toArrayLike(w,t,i)},n.prototype.toArray=function(t,i){return thi" - "s.toArrayLike(Array,t,i)},n.prototype.toArrayLike=function(t,i,h){var n=this" - ".byteLength(),e=h||Math.max(1,n);r(n<=e,\"byte array longer than desired len" - "gth\"),r(e>0,\"Requested array length <= 0\"),this.strip();var o,s,u=\"le\"=" - "==i,a=new t(e),l=this.clone();if(u){for(s=0;!l.isZero();s++)o=l.andln(255),l" - ".iushrn(8),a[s]=o;for(;s=4096&&(r+=13,i>>>=13),i>=64&&(r+=7,i" - ">>>=7),i>=8&&(r+=4,i>>>=4),i>=2&&(r+=2,i>>>=2),r+i},n.prototype._zeroBits=fu" - "nction(t){if(0===t)return 26;var i=t,r=0;return 0===(8191&i)&&(r+=13,i>>>=13" - "),0===(127&i)&&(r+=7,i>>>=7),0===(15&i)&&(r+=4,i>>>=4),0===(3&i)&&(r+=2,i>>>" - "=2),0===(1&i)&&r++,r},n.prototype.bitLength=function(){var t=this.words[this" - ".length-1],i=this._countBits(t);return 26*(this.length-1)+i},n.prototype.zer" - "oBits=function(){if(this.isZero())return 0;for(var t=0,i=0;it.length?thi" - "s.clone().ior(t):t.clone().ior(this)},n.prototype.uor=function(t){return thi" - "s.length>t.length?this.clone().iuor(t):t.clone().iuor(this)},n.prototype.iua" - "nd=function(t){var i;i=this.length>t.length?t:this;for(var r=0;rt.length?this" - ".clone().iand(t):t.clone().iand(this)},n.prototype.uand=function(t){return t" - "his.length>t.length?this.clone().iuand(t):t.clone().iuand(this)},n.prototype" - ".iuxor=function(t){var i,r;this.length>t.length?(i=this,r=t):(i=t,r=this);fo" - "r(var h=0;ht.length?thi" - "s.clone().ixor(t):t.clone().ixor(this)},n.prototype.uxor=function(t){return " - "this.length>t.length?this.clone().iuxor(t):t.clone().iuxor(this)},n.prototyp" - "e.inotn=function(t){r(\"number\"==typeof t&&t>=0);var i=0|Math.ceil(t/26),h=" - "t%26;this._expand(i),h>0&&i--;for(var n=0;n0&&(this.words[n]=~this.words[n]&67108863>>26-h),this.s" - "trip()},n.prototype.notn=function(t){return this.clone().inotn(t)},n.prototy" - "pe.setn=function(t,i){r(\"number\"==typeof t&&t>=0);var h=t/26|0,n=t%26;retu" - "rn this._expand(h+1),i?this.words[h]=this.words[h]|1<t.length?(r" - "=this,h=t):(r=t,h=this);for(var n=0,e=0;e>>26;for(;0!==n&&e>>26;if(this.length=r.length,0" - "!==n)this.words[this.length]=n,this.length++;else if(r!==this)for(;et.length?this.clone().iadd(t):t.clone().iadd" - "(this)},n.prototype.isub=function(t){if(0!==t.negative){t.negative=0;var i=t" - "his.iadd(t);return t.negative=1,i._normSign()}if(0!==this.negative)return th" - "is.negative=0,this.iadd(t),this.negative=1,this._normSign();var r=this.cmp(t" - ");if(0===r)return this.negative=0,this.length=1,this.words[0]=0,this;var h,n" - ";r>0?(h=this,n=t):(h=t,n=this);for(var e=0,o=0;o>26,this.words[o]=67108863&i;for(;0!==e&&o>26,this.words[o]=67108863&i;if(0===e&&o>>13,d=0|o[1],p=81" - "91&d,M=d>>>13,v=0|o[2],g=8191&v,c=v>>>13,w=0|o[3],y=8191&w,b=w>>>13,_=0|o[4]" - ",k=8191&_,A=_>>>13,x=0|o[5],S=8191&x,Z=x>>>13,q=0|o[6],R=8191&q,B=q>>>13,N=0" - "|o[7],L=8191&N,I=N>>>13,z=0|o[8],T=8191&z,E=z>>>13,O=0|o[9],j=8191&O,K=O>>>1" - "3,P=0|s[0],F=8191&P,C=P>>>13,D=0|s[1],H=8191&D,J=D>>>13,U=0|s[2],G=8191&U,Q=" - "U>>>13,V=0|s[3],W=8191&V,X=V>>>13,Y=0|s[4],$=8191&Y,tt=Y>>>13,it=0|s[5],rt=8" - "191&it,ht=it>>>13,nt=0|s[6],et=8191&nt,ot=nt>>>13,st=0|s[7],ut=8191&st,at=st" - ">>>13,lt=0|s[8],mt=8191<,ft=lt>>>13,dt=0|s[9],pt=8191&dt,Mt=dt>>>13;r.nega" - "tive=t.negative^i.negative,r.length=19,h=Math.imul(m,F),n=Math.imul(m,C),n=n" - "+Math.imul(f,F)|0,e=Math.imul(f,C);var vt=(a+h|0)+((8191&n)<<13)|0;a=(e+(n>>" - ">13)|0)+(vt>>>26)|0,vt&=67108863,h=Math.imul(p,F),n=Math.imul(p,C),n=n+Math." - "imul(M,F)|0,e=Math.imul(M,C),h=h+Math.imul(m,H)|0,n=n+Math.imul(m,J)|0,n=n+M" - "ath.imul(f,H)|0,e=e+Math.imul(f,J)|0;var gt=(a+h|0)+((8191&n)<<13)|0;a=(e+(n" - ">>>13)|0)+(gt>>>26)|0,gt&=67108863,h=Math.imul(g,F),n=Math.imul(g,C),n=n+Mat" - "h.imul(c,F)|0,e=Math.imul(c,C),h=h+Math.imul(p,H)|0,n=n+Math.imul(p,J)|0,n=n" - "+Math.imul(M,H)|0,e=e+Math.imul(M,J)|0,h=h+Math.imul(m,G)|0,n=n+Math.imul(m," - "Q)|0,n=n+Math.imul(f,G)|0,e=e+Math.imul(f,Q)|0;var ct=(a+h|0)+((8191&n)<<13)" - "|0;a=(e+(n>>>13)|0)+(ct>>>26)|0,ct&=67108863,h=Math.imul(y,F),n=Math.imul(y," - "C),n=n+Math.imul(b,F)|0,e=Math.imul(b,C),h=h+Math.imul(g,H)|0,n=n+Math.imul(" - "g,J)|0,n=n+Math.imul(c,H)|0,e=e+Math.imul(c,J)|0,h=h+Math.imul(p,G)|0,n=n+Ma" - "th.imul(p,Q)|0,n=n+Math.imul(M,G)|0,e=e+Math.imul(M,Q)|0,h=h+Math.imul(m,W)|" - "0,n=n+Math.imul(m,X)|0,n=n+Math.imul(f,W)|0,e=e+Math.imul(f,X)|0;var wt=(a+h" - "|0)+((8191&n)<<13)|0;a=(e+(n>>>13)|0)+(wt>>>26)|0,wt&=67108863,h=Math.imul(k" - ",F),n=Math.imul(k,C),n=n+Math.imul(A,F)|0,e=Math.imul(A,C),h=h+Math.imul(y,H" - ")|0,n=n+Math.imul(y,J)|0,n=n+Math.imul(b,H)|0,e=e+Math.imul(b,J)|0,h=h+Math." - "imul(g,G)|0,n=n+Math.imul(g,Q)|0,n=n+Math.imul(c,G)|0,e=e+Math.imul(c,Q)|0,h" - "=h+Math.imul(p,W)|0,n=n+Math.imul(p,X)|0,n=n+Math.imul(M,W)|0,e=e+Math.imul(" - "M,X)|0,h=h+Math.imul(m,$)|0,n=n+Math.imul(m,tt)|0,n=n+Math.imul(f,$)|0,e=e+M" - "ath.imul(f,tt)|0;var yt=(a+h|0)+((8191&n)<<13)|0;a=(e+(n>>>13)|0)+(yt>>>26)|" - "0,yt&=67108863,h=Math.imul(S,F),n=Math.imul(S,C),n=n+Math.imul(Z,F)|0,e=Math" - ".imul(Z,C),h=h+Math.imul(k,H)|0,n=n+Math.imul(k,J)|0,n=n+Math.imul(A,H)|0,e=" - "e+Math.imul(A,J)|0,h=h+Math.imul(y,G)|0,n=n+Math.imul(y,Q)|0,n=n+Math.imul(b" - ",G)|0,e=e+Math.imul(b,Q)|0,h=h+Math.imul(g,W)|0,n=n+Math.imul(g,X)|0,n=n+Mat" - "h.imul(c,W)|0,e=e+Math.imul(c,X)|0,h=h+Math.imul(p,$)|0,n=n+Math.imul(p,tt)|" - "0,n=n+Math.imul(M,$)|0,e=e+Math.imul(M,tt)|0,h=h+Math.imul(m,rt)|0,n=n+Math." - "imul(m,ht)|0,n=n+Math.imul(f,rt)|0,e=e+Math.imul(f,ht)|0;var bt=(a+h|0)+((81" - "91&n)<<13)|0;a=(e+(n>>>13)|0)+(bt>>>26)|0,bt&=67108863,h=Math.imul(R,F),n=Ma" - "th.imul(R,C),n=n+Math.imul(B,F)|0,e=Math.imul(B,C),h=h+Math.imul(S,H)|0,n=n+" - "Math.imul(S,J)|0,n=n+Math.imul(Z,H)|0,e=e+Math.imul(Z,J)|0,h=h+Math.imul(k,G" - ")|0,n=n+Math.imul(k,Q)|0,n=n+Math.imul(A,G)|0,e=e+Math.imul(A,Q)|0,h=h+Math." - "imul(y,W)|0,n=n+Math.imul(y,X)|0,n=n+Math.imul(b,W)|0,e=e+Math.imul(b,X)|0,h" - "=h+Math.imul(g,$)|0,n=n+Math.imul(g,tt)|0,n=n+Math.imul(c,$)|0,e=e+Math.imul" - "(c,tt)|0,h=h+Math.imul(p,rt)|0,n=n+Math.imul(p,ht)|0,n=n+Math.imul(M,rt)|0,e" - "=e+Math.imul(M,ht)|0,h=h+Math.imul(m,et)|0,n=n+Math.imul(m,ot)|0,n=n+Math.im" - "ul(f,et)|0,e=e+Math.imul(f,ot)|0;var _t=(a+h|0)+((8191&n)<<13)|0;a=(e+(n>>>1" - "3)|0)+(_t>>>26)|0,_t&=67108863,h=Math.imul(L,F),n=Math.imul(L,C),n=n+Math.im" - "ul(I,F)|0,e=Math.imul(I,C),h=h+Math.imul(R,H)|0,n=n+Math.imul(R,J)|0,n=n+Mat" - "h.imul(B,H)|0,e=e+Math.imul(B,J)|0,h=h+Math.imul(S,G)|0,n=n+Math.imul(S,Q)|0" - ",n=n+Math.imul(Z,G)|0,e=e+Math.imul(Z,Q)|0,h=h+Math.imul(k,W)|0,n=n+Math.imu" - "l(k,X)|0,n=n+Math.imul(A,W)|0,e=e+Math.imul(A,X)|0,h=h+Math.imul(y,$)|0,n=n+" - "Math.imul(y,tt)|0,n=n+Math.imul(b,$)|0,e=e+Math.imul(b,tt)|0,h=h+Math.imul(g" - ",rt)|0,n=n+Math.imul(g,ht)|0,n=n+Math.imul(c,rt)|0,e=e+Math.imul(c,ht)|0,h=h" - "+Math.imul(p,et)|0,n=n+Math.imul(p,ot)|0,n=n+Math.imul(M,et)|0,e=e+Math.imul" - "(M,ot)|0,h=h+Math.imul(m,ut)|0,n=n+Math.imul(m,at)|0,n=n+Math.imul(f,ut)|0,e" - "=e+Math.imul(f,at)|0;var kt=(a+h|0)+((8191&n)<<13)|0;a=(e+(n>>>13)|0)+(kt>>>" - "26)|0,kt&=67108863,h=Math.imul(T,F),n=Math.imul(T,C),n=n+Math.imul(E,F)|0,e=" - "Math.imul(E,C),h=h+Math.imul(L,H)|0,n=n+Math.imul(L,J)|0,n=n+Math.imul(I,H)|" - "0,e=e+Math.imul(I,J)|0,h=h+Math.imul(R,G)|0,n=n+Math.imul(R,Q)|0,n=n+Math.im" - "ul(B,G)|0,e=e+Math.imul(B,Q)|0,h=h+Math.imul(S,W)|0,n=n+Math.imul(S,X)|0,n=n" - "+Math.imul(Z,W)|0,e=e+Math.imul(Z,X)|0,h=h+Math.imul(k,$)|0,n=n+Math.imul(k," - "tt)|0,n=n+Math.imul(A,$)|0,e=e+Math.imul(A,tt)|0,h=h+Math.imul(y,rt)|0,n=n+M" - "ath.imul(y,ht)|0,n=n+Math.imul(b,rt)|0,e=e+Math.imul(b,ht)|0,h=h+Math.imul(g" - ",et)|0,n=n+Math.imul(g,ot)|0,n=n+Math.imul(c,et)|0,e=e+Math.imul(c,ot)|0,h=h" - "+Math.imul(p,ut)|0,n=n+Math.imul(p,at)|0,n=n+Math.imul(M,ut)|0,e=e+Math.imul" - "(M,at)|0,h=h+Math.imul(m,mt)|0,n=n+Math.imul(m,ft)|0,n=n+Math.imul(f,mt)|0,e" - "=e+Math.imul(f,ft)|0;var At=(a+h|0)+((8191&n)<<13)|0;a=(e+(n>>>13)|0)+(At>>>" - "26)|0,At&=67108863,h=Math.imul(j,F),n=Math.imul(j,C),n=n+Math.imul(K,F)|0,e=" - "Math.imul(K,C),h=h+Math.imul(T,H)|0,n=n+Math.imul(T,J)|0,n=n+Math.imul(E,H)|" - "0,e=e+Math.imul(E,J)|0,h=h+Math.imul(L,G)|0,n=n+Math.imul(L,Q)|0,n=n+Math.im" - "ul(I,G)|0,e=e+Math.imul(I,Q)|0,h=h+Math.imul(R,W)|0,n=n+Math.imul(R,X)|0,n=n" - "+Math.imul(B,W)|0,e=e+Math.imul(B,X)|0,h=h+Math.imul(S,$)|0,n=n+Math.imul(S," - "tt)|0,n=n+Math.imul(Z,$)|0,e=e+Math.imul(Z,tt)|0,h=h+Math.imul(k,rt)|0,n=n+M" - "ath.imul(k,ht)|0,n=n+Math.imul(A,rt)|0,e=e+Math.imul(A,ht)|0,h=h+Math.imul(y" - ",et)|0,n=n+Math.imul(y,ot)|0,n=n+Math.imul(b,et)|0,e=e+Math.imul(b,ot)|0,h=h" - "+Math.imul(g,ut)|0,n=n+Math.imul(g,at)|0,n=n+Math.imul(c,ut)|0,e=e+Math.imul" - "(c,at)|0,h=h+Math.imul(p,mt)|0,n=n+Math.imul(p,ft)|0,n=n+Math.imul(M,mt)|0,e" - "=e+Math.imul(M,ft)|0,h=h+Math.imul(m,pt)|0,n=n+Math.imul(m,Mt)|0,n=n+Math.im" - "ul(f,pt)|0,e=e+Math.imul(f,Mt)|0;var xt=(a+h|0)+((8191&n)<<13)|0;a=(e+(n>>>1" - "3)|0)+(xt>>>26)|0,xt&=67108863,h=Math.imul(j,H),n=Math.imul(j,J),n=n+Math.im" - "ul(K,H)|0,e=Math.imul(K,J),h=h+Math.imul(T,G)|0,n=n+Math.imul(T,Q)|0,n=n+Mat" - "h.imul(E,G)|0,e=e+Math.imul(E,Q)|0,h=h+Math.imul(L,W)|0,n=n+Math.imul(L,X)|0" - ",n=n+Math.imul(I,W)|0,e=e+Math.imul(I,X)|0,h=h+Math.imul(R,$)|0,n=n+Math.imu" - "l(R,tt)|0,n=n+Math.imul(B,$)|0,e=e+Math.imul(B,tt)|0,h=h+Math.imul(S,rt)|0,n" - "=n+Math.imul(S,ht)|0,n=n+Math.imul(Z,rt)|0,e=e+Math.imul(Z,ht)|0,h=h+Math.im" - "ul(k,et)|0,n=n+Math.imul(k,ot)|0,n=n+Math.imul(A,et)|0,e=e+Math.imul(A,ot)|0" - ",h=h+Math.imul(y,ut)|0,n=n+Math.imul(y,at)|0,n=n+Math.imul(b,ut)|0,e=e+Math." - "imul(b,at)|0,h=h+Math.imul(g,mt)|0,n=n+Math.imul(g,ft)|0,n=n+Math.imul(c,mt)" - "|0,e=e+Math.imul(c,ft)|0,h=h+Math.imul(p,pt)|0,n=n+Math.imul(p,Mt)|0,n=n+Mat" - "h.imul(M,pt)|0,e=e+Math.imul(M,Mt)|0;var St=(a+h|0)+((8191&n)<<13)|0;a=(e+(n" - ">>>13)|0)+(St>>>26)|0,St&=67108863,h=Math.imul(j,G),n=Math.imul(j,Q),n=n+Mat" - "h.imul(K,G)|0,e=Math.imul(K,Q),h=h+Math.imul(T,W)|0,n=n+Math.imul(T,X)|0,n=n" - "+Math.imul(E,W)|0,e=e+Math.imul(E,X)|0,h=h+Math.imul(L,$)|0,n=n+Math.imul(L," - "tt)|0,n=n+Math.imul(I,$)|0,e=e+Math.imul(I,tt)|0,h=h+Math.imul(R,rt)|0,n=n+M" - "ath.imul(R,ht)|0,n=n+Math.imul(B,rt)|0,e=e+Math.imul(B,ht)|0,h=h+Math.imul(S" - ",et)|0,n=n+Math.imul(S,ot)|0,n=n+Math.imul(Z,et)|0,e=e+Math.imul(Z,ot)|0,h=h" - "+Math.imul(k,ut)|0,n=n+Math.imul(k,at)|0,n=n+Math.imul(A,ut)|0,e=e+Math.imul" - "(A,at)|0,h=h+Math.imul(y,mt)|0,n=n+Math.imul(y,ft)|0,n=n+Math.imul(b,mt)|0,e" - "=e+Math.imul(b,ft)|0,h=h+Math.imul(g,pt)|0,n=n+Math.imul(g,Mt)|0,n=n+Math.im" - "ul(c,pt)|0,e=e+Math.imul(c,Mt)|0;var Zt=(a+h|0)+((8191&n)<<13)|0;a=(e+(n>>>1" - "3)|0)+(Zt>>>26)|0,Zt&=67108863,h=Math.imul(j,W),n=Math.imul(j,X),n=n+Math.im" - "ul(K,W)|0,e=Math.imul(K,X),h=h+Math.imul(T,$)|0,n=n+Math.imul(T,tt)|0,n=n+Ma" - "th.imul(E,$)|0,e=e+Math.imul(E,tt)|0,h=h+Math.imul(L,rt)|0,n=n+Math.imul(L,h" - "t)|0,n=n+Math.imul(I,rt)|0,e=e+Math.imul(I,ht)|0,h=h+Math.imul(R,et)|0,n=n+M" - "ath.imul(R,ot)|0,n=n+Math.imul(B,et)|0,e=e+Math.imul(B,ot)|0,h=h+Math.imul(S" - ",ut)|0,n=n+Math.imul(S,at)|0,n=n+Math.imul(Z,ut)|0,e=e+Math.imul(Z,at)|0,h=h" - "+Math.imul(k,mt)|0,n=n+Math.imul(k,ft)|0,n=n+Math.imul(A,mt)|0,e=e+Math.imul" - "(A,ft)|0,h=h+Math.imul(y,pt)|0,n=n+Math.imul(y,Mt)|0,n=n+Math.imul(b,pt)|0,e" - "=e+Math.imul(b,Mt)|0;var qt=(a+h|0)+((8191&n)<<13)|0;a=(e+(n>>>13)|0)+(qt>>>" - "26)|0,qt&=67108863,h=Math.imul(j,$),n=Math.imul(j,tt),n=n+Math.imul(K,$)|0,e" - "=Math.imul(K,tt),h=h+Math.imul(T,rt)|0,n=n+Math.imul(T,ht)|0,n=n+Math.imul(E" - ",rt)|0,e=e+Math.imul(E,ht)|0,h=h+Math.imul(L,et)|0,n=n+Math.imul(L,ot)|0,n=n" - "+Math.imul(I,et)|0,e=e+Math.imul(I,ot)|0,h=h+Math.imul(R,ut)|0,n=n+Math.imul" - "(R,at)|0,n=n+Math.imul(B,ut)|0,e=e+Math.imul(B,at)|0,h=h+Math.imul(S,mt)|0,n" - "=n+Math.imul(S,ft)|0,n=n+Math.imul(Z,mt)|0,e=e+Math.imul(Z,ft)|0,h=h+Math.im" - "ul(k,pt)|0,n=n+Math.imul(k,Mt)|0,n=n+Math.imul(A,pt)|0,e=e+Math.imul(A,Mt)|0" - ";var Rt=(a+h|0)+((8191&n)<<13)|0;a=(e+(n>>>13)|0)+(Rt>>>26)|0,Rt&=67108863,h" - "=Math.imul(j,rt),n=Math.imul(j,ht),n=n+Math.imul(K,rt)|0,e=Math.imul(K,ht),h" - "=h+Math.imul(T,et)|0,n=n+Math.imul(T,ot)|0,n=n+Math.imul(E,et)|0,e=e+Math.im" - "ul(E,ot)|0,h=h+Math.imul(L,ut)|0,n=n+Math.imul(L,at)|0,n=n+Math.imul(I,ut)|0" - ",e=e+Math.imul(I,at)|0,h=h+Math.imul(R,mt)|0,n=n+Math.imul(R,ft)|0,n=n+Math." - "imul(B,mt)|0,e=e+Math.imul(B,ft)|0,h=h+Math.imul(S,pt)|0,n=n+Math.imul(S,Mt)" - "|0,n=n+Math.imul(Z,pt)|0,e=e+Math.imul(Z,Mt)|0;var Bt=(a+h|0)+((8191&n)<<13)" - "|0;a=(e+(n>>>13)|0)+(Bt>>>26)|0,Bt&=67108863,h=Math.imul(j,et),n=Math.imul(j" - ",ot),n=n+Math.imul(K,et)|0,e=Math.imul(K,ot),h=h+Math.imul(T,ut)|0,n=n+Math." - "imul(T,at)|0,n=n+Math.imul(E,ut)|0,e=e+Math.imul(E,at)|0,h=h+Math.imul(L,mt)" - "|0,n=n+Math.imul(L,ft)|0,n=n+Math.imul(I,mt)|0,e=e+Math.imul(I,ft)|0,h=h+Mat" - "h.imul(R,pt)|0,n=n+Math.imul(R,Mt)|0,n=n+Math.imul(B,pt)|0,e=e+Math.imul(B,M" - "t)|0;var Nt=(a+h|0)+((8191&n)<<13)|0;a=(e+(n>>>13)|0)+(Nt>>>26)|0,Nt&=671088" - "63,h=Math.imul(j,ut),n=Math.imul(j,at),n=n+Math.imul(K,ut)|0,e=Math.imul(K,a" - "t),h=h+Math.imul(T,mt)|0,n=n+Math.imul(T,ft)|0,n=n+Math.imul(E,mt)|0,e=e+Mat" - "h.imul(E,ft)|0,h=h+Math.imul(L,pt)|0,n=n+Math.imul(L,Mt)|0,n=n+Math.imul(I,p" - "t)|0,e=e+Math.imul(I,Mt)|0;var Lt=(a+h|0)+((8191&n)<<13)|0;a=(e+(n>>>13)|0)+" - "(Lt>>>26)|0,Lt&=67108863,h=Math.imul(j,mt),n=Math.imul(j,ft),n=n+Math.imul(K" - ",mt)|0,e=Math.imul(K,ft),h=h+Math.imul(T,pt)|0,n=n+Math.imul(T,Mt)|0,n=n+Mat" - "h.imul(E,pt)|0,e=e+Math.imul(E,Mt)|0;var It=(a+h|0)+((8191&n)<<13)|0;a=(e+(n" - ">>>13)|0)+(It>>>26)|0,It&=67108863,h=Math.imul(j,pt),n=Math.imul(j,Mt),n=n+M" - "ath.imul(K,pt)|0,e=Math.imul(K,Mt);var zt=(a+h|0)+((8191&n)<<13)|0;return a=" - "(e+(n>>>13)|0)+(zt>>>26)|0,zt&=67108863,u[0]=vt,u[1]=gt,u[2]=ct,u[3]=wt,u[4]" - "=yt,u[5]=bt,u[6]=_t,u[7]=kt,u[8]=At,u[9]=xt,u[10]=St,u[11]=Zt,u[12]=qt,u[13]" - "=Rt,u[14]=Bt,u[15]=Nt,u[16]=Lt,u[17]=It,u[18]=zt,0!==a&&(u[19]=a,r.length++)" - ",r};Math.imul||(k=u),n.prototype.mulTo=function(t,i){var r,h=this.length+t.l" - "ength;return r=10===this.length&&10===t.length?k(this,t,i):h<63?u(this,t,i):" - "h<1024?a(this,t,i):l(this,t,i)},m.prototype.makeRBT=function(t){for(var i=ne" - "w Array(t),r=n.prototype._countBits(t)-1,h=0;h>=1;return h},m.prototype.permute=fun" - "ction(t,i,r,h,n,e){for(var o=0;o>>=1)n++;return 1<>>=13,h[2*o+1]=8191&e,e>>>=13;for(o=2*i;o>=26,i+=n/67108864|0,i+=e>>>26,this.words[h]=67108863&e}re" - "turn 0!==i&&(this.words[h]=i,this.length++),this},n.prototype.muln=function(" - "t){return this.clone().imuln(t)},n.prototype.sqr=function(){return this.mul(" - "this)},n.prototype.isqr=function(){return this.imul(this.clone())},n.prototy" - "pe.pow=function(t){var i=s(t);if(0===i.length)return new n(1);for(var r=this" - ",h=0;h=0);var i,h=t%26,n=(t-h)/26,e=67108863" - ">>>26-h<<26-h;if(0!==h){var o=0;for(i=0;i>>26-h}o&&(this.words[i]" - "=o,this.length++)}if(0!==n){for(i=this.length-1;i>=0;i--)this.words[i+n]=thi" - "s.words[i];for(i=0;i=0);var n;n=i?" - "(i-i%26)/26:0;var e=t%26,o=Math.min((t-e)/26,this.length),s=67108863^6710886" - "3>>>e<o)for(this.length-=o,a=0;a" - "=0&&(0!==l||a>=n);a--){var m=0|this.word" - "s[a];this.words[a]=l<<26-e|m>>>e,l=m&s}return u&&0!==l&&(u.words[u.length++]" - "=l),0===this.length&&(this.words[0]=0,this.length=1),this.strip()},n.prototy" - "pe.ishrn=function(t,i,h){return r(0===this.negative),this.iushrn(t,i,h)},n.p" - "rototype.shln=function(t){return this.clone().ishln(t)},n.prototype.ushln=fu" - "nction(t){return this.clone().iushln(t)},n.prototype.shrn=function(t){return" - " this.clone().ishrn(t)},n.prototype.ushrn=function(t){return this.clone().iu" - "shrn(t)},n.prototype.testn=function(t){r(\"number\"==typeof t&&t>=0);var i=t" - "%26,h=(t-i)/26,n=1<=0);var i=t" - "%26,h=(t-i)/26;if(r(0===this.negative,\"imaskn works only with positive numb" - "ers\"),this.length<=h)return this;if(0!==i&&h++,this.length=Math.min(h,this." - "length),0!==i){var n=67108863^67108863>>>i<=67108864;i" - "++)this.words[i]-=67108864,i===this.length-1?this.words[i+1]=1:this.words[i+" - "1]++;return this.length=Math.max(this.length,i+1),this},n.prototype.isubn=fu" - "nction(t){if(r(\"number\"==typeof t),r(t<67108864),t<0)return this.iaddn(-t)" - ";if(0!==this.negative)return this.negative=0,this.iaddn(t),this.negative=1,t" - "his;if(this.words[0]-=t,1===this.length&&this.words[0]<0)this.words[0]=-this" - ".words[0],this.negative=1;else for(var i=0;i>26)-(u/67108864|0),this.words[n+h]=67108863&o}for(;n>26,this.words[n+h]=67108863&o;if(0=" - "==s)return this.strip();for(r(s===-1),s=0,n=0;n>26,this.words[n]=67108863&o;return this.negative=1,this.stri" - "p()},n.prototype._wordDiv=function(t,i){var r=this.length-t.length,h=this.cl" - "one(),e=t,o=0|e.words[e.length-1],s=this._countBits(o);r=26-s,0!==r&&(e=e.us" - "hln(r),h.iushln(r),o=0|e.words[e.length-1]);var u,a=h.length-e.length;if(\"m" - "od\"!==i){u=new n(null),u.length=a+1,u.words=new Array(u.length);for(var l=0" - ";l=0;f--){var d=67108864*(0|h.wor" - "ds[e.length+f])+(0|h.words[e.length+f-1]);for(d=Math.min(d/o|0,67108863),h._" - "ishlnsubmul(e,d,f);0!==h.negative;)d--,h.negative=0,h._ishlnsubmul(e,1,f),h." - "isZero()||(h.negative^=1);u&&(u.words[f]=d)}return u&&u.strip(),h.strip(),\"" - "div\"!==i&&0!==r&&h.iushrn(r),{div:u||null,mod:h}},n.prototype.divmod=functi" - "on(t,i,h){if(r(!t.isZero()),this.isZero())return{div:new n(0),mod:new n(0)};" - "var e,o,s;return 0!==this.negative&&0===t.negative?(s=this.neg().divmod(t,i)" - ",\"mod\"!==i&&(e=s.div.neg()),\"div\"!==i&&(o=s.mod.neg(),h&&0!==o.negative&" - "&o.iadd(t)),{div:e,mod:o}):0===this.negative&&0!==t.negative?(s=this.divmod(" - "t.neg(),i),\"mod\"!==i&&(e=s.div.neg()),{div:e,mod:s.mod}):0!==(this.negativ" - "e&t.negative)?(s=this.neg().divmod(t.neg(),i),\"div\"!==i&&(o=s.mod.neg(),h&" - "&0!==o.negative&&o.isub(t)),{div:s.div,mod:o}):t.length>this.length||this.cm" - "p(t)<0?{div:new n(0),mod:this}:1===t.length?\"div\"===i?{div:this.divn(t.wor" - "ds[0]),mod:null}:\"mod\"===i?{div:null,mod:new n(this.modn(t.words[0]))}:{di" - "v:this.divn(t.words[0]),mod:new n(this.modn(t.words[0]))}:this._wordDiv(t,i)" - "},n.prototype.div=function(t){return this.divmod(t,\"div\",!1).div},n.protot" - "ype.mod=function(t){return this.divmod(t,\"mod\",!1).mod},n.prototype.umod=f" - "unction(t){return this.divmod(t,\"mod\",!0).mod},n.prototype.divRound=functi" - "on(t){var i=this.divmod(t);if(i.mod.isZero())return i.div;var r=0!==i.div.ne" - "gative?i.mod.isub(t):i.mod,h=t.ushrn(1),n=t.andln(1),e=r.cmp(h);\n" - "return e<0||1===n&&0===e?i.div:0!==i.div.negative?i.div.isubn(1):i.div.iaddn" - "(1)},n.prototype.modn=function(t){r(t<=67108863);for(var i=(1<<26)%t,h=0,n=t" - "his.length-1;n>=0;n--)h=(i*h+(0|this.words[n]))%t;return h},n.prototype.idiv" - "n=function(t){r(t<=67108863);for(var i=0,h=this.length-1;h>=0;h--){var n=(0|" - "this.words[h])+67108864*i;this.words[h]=n/t|0,i=n%t}return this.strip()},n.p" - "rototype.divn=function(t){return this.clone().idivn(t)},n.prototype.egcd=fun" - "ction(t){r(0===t.negative),r(!t.isZero());var i=this,h=t.clone();i=0!==i.neg" - "ative?i.umod(t):i.clone();for(var e=new n(1),o=new n(0),s=new n(0),u=new n(1" - "),a=0;i.isEven()&&h.isEven();)i.iushrn(1),h.iushrn(1),++a;for(var l=h.clone(" - "),m=i.clone();!i.isZero();){for(var f=0,d=1;0===(i.words[0]&d)&&f<26;++f,d<<" - "=1);if(f>0)for(i.iushrn(f);f-- >0;)(e.isOdd()||o.isOdd())&&(e.iadd(l),o.isub" - "(m)),e.iushrn(1),o.iushrn(1);for(var p=0,M=1;0===(h.words[0]&M)&&p<26;++p,M<" - "<=1);if(p>0)for(h.iushrn(p);p-- >0;)(s.isOdd()||u.isOdd())&&(s.iadd(l),u.isu" - "b(m)),s.iushrn(1),u.iushrn(1);i.cmp(h)>=0?(i.isub(h),e.isub(s),o.isub(u)):(h" - ".isub(i),s.isub(e),u.isub(o))}return{a:s,b:u,gcd:h.iushln(a)}},n.prototype._" - "invmp=function(t){r(0===t.negative),r(!t.isZero());var i=this,h=t.clone();i=" - "0!==i.negative?i.umod(t):i.clone();for(var e=new n(1),o=new n(0),s=h.clone()" - ";i.cmpn(1)>0&&h.cmpn(1)>0;){for(var u=0,a=1;0===(i.words[0]&a)&&u<26;++u,a<<" - "=1);if(u>0)for(i.iushrn(u);u-- >0;)e.isOdd()&&e.iadd(s),e.iushrn(1);for(var " - "l=0,m=1;0===(h.words[0]&m)&&l<26;++l,m<<=1);if(l>0)for(h.iushrn(l);l-- >0;)o" - ".isOdd()&&o.iadd(s),o.iushrn(1);i.cmp(h)>=0?(i.isub(h),e.isub(o)):(h.isub(i)" - ",o.isub(e))}var f;return f=0===i.cmpn(1)?e:o,f.cmpn(0)<0&&f.iadd(t),f},n.pro" - "totype.gcd=function(t){if(this.isZero())return t.abs();if(t.isZero())return " - "this.abs();var i=this.clone(),r=t.clone();i.negative=0,r.negative=0;for(var " - "h=0;i.isEven()&&r.isEven();h++)i.iushrn(1),r.iushrn(1);for(;;){for(;i.isEven" - "();)i.iushrn(1);for(;r.isEven();)r.iushrn(1);var n=i.cmp(r);if(n<0){var e=i;" - "i=r,r=e}else if(0===n||0===r.cmpn(1))break;i.isub(r)}return r.iushln(h)},n.p" - "rototype.invm=function(t){return this.egcd(t).a.umod(t)},n.prototype.isEven=" - "function(){return 0===(1&this.words[0])},n.prototype.isOdd=function(){return" - " 1===(1&this.words[0])},n.prototype.andln=function(t){return this.words[0]&t" - "},n.prototype.bincn=function(t){r(\"number\"==typeof t);var i=t%26,h=(t-i)/2" - "6,n=1<>>26," - "s&=67108863,this.words[o]=s}return 0!==e&&(this.words[o]=e,this.length++),th" - "is},n.prototype.isZero=function(){return 1===this.length&&0===this.words[0]}" - ",n.prototype.cmpn=function(t){var i=t<0;if(0!==this.negative&&!i)return-1;if" - "(0===this.negative&&i)return 1;this.strip();var h;if(this.length>1)h=1;else{" - "i&&(t=-t),r(t<=67108863,\"Number is too big\");var n=0|this.words[0];h=n===t" - "?0:nt.length)return 1;if(this.length=0;r--){var h=0|this.words[r],n=0|t.wo" - "rds[r];if(h!==n){hn&&(i=1);break}}return i},n.prototype.gtn=functi" - "on(t){return 1===this.cmpn(t)},n.prototype.gt=function(t){return 1===this.cm" - "p(t)},n.prototype.gten=function(t){return this.cmpn(t)>=0},n.prototype.gte=f" - "unction(t){return this.cmp(t)>=0},n.prototype.ltn=function(t){return this.cm" - "pn(t)===-1},n.prototype.lt=function(t){return this.cmp(t)===-1},n.prototype." - "lten=function(t){return this.cmpn(t)<=0},n.prototype.lte=function(t){return " - "this.cmp(t)<=0},n.prototype.eqn=function(t){return 0===this.cmpn(t)},n.proto" - "type.eq=function(t){return 0===this.cmp(t)},n.red=function(t){return new g(t" - ")},n.prototype.toRed=function(t){return r(!this.red,\"Already a number in re" - "duction context\"),r(0===this.negative,\"red works only with positives\"),t." - "convertTo(this)._forceRed(t)},n.prototype.fromRed=function(){return r(this.r" - "ed,\"fromRed works only with numbers in reduction context\"),this.red.conver" - "tFrom(this)},n.prototype._forceRed=function(t){return this.red=t,this},n.pro" - "totype.forceRed=function(t){return r(!this.red,\"Already a number in reducti" - "on context\"),this._forceRed(t)},n.prototype.redAdd=function(t){return r(thi" - "s.red,\"redAdd works only with red numbers\"),this.red.add(this,t)},n.protot" - "ype.redIAdd=function(t){return r(this.red,\"redIAdd works only with red numb" - "ers\"),this.red.iadd(this,t)},n.prototype.redSub=function(t){return r(this.r" - "ed,\"redSub works only with red numbers\"),this.red.sub(this,t)},n.prototype" - ".redISub=function(t){return r(this.red,\"redISub works only with red numbers" - "\"),this.red.isub(this,t)},n.prototype.redShl=function(t){return r(this.red," - "\"redShl works only with red numbers\"),this.red.shl(this,t)},n.prototype.re" - "dMul=function(t){return r(this.red,\"redMul works only with red numbers\"),t" - "his.red._verify2(this,t),this.red.mul(this,t)},n.prototype.redIMul=function(" - "t){return r(this.red,\"redMul works only with red numbers\"),this.red._verif" - "y2(this,t),this.red.imul(this,t)},n.prototype.redSqr=function(){return r(thi" - "s.red,\"redSqr works only with red numbers\"),this.red._verify1(this),this.r" - "ed.sqr(this)},n.prototype.redISqr=function(){return r(this.red,\"redISqr wor" - "ks only with red numbers\"),this.red._verify1(this),this.red.isqr(this)},n.p" - "rototype.redSqrt=function(){return r(this.red,\"redSqrt works only with red " - "numbers\"),this.red._verify1(this),this.red.sqrt(this)},n.prototype.redInvm=" - "function(){return r(this.red,\"redInvm works only with red numbers\"),this.r" - "ed._verify1(this),this.red.invm(this)},n.prototype.redNeg=function(){return " - "r(this.red,\"redNeg works only with red numbers\"),this.red._verify1(this),t" - "his.red.neg(this)},n.prototype.redPow=function(t){return r(this.red&&!t.red," - "\"redPow(normalNum)\"),this.red._verify1(this),this.red.pow(this,t)};var A={" - "k256:null,p224:null,p192:null,p25519:null};f.prototype._tmp=function(){var t" - "=new n(null);return t.words=new Array(Math.ceil(this.n/13)),t},f.prototype.i" - "reduce=function(t){var i,r=t;do this.split(r,this.tmp),r=this.imulK(r),r=r.i" - "add(this.tmp),i=r.bitLength();while(i>this.n);var h=i0?r.isub(this.p):r.strip(),r},f." - "prototype.split=function(t,i){t.iushrn(this.n,0,i)},f.prototype.imulK=functi" - "on(t){return t.imul(this.k)},h(d,f),d.prototype.split=function(t,i){for(var " - "r=4194303,h=Math.min(t.length,9),n=0;n>>22,e=o}e>>>=22,t.words[n-10]=e,0===e&&t.length>10?t.length-=10" - ":t.length-=9},d.prototype.imulK=function(t){t.words[t.length]=0,t.words[t.le" - "ngth+1]=0,t.length+=2;for(var i=0,r=0;r>>=26,t.words[r]=n,i=h}return 0!==i&&(t.words[" - "t.length++]=i),t},n._prime=function t(i){if(A[i])return A[i];var t;if(\"k256" - "\"===i)t=new d;else if(\"p224\"===i)t=new p;else if(\"p192\"===i)t=new M;els" - "e{if(\"p25519\"!==i)throw new Error(\"Unknown prime \"+i);t=new v}return A[i" - "]=t,t},g.prototype._verify1=function(t){r(0===t.negative,\"red works only wi" - "th positives\"),r(t.red,\"red works only with red numbers\")},g.prototype._v" - "erify2=function(t,i){r(0===(t.negative|i.negative),\"red works only with pos" - "itives\"),r(t.red&&t.red===i.red,\"red works only with red numbers\")},g.pro" - "totype.imod=function(t){return this.prime?this.prime.ireduce(t)._forceRed(th" - "is):t.umod(this.m)._forceRed(this)},g.prototype.neg=function(t){return t.isZ" - "ero()?t.clone():this.m.sub(t)._forceRed(this)},g.prototype.add=function(t,i)" - "{this._verify2(t,i);var r=t.add(i);return r.cmp(this.m)>=0&&r.isub(this.m),r" - "._forceRed(this)},g.prototype.iadd=function(t,i){this._verify2(t,i);var r=t." - "iadd(i);return r.cmp(this.m)>=0&&r.isub(this.m),r},g.prototype.sub=function(" - "t,i){this._verify2(t,i);var r=t.sub(i);return r.cmpn(0)<0&&r.iadd(this.m),r." - "_forceRed(this)},g.prototype.isub=function(t,i){this._verify2(t,i);var r=t.i" - "sub(i);return r.cmpn(0)<0&&r.iadd(this.m),r},g.prototype.shl=function(t,i){r" - "eturn this._verify1(t),this.imod(t.ushln(i))},g.prototype.imul=function(t,i)" - "{return this._verify2(t,i),this.imod(t.imul(i))},g.prototype.mul=function(t," - "i){return this._verify2(t,i),this.imod(t.mul(i))},g.prototype.isqr=function(" - "t){return this.imul(t,t.clone())},g.prototype.sqr=function(t){return this.mu" - "l(t,t)},g.prototype.sqrt=function(t){if(t.isZero())return t.clone();var i=th" - "is.m.andln(3);if(r(i%2===1),3===i){var h=this.m.add(new n(1)).iushrn(2);retu" - "rn this.pow(t,h)}for(var e=this.m.subn(1),o=0;!e.isZero()&&0===e.andln(1);)o" - "++,e.iushrn(1);r(!e.isZero());var s=new n(1).toRed(this),u=s.redNeg(),a=this" - ".m.subn(1).iushrn(1),l=this.m.bitLength();for(l=new n(2*l*l).toRed(this);0!=" - "=this.pow(l,a).cmp(u);)l.redIAdd(u);for(var m=this.pow(l,e),f=this.pow(t,e.a" - "ddn(1).iushrn(1)),d=this.pow(t,e),p=o;0!==d.cmp(s);){for(var M=d,v=0;0!==M.c" - "mp(s);v++)M=M.redSqr();r(v=0;e--" - "){for(var l=i.words[e],m=a-1;m>=0;m--){var f=l>>m&1;o!==h[0]&&(o=this.sqr(o)" - "),0!==f||0!==s?(s<<=1,s|=f,u++,(u===r||0===e&&0===m)&&(o=this.mul(o,h[s]),u=" - "0,s=0)):u=0}a=26}return o},g.prototype.convertTo=function(t){var i=t.umod(th" - "is.m);return i===t?i.clone():i},g.prototype.convertFrom=function(t){var i=t." - "clone();return i.red=null,i},n.mont=function(t){return new c(t)},h(c,g),c.pr" - "ototype.convertTo=function(t){return this.imod(t.ushln(this.shift))},c.proto" - "type.convertFrom=function(t){var i=this.imod(t.mul(this.rinv));return i.red=" - "null,i},c.prototype.imul=function(t,i){if(t.isZero()||i.isZero())return t.wo" - "rds[0]=0,t.length=1,t;var r=t.imul(i),h=r.maskn(this.shift).mul(this.minv).i" - "maskn(this.shift).mul(this.m),n=r.isub(h).iushrn(this.shift),e=n;return n.cm" - "p(this.m)>=0?e=n.isub(this.m):n.cmpn(0)<0&&(e=n.iadd(this.m)),e._forceRed(th" - "is)},c.prototype.mul=function(t,i){if(t.isZero()||i.isZero())return new n(0)" - "._forceRed(this);var r=t.mul(i),h=r.maskn(this.shift).mul(this.minv).imaskn(" - "this.shift).mul(this.m),e=r.isub(h).iushrn(this.shift),o=e;return e.cmp(this" - ".m)>=0?o=e.isub(this.m):e.cmpn(0)<0&&(o=e.iadd(this.m)),o._forceRed(this)},c" - ".prototype.invm=function(t){var i=this.imod(t._invmp(this.m).mul(this.r2));r" - "eturn i._forceRed(this)}}(\"undefined\"==typeof module||module,this);\n" - "},{\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/buffer/index.js\"}],\"/reth" - "inkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modu" - "les/browserify/node_modules/crypto-browserify/node_modules/brorand/index.js\"" - ":[function(require,module,exports){\n" - "function Rand(n){this.rand=n}var r;if(module.exports=function(n){return r||(" - "r=new Rand(null)),r.generate(n)},module.exports.Rand=Rand,Rand.prototype.gen" - "erate=function(n){return this._rand(n)},\"object\"==typeof window)window.cry" - "pto&&window.crypto.getRandomValues?Rand.prototype._rand=function(n){var r=ne" - "w Uint8Array(n);return window.crypto.getRandomValues(r),r}:window.msCrypto&&" - "window.msCrypto.getRandomValues?Rand.prototype._rand=function(n){var r=new U" - "int8Array(n);return window.msCrypto.getRandomValues(r),r}:Rand.prototype._ra" - "nd=function(){throw new Error(\"Not implemented yet\")};else try{var crypto=" - "require(\"crypto\");Rand.prototype._rand=function(n){return crypto.randomByt" - "es(n)}}catch(n){Rand.prototype._rand=function(n){for(var r=new Uint8Array(n)" - ",t=0;tuint_max||t<0?(i=Math.abs(t)%uin" - "t_max,t<0?uint_max-i:i):t}function scrub_vec(t){for(var e=0;e>>8^255&i^99,this.SBOX[n]=i," - "this.INV_SBOX[i]=n,S=t[n],u=t[S],_=t[u],r=257*t[i]^16843008*i,this.SUB_MIX[0" - "][n]=r<<24|r>>>8,this.SUB_MIX[1][n]=r<<16|r>>>16,this.SUB_MIX[2][n]=r<<8|r>>" - ">24,this.SUB_MIX[3][n]=r,r=16843009*_^65537*u^257*S^16843008*n,this.INV_SUB_" - "MIX[0][i]=r<<24|r>>>8,this.INV_SUB_MIX[1][i]=r<<16|r>>>16,this.INV_SUB_MIX[2" - "][i]=r<<8|r>>>24,this.INV_SUB_MIX[3][i]=r,0===n?n=o=1:(n=S^t[t[t[_^S]]],o^=t" - "[t[o]]);return!0};var G=new Global;AES.blockSize=16,AES.prototype.blockSize=" - "AES.blockSize,AES.keySize=32,AES.prototype.keySize=AES.keySize,AES.prototype" - "._doReset=function(){var t,e,i,r,n,S;for(i=this._key,e=i.length,this._nRound" - "s=e+6,n=4*(this._nRounds+1),this._keySchedule=[],r=0;r>>24,S=G.SBOX[S>>" - ">24]<<24|G.SBOX[S>>>16&255]<<16|G.SBOX[S>>>8&255]<<8|G.SBOX[255&S],S^=G.RCON" - "[r/e|0]<<24):e>6&&r%e===4?S=G.SBOX[S>>>24]<<24|G.SBOX[S>>>16&255]<<16|G.SBOX" - "[S>>>8&255]<<8|G.SBOX[255&S]:void 0,this._keySchedule[r-e]^S);for(this._invK" - "eySchedule=[],t=0;t>>24]]^G.INV_SUB_MIX[1][G" - ".SBOX[S>>>16&255]]^G.INV_SUB_MIX[2][G.SBOX[S>>>8&255]]^G.INV_SUB_MIX[3][G.SB" - "OX[255&S]];return!0},AES.prototype.encryptBlock=function(t){t=bufferToArray(" - "new Buffer(t));var e=this._doCryptBlock(t,this._keySchedule,G.SUB_MIX,G.SBOX" - "),i=new Buffer(16);return i.writeUInt32BE(e[0],0),i.writeUInt32BE(e[1],4),i." - "writeUInt32BE(e[2],8),i.writeUInt32BE(e[3],12),i},AES.prototype.decryptBlock" - "=function(t){t=bufferToArray(new Buffer(t));var e=[t[3],t[1]];t[1]=e[0],t[3]" - "=e[1];var i=this._doCryptBlock(t,this._invKeySchedule,G.INV_SUB_MIX,G.INV_SB" - "OX),r=new Buffer(16);return r.writeUInt32BE(i[0],0),r.writeUInt32BE(i[3],4)," - "r.writeUInt32BE(i[2],8),r.writeUInt32BE(i[1],12),r},AES.prototype.scrub=func" - "tion(){scrub_vec(this._keySchedule),scrub_vec(this._invKeySchedule),scrub_ve" - "c(this._key)},AES.prototype._doCryptBlock=function(t,e,i,r){var n,S,u,_,o,h," - "B,s,c;S=t[0]^e[0],u=t[1]^e[1],_=t[2]^e[2],o=t[3]^e[3],n=4;for(var f=1;f>>24]^i[1][u>>>16&255]^i[2][_>>>8&255]^i[3][255&o]^e[" - "n++],B=i[0][u>>>24]^i[1][_>>>16&255]^i[2][o>>>8&255]^i[3][255&S]^e[n++],s=i[" - "0][_>>>24]^i[1][o>>>16&255]^i[2][S>>>8&255]^i[3][255&u]^e[n++],c=i[0][o>>>24" - "]^i[1][S>>>16&255]^i[2][u>>>8&255]^i[3][255&_]^e[n++],S=h,u=B,_=s,o=c;return" - " h=(r[S>>>24]<<24|r[u>>>16&255]<<16|r[_>>>8&255]<<8|r[255&o])^e[n++],B=(r[u>" - ">>24]<<24|r[_>>>16&255]<<16|r[o>>>8&255]<<8|r[255&S])^e[n++],s=(r[_>>>24]<<2" - "4|r[o>>>16&255]<<16|r[S>>>8&255]<<8|r[255&u])^e[n++],c=(r[o>>>24]<<24|r[S>>>" - "16&255]<<16|r[u>>>8&255]<<8|r[255&_])^e[n++],[fixup_uint32(h),fixup_uint32(B" - "),fixup_uint32(s),fixup_uint32(c)]},exports.AES=AES;\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/buffer/index.js\"}],\"/reth" - "inkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modu" - "les/browserify/node_modules/crypto-browserify/node_modules/browserify-aes/au" - "thCipher.js\":[function(require,module,exports){\n" - "(function (Buffer){\n" - "function StreamCipher(t,e,r,h){if(!(this instanceof StreamCipher))return new" - " StreamCipher(t,e,r);Transform.call(this),this._finID=Buffer.concat([r,new B" - "uffer([0,0,0,1])]),r=Buffer.concat([r,new Buffer([0,0,0,2])]),this._cipher=n" - "ew aes.AES(e),this._prev=new Buffer(r.length),this._cache=new Buffer(\"\"),t" - "his._secCache=new Buffer(\"\"),this._decrypt=h,this._alen=0,this._len=0,r.co" - "py(this._prev),this._mode=t;var i=new Buffer(4);i.fill(0),this._ghash=new GH" - "ASH(this._cipher.encryptBlock(i)),this._authTag=null,this._called=!1}functio" - "n xorTest(t,e){var r=0;t.length!==e.length&&r++;for(var h=Math.min(t.length," - "e.length),i=-1;++i16)return r=this.cache.sli" - "ce(0,16),this.cache=this.cache.slice(16),r}else if(this.cache.length>=16)ret" - "urn r=this.cache.slice(0,16),this.cache=this.cache.slice(16),r;return null}," - "Splitter.prototype.flush=function(){if(this.cache.length)return this.cache};" - "var modelist={ECB:require(\"./modes/ecb\"),CBC:require(\"./modes/cbc\"),CFB:" - "require(\"./modes/cfb\"),CFB8:require(\"./modes/cfb8\"),CFB1:require(\"./mod" - "es/cfb1\"),OFB:require(\"./modes/ofb\"),CTR:require(\"./modes/ctr\"),GCM:req" - "uire(\"./modes/ctr\")};exports.createDecipher=createDecipher,exports.createD" - "ecipheriv=createDecipheriv;\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"./aes\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethi" - "nkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_modul" - "es/browserify-aes/aes.js\",\"./authCipher\":\"/rethinkdb/build/external/admi" - "n-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modul" - "es/crypto-browserify/node_modules/browserify-aes/authCipher.js\",\"./modes\"" - ":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/n" - "ode_modules/browserify/node_modules/crypto-browserify/node_modules/browserif" - "y-aes/modes.js\",\"./modes/cbc\":\"/rethinkdb/build/external/admin-deps_2.0." - "4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-b" - "rowserify/node_modules/browserify-aes/modes/cbc.js\",\"./modes/cfb\":\"/reth" - "inkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modu" - "les/browserify/node_modules/crypto-browserify/node_modules/browserify-aes/mo" - "des/cfb.js\",\"./modes/cfb1\":\"/rethinkdb/build/external/admin-deps_2.0.4/n" - "ode_modules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-brow" - "serify/node_modules/browserify-aes/modes/cfb1.js\",\"./modes/cfb8\":\"/rethi" - "nkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modul" - "es/browserify/node_modules/crypto-browserify/node_modules/browserify-aes/mod" - "es/cfb8.js\",\"./modes/ctr\":\"/rethinkdb/build/external/admin-deps_2.0.4/no" - "de_modules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-brows" - "erify/node_modules/browserify-aes/modes/ctr.js\",\"./modes/ecb\":\"/rethinkd" - "b/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/" - "browserify/node_modules/crypto-browserify/node_modules/browserify-aes/modes/" - "ecb.js\",\"./modes/ofb\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_m" - "odules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserif" - "y/node_modules/browserify-aes/modes/ofb.js\",\"./streamCipher\":\"/rethinkdb" - "/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/b" - "rowserify/node_modules/crypto-browserify/node_modules/browserify-aes/streamC" - "ipher.js\",\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modu" - "les/rethinkdb-webui/node_modules/browserify/node_modules/buffer/index.js\",\"" - "cipher-base\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_modu" - "les/cipher-base/index.js\",\"evp_bytestokey\":\"/rethinkdb/build/external/ad" - "min-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_mod" - "ules/crypto-browserify/node_modules/evp_bytestokey/index.js\",\"inherits\":\"" - "/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node" - "_modules/inherits/inherits_browser.js\"}],\"/rethinkdb/build/external/admin-" - "deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules" - "/crypto-browserify/node_modules/browserify-aes/encrypter.js\":[function(requ" - "ire,module,exports){\n" - "(function (Buffer){\n" - "function Cipher(e,r,t){return this instanceof Cipher?(Transform.call(this),t" - "his._cache=new Splitter,this._cipher=new aes.AES(r),this._prev=new Buffer(t." - "length),t.copy(this._prev),this._mode=e,void(this._autopadding=!0)):new Ciph" - "er(e,r,t)}function Splitter(){return this instanceof Splitter?void(this.cach" - "e=new Buffer(\"\")):new Splitter}function createCipheriv(e,r,t){var i=modes[" - "e.toLowerCase()];if(!i)throw new TypeError(\"invalid suite type\");if(\"stri" - "ng\"==typeof t&&(t=new Buffer(t)),\"string\"==typeof r&&(r=new Buffer(r)),r." - "length!==i.key/8)throw new TypeError(\"invalid key length \"+r.length);if(t." - "length!==i.iv)throw new TypeError(\"invalid iv length \"+t.length);return\"s" - "tream\"===i.type?new StreamCipher(modelist[i.mode],r,t):\"auth\"===i.type?ne" - "w AuthCipher(modelist[i.mode],r,t):new Cipher(modelist[i.mode],r,t)}function" - " createCipher(e,r){var t=modes[e.toLowerCase()];if(!t)throw new TypeError(\"" - "invalid suite type\");var i=ebtk(r,!1,t.key,t.iv);return createCipheriv(e,i." - "key,i.iv)}var aes=require(\"./aes\"),Transform=require(\"cipher-base\"),inhe" - "rits=require(\"inherits\"),modes=require(\"./modes\"),ebtk=require(\"evp_byt" - "estokey\"),StreamCipher=require(\"./streamCipher\"),AuthCipher=require(\"./a" - "uthCipher\");inherits(Cipher,Transform),Cipher.prototype._update=function(e)" - "{this._cache.add(e);for(var r,t,i=[];r=this._cache.get();)t=this._mode.encry" - "pt(this,r),i.push(t);return Buffer.concat(i)},Cipher.prototype._final=functi" - "on(){var e=this._cache.flush();if(this._autopadding)return e=this._mode.encr" - "ypt(this,e),this._cipher.scrub(),e;if(\"10101010101010101010101010101010\"!=" - "=e.toString(\"hex\"))throw this._cipher.scrub(),new Error(\"data not multipl" - "e of block length\")},Cipher.prototype.setAutoPadding=function(e){return thi" - "s._autopadding=!!e,this},Splitter.prototype.add=function(e){this.cache=Buffe" - "r.concat([this.cache,e])},Splitter.prototype.get=function(){if(this.cache.le" - "ngth>15){var e=this.cache.slice(0,16);return this.cache=this.cache.slice(16)" - ",e}return null},Splitter.prototype.flush=function(){for(var e=16-this.cache." - "length,r=new Buffer(e),t=-1;++tuint_max||t<0?(e=Math.abs(t)%uint_max,t<" - "0?uint_max-e:e):t}function xor(t,r){return[t[0]^r[0],t[1]^r[1],t[2]^r[2],t[3" - "]^r[3]]}var zeros=new Buffer(16);zeros.fill(0),module.exports=GHASH,GHASH.pr" - "ototype.ghash=function(t){for(var r=-1;++r0;t--)i[t]=i[t]>>>1|(1&i[t-1])<<31;i[0]" - "=i[0]>>>1,e&&(i[0]=i[0]^225<<24)}this.state=fromArray(a)},GHASH.prototype.up" - "date=function(t){this.cache=Buffer.concat([this.cache,t]);for(var r;this.cac" - "he.length>=16;)r=this.cache.slice(0,16),this.cache=this.cache.slice(16),this" - ".ghash(r)},GHASH.prototype.final=function(t,r){return this.cache.length&&thi" - "s.ghash(Buffer.concat([this.cache,zeros],16)),this.ghash(fromArray([0,t,0,r]" - ")),this.state};var uint_max=Math.pow(2,32);\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/buffer/index.js\"}],\"/reth" - "inkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modu" - "les/browserify/node_modules/crypto-browserify/node_modules/browserify-aes/mo" - "des.js\":[function(require,module,exports){\n" - "exports[\"aes-128-ecb\"]={cipher:\"AES\",key:128,iv:0,mode:\"ECB\",type:\"bl" - "ock\"},exports[\"aes-192-ecb\"]={cipher:\"AES\",key:192,iv:0,mode:\"ECB\",ty" - "pe:\"block\"},exports[\"aes-256-ecb\"]={cipher:\"AES\",key:256,iv:0,mode:\"E" - "CB\",type:\"block\"},exports[\"aes-128-cbc\"]={cipher:\"AES\",key:128,iv:16," - "mode:\"CBC\",type:\"block\"},exports[\"aes-192-cbc\"]={cipher:\"AES\",key:19" - "2,iv:16,mode:\"CBC\",type:\"block\"},exports[\"aes-256-cbc\"]={cipher:\"AES\"" - ",key:256,iv:16,mode:\"CBC\",type:\"block\"},exports.aes128=exports[\"aes-128" - "-cbc\"],exports.aes192=exports[\"aes-192-cbc\"],exports.aes256=exports[\"aes" - "-256-cbc\"],exports[\"aes-128-cfb\"]={cipher:\"AES\",key:128,iv:16,mode:\"CF" - "B\",type:\"stream\"},exports[\"aes-192-cfb\"]={cipher:\"AES\",key:192,iv:16," - "mode:\"CFB\",type:\"stream\"},exports[\"aes-256-cfb\"]={cipher:\"AES\",key:2" - "56,iv:16,mode:\"CFB\",type:\"stream\"},exports[\"aes-128-cfb8\"]={cipher:\"A" - "ES\",key:128,iv:16,mode:\"CFB8\",type:\"stream\"},exports[\"aes-192-cfb8\"]=" - "{cipher:\"AES\",key:192,iv:16,mode:\"CFB8\",type:\"stream\"},exports[\"aes-2" - "56-cfb8\"]={cipher:\"AES\",key:256,iv:16,mode:\"CFB8\",type:\"stream\"},expo" - "rts[\"aes-128-cfb1\"]={cipher:\"AES\",key:128,iv:16,mode:\"CFB1\",type:\"str" - "eam\"},exports[\"aes-192-cfb1\"]={cipher:\"AES\",key:192,iv:16,mode:\"CFB1\"" - ",type:\"stream\"},exports[\"aes-256-cfb1\"]={cipher:\"AES\",key:256,iv:16,mo" - "de:\"CFB1\",type:\"stream\"},exports[\"aes-128-ofb\"]={cipher:\"AES\",key:12" - "8,iv:16,mode:\"OFB\",type:\"stream\"},exports[\"aes-192-ofb\"]={cipher:\"AES" - "\",key:192,iv:16,mode:\"OFB\",type:\"stream\"},exports[\"aes-256-ofb\"]={cip" - "her:\"AES\",key:256,iv:16,mode:\"OFB\",type:\"stream\"},exports[\"aes-128-ct" - "r\"]={cipher:\"AES\",key:128,iv:16,mode:\"CTR\",type:\"stream\"},exports[\"a" - "es-192-ctr\"]={cipher:\"AES\",key:192,iv:16,mode:\"CTR\",type:\"stream\"},ex" - "ports[\"aes-256-ctr\"]={cipher:\"AES\",key:256,iv:16,mode:\"CTR\",type:\"str" - "eam\"},exports[\"aes-128-gcm\"]={cipher:\"AES\",key:128,iv:12,mode:\"GCM\",t" - "ype:\"auth\"},exports[\"aes-192-gcm\"]={cipher:\"AES\",key:192,iv:12,mode:\"" - "GCM\",type:\"auth\"},exports[\"aes-256-gcm\"]={cipher:\"AES\",key:256,iv:12," - "mode:\"GCM\",type:\"auth\"};\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/browserify/node_modules/crypto-browserify/node_modules/brow" - "serify-aes/modes/cbc.js\":[function(require,module,exports){\n" - "var xor=require(\"buffer-xor\");exports.encrypt=function(r,e){var p=xor(e,r." - "_prev);return r._prev=r._cipher.encryptBlock(p),r._prev},exports.decrypt=fun" - "ction(r,e){var p=r._prev;r._prev=e;var c=r._cipher.decryptBlock(e);return xo" - "r(c,p)};\n" - "},{\"buffer-xor\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/" - "rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_" - "modules/browserify-aes/node_modules/buffer-xor/index.js\"}],\"/rethinkdb/bui" - "ld/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/brows" - "erify/node_modules/crypto-browserify/node_modules/browserify-aes/modes/cfb.j" - "s\":[function(require,module,exports){\n" - "(function (Buffer){\n" - "function encryptStart(e,r,c){var t=r.length,n=xor(r,e._cache);return e._cach" - "e=e._cache.slice(t),e._prev=Buffer.concat([e._prev,c?r:n]),n}var xor=require" - "(\"buffer-xor\");exports.encrypt=function(e,r,c){for(var t,n=new Buffer(\"\"" - ");r.length;){if(0===e._cache.length&&(e._cache=e._cipher.encryptBlock(e._pre" - "v),e._prev=new Buffer(\"\")),!(e._cache.length<=r.length)){n=Buffer.concat([" - "n,encryptStart(e,r,c)]);break}t=e._cache.length,n=Buffer.concat([n,encryptSt" - "art(e,r.slice(0,t),c)]),r=r.slice(t)}return n};\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/buffer/index.js\",\"buffer-" - "xor\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/browserify/node_modules/crypto-browserify/node_modules/brow" - "serify-aes/node_modules/buffer-xor/index.js\"}],\"/rethinkdb/build/external/" - "admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_m" - "odules/crypto-browserify/node_modules/browserify-aes/modes/cfb1.js\":[functi" - "on(require,module,exports){\n" - "(function (Buffer){\n" - "function encryptByte(r,e,n){for(var t,f,c,u=-1,o=8,p=0;++u>u%8,r._prev=shiftI" - "n(r._prev,n?f:c);return p}function shiftIn(r,e){var n=r.length,t=-1,f=new Bu" - "ffer(r.length);for(r=Buffer.concat([r,new Buffer([e])]);++t>7;return f}exports.encrypt=function(r,e,n){for(var t=e.length,f=new " - "Buffer(t),c=-1;++c0;i--)e+=this._buffer(t,e" - "),r+=this._flushBuffer(f,r);return e+=this._buffer(t,e),f},Cipher.prototype." - "final=function(t){var e;t&&(e=this.update(t));var r;return r=\"encrypt\"===t" - "his.type?this._finalEncrypt():this._finalDecrypt(),e?e.concat(r):r},Cipher.p" - "rototype._pad=function(t,e){if(0===e)return!1;for(;e>>1];r=utils.r28shl(r,n),s=utils.r28shl(s,n),utils.p" - "c2(r,s,t.keys,i)}},DES.prototype._update=function(t,e,r,s){var i=this._desSt" - "ate,n=utils.readUInt32BE(t,e),p=utils.readUInt32BE(t,e+4);utils.ip(n,p,i.tmp" - ",0),n=i.tmp[0],p=i.tmp[1],\"encrypt\"===this.type?this._encrypt(i,n,p,i.tmp," - "0):this._decrypt(i,n,p,i.tmp,0),n=i.tmp[0],p=i.tmp[1],utils.writeUInt32BE(r," - "n,s),utils.writeUInt32BE(r,p,s+4)},DES.prototype._pad=function(t,e){for(var " - "r=t.length-e,s=e;s>>0,n=y}utils.rip(p,n,s,i)},DES.prototype._decryp" - "t=function(t,e,r,s,i){for(var n=r,p=e,u=t.keys.length-2;u>=0;u-=2){var l=t.k" - "eys[u],a=t.keys[u+1];utils.expand(n,t.tmp,0),l^=t.tmp[0],a^=t.tmp[1];var h=u" - "tils.substitute(l,a),o=utils.permute(h),y=n;n=(p^o)>>>0,p=y}utils.rip(n,p,s," - "i)};\n" - "},{\"../des\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_modu" - "les/browserify-cipher/node_modules/browserify-des/node_modules/des.js/lib/de" - "s.js\",\"inherits\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_module" - "s/rethinkdb-webui/node_modules/inherits/inherits_browser.js\",\"minimalistic" - "-assert\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkd" - "b-webui/node_modules/browserify/node_modules/crypto-browserify/node_modules/" - "minimalistic-assert/index.js\"}],\"/rethinkdb/build/external/admin-deps_2.0." - "4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-b" - "rowserify/node_modules/browserify-cipher/node_modules/browserify-des/node_mo" - "dules/des.js/lib/des/ede.js\":[function(require,module,exports){\n" - "\"use strict\";function EDEState(e,t){assert.equal(t.length,24,\"Invalid key" - " length\");var r=t.slice(0,8),p=t.slice(8,16),i=t.slice(16,24);\"encrypt\"==" - "=e?this.ciphers=[DES.create({type:\"encrypt\",key:r}),DES.create({type:\"dec" - "rypt\",key:p}),DES.create({type:\"encrypt\",key:i})]:this.ciphers=[DES.creat" - "e({type:\"decrypt\",key:i}),DES.create({type:\"encrypt\",key:p}),DES.create(" - "{type:\"decrypt\",key:r})]}function EDE(e){Cipher.call(this,e);var t=new EDE" - "State(this.type,this.options.key);this._edeState=t}var assert=require(\"mini" - "malistic-assert\"),inherits=require(\"inherits\"),des=require(\"../des\"),Ci" - "pher=des.Cipher,DES=des.DES;inherits(EDE,Cipher),module.exports=EDE,EDE.crea" - "te=function(e){return new EDE(e)},EDE.prototype._update=function(e,t,r,p){va" - "r i=this._edeState;i.ciphers[0]._update(e,t,r,p),i.ciphers[1]._update(r,p,r," - "p),i.ciphers[2]._update(r,p,r,p)},EDE.prototype._pad=DES.prototype._pad,EDE." - "prototype._unpad=DES.prototype._unpad;\n" - "},{\"../des\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_modu" - "les/browserify-cipher/node_modules/browserify-des/node_modules/des.js/lib/de" - "s.js\",\"inherits\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_module" - "s/rethinkdb-webui/node_modules/inherits/inherits_browser.js\",\"minimalistic" - "-assert\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkd" - "b-webui/node_modules/browserify/node_modules/crypto-browserify/node_modules/" - "minimalistic-assert/index.js\"}],\"/rethinkdb/build/external/admin-deps_2.0." - "4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-b" - "rowserify/node_modules/browserify-cipher/node_modules/browserify-des/node_mo" - "dules/des.js/lib/des/utils.js\":[function(require,module,exports){\n" - "\"use strict\";exports.readUInt32BE=function(r,o){var t=r[0+o]<<24|r[1+o]<<1" - "6|r[2+o]<<8|r[3+o];return t>>>0},exports.writeUInt32BE=function(r,o,t){r[0+t" - "]=o>>>24,r[1+t]=o>>>16&255,r[2+t]=o>>>8&255,r[3+t]=255&o},exports.ip=functio" - "n(r,o,t,a){for(var e=0,f=0,n=6;n>=0;n-=2){for(var v=0;v<=24;v+=8)e<<=1,e|=o>" - ">>v+n&1;for(var v=0;v<=24;v+=8)e<<=1,e|=r>>>v+n&1}for(var n=6;n>=0;n-=2){for" - "(var v=1;v<=25;v+=8)f<<=1,f|=o>>>v+n&1;for(var v=1;v<=25;v+=8)f<<=1,f|=r>>>v" - "+n&1}t[a+0]=e>>>0,t[a+1]=f>>>0},exports.rip=function(r,o,t,a){for(var e=0,f=" - "0,n=0;n<4;n++)for(var v=24;v>=0;v-=8)e<<=1,e|=o>>>v+n&1,e<<=1,e|=r>>>v+n&1;f" - "or(var n=4;n<8;n++)for(var v=24;v>=0;v-=8)f<<=1,f|=o>>>v+n&1,f<<=1,f|=r>>>v+" - "n&1;t[a+0]=e>>>0,t[a+1]=f>>>0},exports.pc1=function(r,o,t,a){for(var e=0,f=0" - ",n=7;n>=5;n--){for(var v=0;v<=24;v+=8)e<<=1,e|=o>>v+n&1;for(var v=0;v<=24;v+" - "=8)e<<=1,e|=r>>v+n&1}for(var v=0;v<=24;v+=8)e<<=1,e|=o>>v+n&1;for(var n=1;n<" - "=3;n++){for(var v=0;v<=24;v+=8)f<<=1,f|=o>>v+n&1;for(var v=0;v<=24;v+=8)f<<=" - "1,f|=r>>v+n&1}for(var v=0;v<=24;v+=8)f<<=1,f|=r>>v+n&1;t[a+0]=e>>>0,t[a+1]=f" - ">>>0},exports.r28shl=function(r,o){return r<>>28-o};var pc2ta" - "ble=[14,11,17,4,27,23,25,0,13,22,7,18,5,9,16,24,2,20,12,21,1,8,15,26,15,4,25" - ",19,9,1,26,16,5,11,23,8,12,7,17,0,22,3,10,14,6,20,27,24];exports.pc2=functio" - "n(r,o,t,a){for(var e=0,f=0,n=pc2table.length>>>1,v=0;v>>pc" - "2table[v]&1;for(var v=n;v>>pc2table[v]&1;t[a" - "+0]=e>>>0,t[a+1]=f>>>0},exports.expand=function(r,o,t){var a=0,e=0;a=(1&r)<<" - "5|r>>>27;for(var f=23;f>=15;f-=4)a<<=6,a|=r>>>f&63;for(var f=11;f>=3;f-=4)e|" - "=r>>>f&63,e<<=6;e|=(31&r)<<1|r>>>31,o[t+0]=a>>>0,o[t+1]=e>>>0};var sTable=[1" - "4,0,4,15,13,7,1,4,2,14,15,2,11,13,8,1,3,10,10,6,6,12,12,11,5,9,9,5,0,3,7,8,4" - ",15,1,12,14,8,8,2,13,4,6,9,2,1,11,7,15,5,12,11,9,3,7,14,3,10,10,0,5,6,0,13,1" - "5,3,1,13,8,4,14,7,6,15,11,2,3,8,4,14,9,12,7,0,2,1,13,10,12,6,0,9,5,11,10,5,0" - ",13,14,8,7,10,11,1,10,3,4,15,13,4,1,2,5,11,8,6,12,7,6,12,9,0,3,5,2,14,15,9,1" - "0,13,0,7,9,0,14,9,6,3,3,4,15,6,5,10,1,2,13,8,12,5,7,14,11,12,4,11,2,15,8,1,1" - "3,1,6,10,4,13,9,0,8,6,15,9,3,8,0,7,11,4,1,15,2,14,12,3,5,11,10,5,14,2,7,12,7" - ",13,13,8,14,11,3,5,0,6,6,15,9,0,10,3,1,4,2,7,8,2,5,12,11,1,12,10,4,14,15,9,1" - "0,3,6,15,9,0,0,6,12,10,11,1,7,13,13,8,15,9,1,4,3,5,14,11,5,12,2,7,8,2,4,14,2" - ",14,12,11,4,2,1,12,7,4,10,7,11,13,6,1,8,5,5,0,3,15,15,10,13,3,0,9,14,8,9,6,4" - ",11,2,8,1,12,11,7,10,1,13,14,7,2,8,13,15,6,9,15,12,0,5,9,6,10,3,4,0,5,14,3,1" - "2,10,1,15,10,4,15,2,9,7,2,12,6,9,8,5,0,6,13,1,3,13,4,14,14,0,7,11,5,3,11,8,9" - ",4,14,3,15,2,5,12,2,9,8,5,12,15,3,10,7,11,0,14,4,1,10,7,1,6,13,0,11,8,6,13,4" - ",13,11,0,2,11,14,7,15,4,0,9,8,1,13,10,3,14,12,3,9,5,7,12,5,2,10,15,6,8,1,6,1" - ",6,4,11,11,13,13,8,12,1,3,4,7,10,14,7,10,9,15,5,6,0,8,15,0,14,5,2,9,3,2,12,1" - "3,1,2,15,8,13,4,8,6,10,15,3,11,7,1,4,10,12,9,5,3,6,14,11,5,0,0,14,12,9,7,2,7" - ",2,11,1,4,14,1,7,9,4,12,10,14,8,2,13,0,15,6,12,10,9,13,0,15,3,3,5,5,6,8,11];" - "exports.substitute=function(r,o){for(var t=0,a=0;a<4;a++){var e=r>>>18-6*a&6" - "3,f=sTable[64*a+e];t<<=4,t|=f}for(var a=0;a<4;a++){var e=o>>>18-6*a&63,f=sTa" - "ble[256+64*a+e];t<<=4,t|=f}return t>>>0};var permuteTable=[16,25,12,11,3,20," - "4,15,31,17,9,6,27,14,1,22,30,24,8,18,0,5,29,23,13,19,2,26,10,21,28,7];export" - "s.permute=function(r){for(var o=0,t=0;t>" - ">permuteTable[t]&1;return o>>>0},exports.padSplit=function(r,o,t){for(var a=" - "r.toString(2);a.length=0||!r.umod(e.prime1)||!r." - "umod(e.prime2);)r=new bn(randomBytes(n));return r}var bn=require(\"bn.js\")," - "randomBytes=require(\"randombytes\");module.exports=crt,crt.getr=getr;\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"bn.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethi" - "nkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_modul" - "es/bn.js/lib/bn.js\",\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4" - "/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/buffer/in" - "dex.js\",\"randombytes\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_m" - "odules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserif" - "y/node_modules/randombytes/browser.js\"}],\"/rethinkdb/build/external/admin-" - "deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules" - "/crypto-browserify/node_modules/browserify-sign/algos.js\":[function(require" - ",module,exports){\n" - "(function (Buffer){\n" - "\"use strict\";exports[\"RSA-SHA224\"]=exports.sha224WithRSAEncryption={sign" - ":\"rsa\",hash:\"sha224\",id:new Buffer(\"302d300d060960864801650304020405000" - "41c\",\"hex\")},exports[\"RSA-SHA256\"]=exports.sha256WithRSAEncryption={sig" - "n:\"rsa\",hash:\"sha256\",id:new Buffer(\"3031300d06096086480165030402010500" - "0420\",\"hex\")},exports[\"RSA-SHA384\"]=exports.sha384WithRSAEncryption={si" - "gn:\"rsa\",hash:\"sha384\",id:new Buffer(\"3041300d0609608648016503040202050" - "00430\",\"hex\")},exports[\"RSA-SHA512\"]=exports.sha512WithRSAEncryption={s" - "ign:\"rsa\",hash:\"sha512\",id:new Buffer(\"3051300d060960864801650304020305" - "000440\",\"hex\")},exports[\"RSA-SHA1\"]={sign:\"rsa\",hash:\"sha1\",id:new " - "Buffer(\"3021300906052b0e03021a05000414\",\"hex\")},exports[\"ecdsa-with-SHA" - "1\"]={sign:\"ecdsa\",hash:\"sha1\",id:new Buffer(\"\",\"hex\")},exports.DSA=" - "exports[\"DSA-SHA1\"]=exports[\"DSA-SHA\"]={sign:\"dsa\",hash:\"sha1\",id:ne" - "w Buffer(\"\",\"hex\")},exports[\"DSA-SHA224\"]=exports[\"DSA-WITH-SHA224\"]" - "={sign:\"dsa\",hash:\"sha224\",id:new Buffer(\"\",\"hex\")},exports[\"DSA-SH" - "A256\"]=exports[\"DSA-WITH-SHA256\"]={sign:\"dsa\",hash:\"sha256\",id:new Bu" - "ffer(\"\",\"hex\")},exports[\"DSA-SHA384\"]=exports[\"DSA-WITH-SHA384\"]={si" - "gn:\"dsa\",hash:\"sha384\",id:new Buffer(\"\",\"hex\")},exports[\"DSA-SHA512" - "\"]=exports[\"DSA-WITH-SHA512\"]={sign:\"dsa\",hash:\"sha512\",id:new Buffer" - "(\"\",\"hex\")},exports[\"DSA-RIPEMD160\"]={sign:\"dsa\",hash:\"rmd160\",id:" - "new Buffer(\"\",\"hex\")},exports[\"RSA-RIPEMD160\"]=exports.ripemd160WithRS" - "A={sign:\"rsa\",hash:\"rmd160\",id:new Buffer(\"3021300906052b24030201050004" - "14\",\"hex\")},exports[\"RSA-MD5\"]=exports.md5WithRSAEncryption={sign:\"rsa" - "\",hash:\"md5\",id:new Buffer(\"3020300c06082a864886f70d020505000410\",\"hex" - "\")};\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/buffer/index.js\"}],\"/reth" - "inkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modu" - "les/browserify/node_modules/crypto-browserify/node_modules/browserify-sign/b" - "rowser.js\":[function(require,module,exports){\n" - "(function (Buffer){\n" - "function Sign(e){stream.Writable.call(this);var t=algos[e];if(!t)throw new E" - "rror(\"Unknown message digest\");this._hashType=t.hash,this._hash=createHash" - "(t.hash),this._tag=t.id,this._signType=t.sign}function Verify(e){stream.Writ" - "able.call(this);var t=algos[e];if(!t)throw new Error(\"Unknown message diges" - "t\");this._hash=createHash(t.hash),this._tag=t.id,this._signType=t.sign}func" - "tion createSign(e){return new Sign(e)}function createVerify(e){return new Ve" - "rify(e)}var _algos=require(\"./algos\"),createHash=require(\"create-hash\")," - "inherits=require(\"inherits\"),sign=require(\"./sign\"),stream=require(\"str" - "eam\"),verify=require(\"./verify\"),algos={};Object.keys(_algos).forEach(fun" - "ction(e){algos[e]=algos[e.toLowerCase()]=_algos[e]}),inherits(Sign,stream.Wr" - "itable),Sign.prototype._write=function(e,t,i){this._hash.update(e),i()},Sign" - ".prototype.update=function(e,t){return\"string\"==typeof e&&(e=new Buffer(e," - "t)),this._hash.update(e),this},Sign.prototype.sign=function(e,t){this.end();" - "var i=this._hash.digest(),r=sign(Buffer.concat([this._tag,i]),e,this._hashTy" - "pe,this._signType);return t?r.toString(t):r},inherits(Verify,stream.Writable" - "),Verify.prototype._write=function(e,t,i){this._hash.update(e),i()},Verify.p" - "rototype.update=function(e,t){return\"string\"==typeof e&&(e=new Buffer(e,t)" - "),this._hash.update(e),this},Verify.prototype.verify=function(e,t,i){\"strin" - "g\"==typeof t&&(t=new Buffer(t,i)),this.end();var r=this._hash.digest();retu" - "rn verify(t,Buffer.concat([this._tag,r]),e,this._signType)},module.exports={" - "Sign:createSign,Verify:createVerify,createSign:createSign,createVerify:creat" - "eVerify};\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"./algos\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/ret" - "hinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_mod" - "ules/browserify-sign/algos.js\",\"./sign\":\"/rethinkdb/build/external/admin" - "-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_module" - "s/crypto-browserify/node_modules/browserify-sign/sign.js\",\"./verify\":\"/r" - "ethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_m" - "odules/browserify/node_modules/crypto-browserify/node_modules/browserify-sig" - "n/verify.js\",\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_m" - "odules/rethinkdb-webui/node_modules/browserify/node_modules/buffer/index.js\"" - ",\"create-hash\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/r" - "ethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_m" - "odules/create-hash/browser.js\",\"inherits\":\"/rethinkdb/build/external/adm" - "in-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/inherits/inherits_br" - "owser.js\",\"stream\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modu" - "les/rethinkdb-webui/node_modules/browserify/node_modules/stream-browserify/i" - "ndex.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethin" - "kdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_module" - "s/browserify-sign/curves.js\":[function(require,module,exports){\n" - "\"use strict\";exports[\"1.3.132.0.10\"]=\"secp256k1\",exports[\"1.3.132.0.3" - "3\"]=\"p224\",exports[\"1.2.840.10045.3.1.1\"]=\"p192\",exports[\"1.2.840.10" - "045.3.1.7\"]=\"p256\",exports[\"1.3.132.0.34\"]=\"p384\",exports[\"1.3.132.0" - ".35\"]=\"p521\";\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/browserify/node_modules/crypto-browserify/node_modules/brow" - "serify-sign/sign.js\":[function(require,module,exports){\n" - "(function (Buffer){\n" - "function sign(e,r,t,n){var a=parseKeys(r);if(a.curve){if(\"ecdsa\"!==n)throw" - " new Error(\"wrong private key type\");return ecSign(e,a)}if(\"dsa\"===a.typ" - "e){if(\"dsa\"!==n)throw new Error(\"wrong private key type\");return dsaSign" - "(e,a,t)}if(\"rsa\"!==n)throw new Error(\"wrong private key type\");for(var i" - "=a.modulus.byteLength(),u=[0,1];e.length+u.length+10&&t.ishrn(n),t}func" - "tion bits2octets(e,r){e=bits2int(e,r),e=e.mod(r);var t=new Buffer(e.toArray(" - "));if(t.length=r)throw new Error(\"invalid sig\")}var c" - "urves=require(\"./curves\"),elliptic=require(\"elliptic\"),parseKeys=require" - "(\"parse-asn1\"),BN=require(\"bn.js\"),EC=elliptic.ec;module.exports=verify;" - "\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"./curves\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/re" - "thinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_mo" - "dules/browserify-sign/curves.js\",\"bn.js\":\"/rethinkdb/build/external/admi" - "n-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modul" - "es/crypto-browserify/node_modules/bn.js/lib/bn.js\",\"buffer\":\"/rethinkdb/" - "build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/br" - "owserify/node_modules/buffer/index.js\",\"elliptic\":\"/rethinkdb/build/exte" - "rnal/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/n" - "ode_modules/crypto-browserify/node_modules/elliptic/lib/elliptic.js\",\"pars" - "e-asn1\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb" - "-webui/node_modules/browserify/node_modules/crypto-browserify/node_modules/p" - "arse-asn1/index.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_mod" - "ules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/" - "node_modules/cipher-base/index.js\":[function(require,module,exports){\n" - "(function (Buffer){\n" - "function CipherBase(t){Transform.call(this),this.hashMode=\"string\"==typeof" - " t,this.hashMode?this[t]=this._finalOrDigest:this.final=this._finalOrDigest," - "this._decoder=null,this._encoding=null}var Transform=require(\"stream\").Tra" - "nsform,inherits=require(\"inherits\"),StringDecoder=require(\"string_decoder" - "\").StringDecoder;module.exports=CipherBase,inherits(CipherBase,Transform),C" - "ipherBase.prototype.update=function(t,e,r){\"string\"==typeof t&&(t=new Buff" - "er(t,e));var i=this._update(t);return this.hashMode?this:(r&&(i=this._toStri" - "ng(i,r)),i)},CipherBase.prototype.setAutoPadding=function(){},CipherBase.pro" - "totype.getAuthTag=function(){throw new Error(\"trying to get auth tag in uns" - "upported state\")},CipherBase.prototype.setAuthTag=function(){throw new Erro" - "r(\"trying to set auth tag in unsupported state\")},CipherBase.prototype.set" - "AAD=function(){throw new Error(\"trying to set aad in unsupported state\")}," - "CipherBase.prototype._transform=function(t,e,r){var i;try{this.hashMode?this" - "._update(t):this.push(this._update(t))}catch(t){i=t}finally{r(i)}},CipherBas" - "e.prototype._flush=function(t){var e;try{this.push(this._final())}catch(t){e" - "=t}finally{t(e)}},CipherBase.prototype._finalOrDigest=function(t){var e=this" - "._final()||new Buffer(\"\");return t&&(e=this._toString(e,t,!0)),e},CipherBa" - "se.prototype._toString=function(t,e,r){if(this._decoder||(this._decoder=new " - "StringDecoder(e),this._encoding=e),this._encoding!==e)throw new Error(\"can'" - "t switch encodings\");var i=this._decoder.write(t);return r&&(i+=this._decod" - "er.end()),i};\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/buffer/index.js\",\"inherit" - "s\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webu" - "i/node_modules/inherits/inherits_browser.js\",\"stream\":\"/rethinkdb/build/" - "external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browseri" - "fy/node_modules/stream-browserify/index.js\",\"string_decoder\":\"/rethinkdb" - "/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/b" - "rowserify/node_modules/string_decoder/index.js\"}],\"/rethinkdb/build/extern" - "al/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/nod" - "e_modules/crypto-browserify/node_modules/create-ecdh/browser.js\":[function(" - "require,module,exports){\n" - "(function (Buffer){\n" - "function ECDH(e){this.curveType=aliases[e],this.curveType||(this.curveType={" - "name:e}),this.curve=new elliptic.ec(this.curveType.name),this.keys=void 0}fu" - "nction formatReturnValue(e,t,r){Array.isArray(e)||(e=e.toArray());var i=new " - "Buffer(e);if(r&&i.length>5]|=128<<_%32,d[(_+64>>>9<<4)+14]" - "=_;for(var m=1732584193,f=-271733879,i=-1732584194,h=271733878,r=0;r>16)+(_>>16)+(m>>16)" - ";return f<<16|65535&m}function bit_rol(d,_){return d<<_|d>>>32-_}var helpers" - "=require(\"./helpers\");module.exports=function(d){return helpers.hash(d,cor" - "e_md5,16)};\n" - "},{\"./helpers\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/r" - "ethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_m" - "odules/create-hash/helpers.js\"}],\"/rethinkdb/build/external/admin-deps_2.0" - ".4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-" - "browserify/node_modules/create-hash/node_modules/ripemd160/lib/ripemd160.js\"" - ":[function(require,module,exports){\n" - "(function (Buffer){\n" - "function bytesToWords(r){for(var f=[],n=0,t=0;n>>5]|=" - "r[n]<<24-t%32;return f}function wordsToBytes(r){for(var f=[],n=0;n<32*r.leng" - "th;n+=8)f.push(r[n>>>5]>>>24-n%32&255);return f}function processBlock(r,f,n)" - "{for(var t=0;t<16;t++){var o=n+t,e=f[o];f[o]=16711935&(e<<8|e>>>24)|42782553" - "60&(e<<24|e>>>8)}var u,l,s,h,c,i,a,v,d,p;i=u=r[0],a=l=r[1],v=s=r[2],d=h=r[3]" - ",p=c=r[4];var g;for(t=0;t<80;t+=1)g=u+f[n+zl[t]]|0,g+=t<16?f1(l,s,h)+hl[0]:t" - "<32?f2(l,s,h)+hl[1]:t<48?f3(l,s,h)+hl[2]:t<64?f4(l,s,h)+hl[3]:f5(l,s,h)+hl[4" - "],g=0|g,g=rotl(g,sl[t]),g=g+c|0,u=c,c=h,h=rotl(s,10),s=l,l=g,g=i+f[n+zr[t]]|" - "0,g+=t<16?f5(a,v,d)+hr[0]:t<32?f4(a,v,d)+hr[1]:t<48?f3(a,v,d)+hr[2]:t<64?f2(" - "a,v,d)+hr[3]:f1(a,v,d)+hr[4],g=0|g,g=rotl(g,sr[t]),g=g+p|0,i=p,p=d,d=rotl(v," - "10),v=a,a=g;g=r[1]+s+d|0,r[1]=r[2]+h+p|0,r[2]=r[3]+c+i|0,r[3]=r[4]+u+a|0,r[4" - "]=r[0]+l+v|0,r[0]=g}function f1(r,f,n){return r^f^n}function f2(r,f,n){retur" - "n r&f|~r&n}function f3(r,f,n){return(r|~f)^n}function f4(r,f,n){return r&n|f" - "&~n}function f5(r,f,n){return r^(f|~n)}function rotl(r,f){return r<>>32" - "-f}function ripemd160(r){var f=[1732584193,4023233417,2562383102,271733878,3" - "285377520];\"string\"==typeof r&&(r=new Buffer(r,\"utf8\"));var n=bytesToWor" - "ds(r),t=8*r.length,o=8*r.length;n[t>>>5]|=128<<24-t%32,n[(t+64>>>9<<4)+14]=1" - "6711935&(o<<8|o>>>24)|4278255360&(o<<24|o>>>8);for(var e=0;e>>24)|" - "4278255360&(u<<24|u>>>8)}var l=wordsToBytes(f);return new Buffer(l)}var zl=[" - "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8," - "3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2," - "4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13],zr=[5,14,7,0,9,2,11,4,13,6,15,8,1,10," - "3,12,6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0," - "4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3," - "9,11],sl=[11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15" - ",9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8," - "9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6],sr=[8,9,9,11,13,1" - "5,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11" - ",8,6,6,14,12,13,5,14,13,13,7,5,15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,1" - "2,9,12,5,14,6,8,13,6,5,15,13,11,11],hl=[0,1518500249,1859775393,2400959708,2" - "840853838],hr=[1352829926,1548603684,1836072691,2053994217,0];module.exports" - "=ripemd160;\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/buffer/index.js\"}],\"/reth" - "inkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modu" - "les/browserify/node_modules/crypto-browserify/node_modules/create-hmac/brows" - "er.js\":[function(require,module,exports){\n" - "(function (Buffer){\n" - "\"use strict\";function Hmac(t,e){Transform.call(this),t=t.toLowerCase(),\"s" - "tring\"==typeof e&&(e=new Buffer(e));var r=\"sha512\"===t||\"sha384\"===t?12" - "8:64;this._alg=t,this._key=e,e.length>r?e=createHash(t).update(e).digest():e" - ".lengthe;)i.ishr" - "n(1);if(i.isEven()&&i.iadd(ONE),i.testn(1)||i.iadd(TWO),r.cmp(TWO)){if(!r.cm" - "p(FIVE))for(;i.mod(TEN).cmp(THREE);)i.iadd(FOUR)}else for(;i.mod(TWENTYFOUR)" - ".cmp(ELEVEN);)i.iadd(FOUR);if(n=i.shrn(1),simpleSieve(n)&&simpleSieve(i)&&fe" - "rmatTest(n)&&fermatTest(i)&&millerRabin.test(n)&&millerRabin.test(i))return " - "i}}var randomBytes=require(\"randombytes\");module.exports=findPrime,findPri" - "me.simpleSieve=simpleSieve,findPrime.fermatTest=fermatTest;var BN=require(\"" - "bn.js\"),TWENTYFOUR=new BN(24),MillerRabin=require(\"miller-rabin\"),millerR" - "abin=new MillerRabin,ONE=new BN(1),TWO=new BN(2),FIVE=new BN(5),SIXTEEN=new " - "BN(16),EIGHT=new BN(8),TEN=new BN(10),THREE=new BN(3),SEVEN=new BN(7),ELEVEN" - "=new BN(11),FOUR=new BN(4),TWELVE=new BN(12),primes=null;\n" - "},{\"bn.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethi" - "nkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_modul" - "es/bn.js/lib/bn.js\",\"miller-rabin\":\"/rethinkdb/build/external/admin-deps" - "_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/cry" - "pto-browserify/node_modules/diffie-hellman/node_modules/miller-rabin/lib/mr." - "js\",\"randombytes\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/no" - "de_modules/randombytes/browser.js\"}],\"/rethinkdb/build/external/admin-deps" - "_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/cry" - "pto-browserify/node_modules/diffie-hellman/lib/primes.json\":[function(requi" - "re,module,exports){\n" - "module.exports={\n" - " \"modp1\": {\n" - " \"gen\": \"02\",\n" - " \"prime\": \"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e0" - "88a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe" - "1356d6d51c245e485b576625e7ec6f44c42e9a63a3620ffffffffffffffff\"\n" - " },\n" - " \"modp2\": {\n" - " \"gen\": \"02\",\n" - " \"prime\": \"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e0" - "88a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe" - "1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa" - "5ae9f24117c4b1fe649286651ece65381ffffffffffffffff\"\n" - " },\n" - " \"modp5\": {\n" - " \"gen\": \"02\",\n" - " \"prime\": \"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e0" - "88a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe" - "1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa" - "5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd2" - "4cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c0" - "8ca237327ffffffffffffffff\"\n" - " },\n" - " \"modp14\": {\n" - " \"gen\": \"02\",\n" - " \"prime\": \"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e0" - "88a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe" - "1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa" - "5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd2" - "4cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c0" - "8ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2" - "bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aacaa68fffffffffffffff" - "f\"\n" - " },\n" - " \"modp15\": {\n" - " \"gen\": \"02\",\n" - " \"prime\": \"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e0" - "88a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe" - "1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa" - "5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd2" - "4cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c0" - "8ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2" - "bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a3" - "3a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0" - "933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b1" - "8177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b8" - "2d120a93ad2caffffffffffffffff\"\n" - " },\n" - " \"modp16\": {\n" - " \"gen\": \"02\",\n" - " \"prime\": \"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e0" - "88a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe" - "1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa" - "5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd2" - "4cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c0" - "8ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2" - "bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a3" - "3a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0" - "933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b1" - "8177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b8" - "2d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bd" - "a2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa09" - "0c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea9" - "88d8fddc186ffb7dc90a6c08f4df435c934063199ffffffffffffffff\"\n" - " },\n" - " \"modp17\": {\n" - " \"gen\": \"02\",\n" - " \"prime\": \"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e0" - "88a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe" - "1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa" - "5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd2" - "4cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c0" - "8ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2" - "bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a3" - "3a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0" - "933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b1" - "8177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b8" - "2d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bd" - "a2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa09" - "0c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea9" - "88d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec97" - "51e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a891" - "8da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378" - "cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a8403" - "1900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8" - "f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff" - "0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7" - "474d6e694f91e6dcc4024ffffffffffffffff\"\n" - " },\n" - " \"modp18\": {\n" - " \"gen\": \"02\",\n" - " \"prime\": \"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e0" - "88a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe" - "1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa" - "5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd2" - "4cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c0" - "8ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2" - "bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a3", - 65469) + std::string( - "3a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0" - "933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b1" - "8177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b8" - "2d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bd" - "a2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa09" - "0c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea9" - "88d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec97" - "51e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a891" - "8da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378" - "cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a8403" - "1900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8" - "f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff" - "0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7" - "474d6e694f91e6dbe115974a3926f12fee5e438777cb6a932df8cd8bec4d073b931ba3bc832b" - "68d9dd300741fa7bf8afc47ed2576f6936ba424663aab639c5ae4f5683423b4742bf1c978238" - "f16cbe39d652de3fdb8befc848ad922222e04a4037c0713eb57a81a23f0c73473fc646cea306" - "b4bcbc8862f8385ddfa9d4b7fa2c087e879683303ed5bdd3a062b3cf5b3a278a66d2a13f83f4" - "4f82ddf310ee074ab6a364597e899a0255dc164f31cc50846851df9ab48195ded7ea1b1d510b" - "d7ee74d73faf36bc31ecfa268359046f4eb879f924009438b481c6cd7889a002ed5ee382bc91" - "90da6fc026e479558e4475677e9aa9e3050e2765694dfc81f56e880b96e7160c980dd98edd3d" - "fffffffffffffffff\"\n" - " }\n" - "}\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/browserify/node_modules/crypto-browserify/node_modules/diff" - "ie-hellman/node_modules/miller-rabin/lib/mr.js\":[function(require,module,ex" - "ports){\n" - "function MillerRabin(r){this.rand=r||new brorand.Rand}var bn=require(\"bn.js" - "\"),brorand=require(\"brorand\");module.exports=MillerRabin,MillerRabin.crea" - "te=function(r){return new MillerRabin(r)},MillerRabin.prototype._rand=functi" - "on(r){var n=r.bitLength(),e=this.rand.generate(Math.ceil(n/8));e[0]|=3;var t" - "=7&n;return 0!==t&&(e[e.length-1]>>=7-t),new bn(e)},MillerRabin.prototype.te" - "st=function(r,n,e){var t=r.bitLength(),a=bn.mont(r),i=new bn(1).toRed(a);n||" - "(n=Math.max(1,t/48|0));for(var o=r.subn(1),b=o.subn(1),d=0;!o.testn(d);d++);" - "for(var u=r.shrn(d),f=o.toRed(a),c=!0;n>0;n--){var s=this._rand(b);e&&e(s);v" - "ar l=s.toRed(a).redPow(u);if(0!==l.cmp(i)&&0!==l.cmp(f)){for(var m=1;m0;n--){var f=this._rand(o),c=r.gcd(f);if(0!==c.cmpn(1))return c;var s=f.to" - "Red(t).redPow(d);if(0!==s.cmp(a)&&0!==s.cmp(u)){for(var l=1;l0?thi" - "s.redN=null:(this._maxwellTrick=!0,this.redN=this.n.toRed(this.red))}functio" - "n BasePoint(t,e){this.curve=t,this.type=e,this.precomputed=null}var BN=requi" - "re(\"bn.js\"),elliptic=require(\"../../elliptic\"),utils=elliptic.utils,getN" - "AF=utils.getNAF,getJSF=utils.getJSF,assert=utils.assert;module.exports=BaseC" - "urve,BaseCurve.prototype.point=function(){throw new Error(\"Not implemented\"" - ")},BaseCurve.prototype.validate=function(){throw new Error(\"Not implemented" - "\")},BaseCurve.prototype._fixedNafMul=function(t,e){assert(t.precomputed);va" - "r r=t._getDoubles(),n=getNAF(e,1),i=(1<=s;e--)a=(a<" - "<1)+n[e];o.push(a)}for(var p=this.jpoint(null,null,null),u=this.jpoint(null," - "null,null),d=i;d>0;d--){for(var s=0;s=0;a--){for(var e=0;a>=0&&0===o[a];a--)e++;if(a>=0&&e++,s=" - "s.dblp(e),a<0)break;var p=o[a];assert(0!==p),s=\"affine\"===t.type?p>0?s.mix" - "edAdd(i[p-1>>1]):s.mixedAdd(i[-p-1>>1].neg()):p>0?s.add(i[p-1>>1]):s.add(i[-" - "p-1>>1].neg())}return\"affine\"===t.type?s.toP():s},BaseCurve.prototype._wna" - "fMulAdd=function(t,e,r,n,i){for(var o=this._wnafT1,s=this._wnafT2,a=this._wn" - "afT3,p=0,u=0;u=1;u-=2){var h=u-1,f=u;if(1===o[h]&&1===o[f]){var c=[e[h]" - ",null,null,e[f]];0===e[h].y.cmp(e[f].y)?(c[1]=e[h].add(e[f]),c[2]=e[h].toJ()" - ".mixedAdd(e[f].neg())):0===e[h].y.cmp(e[f].y.redNeg())?(c[1]=e[h].toJ().mixe" - "dAdd(e[f]),c[2]=e[h].add(e[f].neg())):(c[1]=e[h].toJ().mixedAdd(e[f]),c[2]=e" - "[h].toJ().mixedAdd(e[f].neg()));var v=[-3,-1,-5,-7,0,7,5,1,3],g=getJSF(r[h]," - "r[f]);p=Math.max(g[0].length,p),a[h]=new Array(p),a[f]=new Array(p);for(var " - "m=0;m=0;u--){for(var b=0;u>=0;){for(var N=!0,m=0;m=0&&b++,B=B.dblp(b),u<0)b" - "reak;for(var m=0;m0?d=s[m][_-1>>1]:_<0&&(d=s[" - "m][-_-1>>1].neg()),B=\"affine\"===d.type?B.mixedAdd(d):B.add(d))}}for(var u=" - "0;u=Math.ceil((t.bitLength()+" - "1)/e.step)},BasePoint.prototype._getDoubles=function(t,e){if(this.precompute" - "d&&this.precomputed.doubles)return this.precomputed.doubles;for(var r=[this]" - ",n=this,i=0;i\":\"\"},Point.prototype.isInfi" - "nity=function(){return 0===this.x.cmpn(0)&&0===this.y.cmp(this.z)},Point.pro" - "totype._extDbl=function(){var t=this.x.redSqr(),r=this.y.redSqr(),e=this.z.r" - "edSqr();e=e.redIAdd(e);var i=this.curve._mulA(t),d=this.x.redAdd(this.y).red" - "Sqr().redISub(t).redISub(r),s=i.redAdd(r),u=s.redSub(e),n=i.redSub(r),h=d.re" - "dMul(u),o=s.redMul(n),l=d.redMul(n),c=u.redMul(s);return this.curve.point(h," - "o,c,l)},Point.prototype._projDbl=function(){var t,r,e,i=this.x.redAdd(this.y" - ").redSqr(),d=this.x.redSqr(),s=this.y.redSqr();if(this.curve.twisted){var u=" - "this.curve._mulA(d),n=u.redAdd(s);if(this.zOne)t=i.redSub(d).redSub(s).redMu" - "l(n.redSub(this.curve.two)),r=n.redMul(u.redSub(s)),e=n.redSqr().redSub(n).r" - "edSub(n);else{var h=this.z.redSqr(),o=n.redSub(h).redISub(h);t=i.redSub(d).r" - "edISub(s).redMul(o),r=n.redMul(u.redSub(s)),e=n.redMul(o)}}else{var u=d.redA" - "dd(s),h=this.curve._mulC(this.c.redMul(this.z)).redSqr(),o=u.redSub(h).redSu" - "b(h);t=this.curve._mulC(i.redISub(u)).redMul(o),r=this.curve._mulC(u).redMul" - "(d.redISub(s)),e=u.redMul(o)}return this.curve.point(t,r,e)},Point.prototype" - ".dbl=function(){return this.isInfinity()?this:this.curve.extended?this._extD" - "bl():this._projDbl()},Point.prototype._extAdd=function(t){var r=this.y.redSu" - "b(this.x).redMul(t.y.redSub(t.x)),e=this.y.redAdd(this.x).redMul(t.y.redAdd(" - "t.x)),i=this.t.redMul(this.curve.dd).redMul(t.t),d=this.z.redMul(t.z.redAdd(" - "t.z)),s=e.redSub(r),u=d.redSub(i),n=d.redAdd(i),h=e.redAdd(r),o=s.redMul(u)," - "l=n.redMul(h),c=s.redMul(h),p=u.redMul(n);return this.curve.point(o,l,p,c)}," - "Point.prototype._projAdd=function(t){var r,e,i=this.z.redMul(t.z),d=i.redSqr" - "(),s=this.x.redMul(t.x),u=this.y.redMul(t.y),n=this.curve.d.redMul(s).redMul" - "(u),h=d.redSub(n),o=d.redAdd(n),l=this.x.redAdd(this.y).redMul(t.x.redAdd(t." - "y)).redISub(s).redISub(u),c=i.redMul(h).redMul(l);return this.curve.twisted?" - "(r=i.redMul(o).redMul(u.redSub(this.curve._mulA(s))),e=h.redMul(o)):(r=i.red" - "Mul(o).redMul(u.redSub(s)),e=this.curve._mulC(h).redMul(o)),this.curve.point" - "(c,r,e)},Point.prototype.add=function(t){return this.isInfinity()?t:t.isInfi" - "nity()?this:this.curve.extended?this._extAdd(t):this._projAdd(t)},Point.prot" - "otype.mul=function(t){return this._hasDoubles(t)?this.curve._fixedNafMul(thi" - "s,t):this.curve._wnafMul(this,t)},Point.prototype.mulAdd=function(t,r,e){ret" - "urn this.curve._wnafMulAdd(1,[this,r],[t,e],2,!1)},Point.prototype.jmulAdd=f" - "unction(t,r,e){return this.curve._wnafMulAdd(1,[this,r],[t,e],2,!0)},Point.p" - "rototype.normalize=function(){if(this.zOne)return this;var t=this.z.redInvm(" - ");return this.x=this.x.redMul(t),this.y=this.y.redMul(t),this.t&&(this.t=thi" - "s.t.redMul(t)),this.z=this.curve.one,this.zOne=!0,this},Point.prototype.neg=" - "function(){return this.curve.point(this.x.redNeg(),this.y,this.z,this.t&&thi" - "s.t.redNeg())},Point.prototype.getX=function(){return this.normalize(),this." - "x.fromRed()},Point.prototype.getY=function(){return this.normalize(),this.y." - "fromRed()},Point.prototype.eq=function(t){return this===t||0===this.getX().c" - "mp(t.getX())&&0===this.getY().cmp(t.getY())},Point.prototype.eqXToP=function" - "(t){var r=t.toRed(this.curve.red).redMul(this.z);if(0===this.x.cmp(r))return" - "!0;for(var e=t.clone(),i=this.curve.redN.redMul(this.z);;){if(e.iadd(this.cu" - "rve.n),e.cmp(this.curve.p)>=0)return!1;if(r.redIAdd(i),0===this.x.cmp(r))ret" - "urn!0}return!1},Point.prototype.toP=Point.prototype.normalize,Point.prototyp" - "e.mixedAdd=Point.prototype.add;\n" - "},{\"../../elliptic\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modu" - "les/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/n" - "ode_modules/elliptic/lib/elliptic.js\",\"../curve\":\"/rethinkdb/build/exter" - "nal/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/no" - "de_modules/crypto-browserify/node_modules/elliptic/lib/elliptic/curve/index." - "js\",\"bn.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/ret" - "hinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_mod" - "ules/bn.js/lib/bn.js\",\"inherits\":\"/rethinkdb/build/external/admin-deps_2" - ".0.4/node_modules/rethinkdb-webui/node_modules/inherits/inherits_browser.js\"" - "}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui" - "/node_modules/browserify/node_modules/crypto-browserify/node_modules/ellipti" - "c/lib/elliptic/curve/index.js\":[function(require,module,exports){\n" - "\"use strict\";var curve=exports;curve.base=require(\"./base\"),curve.short=" - "require(\"./short\"),curve.mont=require(\"./mont\"),curve.edwards=require(\"" - "./edwards\");\n" - "},{\"./base\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_modu" - "les/elliptic/lib/elliptic/curve/base.js\",\"./edwards\":\"/rethinkdb/build/e" - "xternal/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserif" - "y/node_modules/crypto-browserify/node_modules/elliptic/lib/elliptic/curve/ed" - "wards.js\",\"./mont\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modu" - "les/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/n" - "ode_modules/elliptic/lib/elliptic/curve/mont.js\",\"./short\":\"/rethinkdb/b" - "uild/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/bro" - "wserify/node_modules/crypto-browserify/node_modules/elliptic/lib/elliptic/cu" - "rve/short.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/r" - "ethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_m" - "odules/elliptic/lib/elliptic/curve/mont.js\":[function(require,module,export" - "s){\n" - "\"use strict\";function MontCurve(t){Base.call(this,\"mont\",t),this.a=new B" - "N(t.a,16).toRed(this.red),this.b=new BN(t.b,16).toRed(this.red),this.i4=new " - "BN(4).toRed(this.red).redInvm(),this.two=new BN(2).toRed(this.red),this.a24=" - "this.i4.redMul(this.a.redAdd(this.two))}function Point(t,r,e){Base.BasePoint" - ".call(this,t,\"projective\"),null===r&&null===e?(this.x=this.curve.one,this." - "z=this.curve.zero):(this.x=new BN(r,16),this.z=new BN(e,16),this.x.red||(thi" - "s.x=this.x.toRed(this.curve.red)),this.z.red||(this.z=this.z.toRed(this.curv" - "e.red)))}var curve=require(\"../curve\"),BN=require(\"bn.js\"),inherits=requ" - "ire(\"inherits\"),Base=curve.base,elliptic=require(\"../../elliptic\"),utils" - "=elliptic.utils;inherits(MontCurve,Base),module.exports=MontCurve,MontCurve." - "prototype.validate=function(t){var r=t.normalize().x,e=r.redSqr(),i=e.redMul" - "(r).redAdd(e.redMul(this.a)).redAdd(r),o=i.redSqrt();return 0===o.redSqr().c" - "mp(i)},inherits(Point,Base.BasePoint),MontCurve.prototype.decodePoint=functi" - "on(t,r){return this.point(utils.toArray(t,r),1)},MontCurve.prototype.point=f" - "unction(t,r){return new Point(this,t,r)},MontCurve.prototype.pointFromJSON=f" - "unction(t){return Point.fromJSON(this,t)},Point.prototype.precompute=functio" - "n(){},Point.prototype._encode=function(){return this.getX().toArray(\"be\",t" - "his.curve.p.byteLength())},Point.fromJSON=function(t,r){return new Point(t,r" - "[0],r[1]||t.one)},Point.prototype.inspect=function(){return this.isInfinity(" - ")?\"\":\"\"},Point.prototype.isInfinity=f" - "unction(){return 0===this.z.cmpn(0)},Point.prototype.dbl=function(){var t=th" - "is.x.redAdd(this.z),r=t.redSqr(),e=this.x.redSub(this.z),i=e.redSqr(),o=r.re" - "dSub(i),n=r.redMul(i),u=o.redMul(i.redAdd(this.curve.a24.redMul(o)));return " - "this.curve.point(n,u)},Point.prototype.add=function(){throw new Error(\"Not " - "supported on Montgomery curve\")},Point.prototype.diffAdd=function(t,r){var " - "e=this.x.redAdd(this.z),i=this.x.redSub(this.z),o=t.x.redAdd(t.z),n=t.x.redS" - "ub(t.z),u=n.redMul(e),d=o.redMul(i),s=r.z.redMul(u.redAdd(d).redSqr()),h=r.x" - ".redMul(u.redISub(d).redSqr());return this.curve.point(s,h)},Point.prototype" - ".mul=function(t){for(var r=t.clone(),e=this,i=this.curve.point(null,null),o=" - "this,n=[];0!==r.cmpn(0);r.iushrn(1))n.push(r.andln(1));for(var u=n.length-1;" - "u>=0;u--)0===n[u]?(e=e.diffAdd(i,o),i=i.dbl()):(i=e.diffAdd(i,o),e=e.dbl());" - "return i},Point.prototype.mulAdd=function(){throw new Error(\"Not supported " - "on Montgomery curve\")},Point.prototype.jumlAdd=function(){throw new Error(\"" - "Not supported on Montgomery curve\")},Point.prototype.eq=function(t){return " - "0===this.getX().cmp(t.getX())},Point.prototype.normalize=function(){return t" - "his.x=this.x.redMul(this.z.redInvm()),this.z=this.curve.one,this},Point.prot" - "otype.getX=function(){return this.normalize(),this.x.fromRed()};\n" - "},{\"../../elliptic\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modu" - "les/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/n" - "ode_modules/elliptic/lib/elliptic.js\",\"../curve\":\"/rethinkdb/build/exter" - "nal/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/no" - "de_modules/crypto-browserify/node_modules/elliptic/lib/elliptic/curve/index." - "js\",\"bn.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/ret" - "hinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_mod" - "ules/bn.js/lib/bn.js\",\"inherits\":\"/rethinkdb/build/external/admin-deps_2" - ".0.4/node_modules/rethinkdb-webui/node_modules/inherits/inherits_browser.js\"" - "}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui" - "/node_modules/browserify/node_modules/crypto-browserify/node_modules/ellipti" - "c/lib/elliptic/curve/short.js\":[function(require,module,exports){\n" - "\"use strict\";function ShortCurve(r){Base.call(this,\"short\",r),this.a=new" - " BN(r.a,16).toRed(this.red),this.b=new BN(r.b,16).toRed(this.red),this.tinv=" - "this.two.redInvm(),this.zeroA=0===this.a.fromRed().cmpn(0),this.threeA=0===t" - "his.a.fromRed().sub(this.p).cmpn(-3),this.endo=this._getEndomorphism(r),this" - "._endoWnafT1=new Array(4),this._endoWnafT2=new Array(4)}function Point(r,e,t" - ",d){Base.BasePoint.call(this,r,\"affine\"),null===e&&null===t?(this.x=null,t" - "his.y=null,this.inf=!0):(this.x=new BN(e,16),this.y=new BN(t,16),d&&(this.x." - "forceRed(this.curve.red),this.y.forceRed(this.curve.red)),this.x.red||(this." - "x=this.x.toRed(this.curve.red)),this.y.red||(this.y=this.y.toRed(this.curve." - "red)),this.inf=!1)}function JPoint(r,e,t,d){Base.BasePoint.call(this,r,\"jac" - "obian\"),null===e&&null===t&&null===d?(this.x=this.curve.one,this.y=this.cur" - "ve.one,this.z=new BN(0)):(this.x=new BN(e,16),this.y=new BN(t,16),this.z=new" - " BN(d,16)),this.x.red||(this.x=this.x.toRed(this.curve.red)),this.y.red||(th" - "is.y=this.y.toRed(this.curve.red)),this.z.red||(this.z=this.z.toRed(this.cur" - "ve.red)),this.zOne=this.z===this.curve.one}var curve=require(\"../curve\"),e" - "lliptic=require(\"../../elliptic\"),BN=require(\"bn.js\"),inherits=require(\"" - "inherits\"),Base=curve.base,assert=elliptic.utils.assert;inherits(ShortCurve" - ",Base),module.exports=ShortCurve,ShortCurve.prototype._getEndomorphism=funct" - "ion(r){if(this.zeroA&&this.g&&this.n&&1===this.p.modn(3)){var e,t;if(r.beta)" - "e=new BN(r.beta,16).toRed(this.red);else{var d=this._getEndoRoots(this.p);e=" - "d[0].cmp(d[1])<0?d[0]:d[1],e=e.toRed(this.red)}if(r.lambda)t=new BN(r.lambda" - ",16);else{var i=this._getEndoRoots(this.n);0===this.g.mul(i[0]).x.cmp(this.g" - ".x.redMul(e))?t=i[0]:(t=i[1],assert(0===this.g.mul(t).x.cmp(this.g.x.redMul(" - "e))))}var n;return n=r.basis?r.basis.map(function(r){return{a:new BN(r.a,16)" - ",b:new BN(r.b,16)}}):this._getEndoBasis(t),{beta:e,lambda:t,basis:n}}},Short" - "Curve.prototype._getEndoRoots=function(r){var e=r===this.p?this.red:BN.mont(" - "r),t=new BN(2).toRed(e).redInvm(),d=t.redNeg(),i=new BN(3).toRed(e).redNeg()" - ".redSqrt().redMul(t),n=d.redAdd(i).fromRed(),u=d.redSub(i).fromRed();return[" - "n,u]},ShortCurve.prototype._getEndoBasis=function(r){for(var e,t,d,i,n,u,s,o" - ",h,l=this.n.ushrn(Math.floor(this.n.bitLength()/2)),p=r,a=this.n.clone(),c=n" - "ew BN(1),f=new BN(0),v=new BN(0),S=new BN(1),b=0;0!==p.cmpn(0);){var I=a.div" - "(p);o=a.sub(I.mul(p)),h=v.sub(I.mul(c));var y=S.sub(I.mul(f));if(!d&&o.cmp(l" - ")<0)e=s.neg(),t=c,d=o.neg(),i=h;else if(d&&2===++b)break;s=o,a=p,p=o,v=c,c=h" - ",S=f,f=y}n=o.neg(),u=h;var A=d.sqr().add(i.sqr()),m=n.sqr().add(u.sqr());ret" - "urn m.cmp(A)>=0&&(n=e,u=t),d.negative&&(d=d.neg(),i=i.neg()),n.negative&&(n=" - "n.neg(),u=u.neg()),[{a:d,b:i},{a:n,b:u}]},ShortCurve.prototype._endoSplit=fu" - "nction(r){var e=this.endo.basis,t=e[0],d=e[1],i=d.b.mul(r).divRound(this.n)," - "n=t.b.neg().mul(r).divRound(this.n),u=i.mul(t.a),s=n.mul(d.a),o=i.mul(t.b),h" - "=n.mul(d.b),l=r.sub(u).sub(s),p=o.add(h).neg();return{k1:l,k2:p}},ShortCurve" - ".prototype.pointFromX=function(r,e){r=new BN(r,16),r.red||(r=r.toRed(this.re" - "d));var t=r.redSqr().redMul(r).redIAdd(r.redMul(this.a)).redIAdd(this.b),d=t" - ".redSqrt();if(0!==d.redSqr().redSub(t).cmp(this.zero))throw new Error(\"inva" - "lid point\");var i=d.fromRed().isOdd();return(e&&!i||!e&&i)&&(d=d.redNeg())," - "this.point(r,d)},ShortCurve.prototype.validate=function(r){if(r.inf)return!0" - ";var e=r.x,t=r.y,d=this.a.redMul(e),i=e.redSqr().redMul(e).redIAdd(d).redIAd" - "d(this.b);return 0===t.redSqr().redISub(i).cmpn(0)},ShortCurve.prototype._en" - "doWnafMulAdd=function(r,e,t){for(var d=this._endoWnafT1,i=this._endoWnafT2,n" - "=0;n\":\"\"},Point.prototype.isInfinity=function(){return this.inf},Point.p" - "rototype.add=function(r){if(this.inf)return r;if(r.inf)return this;if(this.e" - "q(r))return this.dbl();if(this.neg().eq(r))return this.curve.point(null,null" - ");if(0===this.x.cmp(r.x))return this.curve.point(null,null);var e=this.y.red" - "Sub(r.y);0!==e.cmpn(0)&&(e=e.redMul(this.x.redSub(r.x).redInvm()));var t=e.r" - "edSqr().redISub(this.x).redISub(r.x),d=e.redMul(this.x.redSub(t)).redISub(th" - "is.y);return this.curve.point(t,d)},Point.prototype.dbl=function(){if(this.i" - "nf)return this;var r=this.y.redAdd(this.y);if(0===r.cmpn(0))return this.curv" - "e.point(null,null);var e=this.curve.a,t=this.x.redSqr(),d=r.redInvm(),i=t.re" - "dAdd(t).redIAdd(t).redIAdd(e).redMul(d),n=i.redSqr().redISub(this.x.redAdd(t" - "his.x)),u=i.redMul(this.x.redSub(n)).redISub(this.y);return this.curve.point" - "(n,u)},Point.prototype.getX=function(){return this.x.fromRed()},Point.protot" - "ype.getY=function(){return this.y.fromRed()},Point.prototype.mul=function(r)" - "{return r=new BN(r,16),this._hasDoubles(r)?this.curve._fixedNafMul(this,r):t" - "his.curve.endo?this.curve._endoWnafMulAdd([this],[r]):this.curve._wnafMul(th" - "is,r)},Point.prototype.mulAdd=function(r,e,t){var d=[this,e],i=[r,t];return " - "this.curve.endo?this.curve._endoWnafMulAdd(d,i):this.curve._wnafMulAdd(1,d,i" - ",2)},Point.prototype.jmulAdd=function(r,e,t){var d=[this,e],i=[r,t];return t" - "his.curve.endo?this.curve._endoWnafMulAdd(d,i,!0):this.curve._wnafMulAdd(1,d" - ",i,2,!0)},Point.prototype.eq=function(r){return this===r||this.inf===r.inf&&" - "(this.inf||0===this.x.cmp(r.x)&&0===this.y.cmp(r.y))},Point.prototype.neg=fu" - "nction(r){if(this.inf)return this;var e=this.curve.point(this.x,this.y.redNe" - "g());if(r&&this.precomputed){var t=this.precomputed,d=function(r){return r.n" - "eg()};e.precomputed={naf:t.naf&&{wnd:t.naf.wnd,points:t.naf.points.map(d)},d" - "oubles:t.doubles&&{step:t.doubles.step,points:t.doubles.points.map(d)}}}retu" - "rn e},Point.prototype.toJ=function(){if(this.inf)return this.curve.jpoint(nu" - "ll,null,null);var r=this.curve.jpoint(this.x,this.y,this.curve.one);return r" - "},inherits(JPoint,Base.BasePoint),ShortCurve.prototype.jpoint=function(r,e,t" - "){return new JPoint(this,r,e,t)},JPoint.prototype.toP=function(){if(this.isI" - "nfinity())return this.curve.point(null,null);var r=this.z.redInvm(),e=r.redS" - "qr(),t=this.x.redMul(e),d=this.y.redMul(e).redMul(r);return this.curve.point" - "(t,d)},JPoint.prototype.neg=function(){return this.curve.jpoint(this.x,this." - "y.redNeg(),this.z)},JPoint.prototype.add=function(r){if(this.isInfinity())re" - "turn r;if(r.isInfinity())return this;var e=r.z.redSqr(),t=this.z.redSqr(),d=" - "this.x.redMul(e),i=r.x.redMul(t),n=this.y.redMul(e.redMul(r.z)),u=r.y.redMul" - "(t.redMul(this.z)),s=d.redSub(i),o=n.redSub(u);if(0===s.cmpn(0))return 0!==o" - ".cmpn(0)?this.curve.jpoint(null,null,null):this.dbl();var h=s.redSqr(),l=h.r" - "edMul(s),p=d.redMul(h),a=o.redSqr().redIAdd(l).redISub(p).redISub(p),c=o.red" - "Mul(p.redISub(a)).redISub(n.redMul(l)),f=this.z.redMul(r.z).redMul(s);return" - " this.curve.jpoint(a,c,f)},JPoint.prototype.mixedAdd=function(r){if(this.isI" - "nfinity())return r.toJ();if(r.isInfinity())return this;var e=this.z.redSqr()" - ",t=this.x,d=r.x.redMul(e),i=this.y,n=r.y.redMul(e).redMul(this.z),u=t.redSub" - "(d),s=i.redSub(n);if(0===u.cmpn(0))return 0!==s.cmpn(0)?this.curve.jpoint(nu" - "ll,null,null):this.dbl();var o=u.redSqr(),h=o.redMul(u),l=t.redMul(o),p=s.re" - "dSqr().redIAdd(h).redISub(l).redISub(l),a=s.redMul(l.redISub(p)).redISub(i.r" - "edMul(h)),c=this.z.redMul(u);return this.curve.jpoint(p,a,c)},JPoint.prototy" - "pe.dblp=function(r){if(0===r)return this;if(this.isInfinity())return this;if" - "(!r)return this.dbl();if(this.curve.zeroA||this.curve.threeA){for(var e=this" - ",t=0;t=0)return!1;if(t.redIAdd(i),0===this.x.cmp(t))return!0}return!1},JPoin" - "t.prototype.inspect=function(){return this.isInfinity()?\"\":\"\"},JPoint.prototype.isInfinity=function(" - "){return 0===this.z.cmpn(0)};\n" - "},{\"../../elliptic\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modu" - "les/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/n" - "ode_modules/elliptic/lib/elliptic.js\",\"../curve\":\"/rethinkdb/build/exter" - "nal/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/no" - "de_modules/crypto-browserify/node_modules/elliptic/lib/elliptic/curve/index." - "js\",\"bn.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/ret" - "hinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_mod" - "ules/bn.js/lib/bn.js\",\"inherits\":\"/rethinkdb/build/external/admin-deps_2" - ".0.4/node_modules/rethinkdb-webui/node_modules/inherits/inherits_browser.js\"" - "}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui" - "/node_modules/browserify/node_modules/crypto-browserify/node_modules/ellipti" - "c/lib/elliptic/curves.js\":[function(require,module,exports){\n" - "\"use strict\";function PresetCurve(f){\"short\"===f.type?this.curve=new ell" - "iptic.curve.short(f):\"edwards\"===f.type?this.curve=new elliptic.curve.edwa" - "rds(f):this.curve=new elliptic.curve.mont(f),this.g=this.curve.g,this.n=this" - ".curve.n,this.hash=f.hash,assert(this.g.validate(),\"Invalid curve\"),assert" - "(this.g.mul(this.n).isInfinity(),\"Invalid curve, G*N != O\")}function defin" - "eCurve(f,e){Object.defineProperty(curves,f,{configurable:!0,enumerable:!0,ge" - "t:function(){var a=new PresetCurve(e);return Object.defineProperty(curves,f," - "{configurable:!0,enumerable:!0,value:a}),a}})}var curves=exports,hash=requir" - "e(\"hash.js\"),elliptic=require(\"../elliptic\"),assert=elliptic.utils.asser" - "t;curves.PresetCurve=PresetCurve,defineCurve(\"p192\",{type:\"short\",prime:" - "\"p192\",p:\"ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff\",a:\"fff" - "fffff ffffffff ffffffff fffffffe ffffffff fffffffc\",b:\"64210519 e59c80e7 0" - "fa7e9ab 72243049 feb8deec c146b9b1\",n:\"ffffffff ffffffff ffffffff 99def836" - " 146bc9b1 b4d22831\",hash:hash.sha256,gRed:!1,g:[\"188da80e b03090f6 7cbf20e" - "b 43a18800 f4ff0afd 82ff1012\",\"07192b95 ffc8da78 631011ed 6b24cdd5 73f977a" - "1 1e794811\"]}),defineCurve(\"p224\",{type:\"short\",prime:\"p224\",p:\"ffff" - "ffff ffffffff ffffffff ffffffff 00000000 00000000 00000001\",a:\"ffffffff ff" - "ffffff ffffffff fffffffe ffffffff ffffffff fffffffe\",b:\"b4050a85 0c04b3ab " - "f5413256 5044b0b7 d7bfd8ba 270b3943 2355ffb4\",n:\"ffffffff ffffffff fffffff" - "f ffff16a2 e0b8f03e 13dd2945 5c5c2a3d\",hash:hash.sha256,gRed:!1,g:[\"b70e0c" - "bd 6bb4bf7f 321390b9 4a03c1d3 56c21122 343280d6 115c1d21\",\"bd376388 b5f723" - "fb 4c22dfe6 cd4375a0 5a074764 44d58199 85007e34\"]}),defineCurve(\"p256\",{t" - "ype:\"short\",prime:null,p:\"ffffffff 00000001 00000000 00000000 00000000 ff" - "ffffff ffffffff ffffffff\",a:\"ffffffff 00000001 00000000 00000000 00000000 " - "ffffffff ffffffff fffffffc\",b:\"5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b" - "0 cc53b0f6 3bce3c3e 27d2604b\",n:\"ffffffff 00000000 ffffffff ffffffff bce6f" - "aad a7179e84 f3b9cac2 fc632551\",hash:hash.sha256,gRed:!1,g:[\"6b17d1f2 e12c" - "4247 f8bce6e5 63a440f2 77037d81 2deb33a0 f4a13945 d898c296\",\"4fe342e2 fe1a" - "7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece cbb64068 37bf51f5\"]}),defineCurve(" - "\"p384\",{type:\"short\",prime:null,p:\"ffffffff ffffffff ffffffff ffffffff " - "ffffffff ffffffff ffffffff fffffffe ffffffff 00000000 00000000 ffffffff\",a:" - "\"ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe ff" - "ffffff 00000000 00000000 fffffffc\",b:\"b3312fa7 e23ee7e4 988e056b e3f82d19 " - "181d9c6e fe814112 0314088f 5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef\",n:" - "\"ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff c7634d81 f4372ddf 58" - "1a0db2 48b0a77a ecec196a ccc52973\",hash:hash.sha384,gRed:!1,g:[\"aa87ca22 b" - "e8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98 59f741e0 82542a38 5502f25d bf552" - "96c 3a545e38 72760ab7\",\"3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a1" - "47c e9da3113 b5f0b8c0 0a60b1ce 1d7e819d 7a431d7c 90ea0e5f\"]}),defineCurve(\"" - "p521\",{type:\"short\",prime:null,p:\"000001ff ffffffff ffffffff ffffffff ff" - "ffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffff" - "ff ffffffff ffffffff ffffffff ffffffff\",a:\"000001ff ffffffff ffffffff ffff" - "ffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff" - " ffffffff ffffffff ffffffff ffffffff fffffffc\",b:\"00000051 953eb961 8e1c9a" - "1f 929a21a0 b68540ee a2da725b 99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1" - "652c0bd 3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00\",n:\"000001ff ffffffff" - " ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffa 51868783 bf2" - "f966b 7fcc0148 f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409\",hash:hash.sha5" - "12,gRed:!1,g:[\"000000c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139 053fb5" - "21 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127 a2ffa8de 3348b3c1 856a429b f" - "97e7e31 c2e5bd66\",\"00000118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449 5" - "79b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901 3fad0761 353c7086 a272c" - "240 88be9476 9fd16650\"]}),defineCurve(\"curve25519\",{type:\"mont\",prime:\"" - "p25519\",p:\"7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffff" - "ffed\",a:\"76d06\",b:\"0\",n:\"1000000000000000 0000000000000000 14def9dea2f" - "79cd6 5812631a5cf5d3ed\",hash:hash.sha256,gRed:!1,g:[\"9\"]}),defineCurve(\"" - "ed25519\",{type:\"edwards\",prime:\"p25519\",p:\"7fffffffffffffff ffffffffff" - "ffffff ffffffffffffffff ffffffffffffffed\",a:\"-1\",c:\"1\",d:\"52036cee2b6f" - "fe73 8cc740797779e898 00700a4d4141d8ab 75eb4dca135978a3\",n:\"10000000000000" - "00 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed\",hash:hash.sha256,gRe" - "d:!1,g:[\"216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a\"" - ",\"6666666666666666666666666666666666666666666666666666666666666658\"]});var" - " pre;try{pre=require(\"./precomputed/secp256k1\")}catch(f){pre=void 0}define" - "Curve(\"secp256k1\",{type:\"short\",prime:\"k256\",p:\"ffffffff ffffffff fff" - "fffff ffffffff ffffffff ffffffff fffffffe fffffc2f\",a:\"0\",b:\"7\",n:\"fff" - "fffff ffffffff ffffffff fffffffe baaedce6 af48a03b bfd25e8c d0364141\",h:\"1" - "\",hash:hash.sha256,beta:\"7ae96a2b657c07106e64479eac3434e99cf0497512f58995c" - "1396c28719501ee\",lambda:\"5363ad4cc05c30e0a5261c028812645a122e22ea20816678d" - "f02967c1b23bd72\",basis:[{a:\"3086d221a7d46bcde86c90e49284eb15\",b:\"-e4437e" - "d6010e88286f547fa90abfe4c3\"},{a:\"114ca50f7a8e2f3f657c1108d9d44cfd8\",b:\"3" - "086d221a7d46bcde86c90e49284eb15\"}],gRed:!1,g:[\"79be667ef9dcbbac55a06295ce8" - "70b07029bfcdb2dce28d959f2815b16f81798\",\"483ada7726a3c4655da4fbfc0e1108a8fd" - "17b448a68554199c47d08ffb10d4b8\",pre]});\n" - "},{\"../elliptic\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules" - "/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node" - "_modules/elliptic/lib/elliptic.js\",\"./precomputed/secp256k1\":\"/rethinkdb" - "/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/b" - "rowserify/node_modules/crypto-browserify/node_modules/elliptic/lib/elliptic/" - "precomputed/secp256k1.js\",\"hash.js\":\"/rethinkdb/build/external/admin-dep" - "s_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/cr" - "ypto-browserify/node_modules/elliptic/node_modules/hash.js/lib/hash.js\"}],\"" - "/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node" - "_modules/browserify/node_modules/crypto-browserify/node_modules/elliptic/lib" - "/elliptic/ec/index.js\":[function(require,module,exports){\n" - "\"use strict\";function EC(e){return this instanceof EC?(\"string\"==typeof " - "e&&(assert(elliptic.curves.hasOwnProperty(e),\"Unknown curve \"+e),e=ellipti" - "c.curves[e]),e instanceof elliptic.curves.PresetCurve&&(e={curve:e}),this.cu" - "rve=e.curve.curve,this.n=this.curve.n,this.nh=this.n.ushrn(1),this.g=this.cu" - "rve.g,this.g=e.curve.g,this.g.precompute(e.curve.n.bitLength()+1),void(this." - "hash=e.hash||e.curve.hash)):new EC(e)}var BN=require(\"bn.js\"),elliptic=req" - "uire(\"../../elliptic\"),utils=elliptic.utils,assert=utils.assert,KeyPair=re" - "quire(\"./key\"),Signature=require(\"./signature\");module.exports=EC,EC.pro" - "totype.keyPair=function(e){return new KeyPair(this,e)},EC.prototype.keyFromP" - "rivate=function(e,t){return KeyPair.fromPrivate(this,e,t)},EC.prototype.keyF" - "romPublic=function(e,t){return KeyPair.fromPublic(this,e,t)},EC.prototype.ge" - "nKeyPair=function(e){e||(e={});for(var t=new elliptic.hmacDRBG({hash:this.ha" - "sh,pers:e.pers,entropy:e.entropy||elliptic.rand(this.hash.hmacStrength),nonc" - "e:this.n.toArray()}),r=this.n.byteLength(),i=this.n.sub(new BN(2));;){var n=" - "new BN(t.generate(r));if(!(n.cmp(i)>0))return n.iaddn(1),this.keyFromPrivate" - "(n)}},EC.prototype._truncateToN=function(e,t){var r=8*e.byteLength()-this.n." - "bitLength();return r>0&&(e=e.ushrn(r)),!t&&e.cmp(this.n)>=0?e.sub(this.n):e}" - ",EC.prototype.sign=function(e,t,r,i){\"object\"==typeof r&&(i=r,r=null),i||(" - "i={}),t=this.keyFromPrivate(t,r),e=this._truncateToN(new BN(e,16));for(var n" - "=this.n.byteLength(),s=t.getPrivate().toArray(\"be\",n),u=e.toArray(\"be\",n" - "),h=new elliptic.hmacDRBG({hash:this.hash,entropy:s,nonce:u,pers:i.pers,pers" - "Enc:i.persEnc}),o=this.n.sub(new BN(1)),c=0;!0;c++){var a=i.k?i.k(c):new BN(" - "h.generate(this.n.byteLength()));if(a=this._truncateToN(a,!0),!(a.cmpn(1)<=0" - "||a.cmp(o)>=0)){var p=this.g.mul(a);if(!p.isInfinity()){var v=p.getX(),m=v.u" - "mod(this.n);if(0!==m.cmpn(0)){var y=a.invm(this.n).mul(m.mul(t.getPrivate())" - ".iadd(e));if(y=y.umod(this.n),0!==y.cmpn(0)){var l=(p.getY().isOdd()?1:0)|(0" - "!==v.cmp(m)?2:0);return i.canonical&&y.cmp(this.nh)>0&&(y=this.n.sub(y),l^=1" - "),new Signature({r:m,s:y,recoveryParam:l})}}}}}},EC.prototype.verify=functio" - "n(e,t,r,i){e=this._truncateToN(new BN(e,16)),r=this.keyFromPublic(r,i),t=new" - " Signature(t,\"hex\");var n=t.r,s=t.s;if(n.cmpn(1)<0||n.cmp(this.n)>=0)retur" - "n!1;if(s.cmpn(1)<0||s.cmp(this.n)>=0)return!1;var u=s.invm(this.n),h=u.mul(e" - ").umod(this.n),o=u.mul(n).umod(this.n);if(!this.curve._maxwellTrick){var c=t" - "his.g.mulAdd(h,r.getPublic(),o);return!c.isInfinity()&&0===c.getX().umod(thi" - "s.n).cmp(n)}var c=this.g.jmulAdd(h,r.getPublic(),o);return!c.isInfinity()&&c" - ".eqXToP(n)},EC.prototype.recoverPubKey=function(e,t,r,i){assert((3&r)===r,\"" - "The recovery param is more than two bits\"),t=new Signature(t,i);var n=this." - "n,s=new BN(e),u=t.r,h=t.s,o=1&r,c=r>>1;if(u.cmp(this.curve.p.umod(this.curve" - ".n))>=0&&c)throw new Error(\"Unable to find sencond key candinate\");u=c?thi" - "s.curve.pointFromX(u.add(this.curve.n),o):this.curve.pointFromX(u,o);var a=n" - ".sub(s),p=t.r.invm(n);return this.g.mulAdd(a,u,h).mul(p)},EC.prototype.getKe" - "yRecoveryParam=function(e,t,r,i){if(t=new Signature(t,i),null!==t.recoveryPa" - "ram)return t.recoveryParam;for(var n=0;n<4;n++){var s;try{s=this.recoverPubK" - "ey(e,t,n)}catch(e){continue}if(s.eq(r))return n}throw new Error(\"Unable to " - "find valid recovery factor\")};\n" - "},{\"../../elliptic\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modu" - "les/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/n" - "ode_modules/elliptic/lib/elliptic.js\",\"./key\":\"/rethinkdb/build/external" - "/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_" - "modules/crypto-browserify/node_modules/elliptic/lib/elliptic/ec/key.js\",\"." - "/signature\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethi" - "nkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_modul" - "es/elliptic/lib/elliptic/ec/signature.js\",\"bn.js\":\"/rethinkdb/build/exte" - "rnal/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/n" - "ode_modules/crypto-browserify/node_modules/bn.js/lib/bn.js\"}],\"/rethinkdb/" - "build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/br" - "owserify/node_modules/crypto-browserify/node_modules/elliptic/lib/elliptic/e" - "c/key.js\":[function(require,module,exports){\n" - "\"use strict\";function KeyPair(i,t){this.ec=i,this.priv=null,this.pub=null," - "t.priv&&this._importPrivate(t.priv,t.privEnc),t.pub&&this._importPublic(t.pu" - "b,t.pubEnc)}var BN=require(\"bn.js\");module.exports=KeyPair,KeyPair.fromPub" - "lic=function(i,t,r){return t instanceof KeyPair?t:new KeyPair(i,{pub:t,pubEn" - "c:r})},KeyPair.fromPrivate=function(i,t,r){return t instanceof KeyPair?t:new" - " KeyPair(i,{priv:t,privEnc:r})},KeyPair.prototype.validate=function(){var i=" - "this.getPublic();return i.isInfinity()?{result:!1,reason:\"Invalid public ke" - "y\"}:i.validate()?i.mul(this.ec.curve.n).isInfinity()?{result:!0,reason:null" - "}:{result:!1,reason:\"Public key * N != O\"}:{result:!1,reason:\"Public key " - "is not a point\"}},KeyPair.prototype.getPublic=function(i,t){return\"string\"" - "==typeof i&&(t=i,i=null),this.pub||(this.pub=this.ec.g.mul(this.priv)),t?thi" - "s.pub.encode(t,i):this.pub},KeyPair.prototype.getPrivate=function(i){return\"" - "hex\"===i?this.priv.toString(16,2):this.priv},KeyPair.prototype._importPriva" - "te=function(i,t){this.priv=new BN(i,t||16),this.priv=this.priv.umod(this.ec." - "curve.n)},KeyPair.prototype._importPublic=function(i,t){return i.x||i.y?void" - "(this.pub=this.ec.curve.point(i.x,i.y)):void(this.pub=this.ec.curve.decodePo" - "int(i,t))},KeyPair.prototype.derive=function(i){return i.mul(this.priv).getX" - "()},KeyPair.prototype.sign=function(i,t,r){return this.ec.sign(i,this,t,r)}," - "KeyPair.prototype.verify=function(i,t){return this.ec.verify(i,t,this)},KeyP" - "air.prototype.inspect=function(){return\"\"};\n" - "},{\"bn.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethi" - "nkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_modul" - "es/bn.js/lib/bn.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_mod" - "ules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/" - "node_modules/elliptic/lib/elliptic/ec/signature.js\":[function(require,modul" - "e,exports){\n" - "\"use strict\";function Signature(t,r){return t instanceof Signature?t:void(" - "this._importDER(t,r)||(assert(t.r&&t.s,\"Signature without r or s\"),this.r=" - "new BN(t.r,16),this.s=new BN(t.s,16),void 0===t.recoveryParam?this.recoveryP" - "aram=null:this.recoveryParam=t.recoveryParam))}function Position(){this.plac" - "e=0}function getLength(t,r){var e=t[r.place++];if(!(128&e))return e;for(var " - "n=15&e,i=0,a=0,c=r.place;a>>3);for(t.push(128|e);--e;)t.push(r>>>" - "(e<<3)&255);t.push(r)}var BN=require(\"bn.js\"),elliptic=require(\"../../ell" - "iptic\"),utils=elliptic.utils,assert=utils.assert;module.exports=Signature,S" - "ignature.prototype._importDER=function(t,r){t=utils.toArray(t,r);var e=new P" - "osition;if(48!==t[e.place++])return!1;var n=getLength(t,e);if(n+e.place!==t." - "length)return!1;if(2!==t[e.place++])return!1;var i=getLength(t,e),a=t.slice(" - "e.place,i+e.place);if(e.place+=i,2!==t[e.place++])return!1;var c=getLength(t" - ",e);if(t.length!==c+e.place)return!1;var s=t.slice(e.place,c+e.place);return" - " 0===a[0]&&128&a[1]&&(a=a.slice(1)),0===s[0]&&128&s[1]&&(s=s.slice(1)),this." - "r=new BN(a),this.s=new BN(s),this.recoveryParam=null,!0},Signature.prototype" - ".toDER=function(t){var r=this.r.toArray(),e=this.s.toArray();for(128&r[0]&&(" - "r=[0].concat(r)),128&e[0]&&(e=[0].concat(e)),r=rmPadding(r),e=rmPadding(e);!" - "(e[0]||128&e[1]);)e=e.slice(1);var n=[2];constructLength(n,r.length),n=n.con" - "cat(r),n.push(2),constructLength(n,e.length);var i=n.concat(e),a=[48];return" - " constructLength(a,i.length),a=a.concat(i),utils.encode(a,t)};\n" - "},{\"../../elliptic\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modu" - "les/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/n" - "ode_modules/elliptic/lib/elliptic.js\",\"bn.js\":\"/rethinkdb/build/external" - "/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_" - "modules/crypto-browserify/node_modules/bn.js/lib/bn.js\"}],\"/rethinkdb/buil" - "d/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browse" - "rify/node_modules/crypto-browserify/node_modules/elliptic/lib/elliptic/eddsa" - "/index.js\":[function(require,module,exports){\n" - "\"use strict\";function EDDSA(t){if(assert(\"ed25519\"===t,\"only tested wit" - "h ed25519 so far\"),!(this instanceof EDDSA))return new EDDSA(t);var t=ellip" - "tic.curves[t].curve;this.curve=t,this.g=t.g,this.g.precompute(t.n.bitLength(" - ")+1),this.pointClass=t.point().constructor,this.encodingLength=Math.ceil(t.n" - ".bitLength()/8),this.hash=hash.sha512}var hash=require(\"hash.js\"),elliptic" - "=require(\"../../elliptic\"),utils=elliptic.utils,assert=utils.assert,parseB" - "ytes=utils.parseBytes,KeyPair=require(\"./key\"),Signature=require(\"./signa" - "ture\");module.exports=EDDSA,EDDSA.prototype.sign=function(t,e){t=parseBytes" - "(t);var i=this.keyFromSecret(e),r=this.hashInt(i.messagePrefix(),t),n=this.g" - ".mul(r),s=this.encodePoint(n),o=this.hashInt(s,i.pubBytes(),t).mul(i.priv())" - ",u=r.add(o).umod(this.curve.n);return this.makeSignature({R:n,S:u,Rencoded:s" - "})},EDDSA.prototype.verify=function(t,e,i){t=parseBytes(t),e=this.makeSignat" - "ure(e);var r=this.keyFromPublic(i),n=this.hashInt(e.Rencoded(),r.pubBytes()," - "t),s=this.g.mul(e.S()),o=e.R().add(r.pub().mul(n));return o.eq(s)},EDDSA.pro" - "totype.hashInt=function(){for(var t=this.hash(),e=0;e=this.minEntropy/8,\"Not enough entropy" - ". Minimum is: \"+this.minEntropy+\" bits\"),this._init(e,s,i)}var hash=requi" - "re(\"hash.js\"),elliptic=require(\"../elliptic\"),utils=elliptic.utils,asser" - "t=utils.assert;module.exports=HmacDRBG,HmacDRBG.prototype._init=function(t,e" - ",s){var i=t.concat(e).concat(s);this.K=new Array(this.outLen/8),this.V=new A" - "rray(this.outLen/8);for(var h=0;h=this.minEntropy/8,\"Not enough entropy." - " Minimum is: \"+this.minEntropy+\" bits\"),this._update(t.concat(s||[])),thi" - "s.reseed=1},HmacDRBG.prototype.generate=function(t,e,s,i){if(this.reseed>thi" - "s.reseedInterval)throw new Error(\"Reseed is required\");\"string\"!=typeof " - "e&&(i=s,s=e,e=null),s&&(s=utils.toArray(s,i),this._update(s));for(var h=[];h" - ".length>8,s=" - "255&i;o?e.push(o,s):e.push(s)}return e}function zero2(r){return 1===r.length" - "?\"0\"+r:r}function toHex(r){for(var t=\"\",e=0;e=0;){var o;if(i.isOdd()){var s=i.andln(n-1);o=s>(n>>1)-1?(n>>1)" - "-s:s,i.isubn(o)}else o=0;e.push(o);for(var u=0!==i.cmpn(0)&&0===i.andln(n-1)" - "?t+1:1,a=1;a0||t.cmpn(-i)>0;" - "){var o=r.andln(3)+n&3,s=t.andln(3)+i&3;3===o&&(o=-1),3===s&&(s=-1);var u;if" - "(0===(1&o))u=0;else{var a=r.andln(7)+n&7;u=3!==a&&5!==a||2!==s?o:-o}e[0].pus" - "h(u);var l;if(0===(1&s))l=0;else{var a=t.andln(7)+i&7;l=3!==a&&5!==a||2!==o?" - "s:-s}e[1].push(l),2*n===u+1&&(n=1-n),2*i===l+1&&(i=1-i),r.iushrn(1),t.iushrn" - "(1)}return e}function cachedProperty(r,t,e){var n=\"_\"+t;r.prototype[t]=fun" - "ction(){return void 0!==this[n]?this[n]:this[n]=e.call(this)}}function parse" - "Bytes(r){return\"string\"==typeof r?utils.toArray(r,\"hex\"):r}function intF" - "romLE(r){return new BN(r,\"hex\",\"le\")}var utils=exports,BN=require(\"bn.j" - "s\");utils.assert=function(r,t){if(!r)throw new Error(t||\"Assertion failed\"" - ")},utils.toArray=toArray,utils.zero2=zero2,utils.toHex=toHex,utils.encode=fu" - "nction(r,t){return\"hex\"===t?toHex(r):r},utils.getNAF=getNAF,utils.getJSF=g" - "etJSF,utils.cachedProperty=cachedProperty,utils.parseBytes=parseBytes,utils." - "intFromLE=intFromLE;\n" - "},{\"bn.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethi" - "nkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_modul" - "es/bn.js/lib/bn.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_mod" - "ules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/" - "node_modules/elliptic/node_modules/hash.js/lib/hash.js\":[function(require,m" - "odule,exports){\n" - "var hash=exports;hash.utils=require(\"./hash/utils\"),hash.common=require(\"" - "./hash/common\"),hash.sha=require(\"./hash/sha\"),hash.ripemd=require(\"./ha" - "sh/ripemd\"),hash.hmac=require(\"./hash/hmac\"),hash.sha1=hash.sha.sha1,hash" - ".sha256=hash.sha.sha256,hash.sha224=hash.sha.sha224,hash.sha384=hash.sha.sha" - "384,hash.sha512=hash.sha.sha512,hash.ripemd160=hash.ripemd.ripemd160;\n" - "},{\"./hash/common\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/no" - "de_modules/elliptic/node_modules/hash.js/lib/hash/common.js\",\"./hash/hmac\"" - ":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/n" - "ode_modules/browserify/node_modules/crypto-browserify/node_modules/elliptic/" - "node_modules/hash.js/lib/hash/hmac.js\",\"./hash/ripemd\":\"/rethinkdb/build" - "/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browser" - "ify/node_modules/crypto-browserify/node_modules/elliptic/node_modules/hash.j" - "s/lib/hash/ripemd.js\",\"./hash/sha\":\"/rethinkdb/build/external/admin-deps" - "_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/cry" - "pto-browserify/node_modules/elliptic/node_modules/hash.js/lib/hash/sha.js\"," - "\"./hash/utils\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/r" - "ethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_m" - "odules/elliptic/node_modules/hash.js/lib/hash/utils.js\"}],\"/rethinkdb/buil" - "d/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browse" - "rify/node_modules/crypto-browserify/node_modules/elliptic/node_modules/hash." - "js/lib/hash/common.js\":[function(require,module,exports){\n" - "function BlockHash(){this.pending=null,this.pendingTotal=0,this.blockSize=th" - "is.constructor.blockSize,this.outSize=this.constructor.outSize,this.hmacStre" - "ngth=this.constructor.hmacStrength,this.padLength=this.constructor.padLength" - "/8,this.endian=\"big\",this._delta8=this.blockSize/8,this._delta32=this.bloc" - "kSize/32}var hash=require(\"../hash\"),utils=hash.utils,assert=utils.assert;" - "exports.BlockHash=BlockHash,BlockHash.prototype.update=function(t,i){if(t=ut" - "ils.toArray(t,i),this.pending?this.pending=this.pending.concat(t):this.pendi" - "ng=t,this.pendingTotal+=t.length,this.pending.length>=this._delta8){t=this.p" - "ending;var h=t.length%this._delta8;this.pending=t.slice(t.length-h,t.length)" - ",0===this.pending.length&&(this.pending=null),t=utils.join32(t,0,t.length-h," - "this.endian);for(var s=0;s>>24&255,s[n++]=t>>>16&255,s[n+" - "+]=t>>>8&255,s[n++]=255&t}else{s[n++]=255&t,s[n++]=t>>>8&255,s[n++]=t>>>16&2" - "55,s[n++]=t>>>24&255,s[n++]=0,s[n++]=0,s[n++]=0,s[n++]=0;for(var e=8;ethis.blockSize&&(t=(new this.Hash).up" - "date(t).digest()),assert(t.length<=this.blockSize);for(var i=t.length;i>>3}functi" - "on g1_256(t){return rotr32(t,17)^rotr32(t,19)^t>>>10}function ft_1(t,h,s,i){" - "return 0===t?ch32(h,s,i):1===t||3===t?p32(h,s,i):2===t?maj32(h,s,i):void 0}f" - "unction ch64_hi(t,h,s,i,r,o){var u=t&s^~t&r;return u<0&&(u+=4294967296),u}fu" - "nction ch64_lo(t,h,s,i,r,o){var u=h&i^~h&o;return u<0&&(u+=4294967296),u}fun" - "ction maj64_hi(t,h,s,i,r,o){var u=t&s^t&r^s&r;return u<0&&(u+=4294967296),u}" - "function maj64_lo(t,h,s,i,r,o){var u=h&i^h&o^i&o;return u<0&&(u+=4294967296)" - ",u}function s0_512_hi(t,h){var s=rotr64_hi(t,h,28),i=rotr64_hi(h,t,2),r=rotr" - "64_hi(h,t,7),o=s^i^r;return o<0&&(o+=4294967296),o}function s0_512_lo(t,h){v" - "ar s=rotr64_lo(t,h,28),i=rotr64_lo(h,t,2),r=rotr64_lo(h,t,7),o=s^i^r;return " - "o<0&&(o+=4294967296),o}function s1_512_hi(t,h){var s=rotr64_hi(t,h,14),i=rot" - "r64_hi(t,h,18),r=rotr64_hi(h,t,9),o=s^i^r;return o<0&&(o+=4294967296),o}func" - "tion s1_512_lo(t,h){var s=rotr64_lo(t,h,14),i=rotr64_lo(t,h,18),r=rotr64_lo(" - "h,t,9),o=s^i^r;return o<0&&(o+=4294967296),o}function g0_512_hi(t,h){var s=r" - "otr64_hi(t,h,1),i=rotr64_hi(t,h,8),r=shr64_hi(t,h,7),o=s^i^r;return o<0&&(o+" - "=4294967296),o}function g0_512_lo(t,h){var s=rotr64_lo(t,h,1),i=rotr64_lo(t," - "h,8),r=shr64_lo(t,h,7),o=s^i^r;return o<0&&(o+=4294967296),o}function g1_512" - "_hi(t,h){var s=rotr64_hi(t,h,19),i=rotr64_hi(h,t,29),r=shr64_hi(t,h,6),o=s^i" - "^r;return o<0&&(o+=4294967296),o}function g1_512_lo(t,h){var s=rotr64_lo(t,h" - ",19),i=rotr64_lo(h,t,29),r=shr64_lo(t,h,6),o=s^i^r;return o<0&&(o+=429496729" - "6),o}var hash=require(\"../hash\"),utils=hash.utils,assert=utils.assert,rotr" - "32=utils.rotr32,rotl32=utils.rotl32,sum32=utils.sum32,sum32_4=utils.sum32_4," - "sum32_5=utils.sum32_5,rotr64_hi=utils.rotr64_hi,rotr64_lo=utils.rotr64_lo,sh" - "r64_hi=utils.shr64_hi,shr64_lo=utils.shr64_lo,sum64=utils.sum64,sum64_hi=uti" - "ls.sum64_hi,sum64_lo=utils.sum64_lo,sum64_4_hi=utils.sum64_4_hi,sum64_4_lo=u" - "tils.sum64_4_lo,sum64_5_hi=utils.sum64_5_hi,sum64_5_lo=utils.sum64_5_lo,Bloc" - "kHash=hash.common.BlockHash,sha256_K=[1116352408,1899447441,3049323471,39210" - "09573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225" - "278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,402222" - "4774,264347078,604807628,770255983,1249150122,1555081692,1996064986,25542208" - "82,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,33824189" - "5,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350" - ",2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,360035280" - "4,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,132" - "2822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,24" - "28436474,2756734187,3204031479,3329325298],sha512_K=[1116352408,3609767458,1" - "899447441,602891725,3049323471,3964484399,3921009573,2173295548,961987163,40" - "81628472,1508970993,3053834265,2453635748,2937671579,2870763221,3664609560,3" - "624381080,2734883394,310598401,1164996542,607225278,1323610764,1426881987,35" - "90304994,1925078388,4068182383,2162078206,991336113,2614888103,633803317,324" - "8222580,3479774868,3835390401,2666613458,4022224774,944711139,264347078,2341" - "262773,604807628,2007800933,770255983,1495990901,1249150122,1856431235,15550" - "81692,3175218132,1996064986,2198950837,2554220882,3999719339,2821834349,7667" - "84016,2952996808,2566594879,3210313671,3203337956,3336571891,1034457026,3584" - "528711,2466948901,113926993,3758326383,338241895,168717936,666307205,1188179" - "964,773529912,1546045734,1294757372,1522805485,1396182291,2643833823,1695183" - "700,2343527390,1986661051,1014477480,2177026350,1206759142,2456956037,344077" - "627,2730485921,1290863460,2820302411,3158454273,3259730800,3505952657,334576" - "4771,106217008,3516065817,3606008344,3600352804,1432725776,4094571909,146703" - "1594,275423344,851169720,430227734,3100823752,506948616,1363258195,659060556" - ",3750685593,883997877,3785050280,958139571,3318307427,1322822218,3812723403," - "1537002063,2003034995,1747873779,3602036899,1955562222,1575990012,2024104815" - ",1125592928,2227730452,2716904306,2361852424,442776044,2428436474,593698344," - "2756734187,3733110249,3204031479,2999351573,3329325298,3815920427,3391569614" - ",3928383900,3515267271,566280711,3940187606,3454069534,4118630271,4000239992" - ",116418474,1914138554,174292421,2731055270,289380356,3203993006,460393269,32" - "0620315,685471733,587496836,852142971,1086792851,1017036298,365543100,112600" - "0580,2618297676,1288033470,3409855158,1501505948,4234509866,1607167915,98716" - "7468,1816402316,1246189591],sha1_K=[1518500249,1859775393,2400959708,3395469" - "782];utils.inherits(SHA256,BlockHash),exports.sha256=SHA256,SHA256.blockSize" - "=512,SHA256.outSize=256,SHA256.hmacStrength=192,SHA256.padLength=64,SHA256.p" - "rototype._update=function(t,h){for(var s=this.W,i=0;i<16;i++)s[i]=t[h+i];for" - "(;i>8,s=255&o;e?n.push(e,s):n.push(s)}else for(var u=0;u>>24|r>>>8&65280|r" - "<<8&16711680|(255&r)<<24;return t>>>0}function toHex32(r,t){for(var n=\"\",u" - "=0;u>>0}return e}function split32(r,t){for(var n=new Array(4*r.length)," - "u=0,o=0;u>>24,n[o+1]=e>>>" - "16&255,n[o+2]=e>>>8&255,n[o+3]=255&e):(n[o+3]=e>>>24,n[o+2]=e>>>16&255,n[o+1" - "]=e>>>8&255,n[o]=255&e)}return n}function rotr32(r,t){return r>>>t|r<<32-t}f" - "unction rotl32(r,t){return r<>>32-t}function sum32(r,t){return r+t>>>0}" - "function sum32_3(r,t,n){return r+t+n>>>0}function sum32_4(r,t,n,u){return r+" - "t+n+u>>>0}function sum32_5(r,t,n,u,o){return r+t+n+u+o>>>0}function assert(r" - ",t){if(!r)throw new Error(t||\"Assertion failed\")}function sum64(r,t,n,u){v" - "ar o=r[t],e=r[t+1],s=u+e>>>0,i=(s>>0,r[t+1]=s}function su" - "m64_hi(r,t,n,u){var o=t+u>>>0,e=(o>>0}function sum64_lo" - "(r,t,n,u){var o=t+u;return o>>>0}function sum64_4_hi(r,t,n,u,o,e,s,i){var l=" - "0,h=t;h=h+u>>>0,l+=h>>0,l+=h>>0,l+=h>>0}function sum64_4_lo(r,t,n,u,o,e,s,i){var l=t+u+e+i;ret" - "urn l>>>0}function sum64_5_hi(r,t,n,u,o,e,s,i,l,h){var _=0,a=t;a=a+u>>>0,_+=" - "a>>0,_+=a>>0,_+=a>>0,_+=a>>0}function sum64_5_lo(r,t,n,u,o,e,s,i,l,h){var _=t+u" - "+e+i+h;return _>>>0}function rotr64_hi(r,t,n){var u=t<<32-n|r>>>n;return u>>" - ">0}function rotr64_lo(r,t,n){var u=r<<32-n|t>>>n;return u>>>0}function shr64" - "_hi(r,t,n){return r>>>n}function shr64_lo(r,t,n){var u=r<<32-n|t>>>n;return " - "u>>>0}var utils=exports,inherits=require(\"inherits\");utils.toArray=toArray" - ",utils.toHex=toHex,utils.htonl=htonl,utils.toHex32=toHex32,utils.zero2=zero2" - ",utils.zero8=zero8,utils.join32=join32,utils.split32=split32,utils.rotr32=ro" - "tr32,utils.rotl32=rotl32,utils.sum32=sum32,utils.sum32_3=sum32_3,utils.sum32" - "_4=sum32_4,utils.sum32_5=sum32_5,utils.assert=assert,utils.inherits=inherits" - ",exports.sum64=sum64,exports.sum64_hi=sum64_hi,exports.sum64_lo=sum64_lo,exp" - "orts.sum64_4_hi=sum64_4_hi,exports.sum64_4_lo=sum64_4_lo,exports.sum64_5_hi=" - "sum64_5_hi,exports.sum64_5_lo=sum64_5_lo,exports.rotr64_hi=rotr64_hi,exports" - ".rotr64_lo=rotr64_lo,exports.shr64_hi=shr64_hi,exports.shr64_lo=shr64_lo;\n" - "},{\"inherits\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/re" - "thinkdb-webui/node_modules/inherits/inherits_browser.js\"}],\"/rethinkdb/bui" - "ld/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/brows" - "erify/node_modules/crypto-browserify/node_modules/elliptic/package.json\":[f" - "unction(require,module,exports){\n" - "module.exports={\n" - " \"_args\": [\n" - " [\n" - " {\n" - " \"raw\": \"elliptic@https://registry.npmjs.org/elliptic/-/elliptic-6" - ".3.1.tgz\",\n" - " \"scope\": null,\n" - " \"escapedName\": \"elliptic\",\n" - " \"name\": \"elliptic\",\n" - " \"rawSpec\": \"https://registry.npmjs.org/elliptic/-/elliptic-6.3.1." - "tgz\",\n" - " \"spec\": \"https://registry.npmjs.org/elliptic/-/elliptic-6.3.1.tgz" - "\",\n" - " \"type\": \"remote\"\n" - " },\n" - " \"/rethinkdb/external/admin-deps_2.0.4.fetch-1s9DjFeS\"\n" - " ],\n" - " [\n" - " {\n" - " \"raw\": \"elliptic@https://registry.npmjs.org/elliptic/-/elliptic-6" - ".3.1.tgz\",\n" - " \"scope\": null,\n" - " \"escapedName\": \"elliptic\",\n" - " \"name\": \"elliptic\",\n" - " \"rawSpec\": \"https://registry.npmjs.org/elliptic/-/elliptic-6.3.1." - "tgz\",\n" - " \"spec\": \"https://registry.npmjs.org/elliptic/-/elliptic-6.3.1.tgz" - "\",\n" - " \"type\": \"remote\"\n" - " },\n" - " \"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui\"\n" - " ]\n" - " ],\n" - " \"_from\": \"elliptic@https://registry.npmjs.org/elliptic/-/elliptic-6.3.1" - ".tgz\",\n" - " \"_id\": \"elliptic@6.3.1\",\n" - " \"_inCache\": true,\n" - " \"_location\": \"/rethinkdb-webui/browserify/crypto-browserify/elliptic\"," - "\n" - " \"_phantomChildren\": {\n" - " \"inherits\": \"2.0.1\"\n" - " },\n" - " \"_requested\": {\n" - " \"raw\": \"elliptic@https://registry.npmjs.org/elliptic/-/elliptic-6.3.1" - ".tgz\",\n" - " \"scope\": null,\n" - " \"escapedName\": \"elliptic\",\n" - " \"name\": \"elliptic\",\n" - " \"rawSpec\": \"https://registry.npmjs.org/elliptic/-/elliptic-6.3.1.tgz\"" - ",\n" - " \"spec\": \"https://registry.npmjs.org/elliptic/-/elliptic-6.3.1.tgz\",\n" - " \"type\": \"remote\"\n" - " },\n" - " \"_requiredBy\": [\n" - " \"/rethinkdb-webui/browserify/crypto-browserify\",\n" - " \"/rethinkdb-webui/browserify/crypto-browserify/browserify-sign\",\n" - " \"/rethinkdb-webui/browserify/crypto-browserify/create-ecdh\"\n" - " ],\n" - " \"_resolved\": \"https://registry.npmjs.org/elliptic/-/elliptic-6.3.1.tgz\"" - ",\n" - " \"_shasum\": \"17781f2109ab0ec686b146bdcff5d2e8c6aeceda\",\n" - " \"_shrinkwrap\": null,\n" - " \"_spec\": \"elliptic@https://registry.npmjs.org/elliptic/-/elliptic-6.3.1" - ".tgz\",\n" - " \"_where\": \"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui\",\n" - " \"author\": {\n" - " \"name\": \"Fedor Indutny\",\n" - " \"email\": \"fedor@indutny.com\"\n" - " },\n" - " \"bugs\": {\n" - " \"url\": \"https://github.com/indutny/elliptic/issues\"\n" - " },\n" - " \"dependencies\": {\n" - " \"bn.js\": \"^4.4.0\",\n" - " \"brorand\": \"^1.0.1\",\n" - " \"hash.js\": \"^1.0.0\",\n" - " \"inherits\": \"^2.0.1\"\n" - " },\n" - " \"description\": \"EC cryptography\",\n" - " \"devDependencies\": {\n" - " \"brfs\": \"^1.4.3\",\n" - " \"coveralls\": \"^2.11.3\",\n" - " \"grunt\": \"^0.4.5\",\n" - " \"grunt-browserify\": \"^5.0.0\",\n" - " \"grunt-contrib-connect\": \"^1.0.0\",\n" - " \"grunt-contrib-copy\": \"^1.0.0\",\n" - " \"grunt-contrib-uglify\": \"^1.0.1\",\n" - " \"grunt-mocha-istanbul\": \"^3.0.1\",\n" - " \"grunt-saucelabs\": \"^8.6.2\",\n" - " \"istanbul\": \"^0.4.2\",\n" - " \"jscs\": \"^2.9.0\",\n" - " \"jshint\": \"^2.6.0\",\n" - " \"mocha\": \"^2.1.0\"\n" - " },\n" - " \"files\": [\n" - " \"lib\"\n" - " ],\n" - " \"homepage\": \"https://github.com/indutny/elliptic\",\n" - " \"keywords\": [\n" - " \"EC\",\n" - " \"Elliptic\",\n" - " \"curve\",\n" - " \"Cryptography\"\n" - " ],\n" - " \"license\": \"MIT\",\n" - " \"main\": \"lib/elliptic.js\",\n" - " \"name\": \"elliptic\",\n" - " \"optionalDependencies\": {},\n" - " \"readme\": \"# Elliptic [![Build Status](https://secure.travis-ci.org/ind" - "utny/elliptic.png)](http://travis-ci.org/indutny/elliptic) [![Coverage Statu" - "s](https://coveralls.io/repos/indutny/elliptic/badge.svg?branch=master&servi" - "ce=github)](https://coveralls.io/github/indutny/elliptic?branch=master) [![C" - "ode Climate](https://codeclimate.com/github/indutny/elliptic/badges/gpa.svg)" - "](https://codeclimate.com/github/indutny/elliptic)\\n\\n[![Saucelabs Test St" - "atus](https://saucelabs.com/browser-matrix/gh-indutny-elliptic.svg)](https:/" - "/saucelabs.com/u/gh-indutny-elliptic)\\n\\nFast elliptic-curve cryptography " - "in a plain javascript implementation.\\n\\nNOTE: Please take a look at http:" - "//safecurves.cr.yp.to/ before choosing a curve\\nfor your cryptography opera" - "tions.\\n\\n## Incentive\\n\\nECC is much slower than regular RSA cryptograp" - "hy, the JS implementations are\\neven more slower.\\n\\n## Benchmarks\\n\\n`" - "``bash\\n$ node benchmarks/index.js\\nBenchmarking: sign\\nelliptic#sign x 2" - "62 ops/sec \xc2\xb1\x30.51% (177 runs sampled)\\neccjs#sign x 55.91 ops/sec " - "\xc2\xb1\x30.90% (144 runs sampled)\\n------------------------\\nFastest is " - "elliptic#sign\\n========================\\nBenchmarking: verify\\nelliptic#v" - "erify x 113 ops/sec \xc2\xb1\x30.50% (166 runs sampled)\\neccjs#verify x 48." - "56 ops/sec \xc2\xb1\x30.36% (125 runs sampled)\\n------------------------\\n" - "Fastest is elliptic#verify\\n========================\\nBenchmarking: gen\\n" - "elliptic#gen x 294 ops/sec \xc2\xb1\x30.43% (176 runs sampled)\\neccjs#gen x" - " 62.25 ops/sec \xc2\xb1\x30.63% (129 runs sampled)\\n-----------------------" - "-\\nFastest is elliptic#gen\\n========================\\nBenchmarking: ecdh\\" - "nelliptic#ecdh x 136 ops/sec \xc2\xb1\x30.85% (156 runs sampled)\\n---------" - "---------------\\nFastest is elliptic#ecdh\\n========================\\n```\\" - "n\\n## API\\n\\n### ECDSA\\n\\n```javascript\\nvar EC = require('elliptic')." - "ec;\\n\\n// Create and initialize EC context\\n// (better do it once and reu" - "se it)\\nvar ec = new EC('secp256k1');\\n\\n// Generate keys\\nvar key = ec." - "genKeyPair();\\n\\n// Sign message (must be an array, or it'll be treated as" - " a hex sequence)\\nvar msg = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];\\nvar sig" - "nature = key.sign(msg);\\n\\n// Export DER encoded signature in Array\\nvar " - "derSign = signature.toDER();\\n\\n// Verify signature\\nconsole.log(key.veri" - "fy(msg, derSign));\\n\\n// CHECK WITH NO PRIVATE KEY\\n\\n// Public key as '" - "04 + x + y'\\nvar pub = '04bb1fa3...';\\n\\n// Signature MUST be either:\\n/" - "/ 1) hex-string of DER-encoded signature; or\\n// 2) DER-encoded signature a" - "s buffer; or\\n// 3) object with two hex-string properties (r and s)\\n\\nva" - "r signature = 'b102ac...'; // case 1\\nvar signature = new Buffer('...'); //" - " case 2\\nvar signature = { r: 'b1fc...', s: '9c42...' }; // case 3\\n\\n// " - "Import public key\\nvar key = ec.keyFromPublic(pub, 'hex');\\n\\n// Verify s" - "ignature\\nconsole.log(key.verify(msg, signature));\\n```\\n\\n### EdDSA\\n\\" - "n```javascript\\nvar EdDSA = require('elliptic').eddsa;\\n\\n// Create and i" - "nitialize EdDSA context\\n// (better do it once and reuse it)\\nvar ec = new" - " EdDSA('ed25519');\\n\\n// Create key pair from secret\\nvar key = ec.keyFro" - "mSecret('693e3c...'); // hex string, array or Buffer\\n\\n// Sign message (m" - "ust be an array, or it'll be treated as a hex sequence)\\nvar msg = [ 0, 1, " - "2, 3, 4, 5, 6, 7, 8, 9, 10 ];\\nvar signature = key.sign(msg).toHex();\\n\\n" - "// Verify signature\\nconsole.log(key.verify(msg, signature));\\n\\n// CHECK" - " WITH NO PRIVATE KEY\\n\\n// Import public key\\nvar pub = '0a1af638...';\\n" - "var key = ec.keyFromPublic(pub, 'hex');\\n\\n// Verify signature\\nvar signa" - "ture = '70bed1...';\\nconsole.log(key.verify(msg, signature));\\n```\\n\\n##" - "# ECDH\\n\\n```javascript\\n// Generate keys\\nvar key1 = ec.genKeyPair();\\" - "nvar key2 = ec.genKeyPair();\\n\\nvar shared1 = key1.derive(key2.getPublic()" - ");\\nvar shared2 = key2.derive(key1.getPublic());\\n\\nconsole.log('Both sha" - "red secrets are BN instances');\\nconsole.log(shared1.toString(16));\\nconso" - "le.log(shared2.toString(16));\\n```\\n\\nNOTE: `.derive()` returns a [BN][1]" - " instance.\\n\\n## Supported curves\\n\\nElliptic.js support following curve" - " types:\\n\\n* Short Weierstrass\\n* Montgomery\\n* Edwards\\n* Twisted Edwa" - "rds\\n\\nFollowing curve 'presets' are embedded into the library:\\n\\n* `se" - "cp256k1`\\n* `p192`\\n* `p224`\\n* `p256`\\n* `p384`\\n* `p521`\\n* `curve25" - "519`\\n* `ed25519`\\n\\nNOTE: That `curve25519` could not be used for ECDSA," - " use `ed25519` instead.\\n\\n### Implementation details\\n\\nECDSA is using " - "deterministic `k` value generation as per [RFC6979][0]. Most of\\nthe curve " - "operations are performed on non-affine coordinates (either projective\\nor e" - "xtended), various windowing techniques are used for different cases.\\n\\nAl" - "l operations are performed in reduction context using [bn.js][1], hashing is" - "\\nprovided by [hash.js][2]\\n\\n### Related projects\\n\\n* [eccrypto][3]: " - "isomorphic implementation of ECDSA, ECDH and ECIES for both\\n browserify a" - "nd node (uses `elliptic` for browser and [secp256k1-node][4] for\\n node)\\" - "n\\n#### LICENSE\\n\\nThis software is licensed under the MIT License.\\n\\n" - "Copyright Fedor Indutny, 2014.\\n\\nPermission is hereby granted, free of ch" - "arge, to any person obtaining a\\ncopy of this software and associated docum" - "entation files (the\\n\\\"Software\\\"), to deal in the Software without res" - "triction, including\\nwithout limitation the rights to use, copy, modify, me" - "rge, publish,\\ndistribute, sublicense, and/or sell copies of the Software, " - "and to permit\\npersons to whom the Software is furnished to do so, subject " - "to the\\nfollowing conditions:\\n\\nThe above copyright notice and this perm" - "ission notice shall be included\\nin all copies or substantial portions of t" - "he Software.\\n\\nTHE SOFTWARE IS PROVIDED \\\"AS IS\\\", WITHOUT WARRANTY O" - "F ANY KIND, EXPRESS\\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIE" - "S OF\\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT" - ". IN\\nNO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLA" - "IM,\\nDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\\" - "nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\\" - "nUSE OR OTHER DEALINGS IN THE SOFTWARE.\\n\\n[0]: http://tools.ietf.org/html" - "/rfc6979\\n[1]: https://github.com/indutny/bn.js\\n[2]: https://github.com/i" - "ndutny/hash.js\\n[3]: https://github.com/bitchan/eccrypto\\n[4]: https://git" - "hub.com/wanderer/secp256k1-node\\n\",\n" - " \"readmeFilename\": \"README.md\",\n" - " \"repository\": {\n" - " \"type\": \"git\",\n" - " \"url\": \"git+ssh://git@github.com/indutny/elliptic.git\"\n" - " },\n" - " \"scripts\": {\n" - " \"jscs\": \"jscs benchmarks/*.js lib/*.js lib/**/*.js lib/**/**/*.js tes" - "t/index.js\",\n" - " \"jshint\": \"jscs benchmarks/*.js lib/*.js lib/**/*.js lib/**/**/*.js t" - "est/index.js\",\n" - " \"lint\": \"npm run jscs && npm run jshint\",\n" - " \"test\": \"npm run lint && npm run unit\",\n" - " \"unit\": \"istanbul test _mocha --reporter=spec test/index.js\",\n" - " \"version\": \"grunt dist && git add dist/\"\n" - " },\n" - " \"version\": \"6.3.1\"\n" - "}\n" - "\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/browserify/node_modules/crypto-browserify/node_modules/evp_" - "bytestokey/index.js\":[function(require,module,exports){\n" - "(function (Buffer){\n" - "function EVP_BytesToKey(e,f,r,u){Buffer.isBuffer(e)||(e=new Buffer(e,\"binar" - "y\")),f&&!Buffer.isBuffer(f)&&(f=new Buffer(f,\"binary\")),r/=8,u=u||0;for(v" - "ar i,n,a=0,t=0,B=new Buffer(r),o=new Buffer(u),h=0,s=[];;){if(h++>0&&s.push(" - "i),s.push(e),f&&s.push(f),i=md5(Buffer.concat(s)),s=[],n=0,r>0)for(;;){if(0=" - "==r)break;if(n===i.length)break;B[a++]=i[n],r--,n++}if(u>0&&n!==i.length)for" - "(;;){if(0===u)break;if(n===i.length)break;o[t++]=i[n],u--,n++}if(0===r&&0===" - "u)break}for(n=0;n>6],o=0===(32&t);if(31===(31&t)){var n=t;f" - "or(t=0;128===(128&n);){if(n=r.readUInt8(e),r.isError(n))return n;t<<=7,t|=12" - "7&n}}else t&=31;var s=der.tag[t];return{cls:i,primitive:o,tag:t,tagStr:s}}fu" - "nction derDecodeLen(r,e,t){var i=r.readUInt8(t);if(r.isError(i))return i;if(" - "!e&&128===i)return null;if(0===(128&i))return i;var o=127&i;if(o>=4)return r" - ".error(\"length octect is too long\");i=0;for(var n=0;n=31?n.error(\"Multi-oc" - "tet tag encoding unsupported\"):(r||(o|=32),o|=der.tagClassByName[t||\"unive" - "rsal\"]<<6)}var inherits=require(\"inherits\"),Buffer=require(\"buffer\").Bu" - "ffer,asn1=require(\"../../asn1\"),base=asn1.base,der=asn1.constants.der;modu" - "le.exports=DEREncoder,DEREncoder.prototype.encode=function(e,r){return this." - "tree._encode(e,r).join()},inherits(DERNode,base.Node),DERNode.prototype._enc" - "odeComposite=function(e,r,t,n){var o=encodeTag(e,r,t,this.reporter);if(n.len" - "gth<128){var i=new Buffer(2);return i[0]=o,i[1]=n.length,this._createEncoder" - "Buffer([i,n])}for(var f=1,s=n.length;s>=256;s>>=8)f++;var i=new Buffer(2+f);" - "i[0]=o,i[1]=128|f;for(var s=1+f,u=n.length;u>0;s--,u>>=8)i[s]=255&u;return t" - "his._createEncoderBuffer([i,n])},DERNode.prototype._encodeStr=function(e,r){" - "if(\"bitstr\"===r)return this._createEncoderBuffer([0|e.unused,e.data]);if(\"" - "bmpstr\"===r){for(var t=new Buffer(2*e.length),n=0;n=40)return this.reporter.error(\"Second objid identifier OOB\");e.splice(" - "0,2,40*e[0]+e[1])}for(var o=0,n=0;n=128;" - "i>>=7)o++}for(var f=new Buffer(o),s=f.length-1,n=e.length-1;n>=0;n--){var i=" - "e[n];for(f[s--]=127&i;(i>>=7)>0;)f[s--]=128|127&i}return this._createEncoder" - "Buffer(f)},DERNode.prototype._encodeTime=function(e,r){var t,n=new Date(e);r" - "eturn\"gentime\"===r?t=[two(n.getFullYear()),two(n.getUTCMonth()+1),two(n.ge" - "tUTCDate()),two(n.getUTCHours()),two(n.getUTCMinutes()),two(n.getUTCSeconds(" - ")),\"Z\"].join(\"\"):\"utctime\"===r?t=[two(n.getFullYear()%100),two(n.getUT" - "CMonth()+1),two(n.getUTCDate()),two(n.getUTCHours()),two(n.getUTCMinutes())," - "two(n.getUTCSeconds()),\"Z\"].join(\"\"):this.reporter.error(\"Encoding \"+r" - "+\" time is not supported yet\"),this._encodeStr(t,\"octstr\")},DERNode.prot" - "otype._encodeNull=function(){return this._createEncoderBuffer(\"\")},DERNode" - ".prototype._encodeInt=function(e,r){if(\"string\"==typeof e){if(!r)return th" - "is.reporter.error(\"String int or enum given, but no values map\");if(!r.has" - "OwnProperty(e))return this.reporter.error(\"Values map doesn't contain: \"+J" - "SON.stringify(e));e=r[e]}if(\"number\"!=typeof e&&!Buffer.isBuffer(e)){var t" - "=e.toArray();!e.sign&&128&t[0]&&t.unshift(0),e=new Buffer(t)}if(Buffer.isBuf" - "fer(e)){var n=e.length;0===e.length&&n++;var o=new Buffer(n);return e.copy(o" - "),0===e.length&&(o[0]=0),this._createEncoderBuffer(o)}if(e<128)return this._" - "createEncoderBuffer(e);if(e<256)return this._createEncoderBuffer([0,e]);for(" - "var n=1,i=e;i>=256;i>>=8)n++;for(var o=new Array(n),i=o.length-1;i>=0;i--)o[" - "i]=255&e,e>>=8;return 128&o[0]&&o.unshift(0),this._createEncoderBuffer(new B" - "uffer(o))},DERNode.prototype._encodeBool=function(e){return this._createEnco" - "derBuffer(e?255:0)},DERNode.prototype._use=function(e,r){return\"function\"=" - "=typeof e&&(e=e(r)),e._getEncoder(\"der\").tree},DERNode.prototype._skipDefa" - "ult=function(e,r,t){var n,o=this._baseState;if(null===o.default)return!1;var" - " i=e.join();if(void 0===o.defaultBuffer&&(o.defaultBuffer=this._encodeValue(" - "o.default,r,t).join()),i.length!==o.defaultBuffer.length)return!1;for(n=0;n<" - "i.length;n++)if(i[n]!==o.defaultBuffer[n])return!1;return!0};\n" - "},{\"../../asn1\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/" - "rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_" - "modules/parse-asn1/node_modules/asn1.js/lib/asn1.js\",\"buffer\":\"/rethinkd" - "b/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/" - "browserify/node_modules/buffer/index.js\",\"inherits\":\"/rethinkdb/build/ex" - "ternal/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/inherits/i" - "nherits_browser.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_mod" - "ules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/" - "node_modules/parse-asn1/node_modules/asn1.js/lib/asn1/encoders/index.js\":[f" - "unction(require,module,exports){\n" - "var encoders=exports;encoders.der=require(\"./der\"),encoders.pem=require(\"" - "./pem\");\n" - "},{\"./der\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethi" - "nkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_modul" - "es/parse-asn1/node_modules/asn1.js/lib/asn1/encoders/der.js\",\"./pem\":\"/r" - "ethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_m" - "odules/browserify/node_modules/crypto-browserify/node_modules/parse-asn1/nod" - "e_modules/asn1.js/lib/asn1/encoders/pem.js\"}],\"/rethinkdb/build/external/a" - "dmin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_mo" - "dules/crypto-browserify/node_modules/parse-asn1/node_modules/asn1.js/lib/asn" - "1/encoders/pem.js\":[function(require,module,exports){\n" - "function PEMEncoder(e){DEREncoder.call(this,e),this.enc=\"pem\"}var inherits" - "=require(\"inherits\"),DEREncoder=require(\"./der\");inherits(PEMEncoder,DER" - "Encoder),module.exports=PEMEncoder,PEMEncoder.prototype.encode=function(e,r)" - "{for(var n=DEREncoder.prototype.encode.call(this,e),o=n.toString(\"base64\")" - ",E=[\"-----BEGIN \"+r.label+\"-----\"],t=0;t=6?\"utf-8\":\"" - "binary\";exports.pbkdf2Sync=function(e,r,t,n,f){Buffer.isBuffer(e)||(e=new B" - "uffer(e,defaultEncoding)),Buffer.isBuffer(r)||(r=new Buffer(r,defaultEncodin" - "g)),checkParameters(t,n),f=f||\"sha1\";var o,a=1,c=new Buffer(n),i=new Buffe" - "r(r.length+4);r.copy(i,0,0,r.length);for(var u,s,p=1;p<=a;p++){i.writeUInt32" - "BE(p,r.length);var d=createHmac(f,e).update(i).digest();o||(o=d.length,s=new" - " Buffer(o),a=Math.ceil(n/o),u=n-(a-1)*o),d.copy(s,0,0,o);for(var l=1;lMAX_ALLOC||e!==e)throw new TypeError(\"Ba" - "d key length\")};\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/browserify/node_modules/crypto-browserify/node_modules/publ" - "ic-encrypt/browser.js\":[function(require,module,exports){\n" - "exports.publicEncrypt=require(\"./publicEncrypt\"),exports.privateDecrypt=re" - "quire(\"./privateDecrypt\"),exports.privateEncrypt=function(r,p){return expo" - "rts.publicEncrypt(r,p,!0)},exports.publicDecrypt=function(r,p){return export" - "s.privateDecrypt(r,p,!0)};\n" - "},{\"./privateDecrypt\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_mo" - "dules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify" - "/node_modules/public-encrypt/privateDecrypt.js\",\"./publicEncrypt\":\"/reth" - "inkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modu" - "les/browserify/node_modules/crypto-browserify/node_modules/public-encrypt/pu" - "blicEncrypt.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules" - "/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node" - "_modules/public-encrypt/mgf.js\":[function(require,module,exports){\n" - "(function (Buffer){\n" - "function i2ops(e){var r=new Buffer(4);return r.writeUInt32BE(e,0),r}var crea" - "teHash=require(\"create-hash\");module.exports=function(e,r){for(var t,a=new" - " Buffer(\"\"),n=0;a.length=e.length){o++;break}var u=e.slice(2" - ",i-1);e.slice(i-1,i);if((\"0002\"!==t.toString(\"hex\")&&!n||\"0001\"!==t.to" - "String(\"hex\")&&n)&&o++,u.length<8&&o++,o)throw new Error(\"decryption erro" - "r\");return e.slice(i)}function compare(r,e){r=new Buffer(r),e=new Buffer(e)" - ";var n=0,t=r.length;r.length!==e.length&&(n++,t=Math.min(r.length,e.length))" - ";for(var i=-1;++io||ne" - "w bn(e).cmp(i.modulus)>=0)throw new Error(\"decryption error\");var u;u=n?wi" - "thPublic(new bn(e),i):crt(e,i);var a=new Buffer(o-u.length);if(a.fill(0),u=B" - "uffer.concat([a,u],o),4===t)return oaep(i,u);if(1===t)return pkcs1(i,u,n);if" - "(3===t)return u;throw new Error(\"unknown padding\")};\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"./mgf\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethi" - "nkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_modul" - "es/public-encrypt/mgf.js\",\"./withPublic\":\"/rethinkdb/build/external/admi" - "n-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modul" - "es/crypto-browserify/node_modules/public-encrypt/withPublic.js\",\"./xor\":\"" - "/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node" - "_modules/browserify/node_modules/crypto-browserify/node_modules/public-encry" - "pt/xor.js\",\"bn.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modu" - "les/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/n" - "ode_modules/bn.js/lib/bn.js\",\"browserify-rsa\":\"/rethinkdb/build/external" - "/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_" - "modules/crypto-browserify/node_modules/browserify-rsa/index.js\",\"buffer\":" - "\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/no" - "de_modules/browserify/node_modules/buffer/index.js\",\"create-hash\":\"/reth" - "inkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modu" - "les/browserify/node_modules/crypto-browserify/node_modules/create-hash/brows" - "er.js\",\"parse-asn1\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_mod" - "ules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/" - "node_modules/parse-asn1/index.js\"}],\"/rethinkdb/build/external/admin-deps_" - "2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/cryp" - "to-browserify/node_modules/public-encrypt/publicEncrypt.js\":[function(requi" - "re,module,exports){\n" - "(function (Buffer){\n" - "function oaep(e,r){var n=e.modulus.byteLength(),o=r.length,t=createHash(\"sh" - "a1\").update(new Buffer(\"\")).digest(),f=t.length,u=2*f;if(o>n-u-2)throw ne" - "w Error(\"message too long\");var a=new Buffer(n-o-u-2);a.fill(0);var s=n-f-" - "1,i=randomBytes(f),w=xor(Buffer.concat([t,a,new Buffer([1]),r],s),mgf(i,s))," - "c=xor(i,mgf(w,f));return new bn(Buffer.concat([new Buffer([0]),c,w],n))}func" - "tion pkcs1(e,r,n){var o=r.length,t=e.modulus.byteLength();if(o>t-11)throw ne" - "w Error(\"message too long\");var f;return n?(f=new Buffer(t-o-3),f.fill(255" - ")):f=nonZero(t-o-3),new bn(Buffer.concat([new Buffer([0,n?1:2]),f,new Buffer" - "([0]),r],t))}function nonZero(e,r){for(var n,o=new Buffer(e),t=0,f=randomByt" - "es(2*e),u=0;t=0)throw new Error(\"data too long for modulus\")}return" - " n?crt(t,f):withPublic(t,f)};\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"./mgf\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethi" - "nkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_modul" - "es/public-encrypt/mgf.js\",\"./withPublic\":\"/rethinkdb/build/external/admi" - "n-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modul" - "es/crypto-browserify/node_modules/public-encrypt/withPublic.js\",\"./xor\":\"" - "/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node" - "_modules/browserify/node_modules/crypto-browserify/node_modules/public-encry" - "pt/xor.js\",\"bn.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modu" - "les/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/n" - "ode_modules/bn.js/lib/bn.js\",\"browserify-rsa\":\"/rethinkdb/build/external" - "/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_" - "modules/crypto-browserify/node_modules/browserify-rsa/index.js\",\"buffer\":" - "\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/no" - "de_modules/browserify/node_modules/buffer/index.js\",\"create-hash\":\"/reth" - "inkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modu" - "les/browserify/node_modules/crypto-browserify/node_modules/create-hash/brows" - "er.js\",\"parse-asn1\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_mod" - "ules/rethinkdb-webui/node_modules/browserify/node_modules/crypto-browserify/" - "node_modules/parse-asn1/index.js\",\"randombytes\":\"/rethinkdb/build/extern" - "al/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/nod" - "e_modules/crypto-browserify/node_modules/randombytes/browser.js\"}],\"/rethi" - "nkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modul" - "es/browserify/node_modules/crypto-browserify/node_modules/public-encrypt/wit" - "hPublic.js\":[function(require,module,exports){\n" - "(function (Buffer){\n" - "function withPublic(e,n){return new Buffer(e.toRed(bn.mont(n.modulus)).redPo" - "w(new bn(n.publicExponent)).fromRed().toArray())}var bn=require(\"bn.js\");m" - "odule.exports=withPublic;\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"bn.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethi" - "nkdb-webui/node_modules/browserify/node_modules/crypto-browserify/node_modul" - "es/bn.js/lib/bn.js\",\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4" - "/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/buffer/in" - "dex.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethink" - "db-webui/node_modules/browserify/node_modules/crypto-browserify/node_modules" - "/public-encrypt/xor.js\":[function(require,module,exports){\n" - "module.exports=function(r,e){for(var n=r.length,o=-1;++o65536)throw new Error(\"request" - "ed too many random bytes\");var e=new global.Uint8Array(r);r>0&&crypto.getRa" - "ndomValues(e);var t=new Buffer(e.buffer);return\"function\"==typeof o?proces" - "s.nextTick(function(){o(null,t)}):t}var crypto=global.crypto||global.msCrypt" - "o;crypto&&crypto.getRandomValues?module.exports=randomBytes:module.exports=o" - "ldBrowser;\n" - "}).call(this,require('_process'),typeof global !== \"undefined\" ? global : " - "typeof self !== \"undefined\" ? self : typeof window !== \"undefined\" ? win" - "dow : {},require(\"buffer\").Buffer)\n" - "},{\"_process\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/re" - "thinkdb-webui/node_modules/browserify/node_modules/process/browser.js\",\"bu" - "ffer\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-w" - "ebui/node_modules/browserify/node_modules/buffer/index.js\"}],\"/rethinkdb/b" - "uild/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/bro" - "wserify/node_modules/events/events.js\":[function(require,module,exports){\n" - "function EventEmitter(){this._events=this._events||{},this._maxListeners=thi" - "s._maxListeners||void 0}function isFunction(e){return\"function\"==typeof e}" - "function isNumber(e){return\"number\"==typeof e}function isObject(e){return\"" - "object\"==typeof e&&null!==e}function isUndefined(e){return void 0===e}modul" - "e.exports=EventEmitter,EventEmitter.EventEmitter=EventEmitter,EventEmitter.p" - "rototype._events=void 0,EventEmitter.prototype._maxListeners=void 0,EventEmi" - "tter.defaultMaxListeners=10,EventEmitter.prototype.setMaxListeners=function(" - "e){if(!isNumber(e)||e<0||isNaN(e))throw TypeError(\"n must be a positive num" - "ber\");return this._maxListeners=e,this},EventEmitter.prototype.emit=functio" - "n(e){var t,i,n,s,r,o;if(this._events||(this._events={}),\"error\"===e&&(!thi" - "s._events.error||isObject(this._events.error)&&!this._events.error.length)){" - "if(t=arguments[1],t instanceof Error)throw t;var h=new Error('Uncaught, unsp" - "ecified \"error\" event. ('+t+\")\");throw h.context=t,h}if(i=this._events[e" - "],isUndefined(i))return!1;if(isFunction(i))switch(arguments.length){case 1:i" - ".call(this);break;case 2:i.call(this,arguments[1]);break;case 3:i.call(this," - "arguments[1],arguments[2]);break;default:s=Array.prototype.slice.call(argume" - "nts,1),i.apply(this,s)}else if(isObject(i))for(s=Array.prototype.slice.call(" - "arguments,1),o=i.slice(),n=o.length,r=0;r0&&this._events" - "[e].length>i&&(this._events[e].warned=!0,console.error(\"(node) warning: pos" - "sible EventEmitter memory leak detected. %d listeners added. Use emitter.set" - "MaxListeners() to increase limit.\",this._events[e].length),\"function\"==ty" - "peof console.trace&&console.trace())),this},EventEmitter.prototype.on=EventE" - "mitter.prototype.addListener,EventEmitter.prototype.once=function(e,t){funct" - "ion i(){this.removeListener(e,i),n||(n=!0,t.apply(this,arguments))}if(!isFun" - "ction(t))throw TypeError(\"listener must be a function\");var n=!1;return i." - "listener=t,this.on(e,i),this},EventEmitter.prototype.removeListener=function" - "(e,t){var i,n,s,r;if(!isFunction(t))throw TypeError(\"listener must be a fun" - "ction\");if(!this._events||!this._events[e])return this;if(i=this._events[e]" - ",s=i.length,n=-1,i===t||isFunction(i.listener)&&i.listener===t)delete this._" - "events[e],this._events.removeListener&&this.emit(\"removeListener\",e,t);els" - "e if(isObject(i)){for(r=s;r-- >0;)if(i[r]===t||i[r].listener&&i[r].listener=" - "==t){n=r;break}if(n<0)return this;1===i.length?(i.length=0,delete this._even" - "ts[e]):i.splice(n,1),this._events.removeListener&&this.emit(\"removeListener" - "\",e,t)}return this},EventEmitter.prototype.removeAllListeners=function(e){v" - "ar t,i;if(!this._events)return this;if(!this._events.removeListener)return 0" - "===arguments.length?this._events={}:this._events[e]&&delete this._events[e]," - "this;if(0===arguments.length){for(t in this._events)\"removeListener\"!==t&&" - "this.removeAllListeners(t);return this.removeAllListeners(\"removeListener\"" - "),this._events={},this}if(i=this._events[e],isFunction(i))this.removeListene" - "r(e,i);else if(i)for(;i.length;)this.removeListener(e,i[i.length-1]);return " - "delete this._events[e],this},EventEmitter.prototype.listeners=function(e){va" - "r t;return t=this._events&&this._events[e]?isFunction(this._events[e])?[this" - "._events[e]]:this._events[e].slice():[]},EventEmitter.prototype.listenerCoun" - "t=function(e){if(this._events){var t=this._events[e];if(isFunction(t))return" - " 1;if(t)return t.length}return 0},EventEmitter.listenerCount=function(e,t){r" - "eturn e.listenerCount(t)};\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/browserify/node_modules/insert-module-globals/node_modules/" - "is-buffer/index.js\":[function(require,module,exports){\n" - "function isBuffer(f){return!!f.constructor&&\"function\"==typeof f.construct" - "or.isBuffer&&f.constructor.isBuffer(f)}function isSlowBuffer(f){return\"func" - "tion\"==typeof f.readFloatLE&&\"function\"==typeof f.slice&&isBuffer(f.slice" - "(0,0))}module.exports=function(f){return null!=f&&(isBuffer(f)||isSlowBuffer" - "(f)||!!f._isBuffer)};\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/browserify/node_modules/process/browser.js\":[function(requ" - "ire,module,exports){\n" - "function defaultSetTimout(){throw new Error(\"setTimeout has not been define" - "d\")}function defaultClearTimeout(){throw new Error(\"clearTimeout has not b" - "een defined\")}function runTimeout(e){if(cachedSetTimeout===setTimeout)retur" - "n setTimeout(e,0);if((cachedSetTimeout===defaultSetTimout||!cachedSetTimeout" - ")&&setTimeout)return cachedSetTimeout=setTimeout,setTimeout(e,0);try{return " - "cachedSetTimeout(e,0)}catch(t){try{return cachedSetTimeout.call(null,e,0)}ca" - "tch(t){return cachedSetTimeout.call(this,e,0)}}}function runClearTimeout(e){" - "if(cachedClearTimeout===clearTimeout)return clearTimeout(e);if((cachedClearT" - "imeout===defaultClearTimeout||!cachedClearTimeout)&&clearTimeout)return cach" - "edClearTimeout=clearTimeout,clearTimeout(e);try{return cachedClearTimeout(e)" - "}catch(t){try{return cachedClearTimeout.call(null,e)}catch(t){return cachedC" - "learTimeout.call(this,e)}}}function cleanUpNextTick(){draining&¤tQueue" - "&&(draining=!1,currentQueue.length?queue=currentQueue.concat(queue):queueInd" - "ex=-1,queue.length&&drainQueue())}function drainQueue(){if(!draining){var e=" - "runTimeout(cleanUpNextTick);draining=!0;for(var t=queue.length;t;){for(curre" - "ntQueue=queue,queue=[];++queueIndex1)for(var u=1;u0)if(t.ended&" - "&!a){var d=new Error(\"stream.push() after EOF\");e.emit(\"error\",d)}else i" - "f(t.endEmitted&&a){var o=new Error(\"stream.unshift() after end event\");e.e" - "mit(\"error\",o)}else{var u;!t.decoder||a||n||(r=t.decoder.write(r),u=!t.obj" - "ectMode&&0===r.length),a||(t.reading=!1),u||(t.flowing&&0===t.length&&!t.syn" - "c?(e.emit(\"data\",r),e.read(0)):(t.length+=t.objectMode?1:r.length,a?t.buff" - "er.unshift(r):t.buffer.push(r),t.needReadable&&emitReadable(e))),maybeReadMo" - "re(e,t)}else a||(t.reading=!1);return needMoreData(t)}function needMoreData(" - "e){return!e.ended&&(e.needReadable||e.length=MAX_HWM?e=MAX_HWM:(e--,e|=e>>>" - "1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}function howMuchToRead(e,t){re" - "turn e<=0||0===t.length&&t.ended?0:t.objectMode?1:e!==e?t.flowing&&t.length?" - "t.buffer.head.data.length:t.length:(e>t.highWaterMark&&(t.highWaterMark=comp" - "uteNewHighWaterMark(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0)" - ")}function chunkInvalid(e,t){var r=null;return Buffer.isBuffer(t)||\"string\"" - "==typeof t||null===t||void 0===t||e.objectMode||(r=new TypeError(\"Invalid n" - "on-string/buffer chunk\")),r}function onEofChunk(e,t){if(!t.ended){if(t.deco" - "der){var r=t.decoder.end();r&&r.length&&(t.buffer.push(r),t.length+=t.object" - "Mode?1:r.length)}t.ended=!0,emitReadable(e)}}function emitReadable(e){var t=" - "e._readableState;t.needReadable=!1,t.emittedReadable||(debug(\"emitReadable\"" - ",t.flowing),t.emittedReadable=!0,t.sync?processNextTick(emitReadable_,e):emi" - "tReadable_(e))}function emitReadable_(e){debug(\"emit readable\"),e.emit(\"r" - "eadable\"),flow(e)}function maybeReadMore(e,t){t.readingMore||(t.readingMore" - "=!0,processNextTick(maybeReadMore_,e,t))}function maybeReadMore_(e,t){for(va" - "r r=t.length;!t.reading&&!t.flowing&&!t.ended&&t.length=t.length?(r=t.de" - "coder?t.buffer.join(\"\"):1===t.buffer.length?t.buffer.head.data:t.buffer.co" - "ncat(t.length),t.buffer.clear()):r=fromListPartial(e,t.buffer,t.decoder),r}f" - "unction fromListPartial(e,t,r){var n;return ei.length?i.length:e;if(a+=d===i.length?i:i.slice(0,e),e-=d,0===e)" - "{d===i.length?(++n,r.next?t.head=r.next:t.head=t.tail=null):(t.head=r,r.data" - "=i.slice(d));break}++n}return t.length-=n,a}function copyFromBuffer(e,t){var" - " r=bufferShim.allocUnsafe(e),n=t.head,a=1;for(n.data.copy(r),e-=n.data.lengt" - "h;n=n.next;){var i=n.data,d=e>i.length?i.length:e;if(i.copy(r,r.length-e,0,d" - "),e-=d,0===e){d===i.length?(++a,n.next?t.head=n.next:t.head=t.tail=null):(t." - "head=n,n.data=i.slice(d));break}++a}return t.length-=a,r}function endReadabl" - "e(e){var t=e._readableState;if(t.length>0)throw new Error('\"endReadable()\"" - " called on non-empty stream');t.endEmitted||(t.ended=!0,processNextTick(endR" - "eadableNT,t,e))}function endReadableNT(e,t){e.endEmitted||0!==e.length||(e.e" - "ndEmitted=!0,t.readable=!1,t.emit(\"end\"))}function forEach(e,t){for(var r=" - "0,n=e.length;r=t.hig" - "hWaterMark||t.ended))return debug(\"read: emitReadable\",t.length,t.ended),0", - 65476) + std::string( - "===t.length&&t.ended?endReadable(this):emitReadable(this),null;if(e=howMuchT" - "oRead(e,t),0===e&&t.ended)return 0===t.length&&endReadable(this),null;var n=" - "t.needReadable;debug(\"need readable\",n),(0===t.length||t.length-e0?fromList(e,t):null," - "null===a?(t.needReadable=!0,e=0):t.length-=e,0===t.length&&(t.ended||(t.need" - "Readable=!0),r!==e&&t.ended&&endReadable(this)),null!==a&&this.emit(\"data\"" - ",a),a},Readable.prototype._read=function(e){this.emit(\"error\",new Error(\"" - "not implemented\"))},Readable.prototype.pipe=function(e,t){function r(e){deb" - "ug(\"onunpipe\"),e===s&&a()}function n(){debug(\"onend\"),e.end()}function a" - "(){debug(\"cleanup\"),e.removeListener(\"close\",o),e.removeListener(\"finis" - "h\",u),e.removeListener(\"drain\",c),e.removeListener(\"error\",d),e.removeL" - "istener(\"unpipe\",r),s.removeListener(\"end\",n),s.removeListener(\"end\",a" - "),s.removeListener(\"data\",i),g=!0,!h.awaitDrain||e._writableState&&!e._wri" - "tableState.needDrain||c()}function i(t){debug(\"ondata\"),b=!1;var r=e.write" - "(t);!1!==r||b||((1===h.pipesCount&&h.pipes===e||h.pipesCount>1&&indexOf(h.pi" - "pes,e)!==-1)&&!g&&(debug(\"false write response, pause\",s._readableState.aw" - "aitDrain),s._readableState.awaitDrain++,b=!0),s.pause())}function d(t){debug" - "(\"onerror\",t),l(),e.removeListener(\"error\",d),0===EElistenerCount(e,\"er" - "ror\")&&e.emit(\"error\",t)}function o(){e.removeListener(\"finish\",u),l()}" - "function u(){debug(\"onfinish\"),e.removeListener(\"close\",o),l()}function " - "l(){debug(\"unpipe\"),s.unpipe(e)}var s=this,h=this._readableState;switch(h." - "pipesCount){case 0:h.pipes=e;break;case 1:h.pipes=[h.pipes,e];break;default:" - "h.pipes.push(e)}h.pipesCount+=1,debug(\"pipe count=%d opts=%j\",h.pipesCount" - ",t);var f=(!t||t.end!==!1)&&e!==process.stdout&&e!==process.stderr,p=f?n:a;h" - ".endEmitted?processNextTick(p):s.once(\"end\",p),e.on(\"unpipe\",r);var c=pi" - "peOnDrain(s);e.on(\"drain\",c);var g=!1,b=!1;return s.on(\"data\",i),prepend" - "Listener(e,\"error\",d),e.once(\"close\",o),e.once(\"finish\",u),e.emit(\"pi" - "pe\",s),h.flowing||(debug(\"pipe resume\"),s.resume()),e},Readable.prototype" - ".unpipe=function(e){var t=this._readableState;if(0===t.pipesCount)return thi" - "s;if(1===t.pipesCount)return e&&e!==t.pipes?this:(e||(e=t.pipes),t.pipes=nul" - "l,t.pipesCount=0,t.flowing=!1,e&&e.emit(\"unpipe\",this),this);if(!e){var r=" - "t.pipes,n=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var a=0;" - "a-1?setImmediate:processNextTick;Writable.WritableState=Writable" - "State;var util=require(\"core-util-is\");util.inherits=require(\"inherits\")" - ";var internalUtil={deprecate:require(\"util-deprecate\")},Stream;!function()" - "{try{Stream=require(\"stream\")}catch(e){}finally{Stream||(Stream=require(\"" - "events\").EventEmitter)}}();var Buffer=require(\"buffer\").Buffer,bufferShim" - "=require(\"buffer-shims\");util.inherits(Writable,Stream);var Duplex;Writabl" - "eState.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;" - ")t.push(e),e=e.next;return t},function(){try{Object.defineProperty(WritableS" - "tate.prototype,\"buffer\",{get:internalUtil.deprecate(function(){return this" - ".getBuffer()},\"_writableState.buffer is deprecated. Use _writableState.getB" - "uffer instead.\")})}catch(e){}}();var Duplex;Writable.prototype.pipe=functio" - "n(){this.emit(\"error\",new Error(\"Cannot pipe, not readable\"))},Writable." - "prototype.write=function(e,t,r){var i=this._writableState,n=!1;return\"funct" - "ion\"==typeof t&&(r=t,t=null),Buffer.isBuffer(e)?t=\"buffer\":t||(t=i.defaul" - "tEncoding),\"function\"!=typeof r&&(r=nop),i.ended?writeAfterEnd(this,r):val" - "idChunk(this,i,e,r)&&(i.pendingcb++,n=writeOrBuffer(this,i,e,t,r)),n},Writab" - "le.prototype.cork=function(){var e=this._writableState;e.corked++},Writable." - "prototype.uncork=function(){var e=this._writableState;e.corked&&(e.corked--," - "e.writing||e.corked||e.finished||e.bufferProcessing||!e.bufferedRequest||cle" - "arBuffer(this,e))},Writable.prototype.setDefaultEncoding=function(e){if(\"st" - "ring\"==typeof e&&(e=e.toLowerCase()),!([\"hex\",\"utf8\",\"utf-8\",\"ascii\"" - ",\"binary\",\"base64\",\"ucs2\",\"ucs-2\",\"utf16le\",\"utf-16le\",\"raw\"]." - "indexOf((e+\"\").toLowerCase())>-1))throw new TypeError(\"Unknown encoding: " - "\"+e);return this._writableState.defaultEncoding=e,this},Writable.prototype." - "_write=function(e,t,r){r(new Error(\"not implemented\"))},Writable.prototype" - "._writev=null,Writable.prototype.end=function(e,t,r){var i=this._writableSta" - "te;\"function\"==typeof e?(r=e,e=null,t=null):\"function\"==typeof t&&(r=t,t" - "=null),null!==e&&void 0!==e&&this.write(e,t),i.corked&&(i.corked=1,this.unco" - "rk()),i.ending||i.finished||endWritable(this,i,r)};\n" - "}).call(this,require('_process'))\n" - "},{\"./_stream_duplex\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_mo" - "dules/rethinkdb-webui/node_modules/browserify/node_modules/readable-stream/l" - "ib/_stream_duplex.js\",\"_process\":\"/rethinkdb/build/external/admin-deps_2" - ".0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/proce" - "ss/browser.js\",\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4/node" - "_modules/rethinkdb-webui/node_modules/browserify/node_modules/buffer/index.j" - "s\",\"buffer-shims\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modul" - "es/rethinkdb-webui/node_modules/browserify/node_modules/readable-stream/node" - "_modules/buffer-shims/index.js\",\"core-util-is\":\"/rethinkdb/build/externa" - "l/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node" - "_modules/readable-stream/node_modules/core-util-is/lib/util.js\",\"events\":" - "\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/no" - "de_modules/browserify/node_modules/events/events.js\",\"inherits\":\"/rethin" - "kdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_module" - "s/inherits/inherits_browser.js\",\"process-nextick-args\":\"/rethinkdb/build" - "/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browser" - "ify/node_modules/readable-stream/node_modules/process-nextick-args/index.js\"" - ",\"stream\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethin" - "kdb-webui/node_modules/browserify/node_modules/stream-browserify/index.js\"," - "\"util-deprecate\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules" - "/rethinkdb-webui/node_modules/browserify/node_modules/readable-stream/node_m" - "odules/util-deprecate/browser.js\"}],\"/rethinkdb/build/external/admin-deps_" - "2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/read" - "able-stream/lib/internal/streams/BufferList.js\":[function(require,module,ex" - "ports){\n" - "\"use strict\";function BufferList(){this.head=null,this.tail=null,this.leng" - "th=0}var Buffer=require(\"buffer\").Buffer,bufferShim=require(\"buffer-shims" - "\");module.exports=BufferList,BufferList.prototype.push=function(t){var e={d" - "ata:t,next:null};this.length>0?this.tail.next=e:this.head=e,this.tail=e,++th" - "is.length},BufferList.prototype.unshift=function(t){var e={data:t,next:this." - "head};0===this.length&&(this.tail=e),this.head=e,++this.length},BufferList.p" - "rototype.shift=function(){if(0!==this.length){var t=this.head.data;return 1=" - "==this.length?this.head=this.tail=null:this.head=this.head.next,--this.lengt" - "h,t}},BufferList.prototype.clear=function(){this.head=this.tail=null,this.le" - "ngth=0},BufferList.prototype.join=function(t){if(0===this.length)return\"\";" - "for(var e=this.head,i=\"\"+e.data;e=e.next;)i+=t+e.data;return i},BufferList" - ".prototype.concat=function(t){if(0===this.length)return bufferShim.alloc(0);" - "if(1===this.length)return this.head.data;for(var e=bufferShim.allocUnsafe(t>" - ">>0),i=this.head,h=0;i;)i.data.copy(e,h),h+=i.data.length,i=i.next;return e}" - ";\n" - "},{\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/buffer/index.js\",\"buffer-" - "shims\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-" - "webui/node_modules/browserify/node_modules/readable-stream/node_modules/buff" - "er-shims/index.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modu" - "les/rethinkdb-webui/node_modules/browserify/node_modules/readable-stream/nod" - "e_modules/buffer-shims/index.js\":[function(require,module,exports){\n" - "(function (global){\n" - "\"use strict\";var buffer=require(\"buffer\"),Buffer=buffer.Buffer,SlowBuffe" - "r=buffer.SlowBuffer,MAX_LEN=buffer.kMaxLength||2147483647;exports.alloc=func" - "tion(r,e,f){if(\"function\"==typeof Buffer.alloc)return Buffer.alloc(r,e,f);" - "if(\"number\"==typeof f)throw new TypeError(\"encoding must not be number\")" - ";if(\"number\"!=typeof r)throw new TypeError(\"size must be a number\");if(r" - ">MAX_LEN)throw new RangeError(\"size is too large\");var n=f,o=e;void 0===o&" - "&(n=void 0,o=0);var t=new Buffer(r);if(\"string\"==typeof o)for(var u=new Bu" - "ffer(o,n),i=u.length,a=-1;++aMAX_LEN)throw new RangeError(\"size is too large\");re" - "turn new Buffer(r)},exports.from=function(r,e,f){if(\"function\"==typeof Buf" - "fer.from&&(!global.Uint8Array||Uint8Array.from!==Buffer.from))return Buffer." - "from(r,e,f);if(\"number\"==typeof r)throw new TypeError('\"value\" argument " - "must not be a number');if(\"string\"==typeof r)return new Buffer(r,e);if(\"u" - "ndefined\"!=typeof ArrayBuffer&&r instanceof ArrayBuffer){var n=e;if(1===arg" - "uments.length)return new Buffer(r);\"undefined\"==typeof n&&(n=0);var o=f;if" - "(\"undefined\"==typeof o&&(o=r.byteLength-n),n>=r.byteLength)throw new Range" - "Error(\"'offset' is out of bounds\");if(o>r.byteLength-n)throw new RangeErro" - "r(\"'length' is out of bounds\");return new Buffer(r.slice(n,n+o))}if(Buffer" - ".isBuffer(r)){var t=new Buffer(r.length);return r.copy(t,0,0,r.length),t}if(" - "r){if(Array.isArray(r)||\"undefined\"!=typeof ArrayBuffer&&r.buffer instance" - "of ArrayBuffer||\"length\"in r)return new Buffer(r);if(\"Buffer\"===r.type&&" - "Array.isArray(r.data))return new Buffer(r.data)}throw new TypeError(\"First " - "argument must be a string, Buffer, ArrayBuffer, Array, or array-like object." - "\")},exports.allocUnsafeSlow=function(r){if(\"function\"==typeof Buffer.allo" - "cUnsafeSlow)return Buffer.allocUnsafeSlow(r);if(\"number\"!=typeof r)throw n" - "ew TypeError(\"size must be a number\");if(r>=MAX_LEN)throw new RangeError(\"" - "size is too large\");return new SlowBuffer(r)};\n" - "}).call(this,typeof global !== \"undefined\" ? global : typeof self !== \"un" - "defined\" ? self : typeof window !== \"undefined\" ? window : {})\n" - "},{\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/buffer/index.js\"}],\"/reth" - "inkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modu" - "les/browserify/node_modules/readable-stream/node_modules/core-util-is/lib/ut" - "il.js\":[function(require,module,exports){\n" - "(function (Buffer){\n" - "function isArray(r){return Array.isArray?Array.isArray(r):\"[object Array]\"" - "===objectToString(r)}function isBoolean(r){return\"boolean\"==typeof r}funct" - "ion isNull(r){return null===r}function isNullOrUndefined(r){return null==r}f" - "unction isNumber(r){return\"number\"==typeof r}function isString(r){return\"" - "string\"==typeof r}function isSymbol(r){return\"symbol\"==typeof r}function " - "isUndefined(r){return void 0===r}function isRegExp(r){return\"[object RegExp" - "]\"===objectToString(r)}function isObject(r){return\"object\"==typeof r&&nul" - "l!==r}function isDate(r){return\"[object Date]\"===objectToString(r)}functio" - "n isError(r){return\"[object Error]\"===objectToString(r)||r instanceof Erro" - "r}function isFunction(r){return\"function\"==typeof r}function isPrimitive(r" - "){return null===r||\"boolean\"==typeof r||\"number\"==typeof r||\"string\"==" - "typeof r||\"symbol\"==typeof r||\"undefined\"==typeof r}function objectToStr" - "ing(r){return Object.prototype.toString.call(r)}exports.isArray=isArray,expo" - "rts.isBoolean=isBoolean,exports.isNull=isNull,exports.isNullOrUndefined=isNu" - "llOrUndefined,exports.isNumber=isNumber,exports.isString=isString,exports.is" - "Symbol=isSymbol,exports.isUndefined=isUndefined,exports.isRegExp=isRegExp,ex" - "ports.isObject=isObject,exports.isDate=isDate,exports.isError=isError,export" - "s.isFunction=isFunction,exports.isPrimitive=isPrimitive,exports.isBuffer=Buf" - "fer.isBuffer;\n" - "}).call(this,{\"isBuffer\":require(\"../../../../insert-module-globals/node_" - "modules/is-buffer/index.js\")})\n" - "},{\"../../../../insert-module-globals/node_modules/is-buffer/index.js\":\"/" - "rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_" - "modules/browserify/node_modules/insert-module-globals/node_modules/is-buffer" - "/index.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/readable-stream/node_module" - "s/isarray/index.js\":[function(require,module,exports){\n" - "arguments[4][\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethi" - "nkdb-webui/node_modules/browserify/node_modules/buffer/node_modules/isarray/" - "index.js\"][0].apply(exports,arguments)\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/browserify/node_modules/readable-stream/node_modules/proces" - "s-nextick-args/index.js\":[function(require,module,exports){\n" - "(function (process){\n" - "\"use strict\";function nextTick(e,n,c,r){if(\"function\"!=typeof e)throw ne" - "w TypeError('\"callback\" argument must be a function');var s,t,o=arguments." - "length;switch(o){case 0:case 1:return process.nextTick(e);case 2:return proc" - "ess.nextTick(function(){e.call(null,n)});case 3:return process.nextTick(func" - "tion(){e.call(null,n,c)});case 4:return process.nextTick(function(){e.call(n" - "ull,n,c,r)});default:for(s=new Array(o-1),t=0;t=8*this._finalSize&&(this._update(this._block),this._block.fill(0)),this" - "._block.writeInt32BE(i,this._blockSize-4);var s=this._update(this._block)||t" - "his._hash();return t?s.toString(t):s},Hash.prototype._update=function(){thro" - "w new Error(\"_update must be implemented by subclass\")},module.exports=Has" - "h;\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/buffer/index.js\"}],\"/reth" - "inkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modu" - "les/browserify/node_modules/sha.js/index.js\":[function(require,module,expor" - "ts){\n" - "var exports=module.exports=function(e){e=e.toLowerCase();var r=exports[e];if" - "(!r)throw new Error(e+\" is not supported (we accept pull requests)\");retur" - "n new r};exports.sha=require(\"./sha\"),exports.sha1=require(\"./sha1\"),exp" - "orts.sha224=require(\"./sha224\"),exports.sha256=require(\"./sha256\"),expor" - "ts.sha384=require(\"./sha384\"),exports.sha512=require(\"./sha512\");\n" - "},{\"./sha\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethi" - "nkdb-webui/node_modules/browserify/node_modules/sha.js/sha.js\",\"./sha1\":\"" - "/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node" - "_modules/browserify/node_modules/sha.js/sha1.js\",\"./sha224\":\"/rethinkdb/" - "build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/br" - "owserify/node_modules/sha.js/sha224.js\",\"./sha256\":\"/rethinkdb/build/ext" - "ernal/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/" - "node_modules/sha.js/sha256.js\",\"./sha384\":\"/rethinkdb/build/external/adm" - "in-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modu" - "les/sha.js/sha384.js\",\"./sha512\":\"/rethinkdb/build/external/admin-deps_2" - ".0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/sha.j" - "s/sha512.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/re" - "thinkdb-webui/node_modules/browserify/node_modules/sha.js/sha.js\":[function" - "(require,module,exports){\n" - "(function (Buffer){\n" - "function Sha(){this.init(),this._w=W,Hash.call(this,64,56)}function rotl5(t)" - "{return t<<5|t>>>27}function rotl30(t){return t<<30|t>>>2}function ft(t,i,h," - "r){return 0===t?i&h|~i&r:2===t?i&h|i&r|h&r:i^h^r}var inherits=require(\"inhe" - "rits\"),Hash=require(\"./hash\"),K=[1518500249,1859775393,-1894007588,-89949" - "7514],W=new Array(80);inherits(Sha,Hash),Sha.prototype.init=function(){retur" - "n this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878" - ",this._e=3285377520,this},Sha.prototype._update=function(t){for(var i=this._" - "w,h=0|this._a,r=0|this._b,s=0|this._c,e=0|this._d,n=0|this._e,_=0;_<16;++_)i" - "[_]=t.readInt32BE(4*_);for(;_<80;++_)i[_]=i[_-3]^i[_-8]^i[_-14]^i[_-16];for(" - "var a=0;a<80;++a){var o=~~(a/20),u=rotl5(h)+ft(o,r,s,e)+n+i[a]+K[o]|0;n=e,e=" - "s,s=rotl30(r),r=h,h=u}this._a=h+this._a|0,this._b=r+this._b|0,this._c=s+this" - "._c|0,this._d=e+this._d|0,this._e=n+this._e|0},Sha.prototype._hash=function(" - "){var t=new Buffer(20);return t.writeInt32BE(0|this._a,0),t.writeInt32BE(0|t" - "his._b,4),t.writeInt32BE(0|this._c,8),t.writeInt32BE(0|this._d,12),t.writeIn" - "t32BE(0|this._e,16),t},module.exports=Sha;\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"./hash\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/sha.js/hash.js\",\"buffer\"" - ":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/n" - "ode_modules/browserify/node_modules/buffer/index.js\",\"inherits\":\"/rethin" - "kdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_module" - "s/inherits/inherits_browser.js\"}],\"/rethinkdb/build/external/admin-deps_2." - "0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/sha.js" - "/sha1.js\":[function(require,module,exports){\n" - "(function (Buffer){\n" - "function Sha1(){this.init(),this._w=W,Hash.call(this,64,56)}function rotl1(t" - "){return t<<1|t>>>31}function rotl5(t){return t<<5|t>>>27}function rotl30(t)" - "{return t<<30|t>>>2}function ft(t,i,h,r){return 0===t?i&h|~i&r:2===t?i&h|i&r" - "|h&r:i^h^r}var inherits=require(\"inherits\"),Hash=require(\"./hash\"),K=[15" - "18500249,1859775393,-1894007588,-899497514],W=new Array(80);inherits(Sha1,Ha" - "sh),Sha1.prototype.init=function(){return this._a=1732584193,this._b=4023233" - "417,this._c=2562383102,this._d=271733878,this._e=3285377520,this},Sha1.proto" - "type._update=function(t){for(var i=this._w,h=0|this._a,r=0|this._b,s=0|this." - "_c,n=0|this._d,e=0|this._e,_=0;_<16;++_)i[_]=t.readInt32BE(4*_);for(;_<80;++" - "_)i[_]=rotl1(i[_-3]^i[_-8]^i[_-14]^i[_-16]);for(var a=0;a<80;++a){var o=~~(a" - "/20),u=rotl5(h)+ft(o,r,s,n)+e+i[a]+K[o]|0;e=n,n=s,s=rotl30(r),r=h,h=u}this._" - "a=h+this._a|0,this._b=r+this._b|0,this._c=s+this._c|0,this._d=n+this._d|0,th" - "is._e=e+this._e|0},Sha1.prototype._hash=function(){var t=new Buffer(20);retu" - "rn t.writeInt32BE(0|this._a,0),t.writeInt32BE(0|this._b,4),t.writeInt32BE(0|" - "this._c,8),t.writeInt32BE(0|this._d,12),t.writeInt32BE(0|this._e,16),t},modu" - "le.exports=Sha1;\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"./hash\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/sha.js/hash.js\",\"buffer\"" - ":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/n" - "ode_modules/browserify/node_modules/buffer/index.js\",\"inherits\":\"/rethin" - "kdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_module" - "s/inherits/inherits_browser.js\"}],\"/rethinkdb/build/external/admin-deps_2." - "0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/sha.js" - "/sha224.js\":[function(require,module,exports){\n" - "(function (Buffer){\n" - "function Sha224(){this.init(),this._w=W,Hash.call(this,64,56)}var inherits=r" - "equire(\"inherits\"),Sha256=require(\"./sha256\"),Hash=require(\"./hash\"),W" - "=new Array(64);inherits(Sha224,Sha256),Sha224.prototype.init=function(){retu" - "rn this._a=3238371032,this._b=914150663,this._c=812702999,this._d=4144912697" - ",this._e=4290775857,this._f=1750603025,this._g=1694076839,this._h=3204075428" - ",this},Sha224.prototype._hash=function(){var t=new Buffer(28);return t.write" - "Int32BE(this._a,0),t.writeInt32BE(this._b,4),t.writeInt32BE(this._c,8),t.wri" - "teInt32BE(this._d,12),t.writeInt32BE(this._e,16),t.writeInt32BE(this._f,20)," - "t.writeInt32BE(this._g,24),t},module.exports=Sha224;\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"./hash\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/sha.js/hash.js\",\"./sha256" - "\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui" - "/node_modules/browserify/node_modules/sha.js/sha256.js\",\"buffer\":\"/rethi" - "nkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modul" - "es/browserify/node_modules/buffer/index.js\",\"inherits\":\"/rethinkdb/build" - "/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/inherit" - "s/inherits_browser.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_" - "modules/rethinkdb-webui/node_modules/browserify/node_modules/sha.js/sha256.j" - "s\":[function(require,module,exports){\n" - "(function (Buffer){\n" - "function Sha256(){this.init(),this._w=W,Hash.call(this,64,56)}function ch(t," - "i,h){return h^t&(i^h)}function maj(t,i,h){return t&i|h&(t|i)}function sigma0" - "(t){return(t>>>2|t<<30)^(t>>>13|t<<19)^(t>>>22|t<<10)}function sigma1(t){ret" - "urn(t>>>6|t<<26)^(t>>>11|t<<21)^(t>>>25|t<<7)}function gamma0(t){return(t>>>" - "7|t<<25)^(t>>>18|t<<14)^t>>>3}function gamma1(t){return(t>>>17|t<<15)^(t>>>1" - "9|t<<13)^t>>>10}var inherits=require(\"inherits\"),Hash=require(\"./hash\")," - "K=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,24536357" - "48,2870763221,3624381080,310598401,607225278,1426881987,1925078388,216207820" - "6,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983," - "1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671" - ",3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,13" - "96182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3" - "259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,50" - "6948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,19555" - "62222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329" - "325298],W=new Array(64);inherits(Sha256,Hash),Sha256.prototype.init=function" - "(){return this._a=1779033703,this._b=3144134277,this._c=1013904242,this._d=2" - "773480762,this._e=1359893119,this._f=2600822924,this._g=528734635,this._h=15" - "41459225,this},Sha256.prototype._update=function(t){for(var i=this._w,h=0|th" - "is._a,s=0|this._b,r=0|this._c,n=0|this._d,_=0|this._e,a=0|this._f,e=0|this._" - "g,u=0|this._h,o=0;o<16;++o)i[o]=t.readInt32BE(4*o);for(;o<64;++o)i[o]=gamma1" - "(i[o-2])+i[o-7]+gamma0(i[o-15])+i[o-16]|0;for(var f=0;f<64;++f){var c=u+sigm" - "a1(_)+ch(_,a,e)+K[f]+i[f]|0,m=sigma0(h)+maj(h,s,r)|0;u=e,e=a,a=_,_=n+c|0,n=r" - ",r=s,s=h,h=c+m|0}this._a=h+this._a|0,this._b=s+this._b|0,this._c=r+this._c|0" - ",this._d=n+this._d|0,this._e=_+this._e|0,this._f=a+this._f|0,this._g=e+this." - "_g|0,this._h=u+this._h|0},Sha256.prototype._hash=function(){var t=new Buffer" - "(32);return t.writeInt32BE(this._a,0),t.writeInt32BE(this._b,4),t.writeInt32" - "BE(this._c,8),t.writeInt32BE(this._d,12),t.writeInt32BE(this._e,16),t.writeI" - "nt32BE(this._f,20),t.writeInt32BE(this._g,24),t.writeInt32BE(this._h,28),t}," - "module.exports=Sha256;\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"./hash\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/sha.js/hash.js\",\"buffer\"" - ":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/n" - "ode_modules/browserify/node_modules/buffer/index.js\",\"inherits\":\"/rethin" - "kdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_module" - "s/inherits/inherits_browser.js\"}],\"/rethinkdb/build/external/admin-deps_2." - "0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/sha.js" - "/sha384.js\":[function(require,module,exports){\n" - "(function (Buffer){\n" - "function Sha384(){this.init(),this._w=W,Hash.call(this,128,112)}var inherits" - "=require(\"inherits\"),SHA512=require(\"./sha512\"),Hash=require(\"./hash\")" - ",W=new Array(160);inherits(Sha384,SHA512),Sha384.prototype.init=function(){r" - "eturn this._ah=3418070365,this._bh=1654270250,this._ch=2438529370,this._dh=3" - "55462360,this._eh=1731405415,this._fh=2394180231,this._gh=3675008525,this._h" - "h=1203062813,this._al=3238371032,this._bl=914150663,this._cl=812702999,this." - "_dl=4144912697,this._el=4290775857,this._fl=1750603025,this._gl=1694076839,t" - "his._hl=3204075428,this},Sha384.prototype._hash=function(){function h(h,i,s)" - "{t.writeInt32BE(h,s),t.writeInt32BE(i,s+4)}var t=new Buffer(48);return h(thi" - "s._ah,this._al,0),h(this._bh,this._bl,8),h(this._ch,this._cl,16),h(this._dh," - "this._dl,24),h(this._eh,this._el,32),h(this._fh,this._fl,40),t},module.expor" - "ts=Sha384;\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"./hash\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/sha.js/hash.js\",\"./sha512" - "\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui" - "/node_modules/browserify/node_modules/sha.js/sha512.js\",\"buffer\":\"/rethi" - "nkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modul" - "es/browserify/node_modules/buffer/index.js\",\"inherits\":\"/rethinkdb/build" - "/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/inherit" - "s/inherits_browser.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_" - "modules/rethinkdb-webui/node_modules/browserify/node_modules/sha.js/sha512.j" - "s\":[function(require,module,exports){\n" - "(function (Buffer){\n" - "function Sha512(){this.init(),this._w=W,Hash.call(this,128,112)}function Ch(" - "h,t,i){return i^h&(t^i)}function maj(h,t,i){return h&t|i&(h|t)}function sigm" - "a0(h,t){return(h>>>28|t<<4)^(t>>>2|h<<30)^(t>>>7|h<<25)}function sigma1(h,t)" - "{return(h>>>14|t<<18)^(h>>>18|t<<14)^(t>>>9|h<<23)}function Gamma0(h,t){retu" - "rn(h>>>1|t<<31)^(h>>>8|t<<24)^h>>>7}function Gamma0l(h,t){return(h>>>1|t<<31" - ")^(h>>>8|t<<24)^(h>>>7|t<<25)}function Gamma1(h,t){return(h>>>19|t<<13)^(t>>" - ">29|h<<3)^h>>>6}function Gamma1l(h,t){return(h>>>19|t<<13)^(t>>>29|h<<3)^(h>" - ">>6|t<<26)}function getCarry(h,t){return h>>>0>>0?1:0}var inherits=requir" - "e(\"inherits\"),Hash=require(\"./hash\"),K=[1116352408,3609767458,1899447441" - ",602891725,3049323471,3964484399,3921009573,2173295548,961987163,4081628472," - "1508970993,3053834265,2453635748,2937671579,2870763221,3664609560,3624381080" - ",2734883394,310598401,1164996542,607225278,1323610764,1426881987,3590304994," - "1925078388,4068182383,2162078206,991336113,2614888103,633803317,3248222580,3" - "479774868,3835390401,2666613458,4022224774,944711139,264347078,2341262773,60" - "4807628,2007800933,770255983,1495990901,1249150122,1856431235,1555081692,317" - "5218132,1996064986,2198950837,2554220882,3999719339,2821834349,766784016,295" - "2996808,2566594879,3210313671,3203337956,3336571891,1034457026,3584528711,24" - "66948901,113926993,3758326383,338241895,168717936,666307205,1188179964,77352" - "9912,1546045734,1294757372,1522805485,1396182291,2643833823,1695183700,23435" - "27390,1986661051,1014477480,2177026350,1206759142,2456956037,344077627,27304" - "85921,1290863460,2820302411,3158454273,3259730800,3505952657,3345764771,1062" - "17008,3516065817,3606008344,3600352804,1432725776,4094571909,1467031594,2754" - "23344,851169720,430227734,3100823752,506948616,1363258195,659060556,37506855" - "93,883997877,3785050280,958139571,3318307427,1322822218,3812723403,153700206" - "3,2003034995,1747873779,3602036899,1955562222,1575990012,2024104815,11255929" - "28,2227730452,2716904306,2361852424,442776044,2428436474,593698344,275673418" - "7,3733110249,3204031479,2999351573,3329325298,3815920427,3391569614,39283839" - "00,3515267271,566280711,3940187606,3454069534,4118630271,4000239992,11641847" - "4,1914138554,174292421,2731055270,289380356,3203993006,460393269,320620315,6" - "85471733,587496836,852142971,1086792851,1017036298,365543100,1126000580,2618" - "297676,1288033470,3409855158,1501505948,4234509866,1607167915,987167468,1816" - "402316,1246189591],W=new Array(160);inherits(Sha512,Hash),Sha512.prototype.i" - "nit=function(){return this._ah=1779033703,this._bh=3144134277,this._ch=10139" - "04242,this._dh=2773480762,this._eh=1359893119,this._fh=2600822924,this._gh=5" - "28734635,this._hh=1541459225,this._al=4089235720,this._bl=2227873595,this._c" - "l=4271175723,this._dl=1595750129,this._el=2917565137,this._fl=725511199,this" - "._gl=4215389547,this._hl=327033209,this},Sha512.prototype._update=function(h" - "){for(var t=this._w,i=0|this._ah,s=0|this._bh,_=0|this._ch,r=0|this._dh,a=0|" - "this._eh,e=0|this._fh,l=0|this._gh,n=0|this._hh,g=0|this._al,f=0|this._bl,u=" - "0|this._cl,c=0|this._dl,m=0|this._el,o=0|this._fl,y=0|this._gl,C=0|this._hl," - "d=0;d<32;d+=2)t[d]=h.readInt32BE(4*d),t[d+1]=h.readInt32BE(4*d+4);for(;d<160" - ";d+=2){var b=t[d-30],p=t[d-30+1],v=Gamma0(b,p),G=Gamma0l(p,b);b=t[d-4],p=t[d" - "-4+1];var w=Gamma1(b,p),S=Gamma1l(p,b),B=t[d-14],E=t[d-14+1],I=t[d-32],j=t[d" - "-32+1],H=G+E|0,q=v+B+getCarry(H,G)|0;H=H+S|0,q=q+w+getCarry(H,S)|0,H=H+j|0,q" - "=q+I+getCarry(H,j)|0,t[d]=q,t[d+1]=H}for(var W=0;W<160;W+=2){q=t[W],H=t[W+1]" - ";var x=maj(i,s,_),A=maj(g,f,u),k=sigma0(i,g),z=sigma0(g,i),D=sigma1(a,m),F=s" - "igma1(m,a),J=K[W],L=K[W+1],M=Ch(a,e,l),N=Ch(m,o,y),O=C+F|0,P=n+D+getCarry(O," - "C)|0;O=O+N|0,P=P+M+getCarry(O,N)|0,O=O+L|0,P=P+J+getCarry(O,L)|0,O=O+H|0,P=P" - "+q+getCarry(O,H)|0;var Q=z+A|0,R=k+x+getCarry(Q,z)|0;n=l,C=y,l=e,y=o,e=a,o=m" - ",m=c+O|0,a=r+P+getCarry(m,c)|0,r=_,c=u,_=s,u=f,s=i,f=g,g=O+Q|0,i=P+R+getCarr" - "y(g,O)|0}this._al=this._al+g|0,this._bl=this._bl+f|0,this._cl=this._cl+u|0,t" - "his._dl=this._dl+c|0,this._el=this._el+m|0,this._fl=this._fl+o|0,this._gl=th" - "is._gl+y|0,this._hl=this._hl+C|0,this._ah=this._ah+i+getCarry(this._al,g)|0," - "this._bh=this._bh+s+getCarry(this._bl,f)|0,this._ch=this._ch+_+getCarry(this" - "._cl,u)|0,this._dh=this._dh+r+getCarry(this._dl,c)|0,this._eh=this._eh+a+get" - "Carry(this._el,m)|0,this._fh=this._fh+e+getCarry(this._fl,o)|0,this._gh=this" - "._gh+l+getCarry(this._gl,y)|0,this._hh=this._hh+n+getCarry(this._hl,C)|0},Sh" - "a512.prototype._hash=function(){function h(h,i,s){t.writeInt32BE(h,s),t.writ" - "eInt32BE(i,s+4)}var t=new Buffer(64);return h(this._ah,this._al,0),h(this._b" - "h,this._bl,8),h(this._ch,this._cl,16),h(this._dh,this._dl,24),h(this._eh,thi" - "s._el,32),h(this._fh,this._fl,40),h(this._gh,this._gl,48),h(this._hh,this._h" - "l,56),t},module.exports=Sha512;\n" - "}).call(this,require(\"buffer\").Buffer)\n" - "},{\"./hash\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/sha.js/hash.js\",\"buffer\"" - ":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/n" - "ode_modules/browserify/node_modules/buffer/index.js\",\"inherits\":\"/rethin" - "kdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_module" - "s/inherits/inherits_browser.js\"}],\"/rethinkdb/build/external/admin-deps_2." - "0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_modules/stream" - "-browserify/index.js\":[function(require,module,exports){\n" - "function Stream(){EE.call(this)}module.exports=Stream;var EE=require(\"event" - "s\").EventEmitter,inherits=require(\"inherits\");inherits(Stream,EE),Stream." - "Readable=require(\"readable-stream/readable.js\"),Stream.Writable=require(\"" - "readable-stream/writable.js\"),Stream.Duplex=require(\"readable-stream/duple" - "x.js\"),Stream.Transform=require(\"readable-stream/transform.js\"),Stream.Pa" - "ssThrough=require(\"readable-stream/passthrough.js\"),Stream.Stream=Stream,S" - "tream.prototype.pipe=function(e,r){function t(r){e.writable&&!1===e.write(r)" - "&&m.pause&&m.pause()}function n(){m.readable&&m.resume&&m.resume()}function " - "a(){u||(u=!0,e.end())}function o(){u||(u=!0,\"function\"==typeof e.destroy&&" - "e.destroy())}function i(e){if(s(),0===EE.listenerCount(this,\"error\"))throw" - " e}function s(){m.removeListener(\"data\",t),e.removeListener(\"drain\",n),m" - ".removeListener(\"end\",a),m.removeListener(\"close\",o),m.removeListener(\"" - "error\",i),e.removeListener(\"error\",i),m.removeListener(\"end\",s),m.remov" - "eListener(\"close\",s),e.removeListener(\"close\",s)}var m=this;m.on(\"data\"" - ",t),e.on(\"drain\",n),e._isStdio||r&&r.end===!1||(m.on(\"end\",a),m.on(\"clo" - "se\",o));var u=!1;return m.on(\"error\",i),e.on(\"error\",i),m.on(\"end\",s)" - ",m.on(\"close\",s),e.on(\"close\",s),e.emit(\"pipe\",m),e};\n" - "},{\"events\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/events/events.js\",\"inheri" - "ts\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-web" - "ui/node_modules/inherits/inherits_browser.js\",\"readable-stream/duplex.js\"" - ":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/n" - "ode_modules/browserify/node_modules/readable-stream/duplex.js\",\"readable-s" - "tream/passthrough.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_mod" - "ules/rethinkdb-webui/node_modules/browserify/node_modules/readable-stream/pa" - "ssthrough.js\",\"readable-stream/readable.js\":\"/rethinkdb/build/external/a" - "dmin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/browserify/node_mo" - "dules/readable-stream/readable.js\",\"readable-stream/transform.js\":\"/reth" - "inkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modu" - "les/browserify/node_modules/readable-stream/transform.js\",\"readable-stream" - "/writable.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/ret" - "hinkdb-webui/node_modules/browserify/node_modules/readable-stream/writable.j" - "s\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/browserify/node_modules/string_decoder/index.js\":[function" - "(require,module,exports){\n" - "function assertEncoding(e){if(e&&!isBufferEncoding(e))throw new Error(\"Unkn" - "own encoding: \"+e)}function passThroughWrite(e){return e.toString(this.enco" - "ding)}function utf16DetectIncompleteChar(e){this.charReceived=e.length%2,thi" - "s.charLength=this.charReceived?2:0}function base64DetectIncompleteChar(e){th" - "is.charReceived=e.length%3,this.charLength=this.charReceived?3:0}var Buffer=" - "require(\"buffer\").Buffer,isBufferEncoding=Buffer.isEncoding||function(e){s" - "witch(e&&e.toLowerCase()){case\"hex\":case\"utf8\":case\"utf-8\":case\"ascii" - "\":case\"binary\":case\"base64\":case\"ucs2\":case\"ucs-2\":case\"utf16le\":" - "case\"utf-16le\":case\"raw\":return!0;default:return!1}},StringDecoder=expor" - "ts.StringDecoder=function(e){switch(this.encoding=(e||\"utf8\").toLowerCase(" - ").replace(/[-_]/,\"\"),assertEncoding(e),this.encoding){case\"utf8\":this.su" - "rrogateSize=3;break;case\"ucs2\":case\"utf16le\":this.surrogateSize=2,this.d" - "etectIncompleteChar=utf16DetectIncompleteChar;break;case\"base64\":this.surr" - "ogateSize=3,this.detectIncompleteChar=base64DetectIncompleteChar;break;defau" - "lt:return void(this.write=passThroughWrite)}this.charBuffer=new Buffer(6),th" - "is.charReceived=0,this.charLength=0};StringDecoder.prototype.write=function(" - "e){for(var t=\"\";this.charLength;){var r=e.length>=this.charLength-this.cha" - "rReceived?this.charLength-this.charReceived:e.length;if(e.copy(this.charBuff" - "er,this.charReceived,0,r),this.charReceived+=r,this.charReceived=55296&&h" - "<=56319)){if(this.charReceived=this.charLength=0,0===e.length)return t;break" - "}this.charLength+=this.surrogateSize,t=\"\"}this.detectIncompleteChar(e);var" - " i=e.length;this.charLength&&(e.copy(this.charBuffer,0,e.length-this.charRec" - "eived,i),i-=this.charReceived),t+=e.toString(this.encoding,0,i);var i=t.leng" - "th-1,h=t.charCodeAt(i);if(h>=55296&&h<=56319){var c=this.surrogateSize;retur" - "n this.charLength+=c,this.charReceived+=c,this.charBuffer.copy(this.charBuff" - "er,c,0,c),e.copy(this.charBuffer,0,0,c),t.substring(0,i)}return t},StringDec" - "oder.prototype.detectIncompleteChar=function(e){for(var t=e.length>=3?3:e.le" - "ngth;t>0;t--){var r=e[e.length-t];if(1==t&&r>>5==6){this.charLength=2;break}" - "if(t<=2&&r>>4==14){this.charLength=3;break}if(t<=3&&r>>3==30){this.charLengt" - "h=4;break}}this.charReceived=t},StringDecoder.prototype.end=function(e){var " - "t=\"\";if(e&&e.length&&(t=this.write(e)),this.charReceived){var r=this.charR" - "eceived,h=this.charBuffer,i=this.encoding;t+=h.slice(0,r).toString(i)}return" - " t};\n" - "},{\"buffer\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/reth" - "inkdb-webui/node_modules/browserify/node_modules/buffer/index.js\"}],\"/reth" - "inkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modu" - "les/browserify/node_modules/vm-browserify/index.js\":[function(require,modul" - "e,exports){\n" - "function Context(){}var indexOf=require(\"indexof\"),Object_keys=function(e)" - "{if(Object.keys)return Object.keys(e);var t=[];for(var r in e)t.push(r);retu" - "rn t},forEach=function(e,t){if(e.forEach)return e.forEach(t);for(var r=0;r0?(r.ids&&(r.ids=[r.name]),e.helpers.each(t,r)):" - "i(this);if(r.data&&r.ids){var a=createFrame(r.data);a.contextPath=Utils.appe" - "ndContextPath(r.data.contextPath,r.name),r={data:a}}return n(t,r)}),e.regist" - "erHelper(\"each\",function(e,t){function r(t,r,n){l&&(l.key=t,l.index=r,l.fi" - "rst=0===r,l.last=!!n,o&&(l.contextPath=o+t)),s+=i(e[t],{data:l,blockParams:U" - "tils.blockParams([e[t],t],[o+t,null])})}if(!t)throw new _Exception2.default(" - "\"Must pass iterator to #each\");var i=t.fn,n=t.inverse,a=0,s=\"\",l=void 0," - "o=void 0;if(t.data&&t.ids&&(o=Utils.appendContextPath(t.data.contextPath,t.i" - "ds[0])+\".\"),isFunction(e)&&(e=e.call(this)),t.data&&(l=createFrame(t.data)" - "),e&&\"object\"==typeof e)if(isArray(e))for(var c=e.length;a= 2.0.0-beta.1\"};exports.REVISION_CHANGES=" - "REVISION_CHANGES;var isArray=Utils.isArray,isFunction=Utils.isFunction,toStr" - "ing=Utils.toString,objectType=\"[object Object]\";HandlebarsEnvironment.prot" - "otype={constructor:HandlebarsEnvironment,logger:logger,log:log,registerHelpe" - "r:function(e,t){if(toString.call(e)===objectType){if(t)throw new _Exception2" - ".default(\"Arg not supported with multiple helpers\");Utils.extend(this.help" - "ers,e)}else this.helpers[e]=t},unregisterHelper:function(e){delete this.help" - "ers[e]},registerPartial:function(e,t){if(toString.call(e)===objectType)Utils" - ".extend(this.partials,e);else{if(\"undefined\"==typeof t)throw new _Exceptio" - "n2.default(\"Attempting to register a partial as undefined\");this.partials[" - "e]=t}},unregisterPartial:function(e){delete this.partials[e]}};var logger={m" - "ethodMap:{0:\"debug\",1:\"info\",2:\"warn\",3:\"error\"},DEBUG:0,INFO:1,WARN" - ":2,ERROR:3,level:1,log:function(e,t){if(\"undefined\"!=typeof console&&logge" - "r.level<=e){var r=logger.methodMap[e];(console[r]||console.log).call(console" - ",t)}}};exports.logger=logger;var log=logger.log;exports.log=log;\n" - "},{\"./exception\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules" - "/rethinkdb-webui/node_modules/handlebars/dist/cjs/handlebars/exception.js\"," - "\"./utils\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethin" - "kdb-webui/node_modules/handlebars/dist/cjs/handlebars/utils.js\"}],\"/rethin" - "kdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_module" - "s/handlebars/dist/cjs/handlebars/exception.js\":[function(require,module,exp" - "orts){\n" - "\"use strict\";function Exception(r,e){var o=e&&e.loc,t=void 0,s=void 0;o&&(" - "t=o.start.line,s=o.start.column,r+=\" - \"+t+\":\"+s);for(var c=Error.protot" - "ype.constructor.call(this,r),i=0;i\":\">\",'\"':\""\",\"'\":\"'\",\"`\":\"`\"},bad" - "Chars=/[&<>\"'`]/g,possible=/[&<>\"'`]/,toString=Object.prototype.toString;e" - "xports.toString=toString;var isFunction=function(t){return\"function\"==type" - "of t};isFunction(/x/)&&(exports.isFunction=isFunction=function(t){return\"fu" - "nction\"==typeof t&&\"[object Function]\"===toString.call(t)});var isFunctio" - "n;exports.isFunction=isFunction;var isArray=Array.isArray||function(t){retur" - "n!(!t||\"object\"!=typeof t)&&\"[object Array]\"===toString.call(t)};exports" - ".isArray=isArray;\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/handlebars/runtime.js\":[function(require,module,exports){\n" - "module.exports=require(\"./dist/cjs/handlebars.runtime\").default;\n" - "},{\"./dist/cjs/handlebars.runtime\":\"/rethinkdb/build/external/admin-deps_" - "2.0.4/node_modules/rethinkdb-webui/node_modules/handlebars/dist/cjs/handleba" - "rs.runtime.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/" - "rethinkdb-webui/node_modules/hbsfy/runtime.js\":[function(require,module,exp" - "orts){\n" - "module.exports=require(\"handlebars/runtime\").default;\n" - "},{\"handlebars/runtime\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_" - "modules/rethinkdb-webui/node_modules/handlebars/runtime.js\"}],\"/rethinkdb/" - "build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/in" - "herits/inherits_browser.js\":[function(require,module,exports){\n" - "\"function\"==typeof Object.create?module.exports=function(t,e){t.super_=e,t" - ".prototype=Object.create(e.prototype,{constructor:{value:t,enumerable:!1,wri" - "table:!0,configurable:!0}})}:module.exports=function(t,e){t.super_=e;var o=f" - "unction(){};o.prototype=e.prototype,t.prototype=new o,t.prototype.constructo" - "r=t};\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/moment/moment.js\":[function(require,module,exports){\n" - "!function(e,t){\"object\"==typeof exports&&\"undefined\"!=typeof module?modu" - "le.exports=t():\"function\"==typeof define&&define.amd?define(t):e.moment=t(" - ")}(this,function(){\"use strict\";function e(){return fs.apply(null,argument" - "s)}function t(e){fs=e}function n(e){return e instanceof Array||\"[object Arr" - "ay]\"===Object.prototype.toString.call(e)}function s(e){return null!=e&&\"[o" - "bject Object]\"===Object.prototype.toString.call(e)}function i(e){var t;for(" - "t in e)return!1;return!0}function r(e){return e instanceof Date||\"[object D" - "ate]\"===Object.prototype.toString.call(e)}function a(e,t){var n,s=[];for(n=" - "0;n0)for(n in _s)s=_s[n],i=t[s],m(i)||(e[s]=i);return e}function y(t)" - "{_(this,t),this._d=new Date(null!=t._d?t._d.getTime():NaN),ys===!1&&(ys=!0,e" - ".updateOffset(this),ys=!1)}function g(e){return e instanceof y||null!=e&&nul" - "l!=e._isAMomentObject}function p(e){return e<0?Math.ceil(e)||0:Math.floor(e)" - "}function w(e){var t=+e,n=0;return 0!==t&&isFinite(t)&&(n=p(t)),n}function v" - "(e,t,n){var s,i=Math.min(e.length,t.length),r=Math.abs(e.length-t.length),a=" - "0;for(s=0;s0?\"future\":\"past\"];ret" - "urn D(n)?n(t):n.replace(/%s/i,t)}function C(e,t){var n=e.toLowerCase();Os[n]" - "=Os[n+\"s\"]=Os[t]=e}function F(e){return\"string\"==typeof e?Os[e]||Os[e.to" - "LowerCase()]:void 0}function H(e){var t,n,s={};for(n in e)o(e,n)&&(t=F(n),t&" - "&(s[t]=e[n]));return s}function L(e,t){xs[e]=t}function G(e){var t=[];for(va" - "r n in e)t.push({unit:n,priority:xs[n]});return t.sort(function(e,t){return " - "e.priority-t.priority}),t}function V(t,n){return function(s){return null!=s?" - "(A(this,t,s),e.updateOffset(this,n),this):j(this,t)}}function j(e,t){return " - "e.isValid()?e._d[\"get\"+(e._isUTC?\"UTC\":\"\")+t]():NaN}function A(e,t,n){" - "e.isValid()&&e._d[\"set\"+(e._isUTC?\"UTC\":\"\")+t](n)}function N(e){return" - " e=F(e),D(this[e])?this[e]():this}function E(e,t){if(\"object\"==typeof e){e" - "=H(e);for(var n=G(e),s=0;s=0;return(r?n?\"+\":\"\":\"-\")+Math.pow(10," - "Math.max(0,i)).toString().substr(1)+s}function z(e,t,n,s){var i=s;\"string\"" - "==typeof s&&(i=function(){return this[s]()}),e&&(Ws[e]=i),t&&(Ws[t[0]]=funct" - "ion(){return I(i.apply(this,arguments),t[1],t[2])}),n&&(Ws[n]=function(){ret" - "urn this.localeData().ordinal(i.apply(this,arguments),e)})}function Z(e){ret" - "urn e.match(/\\[[\\s\\S]/)?e.replace(/^\\[|\\]$/g,\"\"):e.replace(/\\\\/g,\"" - "\")}function q(e){var t,n,s=e.match(bs);for(t=0,n=s.length;t=0&&Ts.test(e);)e=e.replace(Ts,n),Ts.lastInd" - "ex=0,s-=1;return e}function J(e,t,n){Bs[e]=D(t)?t:function(e,s){return e&&n?" - "n:t}}function Q(e,t){return o(Bs,e)?Bs[e](t._strict,t._locale):new RegExp(X(" - "e))}function X(e){return K(e.replace(\"\\\\\",\"\").replace(/\\\\(\\[)|\\\\(" - "\\])|\\[([^\\]\\[]*)\\]|\\\\(.)/g,function(e,t,n,s,i){return t||n||s||i}))}f" - "unction K(e){return e.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g,\"\\\\$&\")}funct" - "ion ee(e,t){var n,s=t;for(\"string\"==typeof e&&(e=[e]),\"number\"==typeof t" - "&&(s=function(e,n){n[t]=w(e)}),n=0;n=0&&isFinite(o.getFullYear())&" - "&o.setFullYear(e),o}function pe(e){var t=new Date(Date.UTC.apply(null,argume" - "nts));return e<100&&e>=0&&isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e)," - "t}function we(e,t,n){var s=7+t-n,i=(7+pe(e,0,s).getUTCDay()-t)%7;return-i+s-" - "1}function ve(e,t,n,s,i){var r,a,o=(7+n-s)%7,u=we(e,s,i),d=1+7*(t-1)+o+u;ret" - "urn d<=0?(r=e-1,a=me(r)+d):d>me(e)?(r=e+1,a=d-me(e)):(r=e,a=d),{year:r,dayOf" - "Year:a}}function Se(e,t,n){var s,i,r=we(e.year(),t,n),a=Math.floor((e.dayOfY" - "ear()-r-1)/7)+1;return a<1?(i=e.year()-1,s=a+Me(i,t,n)):a>Me(e.year(),t,n)?(" - "s=a-Me(e.year(),t,n),i=e.year()+1):(i=e.year(),s=a),{week:s,year:i}}function" - " Me(e,t,n){var s=we(e,t,n),i=we(e+1,t,n);return(me(e)-s+i)/7}function ke(e){" - "return Se(e,this._week.dow,this._week.doy).week}function De(){return this._w" - "eek.dow}function Ye(){return this._week.doy}function Oe(e){var t=this.locale" - "Data().week(this);return null==e?t:this.add(7*(e-t),\"d\")}function xe(e){va" - "r t=Se(this,1,4).week;return null==e?t:this.add(7*(e-t),\"d\")}function be(e" - ",t){return\"string\"!=typeof e?e:isNaN(e)?(e=t.weekdaysParse(e),\"number\"==" - "typeof e?e:null):parseInt(e,10)}function Te(e,t){return\"string\"==typeof e?" - "t.weekdaysParse(e)%7||7:isNaN(e)?null:e}function Pe(e,t){return e?n(this._we" - "ekdays)?this._weekdays[e.day()]:this._weekdays[this._weekdays.isFormat.test(" - "t)?\"format\":\"standalone\"][e.day()]:this._weekdays}function We(e){return " - "e?this._weekdaysShort[e.day()]:this._weekdaysShort}function Re(e){return e?t" - "his._weekdaysMin[e.day()]:this._weekdaysMin}function Ue(e,t,n){var s,i,r,a=e" - ".toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this" - "._shortWeekdaysParse=[],this._minWeekdaysParse=[],s=0;s<7;++s)r=d([2e3,1]).d" - "ay(s),this._minWeekdaysParse[s]=this.weekdaysMin(r,\"\").toLocaleLowerCase()" - ",this._shortWeekdaysParse[s]=this.weekdaysShort(r,\"\").toLocaleLowerCase()," - "this._weekdaysParse[s]=this.weekdays(r,\"\").toLocaleLowerCase();return n?\"" - "dddd\"===t?(i=ws.call(this._weekdaysParse,a),i!==-1?i:null):\"ddd\"===t?(i=w" - "s.call(this._shortWeekdaysParse,a),i!==-1?i:null):(i=ws.call(this._minWeekda" - "ysParse,a),i!==-1?i:null):\"dddd\"===t?(i=ws.call(this._weekdaysParse,a),i!=" - "=-1?i:(i=ws.call(this._shortWeekdaysParse,a),i!==-1?i:(i=ws.call(this._minWe" - "ekdaysParse,a),i!==-1?i:null))):\"ddd\"===t?(i=ws.call(this._shortWeekdaysPa" - "rse,a),i!==-1?i:(i=ws.call(this._weekdaysParse,a),i!==-1?i:(i=ws.call(this._" - "minWeekdaysParse,a),i!==-1?i:null))):(i=ws.call(this._minWeekdaysParse,a),i!" - "==-1?i:(i=ws.call(this._weekdaysParse,a),i!==-1?i:(i=ws.call(this._shortWeek" - "daysParse,a),i!==-1?i:null)))}function Ce(e,t,n){var s,i,r;if(this._weekdays" - "ParseExact)return Ue.call(this,e,t,n);for(this._weekdaysParse||(this._weekda" - "ysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullW" - "eekdaysParse=[]),s=0;s<7;s++){if(i=d([2e3,1]).day(s),n&&!this._fullWeekdaysP" - "arse[s]&&(this._fullWeekdaysParse[s]=new RegExp(\"^\"+this.weekdays(i,\"\")." - "replace(\".\",\".?\")+\"$\",\"i\"),this._shortWeekdaysParse[s]=new RegExp(\"" - "^\"+this.weekdaysShort(i,\"\").replace(\".\",\".?\")+\"$\",\"i\"),this._minW" - "eekdaysParse[s]=new RegExp(\"^\"+this.weekdaysMin(i,\"\").replace(\".\",\".?" - "\")+\"$\",\"i\")),this._weekdaysParse[s]||(r=\"^\"+this.weekdays(i,\"\")+\"|" - "^\"+this.weekdaysShort(i,\"\")+\"|^\"+this.weekdaysMin(i,\"\"),this._weekday" - "sParse[s]=new RegExp(r.replace(\".\",\"\"),\"i\")),n&&\"dddd\"===t&&this._fu" - "llWeekdaysParse[s].test(e))return s;if(n&&\"ddd\"===t&&this._shortWeekdaysPa" - "rse[s].test(e))return s;if(n&&\"dd\"===t&&this._minWeekdaysParse[s].test(e))" - "return s;if(!n&&this._weekdaysParse[s].test(e))return s}}function Fe(e){if(!" - "this.isValid())return null!=e?this:NaN;var t=this._isUTC?this._d.getUTCDay()" - ":this._d.getDay();return null!=e?(e=be(e,this.localeData()),this.add(e-t,\"d" - "\")):t}function He(e){if(!this.isValid())return null!=e?this:NaN;var t=(this" - ".day()+7-this.localeData()._week.dow)%7;return null==e?t:this.add(e-t,\"d\")" - "}function Le(e){if(!this.isValid())return null!=e?this:NaN;if(null!=e){var t" - "=Te(e,this.localeData());return this.day(this.day()%7?t:t-7)}return this.day" - "()||7}function Ge(e){return this._weekdaysParseExact?(o(this,\"_weekdaysRege" - "x\")||Ae.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(o(this" - ",\"_weekdaysRegex\")||(this._weekdaysRegex=yi),this._weekdaysStrictRegex&&e?" - "this._weekdaysStrictRegex:this._weekdaysRegex)}function Ve(e){return this._w" - "eekdaysParseExact?(o(this,\"_weekdaysRegex\")||Ae.call(this),e?this._weekday" - "sShortStrictRegex:this._weekdaysShortRegex):(o(this,\"_weekdaysShortRegex\")" - "||(this._weekdaysShortRegex=gi),this._weekdaysShortStrictRegex&&e?this._week" - "daysShortStrictRegex:this._weekdaysShortRegex)}function je(e){return this._w" - "eekdaysParseExact?(o(this,\"_weekdaysRegex\")||Ae.call(this),e?this._weekday" - "sMinStrictRegex:this._weekdaysMinRegex):(o(this,\"_weekdaysMinRegex\")||(thi" - "s._weekdaysMinRegex=pi),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStr" - "ictRegex:this._weekdaysMinRegex)}function Ae(){function e(e,t){return t.leng" - "th-e.length}var t,n,s,i,r,a=[],o=[],u=[],l=[];for(t=0;t<7;t++)n=d([2e3,1]).d" - "ay(t),s=this.weekdaysMin(n,\"\"),i=this.weekdaysShort(n,\"\"),r=this.weekday" - "s(n,\"\"),a.push(s),o.push(i),u.push(r),l.push(s),l.push(i),l.push(r);for(a." - "sort(e),o.sort(e),u.sort(e),l.sort(e),t=0;t<7;t++)o[t]=K(o[t]),u[t]=K(u[t])," - "l[t]=K(l[t]);this._weekdaysRegex=new RegExp(\"^(\"+l.join(\"|\")+\")\",\"i\"" - "),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._" - "weekdaysRegex,this._weekdaysStrictRegex=new RegExp(\"^(\"+u.join(\"|\")+\")\"" - ",\"i\"),this._weekdaysShortStrictRegex=new RegExp(\"^(\"+o.join(\"|\")+\")\"" - ",\"i\"),this._weekdaysMinStrictRegex=new RegExp(\"^(\"+a.join(\"|\")+\")\",\"" - "i\")}function Ne(){return this.hours()%12||12}function Ee(){return this.hour" - "s()||24}function Ie(e,t){z(e,0,0,function(){return this.localeData().meridie" - "m(this.hours(),this.minutes(),t)})}function ze(e,t){return t._meridiemParse}" - "function Ze(e){return\"p\"===(e+\"\").toLowerCase().charAt(0)}function qe(e," - "t,n){return e>11?n?\"pm\":\"PM\":n?\"am\":\"AM\"}function $e(e){return e?e.t" - "oLowerCase().replace(\"_\",\"-\"):e}function Be(e){for(var t,n,s,i,r=0;r0;){if(s=Je(i.slice(0,t).join(\"-\")))return s;if(n&&n.length>=t&&v" - "(i,n,!0)>=t-1)break;t--}r++}return null}function Je(e){var t=null;if(!ki[e]&" - "&\"undefined\"!=typeof module&&module&&module.require)try{t=wi._abbr,module." - "require(\"./locale/\"+e),Qe(t)}catch(e){}return ki[e]}function Qe(e,t){var n" - ";return e&&(n=m(t)?et(e):Xe(e,t),n&&(wi=n)),wi._abbr}function Xe(e,t){if(nul" - "l!==t){var n=Mi;return t.abbr=e,null!=ki[e]?(k(\"defineLocaleOverride\",\"us" - "e moment.updateLocale(localeName, config) to change an existing locale. mome" - "nt.defineLocale(localeName, config) should only be used for creating a new l" - "ocale See http://momentjs.com/guides/#/warnings/define-locale/ for more info" - ".\"),n=ki[e]._config):null!=t.parentLocale&&(null!=ki[t.parentLocale]?n=ki[t" - ".parentLocale]._config:k(\"parentLocaleUndefined\",\"specified parentLocale " - "is not defined yet. See http://momentjs.com/guides/#/warnings/parent-locale/" - "\")),ki[e]=new x(O(n,t)),Qe(e),ki[e]}return delete ki[e],null}function Ke(e," - "t){if(null!=t){var n,s=Mi;null!=ki[e]&&(s=ki[e]._config),t=O(s,t),n=new x(t)" - ",n.parentLocale=ki[e],ki[e]=n,Qe(e)}else null!=ki[e]&&(null!=ki[e].parentLoc" - "ale?ki[e]=ki[e].parentLocale:null!=ki[e]&&delete ki[e]);return ki[e]}functio" - "n et(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)retur" - "n wi;if(!n(e)){if(t=Je(e))return t;e=[e]}return Be(e)}function tt(){return p" - "s(ki)}function nt(e){var t,n=e._a;return n&&h(e).overflow===-2&&(t=n[Xs]<0||" - "n[Xs]>11?Xs:n[Ks]<1||n[Ks]>se(n[Qs],n[Xs])?Ks:n[ei]<0||n[ei]>24||24===n[ei]&" - "&(0!==n[ti]||0!==n[ni]||0!==n[si])?ei:n[ti]<0||n[ti]>59?ti:n[ni]<0||n[ni]>59" - "?ni:n[si]<0||n[si]>999?si:-1,h(e)._overflowDayOfYear&&(tKs)&&(t=Ks),h" - "(e)._overflowWeeks&&t===-1&&(t=ii),h(e)._overflowWeekday&&t===-1&&(t=ri),h(e" - ").overflow=t),e}function st(e){var t,n,s,i,r,a,o=e._i,u=Di.exec(o)||Yi.exec(" - "o);if(u){for(h(e).iso=!0,t=0,n=xi.length;tme(i)&&(h(e)._overflowDayOfYear=!0),n=pe(i,0,e._dayOfY" - "ear),e._a[Xs]=n.getUTCMonth(),e._a[Ks]=n.getUTCDate()),t=0;t<3&&null==e._a[t" - "];++t)e._a[t]=r[t]=s[t];for(;t<7;t++)e._a[t]=r[t]=null==e._a[t]?2===t?1:0:e." - "_a[t];24===e._a[ei]&&0===e._a[ti]&&0===e._a[ni]&&0===e._a[si]&&(e._nextDay=!" - "0,e._a[ei]=0),e._d=(e._useUTC?pe:ge).apply(null,r),null!=e._tzm&&e._d.setUTC" - "Minutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ei]=24)}}function ut(" - "e){var t,n,s,i,r,a,o,u;t=e._w,null!=t.GG||null!=t.W||null!=t.E?(r=1,a=4,n=rt" - "(t.GG,e._a[Qs],Se(gt(),1,4).year),s=rt(t.W,1),i=rt(t.E,1),(i<1||i>7)&&(u=!0)" - "):(r=e._locale._week.dow,a=e._locale._week.doy,n=rt(t.gg,e._a[Qs],Se(gt(),r," - "a).year),s=rt(t.w,1),null!=t.d?(i=t.d,(i<0||i>6)&&(u=!0)):null!=t.e?(i=t.e+r" - ",(t.e<0||t.e>6)&&(u=!0)):i=r),s<1||s>Me(n,r,a)?h(e)._overflowWeeks=!0:null!=" - "u?h(e)._overflowWeekday=!0:(o=ve(n,s,i,r,a),e._a[Qs]=o.year,e._dayOfYear=o.d" - "ayOfYear)}function dt(t){if(t._f===e.ISO_8601)return void st(t);t._a=[],h(t)" - ".empty=!0;var n,s,i,r,a,o=\"\"+t._i,u=o.length,d=0;for(i=B(t._f,t._locale).m" - "atch(bs)||[],n=0;n0&&h(t).unusedInput.push(a),o=o.slice(o.indexO" - "f(s)+s.length),d+=s.length),Ws[r]?(s?h(t).empty=!1:h(t).unusedTokens.push(r)" - ",ne(r,s,t)):t._strict&&!s&&h(t).unusedTokens.push(r);h(t).charsLeftOver=u-d," - "o.length>0&&h(t).unusedInput.push(o),t._a[ei]<=12&&h(t).bigHour===!0&&t._a[e" - "i]>0&&(h(t).bigHour=void 0),h(t).parsedDateParts=t._a.slice(0),h(t).meridiem" - "=t._meridiem,t._a[ei]=lt(t._locale,t._a[ei],t._meridiem),ot(t),nt(t)}functio" - "n lt(e,t,n){var s;return null==n?t:null!=e.meridiemHour?e.meridiemHour(t,n):" - "null!=e.isPM?(s=e.isPM(n),s&&t<12&&(t+=12),s||12!==t||(t=0),t):t}function ht" - "(e){var t,n,s,i,r;if(0===e._f.length)return h(e).invalidFormat=!0,void(e._d=" - "new Date(NaN));for(i=0;ithis.clone().month(0).utcOffset()||this.u" - "tcOffset()>this.clone().month(5).utcOffset()}function Ft(){if(!m(this._isDST" - "Shifted))return this._isDSTShifted;var e={};if(_(e,this),e=mt(e),e._a){var t" - "=e._isUTC?d(e._a):gt(e._a);this._isDSTShifted=this.isValid()&&v(e._a,t.toArr" - "ay())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Ht(){r" - "eturn!!this.isValid()&&!this._isUTC}function Lt(){return!!this.isValid()&&th" - "is._isUTC}function Gt(){return!!this.isValid()&&(this._isUTC&&0===this._offs" - "et)}function Vt(e,t){var n,s,i,r=e,a=null;return Mt(e)?r={ms:e._milliseconds" - ",d:e._days,M:e._months}:\"number\"==typeof e?(r={},t?r[t]=e:r.milliseconds=e" - "):(a=Ci.exec(e))?(n=\"-\"===a[1]?-1:1,r={y:0,d:w(a[Ks])*n,h:w(a[ei])*n,m:w(a" - "[ti])*n,s:w(a[ni])*n,ms:w(kt(1e3*a[si]))*n}):(a=Fi.exec(e))?(n=\"-\"===a[1]?" - "-1:1,r={y:jt(a[2],n),M:jt(a[3],n),w:jt(a[4],n),d:jt(a[5],n),h:jt(a[6],n),m:j" - "t(a[7],n),s:jt(a[8],n)}):null==r?r={}:\"object\"==typeof r&&(\"from\"in r||\"" - "to\"in r)&&(i=Nt(gt(r.from),gt(r.to)),r={},r.ms=i.milliseconds,r.M=i.months)" - ",s=new St(r),Mt(e)&&o(e,\"_locale\")&&(s._locale=e._locale),s}function jt(e," - "t){var n=e&&parseFloat(e.replace(\",\",\".\"));return(isNaN(n)?0:n)*t}functi" - "on At(e,t){var n={milliseconds:0,months:0};return n.months=t.month()-e.month" - "()+12*(t.year()-e.year()),e.clone().add(n.months,\"M\").isAfter(t)&&--n.mont" - "hs,n.milliseconds=+t-+e.clone().add(n.months,\"M\"),n}function Nt(e,t){var n" - ";return e.isValid()&&t.isValid()?(t=Ot(t,e),e.isBefore(t)?n=At(e,t):(n=At(t," - "e),n.milliseconds=-n.milliseconds,n.months=-n.months),n):{milliseconds:0,mon" - "ths:0}}function Et(e,t){return function(n,s){var i,r;return null===s||isNaN(" - "+s)||(k(t,\"moment().\"+t+\"(period, number) is deprecated. Please use momen" - "t().\"+t+\"(number, period). See http://momentjs.com/guides/#/warnings/add-i" - "nverted-param/ for more info.\"),r=n,n=s,s=r),n=\"string\"==typeof n?+n:n,i=" - "Vt(n,s),It(this,i,e),this}}function It(t,n,s,i){var r=n._milliseconds,a=kt(n" - "._days),o=kt(n._months);t.isValid()&&(i=null==i||i,r&&t._d.setTime(t._d.valu" - "eOf()+r*s),a&&A(t,\"Date\",j(t,\"Date\")+a*s),o&&ue(t,j(t,\"Month\")+o*s),i&" - "&e.updateOffset(t,a||o))}function zt(e,t){var n=e.diff(t,\"days\",!0);return" - " n<-6?\"sameElse\":n<-1?\"lastWeek\":n<0?\"lastDay\":n<1?\"sameDay\":n<2?\"n" - "extDay\":n<7?\"nextWeek\":\"sameElse\"}function Zt(t,n){var s=t||gt(),i=Ot(s" - ",this).startOf(\"day\"),r=e.calendarFormat(this,i)||\"sameElse\",a=n&&(D(n[r" - "])?n[r].call(this,s):n[r]);return this.format(a||this.localeData().calendar(" - "r,this,gt(s)))}function qt(){return new y(this)}function $t(e,t){var n=g(e)?" - "e:gt(e);return!(!this.isValid()||!n.isValid())&&(t=F(m(t)?\"millisecond\":t)" - ",\"millisecond\"===t?this.valueOf()>n.valueOf():n.valueOf()r&&(t=r),Pn.call(this,e,t,n,s,i))}function P" - "n(e,t,n,s,i){var r=ve(e,t,n,s,i),a=pe(r.year,0,r.dayOfYear);return this.year" - "(a.getUTCFullYear()),this.month(a.getUTCMonth()),this.date(a.getUTCDate()),t" - "his}function Wn(e){return null==e?Math.ceil((this.month()+1)/3):this.month(3" - "*(e-1)+this.month()%3)}function Rn(e){var t=Math.round((this.clone().startOf" - "(\"day\")-this.clone().startOf(\"year\"))/864e5)+1;return null==e?t:this.add" - "(e-t,\"d\")}function Un(e,t){t[si]=w(1e3*(\"0.\"+e))}function Cn(){return th" - "is._isUTC?\"UTC\":\"\"}function Fn(){return this._isUTC?\"Coordinated Univer" - "sal Time\":\"\"}function Hn(e){return gt(1e3*e)}function Ln(){return gt.appl" - "y(null,arguments).parseZone()}function Gn(e){return e}function Vn(e,t,n,s){v" - "ar i=et(),r=d().set(s,t);return i[n](r,e)}function jn(e,t,n){if(\"number\"==" - "typeof e&&(t=e,e=void 0),e=e||\"\",null!=t)return Vn(e,t,n,\"month\");var s," - "i=[];for(s=0;s<12;s++)i[s]=Vn(e,s,n,\"month\");return i}function An(e,t,n,s)" - "{\"boolean\"==typeof e?(\"number\"==typeof t&&(n=t,t=void 0),t=t||\"\"):(t=e" - ",n=t,e=!1,\"number\"==typeof t&&(n=t,t=void 0),t=t||\"\");var i=et(),r=e?i._" - "week.dow:0;if(null!=n)return Vn(t,(n+r)%7,s,\"day\");var a,o=[];for(a=0;a<7;" - "a++)o[a]=Vn(t,(a+r)%7,s,\"day\");return o}function Nn(e,t){return jn(e,t,\"m" - "onths\")}function En(e,t){return jn(e,t,\"monthsShort\")}function In(e,t,n){" - "return An(e,t,n,\"weekdays\")}function zn(e,t,n){return An(e,t,n,\"weekdaysS" - "hort\")}function Zn(e,t,n){return An(e,t,n,\"weekdaysMin\");\n" - "}function qn(){var e=this._data;return this._milliseconds=qi(this._milliseco" - "nds),this._days=qi(this._days),this._months=qi(this._months),e.milliseconds=" - "qi(e.milliseconds),e.seconds=qi(e.seconds),e.minutes=qi(e.minutes),e.hours=q" - "i(e.hours),e.months=qi(e.months),e.years=qi(e.years),this}function $n(e,t,n," - "s){var i=Vt(t,n);return e._milliseconds+=s*i._milliseconds,e._days+=s*i._day" - "s,e._months+=s*i._months,e._bubble()}function Bn(e,t){return $n(this,e,t,1)}" - "function Jn(e,t){return $n(this,e,t,-1)}function Qn(e){return e<0?Math.floor" - "(e):Math.ceil(e)}function Xn(){var e,t,n,s,i,r=this._milliseconds,a=this._da" - "ys,o=this._months,u=this._data;return r>=0&&a>=0&&o>=0||r<=0&&a<=0&&o<=0||(r" - "+=864e5*Qn(es(o)+a),a=0,o=0),u.milliseconds=r%1e3,e=p(r/1e3),u.seconds=e%60," - "t=p(e/60),u.minutes=t%60,n=p(t/60),u.hours=n%24,a+=p(n/24),i=p(Kn(a)),o+=i,a" - "-=Qn(es(i)),s=p(o/12),o%=12,u.days=a,u.months=o,u.years=s,this}function Kn(e" - "){return 4800*e/146097}function es(e){return 146097*e/4800}function ts(e){va" - "r t,n,s=this._milliseconds;if(e=F(e),\"month\"===e||\"year\"===e)return t=th" - "is._days+s/864e5,n=this._months+Kn(t),\"month\"===e?n:n/12;switch(t=this._da" - "ys+Math.round(es(this._months)),e){case\"week\":return t/7+s/6048e5;case\"da" - "y\":return t+s/864e5;case\"hour\":return 24*t+s/36e5;case\"minute\":return 1" - "440*t+s/6e4;case\"second\":return 86400*t+s/1e3;case\"millisecond\":return M" - "ath.floor(864e5*t)+s;default:throw new Error(\"Unknown unit \"+e)}}function " - "ns(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536" - "e6*w(this._months/12)}function ss(e){return function(){return this.as(e)}}fu" - "nction is(e){return e=F(e),this[e+\"s\"]()}function rs(e){return function(){" - "return this._data[e]}}function as(){return p(this.days()/7)}function os(e,t," - "n,s,i){return i.relativeTime(t||1,!!n,e,s)}function us(e,t,n){var s=Vt(e).ab" - "s(),i=dr(s.as(\"s\")),r=dr(s.as(\"m\")),a=dr(s.as(\"h\")),o=dr(s.as(\"d\"))," - "u=dr(s.as(\"M\")),d=dr(s.as(\"y\")),l=i0,l[4]=n,os.apply(null,l)}function ds(e){return void 0===e?d" - "r:\"function\"==typeof e&&(dr=e,!0)}function ls(e,t){return void 0!==lr[e]&&" - "(void 0===t?lr[e]:(lr[e]=t,!0))}function hs(e){var t=this.localeData(),n=us(" - "this,!e,t);return e&&(n=t.pastFuture(+this,n)),t.postformat(n)}function cs()" - "{var e,t,n,s=hr(this._milliseconds)/1e3,i=hr(this._days),r=hr(this._months);" - "e=p(s/60),t=p(e/60),s%=60,e%=60,n=p(r/12),r%=12;var a=n,o=r,u=i,d=t,l=e,h=s," - "c=this.asSeconds();return c?(c<0?\"-\":\"\")+\"P\"+(a?a+\"Y\":\"\")+(o?o+\"M" - "\":\"\")+(u?u+\"D\":\"\")+(d||l||h?\"T\":\"\")+(d?d+\"H\":\"\")+(l?l+\"M\":\"" - "\")+(h?h+\"S\":\"\"):\"P0D\"}var fs,ms;ms=Array.prototype.some?Array.prototy" - "pe.some:function(e){for(var t=Object(this),n=t.length>>>0,s=0;s68?1900:2e3" - ")};var hi=V(\"FullYear\",!0);z(\"w\",[\"ww\",2],\"wo\",\"week\"),z(\"W\",[\"" - "WW\",2],\"Wo\",\"isoWeek\"),C(\"week\",\"w\"),C(\"isoWeek\",\"W\"),L(\"week\"" - ",5),L(\"isoWeek\",5),J(\"w\",Ls),J(\"ww\",Ls,Us),J(\"W\",Ls),J(\"WW\",Ls,Us)" - ",te([\"w\",\"ww\",\"W\",\"WW\"],function(e,t,n,s){t[s.substr(0,1)]=w(e)});va" - "r ci={dow:0,doy:6};z(\"d\",0,\"do\",\"day\"),z(\"dd\",0,0,function(e){return" - " this.localeData().weekdaysMin(this,e)}),z(\"ddd\",0,0,function(e){return th" - "is.localeData().weekdaysShort(this,e)}),z(\"dddd\",0,0,function(e){return th" - "is.localeData().weekdays(this,e)}),z(\"e\",0,0,\"weekday\"),z(\"E\",0,0,\"is" - "oWeekday\"),C(\"day\",\"d\"),C(\"weekday\",\"e\"),C(\"isoWeekday\",\"E\"),L(" - "\"day\",11),L(\"weekday\",11),L(\"isoWeekday\",11),J(\"d\",Ls),J(\"e\",Ls),J" - "(\"E\",Ls),J(\"dd\",function(e,t){return t.weekdaysMinRegex(e)}),J(\"ddd\",f" - "unction(e,t){return t.weekdaysShortRegex(e)}),J(\"dddd\",function(e,t){retur" - "n t.weekdaysRegex(e)}),te([\"dd\",\"ddd\",\"dddd\"],function(e,t,n,s){var i=" - "n._locale.weekdaysParse(e,s,n._strict);null!=i?t.d=i:h(n).invalidWeekday=e})" - ",te([\"d\",\"e\",\"E\"],function(e,t,n,s){t[s]=w(e)});var fi=\"Sunday_Monday" - "_Tuesday_Wednesday_Thursday_Friday_Saturday\".split(\"_\"),mi=\"Sun_Mon_Tue_" - "Wed_Thu_Fri_Sat\".split(\"_\"),_i=\"Su_Mo_Tu_We_Th_Fr_Sa\".split(\"_\"),yi=$" - "s,gi=$s,pi=$s;z(\"H\",[\"HH\",2],0,\"hour\"),z(\"h\",[\"hh\",2],0,Ne),z(\"k\"" - ",[\"kk\",2],0,Ee),z(\"hmm\",0,0,function(){return\"\"+Ne.apply(this)+I(this." - "minutes(),2)}),z(\"hmmss\",0,0,function(){return\"\"+Ne.apply(this)+I(this.m" - "inutes(),2)+I(this.seconds(),2)}),z(\"Hmm\",0,0,function(){return\"\"+this.h" - "ours()+I(this.minutes(),2)}),z(\"Hmmss\",0,0,function(){return\"\"+this.hour" - "s()+I(this.minutes(),2)+I(this.seconds(),2)}),Ie(\"a\",!0),Ie(\"A\",!1),C(\"" - "hour\",\"h\"),L(\"hour\",13),J(\"a\",ze),J(\"A\",ze),J(\"H\",Ls),J(\"h\",Ls)" - ",J(\"HH\",Ls,Us),J(\"hh\",Ls,Us),J(\"hmm\",Gs),J(\"hmmss\",Vs),J(\"Hmm\",Gs)" - ",J(\"Hmmss\",Vs),ee([\"H\",\"HH\"],ei),ee([\"a\",\"A\"],function(e,t,n){n._i" - "sPm=n._locale.isPM(e),n._meridiem=e}),ee([\"h\",\"hh\"],function(e,t,n){t[ei" - "]=w(e),h(n).bigHour=!0}),ee(\"hmm\",function(e,t,n){var s=e.length-2;t[ei]=w" - "(e.substr(0,s)),t[ti]=w(e.substr(s)),h(n).bigHour=!0}),ee(\"hmmss\",function" - "(e,t,n){var s=e.length-4,i=e.length-2;t[ei]=w(e.substr(0,s)),t[ti]=w(e.subst" - "r(s,2)),t[ni]=w(e.substr(i)),h(n).bigHour=!0}),ee(\"Hmm\",function(e,t,n){va" - "r s=e.length-2;t[ei]=w(e.substr(0,s)),t[ti]=w(e.substr(s))}),ee(\"Hmmss\",fu" - "nction(e,t,n){var s=e.length-4,i=e.length-2;t[ei]=w(e.substr(0,s)),t[ti]=w(e" - ".substr(s,2)),t[ni]=w(e.substr(i))});var wi,vi=/[ap]\\.?m?\\.?/i,Si=V(\"Hour" - "s\",!0),Mi={calendar:vs,longDateFormat:Ss,invalidDate:Ms,ordinal:ks,ordinalP" - "arse:Ds,relativeTime:Ys,months:oi,monthsShort:ui,week:ci,weekdays:fi,weekday" - "sMin:_i,weekdaysShort:mi,meridiemParse:vi},ki={},Di=/^\\s*((?:[+-]\\d{6}|\\d" - "{4})-(?:\\d\\d-\\d\\d|W\\d\\d-\\d|W\\d\\d|\\d\\d\\d|\\d\\d))(?:(T| )(\\d\\d(" - "?::\\d\\d(?::\\d\\d(?:[.,]\\d+)?)?)?)([\\+\\-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?/" - ",Yi=/^\\s*((?:[+-]\\d{6}|\\d{4})(?:\\d\\d\\d\\d|W\\d\\d\\d|W\\d\\d|\\d\\d\\d" - "|\\d\\d))(?:(T| )(\\d\\d(?:\\d\\d(?:\\d\\d(?:[.,]\\d+)?)?)?)([\\+\\-]\\d\\d(" - "?::?\\d\\d)?|\\s*Z)?)?/,Oi=/Z|[+-]\\d\\d(?::?\\d\\d)?/,xi=[[\"YYYYYY-MM-DD\"" - ",/[+-]\\d{6}-\\d\\d-\\d\\d/],[\"YYYY-MM-DD\",/\\d{4}-\\d\\d-\\d\\d/],[\"GGGG" - "-[W]WW-E\",/\\d{4}-W\\d\\d-\\d/],[\"GGGG-[W]WW\",/\\d{4}-W\\d\\d/,!1],[\"YYY" - "Y-DDD\",/\\d{4}-\\d{3}/],[\"YYYY-MM\",/\\d{4}-\\d\\d/,!1],[\"YYYYYYMMDD\",/[" - "+-]\\d{10}/],[\"YYYYMMDD\",/\\d{8}/],[\"GGGG[W]WWE\",/\\d{4}W\\d{3}/],[\"GGG" - "G[W]WW\",/\\d{4}W\\d{2}/,!1],[\"YYYYDDD\",/\\d{7}/]],bi=[[\"HH:mm:ss.SSSS\"," - "/\\d\\d:\\d\\d:\\d\\d\\.\\d+/],[\"HH:mm:ss,SSSS\",/\\d\\d:\\d\\d:\\d\\d,\\d+" - "/],[\"HH:mm:ss\",/\\d\\d:\\d\\d:\\d\\d/],[\"HH:mm\",/\\d\\d:\\d\\d/],[\"HHmm" - "ss.SSSS\",/\\d\\d\\d\\d\\d\\d\\.\\d+/],[\"HHmmss,SSSS\",/\\d\\d\\d\\d\\d\\d," - "\\d+/],[\"HHmmss\",/\\d\\d\\d\\d\\d\\d/],[\"HHmm\",/\\d\\d\\d\\d/],[\"HH\",/" - "\\d\\d/]],Ti=/^\\/?Date\\((\\-?\\d+)/i;e.createFromInputFallback=M(\"value p" - "rovided is not in a recognized ISO format. moment construction falls back to" - " js Date(), which is not reliable across all browsers and versions. Non ISO " - "date formats are discouraged and will be removed in an upcoming major releas" - "e. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more i" - "nfo.\",function(e){e._d=new Date(e._i+(e._useUTC?\" UTC\":\"\"))}),e.ISO_860" - "1=function(){};var Pi=M(\"moment().min is deprecated, use moment.max instead" - ". http://momentjs.com/guides/#/warnings/min-max/\",function(){var e=gt.apply" - "(null,arguments);return this.isValid()&&e.isValid()?ethis?this:e:f()}),Ri=function(){return Date.n" - "ow?Date.now():+new Date};Dt(\"Z\",\":\"),Dt(\"ZZ\",\"\"),J(\"Z\",Zs),J(\"ZZ\"" - ",Zs),ee([\"Z\",\"ZZ\"],function(e,t,n){n._useUTC=!0,n._tzm=Yt(Zs,e)});var Ui" - "=/([\\+\\-]|\\d\\d)/gi;e.updateOffset=function(){};var Ci=/^(\\-)?(?:(\\d*)[" - ". ])?(\\d+)\\:(\\d+)(?:\\:(\\d+)(\\.\\d*)?)?$/,Fi=/^(-)?P(?:(-?[0-9,.]*)Y)?(" - "?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(" - "?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;Vt.fn=St.prototype;var Hi=Et(1,\"add" - "\"),Li=Et(-1,\"subtract\");e.defaultFormat=\"YYYY-MM-DDTHH:mm:ssZ\",e.defaul" - "tFormatUtc=\"YYYY-MM-DDTHH:mm:ss[Z]\";var Gi=M(\"moment().lang() is deprecat" - "ed. Instead, use moment().localeData() to get the language configuration. Us" - "e moment().locale() to change languages.\",function(e){return void 0===e?thi" - "s.localeData():this.locale(e)});z(0,[\"gg\",2],0,function(){return this.week" - "Year()%100}),z(0,[\"GG\",2],0,function(){return this.isoWeekYear()%100}),Dn(" - "\"gggg\",\"weekYear\"),Dn(\"ggggg\",\"weekYear\"),Dn(\"GGGG\",\"isoWeekYear\"" - "),Dn(\"GGGGG\",\"isoWeekYear\"),C(\"weekYear\",\"gg\"),C(\"isoWeekYear\",\"G" - "G\"),L(\"weekYear\",1),L(\"isoWeekYear\",1),J(\"G\",Is),J(\"g\",Is),J(\"GG\"" - ",Ls,Us),J(\"gg\",Ls,Us),J(\"GGGG\",As,Fs),J(\"gggg\",As,Fs),J(\"GGGGG\",Ns,H" - "s),J(\"ggggg\",Ns,Hs),te([\"gggg\",\"ggggg\",\"GGGG\",\"GGGGG\"],function(e," - "t,n,s){t[s.substr(0,2)]=w(e)}),te([\"gg\",\"GG\"],function(t,n,s,i){n[i]=e.p" - "arseTwoDigitYear(t)}),z(\"Q\",0,\"Qo\",\"quarter\"),C(\"quarter\",\"Q\"),L(\"" - "quarter\",7),J(\"Q\",Rs),ee(\"Q\",function(e,t){t[Xs]=3*(w(e)-1)}),z(\"D\",[" - "\"DD\",2],\"Do\",\"date\"),C(\"date\",\"D\"),L(\"date\",9),J(\"D\",Ls),J(\"D" - "D\",Ls,Us),J(\"Do\",function(e,t){return e?t._ordinalParse:t._ordinalParseLe" - "nient}),ee([\"D\",\"DD\"],Ks),ee(\"Do\",function(e,t){t[Ks]=w(e.match(Ls)[0]" - ",10)});var Vi=V(\"Date\",!0);z(\"DDD\",[\"DDDD\",3],\"DDDo\",\"dayOfYear\")," - "C(\"dayOfYear\",\"DDD\"),L(\"dayOfYear\",4),J(\"DDD\",js),J(\"DDDD\",Cs),ee(" - "[\"DDD\",\"DDDD\"],function(e,t,n){n._dayOfYear=w(e)}),z(\"m\",[\"mm\",2],0," - "\"minute\"),C(\"minute\",\"m\"),L(\"minute\",14),J(\"m\",Ls),J(\"mm\",Ls,Us)" - ",ee([\"m\",\"mm\"],ti);var ji=V(\"Minutes\",!1);z(\"s\",[\"ss\",2],0,\"secon" - "d\"),C(\"second\",\"s\"),L(\"second\",15),J(\"s\",Ls),J(\"ss\",Ls,Us),ee([\"" - "s\",\"ss\"],ni);var Ai=V(\"Seconds\",!1);z(\"S\",0,0,function(){return~~(thi" - "s.millisecond()/100)}),z(0,[\"SS\",2],0,function(){return~~(this.millisecond" - "()/10)}),z(0,[\"SSS\",3],0,\"millisecond\"),z(0,[\"SSSS\",4],0,function(){re" - "turn 10*this.millisecond()}),z(0,[\"SSSSS\",5],0,function(){return 100*this." - "millisecond()}),z(0,[\"SSSSSS\",6],0,function(){return 1e3*this.millisecond(" - ")}),z(0,[\"SSSSSSS\",7],0,function(){return 1e4*this.millisecond()}),z(0,[\"" - "SSSSSSSS\",8],0,function(){return 1e5*this.millisecond()}),z(0,[\"SSSSSSSSS\"" - ",9],0,function(){return 1e6*this.millisecond()}),C(\"millisecond\",\"ms\"),L" - "(\"millisecond\",16),J(\"S\",js,Rs),J(\"SS\",js,Us),J(\"SSS\",js,Cs);var Ni;" - "for(Ni=\"SSSS\";Ni.length<=9;Ni+=\"S\")J(Ni,Es);for(Ni=\"S\";Ni.length<=9;Ni" - "+=\"S\")ee(Ni,Un);var Ei=V(\"Milliseconds\",!1);z(\"z\",0,0,\"zoneAbbr\"),z(" - "\"zz\",0,0,\"zoneName\");var Ii=y.prototype;Ii.add=Hi,Ii.calendar=Zt,Ii.clon" - "e=qt,Ii.diff=en,Ii.endOf=fn,Ii.format=rn,Ii.from=an,Ii.fromNow=on,Ii.to=un,I" - "i.toNow=dn,Ii.get=N,Ii.invalidAt=Mn,Ii.isAfter=$t,Ii.isBefore=Bt,Ii.isBetwee" - "n=Jt,Ii.isSame=Qt,Ii.isSameOrAfter=Xt,Ii.isSameOrBefore=Kt,Ii.isValid=vn,Ii." - "lang=Gi,Ii.locale=ln,Ii.localeData=hn,Ii.max=Wi,Ii.min=Pi,Ii.parsingFlags=Sn" - ",Ii.set=E,Ii.startOf=cn,Ii.subtract=Li,Ii.toArray=gn,Ii.toObject=pn,Ii.toDat" - "e=yn,Ii.toISOString=sn,Ii.toJSON=wn,Ii.toString=nn,Ii.unix=_n,Ii.valueOf=mn," - "Ii.creationData=kn,Ii.year=hi,Ii.isLeapYear=ye,Ii.weekYear=Yn,Ii.isoWeekYear" - "=On,Ii.quarter=Ii.quarters=Wn,Ii.month=de,Ii.daysInMonth=le,Ii.week=Ii.weeks" - "=Oe,Ii.isoWeek=Ii.isoWeeks=xe,Ii.weeksInYear=bn,Ii.isoWeeksInYear=xn,Ii.date" - "=Vi,Ii.day=Ii.days=Fe,Ii.weekday=He,Ii.isoWeekday=Le,Ii.dayOfYear=Rn,Ii.hour" - "=Ii.hours=Si,Ii.minute=Ii.minutes=ji,Ii.second=Ii.seconds=Ai,Ii.millisecond=" - "Ii.milliseconds=Ei,Ii.utcOffset=bt,Ii.utc=Pt,Ii.local=Wt,Ii.parseZone=Rt,Ii." - "hasAlignedHourOffset=Ut,Ii.isDST=Ct,Ii.isLocal=Ht,Ii.isUtcOffset=Lt,Ii.isUtc" - "=Gt,Ii.isUTC=Gt,Ii.zoneAbbr=Cn,Ii.zoneName=Fn,Ii.dates=M(\"dates accessor is" - " deprecated. Use date instead.\",Vi),Ii.months=M(\"months accessor is deprec" - "ated. Use month instead\",de),Ii.years=M(\"years accessor is deprecated. Use" - " year instead\",hi),Ii.zone=M(\"moment().zone is deprecated, use moment().ut" - "cOffset instead. http://momentjs.com/guides/#/warnings/zone/\",Tt),Ii.isDSTS" - "hifted=M(\"isDSTShifted is deprecated. See http://momentjs.com/guides/#/warn" - "ings/dst-shifted/ for more information\",Ft);var zi=Ii,Zi=x.prototype;Zi.cal" - "endar=b,Zi.longDateFormat=T,Zi.invalidDate=P,Zi.ordinal=W,Zi.preparse=Gn,Zi." - "postformat=Gn,Zi.relativeTime=R,Zi.pastFuture=U,Zi.set=Y,Zi.months=ie,Zi.mon" - "thsShort=re,Zi.monthsParse=oe,Zi.monthsRegex=ce,Zi.monthsShortRegex=he,Zi.we" - "ek=ke,Zi.firstDayOfYear=Ye,Zi.firstDayOfWeek=De,Zi.weekdays=Pe,Zi.weekdaysMi" - "n=Re,Zi.weekdaysShort=We,Zi.weekdaysParse=Ce,Zi.weekdaysRegex=Ge,Zi.weekdays" - "ShortRegex=Ve,Zi.weekdaysMinRegex=je,Zi.isPM=Ze,Zi.meridiem=qe,Qe(\"en\",{or" - "dinalParse:/\\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10,n=1===w(e%" - "100/10)?\"th\":1===t?\"st\":2===t?\"nd\":3===t?\"rd\":\"th\";return e+n}}),e" - ".lang=M(\"moment.lang is deprecated. Use moment.locale instead.\",Qe),e.lang" - "Data=M(\"moment.langData is deprecated. Use moment.localeData instead.\",et)" - ";var qi=Math.abs,$i=ss(\"ms\"),Bi=ss(\"s\"),Ji=ss(\"m\"),Qi=ss(\"h\"),Xi=ss(" - "\"d\"),Ki=ss(\"w\"),er=ss(\"M\"),tr=ss(\"y\"),nr=rs(\"milliseconds\"),sr=rs(" - "\"seconds\"),ir=rs(\"minutes\"),rr=rs(\"hours\"),ar=rs(\"days\"),or=rs(\"mon" - "ths\"),ur=rs(\"years\"),dr=Math.round,lr={s:45,m:45,h:22,d:26,M:11},hr=Math." - "abs,cr=St.prototype;cr.abs=qn,cr.add=Bn,cr.subtract=Jn,cr.as=ts,cr.asMillise" - "conds=$i,cr.asSeconds=Bi,cr.asMinutes=Ji,cr.asHours=Qi,cr.asDays=Xi,cr.asWee" - "ks=Ki,cr.asMonths=er,cr.asYears=tr,cr.valueOf=ns,cr._bubble=Xn,cr.get=is,cr." - "milliseconds=nr,cr.seconds=sr,cr.minutes=ir,cr.hours=rr,cr.days=ar,cr.weeks=" - "as,cr.months=or,cr.years=ur,cr.humanize=hs,cr.toISOString=cs,cr.toString=cs," - "cr.toJSON=cs,cr.locale=ln,cr.localeData=hn,cr.toIsoString=M(\"toIsoString() " - "is deprecated. Please use toISOString() instead (notice the capitals)\",cs)," - "cr.lang=Gi,z(\"X\",0,0,\"unix\"),z(\"x\",0,0,\"valueOf\"),J(\"x\",Is),J(\"X\"" - ",qs),ee(\"X\",function(e,t,n){n._d=new Date(1e3*parseFloat(e,10))}),ee(\"x\"" - ",function(e,t,n){n._d=new Date(w(e))}),e.version=\"2.15.0\",t(gt),e.fn=zi,e." - "min=wt,e.max=vt,e.now=Ri,e.utc=d,e.unix=Hn,e.months=Nn,e.isDate=r,e.locale=Q" - "e,e.invalid=f,e.duration=Vt,e.isMoment=g,e.weekdays=In,e.parseZone=Ln,e.loca" - "leData=et,e.isDuration=Mt,e.monthsShort=En,e.weekdaysMin=Zn,e.defineLocale=X" - "e,e.updateLocale=Ke,e.locales=tt,e.weekdaysShort=zn,e.normalizeUnits=F,e.rel" - "ativeTimeRounding=ds,e.relativeTimeThreshold=ls,e.calendarFormat=zt,e.protot" - "ype=zi;var fr=e;return fr});\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/virtual-dom/create-element.js\":[function(require,module,ex" - "ports){\n" - "var createElement=require(\"./vdom/create-element.js\");module.exports=creat" - "eElement;\n" - "},{\"./vdom/create-element.js\":\"/rethinkdb/build/external/admin-deps_2.0.4" - "/node_modules/rethinkdb-webui/node_modules/virtual-dom/vdom/create-element.j" - "s\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/virtual-dom/diff.js\":[function(require,module,exports){\n" - "var diff=require(\"./vtree/diff.js\");module.exports=diff;\n" - "},{\"./vtree/diff.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_mod" - "ules/rethinkdb-webui/node_modules/virtual-dom/vtree/diff.js\"}],\"/rethinkdb" - "/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/v" - "irtual-dom/h.js\":[function(require,module,exports){\n" - "var h=require(\"./virtual-hyperscript/index.js\");module.exports=h;\n" - "},{\"./virtual-hyperscript/index.js\":\"/rethinkdb/build/external/admin-deps" - "_2.0.4/node_modules/rethinkdb-webui/node_modules/virtual-dom/virtual-hypersc" - "ript/index.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/" - "rethinkdb-webui/node_modules/virtual-dom/node_modules/browser-split/index.js" - "\":[function(require,module,exports){\n" - "module.exports=function(e){var t,n=String.prototype.split,l=/()??" "/.exec(\"" - "\")[1]===e;return t=function(t,r,i){if(\"[object RegExp]\"!==Object.prototyp" - "e.toString.call(r))return n.call(t,r,i);var o,p,s,c,g=[],u=(r.ignoreCase?\"i" - "\":\"\")+(r.multiline?\"m\":\"\")+(r.extended?\"x\":\"\")+(r.sticky?\"y\":\"" - "\"),x=0,r=new RegExp(r.source,u+\"g\");for(t+=\"\",l||(o=new RegExp(\"^\"+r." - "source+\"$(?!\\\\s)\",u)),i=i===e?-1>>>0:i>>>0;(p=r.exec(t))&&(s=p.index+p[0" - "].length,!(s>x&&(g.push(t.slice(x,p.index)),!l&&p.length>1&&p[0].replace(o,f" - "unction(){for(var t=1;t1&&p.index=i)));)r.lastIndex===p.index&&r.lastIndex++;return x==" - "=t.length?!c&&r.test(\"\")||g.push(\"\"):g.push(t.slice(x)),g.length>i?g.sli" - "ce(0,i):g}}();\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/virtual-dom/node_modules/ev-store/index.js\":[function(requ" - "ire,module,exports){\n" - "\"use strict\";function EvStore(e){var r=e[hashKey];return r||(r=e[hashKey]=" - "{}),r}var OneVersionConstraint=require(\"individual/one-version\"),MY_VERSIO" - "N=\"7\";OneVersionConstraint(\"ev-store\",MY_VERSION);var hashKey=\"__EV_STO" - "RE_KEY@\"+MY_VERSION;module.exports=EvStore;\n" - "},{\"individual/one-version\":\"/rethinkdb/build/external/admin-deps_2.0.4/n" - "ode_modules/rethinkdb-webui/node_modules/virtual-dom/node_modules/ev-store/n" - "ode_modules/individual/one-version.js\"}],\"/rethinkdb/build/external/admin-" - "deps_2.0.4/node_modules/rethinkdb-webui/node_modules/virtual-dom/node_module" - "s/ev-store/node_modules/individual/index.js\":[function(require,module,expor" - "ts){\n" - "(function (global){\n" - "\"use strict\";function Individual(o,n){return o in root?root[o]:(root[o]=n," - "n)}var root=\"undefined\"!=typeof window?window:\"undefined\"!=typeof global" - "?global:{};module.exports=Individual;\n" - "}).call(this,typeof global !== \"undefined\" ? global : typeof self !== \"un" - "defined\" ? self : typeof window !== \"undefined\" ? window : {})\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/virtual-dom/node_modules/ev-store/node_modules/individual/o" - "ne-version.js\":[function(require,module,exports){\n" - "\"use strict\";function OneVersion(n,e,i){var r=\"__INDIVIDUAL_ONE_VERSION_\"" - "+n,o=r+\"_ENFORCE_SINGLETON\",a=Individual(o,e);if(a!==e)throw new Error(\"C" - "an only have one copy of \"+n+\".\\nYou already have version \"+a+\" install" - "ed.\\nThis means you cannot install version \"+e);return Individual(r,i)}var" - " Individual=require(\"./index.js\");module.exports=OneVersion;\n" - "},{\"./index.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/" - "rethinkdb-webui/node_modules/virtual-dom/node_modules/ev-store/node_modules/" - "individual/index.js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_mo" - "dules/rethinkdb-webui/node_modules/virtual-dom/node_modules/global/document." - "js\":[function(require,module,exports){\n" - "(function (global){\n" - "var topLevel=\"undefined\"!=typeof global?global:\"undefined\"!=typeof windo" - "w?window:{},minDoc=require(\"min-document\");if(\"undefined\"!=typeof docume" - "nt)module.exports=document;else{var doccy=topLevel[\"__GLOBAL_DOCUMENT_CACHE" - "@4\"];doccy||(doccy=topLevel[\"__GLOBAL_DOCUMENT_CACHE@4\"]=minDoc),module.e" - "xports=doccy}\n" - "}).call(this,typeof global !== \"undefined\" ? global : typeof self !== \"un" - "defined\" ? self : typeof window !== \"undefined\" ? window : {})\n" - "},{\"min-document\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_module" - "s/rethinkdb-webui/node_modules/browserify/node_modules/browser-resolve/empty" - ".js\"}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-" - "webui/node_modules/virtual-dom/node_modules/is-object/index.js\":[function(r" - "equire,module,exports){\n" - "\"use strict\";module.exports=function(t){return\"object\"==typeof t&&null!=" - "=t};\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/virtual-dom/node_modules/x-is-array/index.js\":[function(re" - "quire,module,exports){\n" - "function isArray(r){return\"[object Array]\"===toString.call(r)}var nativeIs" - "Array=Array.isArray,toString=Object.prototype.toString;module.exports=native" - "IsArray||isArray;\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/virtual-dom/patch.js\":[function(require,module,exports){\n" - "var patch=require(\"./vdom/patch.js\");module.exports=patch;\n" - "},{\"./vdom/patch.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_mod" - "ules/rethinkdb-webui/node_modules/virtual-dom/vdom/patch.js\"}],\"/rethinkdb" - "/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/v" - "irtual-dom/vdom/apply-properties.js\":[function(require,module,exports){\n" - "function applyProperties(o,t,e){for(var r in t){var i=t[r];void 0===i?remove" - "Property(o,r,i,e):isHook(i)?(removeProperty(o,r,i,e),i.hook&&i.hook(o,r,e?e[" - "r]:void 0)):isObject(i)?patchObject(o,t,e,r,i):o[r]=i}}function removeProper" - "ty(o,t,e,r){if(r){var i=r[t];if(isHook(i))i.unhook&&i.unhook(o,t,e);else if(" - "\"attributes\"===t)for(var v in i)o.removeAttribute(v);else if(\"style\"===t" - ")for(var s in i)o.style[s]=\"\";else\"string\"==typeof i?o[t]=\"\":o[t]=null" - "}}function patchObject(o,t,e,r,i){var v=e?e[r]:void 0;if(\"attributes\"!==r)" - "{if(v&&isObject(v)&&getPrototype(v)!==getPrototype(i))return void(o[r]=i);is" - "Object(o[r])||(o[r]={});var s=\"style\"===r?\"\":void 0;for(var n in i){var " - "p=i[n];o[r][n]=void 0===p?s:p}}else for(var c in i){var u=i[c];void 0===u?o." - "removeAttribute(c):o.setAttribute(c,u)}}function getPrototype(o){return Obje" - "ct.getPrototypeOf?Object.getPrototypeOf(o):o.__proto__?o.__proto__:o.constru" - "ctor?o.constructor.prototype:void 0}var isObject=require(\"is-object\"),isHo" - "ok=require(\"../vnode/is-vhook.js\");module.exports=applyProperties;\n" - "},{\"../vnode/is-vhook.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/nod" - "e_modules/rethinkdb-webui/node_modules/virtual-dom/vnode/is-vhook.js\",\"is-" - "object\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb" - "-webui/node_modules/virtual-dom/node_modules/is-object/index.js\"}],\"/rethi" - "nkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modul" - "es/virtual-dom/vdom/create-element.js\":[function(require,module,exports){\n" - "function createElement(e,r){var t=r?r.document||document:document,n=r?r.warn" - ":null;if(e=handleThunk(e).a,isWidget(e))return e.init();if(isVText(e))return" - " t.createTextNode(e.text);if(!isVNode(e))return n&&n(\"Item is not a valid v" - "irtual dom node\",e),null;var i=null===e.namespace?t.createElement(e.tagName" - "):t.createElementNS(e.namespace,e.tagName),a=e.properties;applyProperties(i," - "a);for(var d=e.children,l=0;l>0,t=n[i],d===u)" - "return t>=e&&t<=r;if(tr))return!0;u=i-1}}return!1}funct" - "ion ascending(n,e){return n>e?1:-1}var noChild={};module.exports=domIndex;\n" - "},{}],\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-we" - "bui/node_modules/virtual-dom/vdom/patch-op.js\":[function(require,module,exp" - "orts){\n" - "function applyPatch(e,r,t){var a=e.type,n=e.vNode,o=e.patch;switch(a){case V" - "Patch.REMOVE:return removeNode(r,n);case VPatch.INSERT:return insertNode(r,o" - ",t);case VPatch.VTEXT:return stringPatch(r,n,o,t);case VPatch.WIDGET:return " - "widgetPatch(r,n,o,t);case VPatch.VNODE:return vNodePatch(r,n,o,t);case VPatc" - "h.ORDER:return reorderChildren(r,o),r;case VPatch.PROPS:return applyProperti" - "es(r,o,n.properties),r;case VPatch.THUNK:return replaceRoot(r,t.patch(r,o,t)" - ");default:return r}}function removeNode(e,r){var t=e.parentNode;return t&&t." - "removeChild(e),destroyWidget(e,r),null}function insertNode(e,r,t){var a=t.re" - "nder(r,t);return e&&e.appendChild(a),e}function stringPatch(e,r,t,a){var n;i" - "f(3===e.nodeType)e.replaceData(0,e.length,t.text),n=e;else{var o=e.parentNod" - "e;n=a.render(t,a),o&&n!==e&&o.replaceChild(n,e)}return n}function widgetPatc" - "h(e,r,t,a){var n,o=updateWidget(r,t);n=o?t.update(r,e)||e:a.render(t,a);var " - "d=e.parentNode;return d&&n!==e&&d.replaceChild(n,e),o||destroyWidget(e,r),n}" - "function vNodePatch(e,r,t,a){var n=e.parentNode,o=a.render(t,a);return n&&o!" - "==e&&n.replaceChild(o,e),o}function destroyWidget(e,r){\"function\"==typeof " - "r.destroy&&isWidget(r)&&r.destroy(e)}function reorderChildren(e,r){for(var t" - ",a,n,o=e.childNodes,d={},i=0;i=c++?null:o[n.to])" - "}function replaceRoot(e,r){return e&&r&&e!==r&&e.parentNode&&e.parentNode.re" - "placeChild(r,e),r}var applyProperties=require(\"./apply-properties\"),isWidg" - "et=require(\"../vnode/is-widget.js\"),VPatch=require(\"../vnode/vpatch.js\")" - ",updateWidget=require(\"./update-widget\");module.exports=applyPatch;\n" - "},{\"../vnode/is-widget.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/no" - "de_modules/rethinkdb-webui/node_modules/virtual-dom/vnode/is-widget.js\",\"." - "./vnode/vpatch.js\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_module" - "s/rethinkdb-webui/node_modules/virtual-dom/vnode/vpatch.js\",\"./apply-prope" - "rties\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-" - "webui/node_modules/virtual-dom/vdom/apply-properties.js\",\"./update-widget\"" - ":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/rethinkdb-webui/n" - "ode_modules/virtual-dom/vdom/update-widget.js\"}],\"/rethinkdb/build/externa" - "l/admin-deps_2.0.4/node_modules/rethinkdb-webui/node_modules/virtual-dom/vdo" - "m/patch.js\":[function(require,module,exports){\n" - "function patch(r,e,t){return t=t||{},t.patch=t.patch&&t.patch!==patch?t.patc" - "h:patchRecursive,t.render=t.render||render,t.patch(r,e,t)}function patchRecu", - 65464) + std::string( - "rsive(r,e,t){var a=patchIndices(e);if(0===a.length)return r;var n=domIndex(r" - ",e.a,a),c=r.ownerDocument;t.document||c===document||(t.document=c);for(var p" - "=0;pu?o:u,d=0;d=h.length?n.length:h[u],P=0;P=p&&o.push(v)}for" - "(var y,V=o.slice(),g=0,T=[],m=[],w=0;w0&&(n=e[e.length-1],!n" - "||\"[object Object]\"!==Object.prototype.toString.call(n)||n instanceof Term" - "Base||(r=n,t=e.slice(0,e.length-1))),function(t,e,r){r.prototype=t.prototype" - ";var n=new r,o=t.apply(n,e);return Object(o)===o?o:n}(Group,[r,this].concat(" - "slice.call(t.map(funcWrap))),function(){})},e.prototype.orderBy=function(){v" - "ar t,e,r,n,o;return r=1<=arguments.length?slice.call(arguments,0):[],n={},e=" - "r,o=r[r.length-1],!o||\"[object Object]\"!==Object.prototype.toString.call(o" - ")||o instanceof TermBase||(n=o,e=r.slice(0,r.length-1)),e=function(){var r,n" - ",o;for(o=[],r=0,n=e.length;r1&&(n=e[e.length-" - "1],!n||\"[object Object]\"!==Object.prototype.toString.call(n)||n instanceof" - " TermBase||(r=n,t=e.slice(0,e.length-1))),function(t,e,r){r.prototype=t.prot" - "otype;var n=new r,o=t.apply(n,e);return Object(o)===o?o:n}(GetAll,[r,this].c" - "oncat(slice.call(t)),function(){})},e.prototype.min=function(){var t,e,r,n;r" - "eturn e=1<=arguments.length?slice.call(arguments,0):[],r={},t=e,1===e.length" - "&&(n=e[0],!n||\"[object Object]\"!==Object.prototype.toString.call(n)||n ins" - "tanceof TermBase||(r=n,t=[])),function(t,e,r){r.prototype=t.prototype;var n=" - "new r,o=t.apply(n,e);return Object(o)===o?o:n}(Min,[r,this].concat(slice.cal" - "l(t.map(funcWrap))),function(){})},e.prototype.max=function(){var t,e,r,n;re" - "turn e=1<=arguments.length?slice.call(arguments,0):[],r={},t=e,1===e.length&" - "&(n=e[0],!n||\"[object Object]\"!==Object.prototype.toString.call(n)||n inst" - "anceof TermBase||(r=n,t=[])),function(t,e,r){r.prototype=t.prototype;var n=n" - "ew r,o=t.apply(n,e);return Object(o)===o?o:n}(Max,[r,this].concat(slice.call" - "(t.map(funcWrap))),function(){})},e.prototype.insert=aropt(function(t,e){ret" - "urn new Insert(e,this,rethinkdb.expr(t))}),e.prototype.setWriteHook=function" - "(){var t;return t=1<=arguments.length?slice.call(arguments,0):[],function(t," - "e,r){r.prototype=t.prototype;var n=new r,o=t.apply(n,e);return Object(o)===o" - "?o:n}(SetWriteHook,[{},this].concat(slice.call(t)),function(){})},e.prototyp" - "e.getWriteHook=function(){return new GetWriteHook({},this)},e.prototype.inde" - "xCreate=varar(1,3,function(t,e,r){return null!=r?new IndexCreate(r,this,t,fu" - "ncWrap(e)):null!=e?\"[object Object]\"!==Object.prototype.toString.call(e)||" - "\"function\"==typeof e||e instanceof TermBase?new IndexCreate({},this,t,func" - "Wrap(e)):new IndexCreate(e,this,t):new IndexCreate({},this,t)}),e.prototype." - "indexDrop=function(){var t;return t=1<=arguments.length?slice.call(arguments" - ",0):[],function(t,e,r){r.prototype=t.prototype;var n=new r,o=t.apply(n,e);re" - "turn Object(o)===o?o:n}(IndexDrop,[{},this].concat(slice.call(t)),function()" - "{})},e.prototype.indexList=function(){var t;return t=1<=arguments.length?sli" - "ce.call(arguments,0):[],function(t,e,r){r.prototype=t.prototype;var n=new r," - "o=t.apply(n,e);return Object(o)===o?o:n}(IndexList,[{},this].concat(slice.ca" - "ll(t)),function(){})},e.prototype.indexStatus=function(){var t;return t=1<=a" - "rguments.length?slice.call(arguments,0):[],function(t,e,r){r.prototype=t.pro" - "totype;var n=new r,o=t.apply(n,e);return Object(o)===o?o:n}(IndexStatus,[{}," - "this].concat(slice.call(t)),function(){})},e.prototype.indexWait=function(){" - "var t;return t=1<=arguments.length?slice.call(arguments,0):[],function(t,e,r" - "){r.prototype=t.prototype;var n=new r,o=t.apply(n,e);return Object(o)===o?o:" - "n}(IndexWait,[{},this].concat(slice.call(t)),function(){})},e.prototype.inde" - "xRename=aropt(function(t,e,r){return new IndexRename(r,this,t,e)}),e.prototy" - "pe.reconfigure=function(t){return new Reconfigure(t,this)},e.prototype.rebal" - "ance=function(){return new Rebalance({},this)},e.prototype.sync=function(){v" - "ar t;return t=1<=arguments.length?slice.call(arguments,0):[],function(t,e,r)" - "{r.prototype=t.prototype;var n=new r,o=t.apply(n,e);return Object(o)===o?o:n" - "}(Sync,[{},this].concat(slice.call(t)),function(){})},e.prototype.grant=func" - "tion(){var t;return t=1<=arguments.length?slice.call(arguments,0):[],functio" - "n(t,e,r){r.prototype=t.prototype;var n=new r,o=t.apply(n,e);return Object(o)" - "===o?o:n}(Grant,[{},this].concat(slice.call(t)),function(){})},e.prototype.t" - "oISO8601=function(){var t;return t=1<=arguments.length?slice.call(arguments," - "0):[],function(t,e,r){r.prototype=t.prototype;var n=new r,o=t.apply(n,e);ret" - "urn Object(o)===o?o:n}(ToISO8601,[{},this].concat(slice.call(t)),function(){" - "})},e.prototype.toEpochTime=function(){var t;return t=1<=arguments.length?sl" - "ice.call(arguments,0):[],function(t,e,r){r.prototype=t.prototype;var n=new r" - ",o=t.apply(n,e);return Object(o)===o?o:n}(ToEpochTime,[{},this].concat(slice" - ".call(t)),function(){})},e.prototype.inTimezone=function(){var t;return t=1<" - "=arguments.length?slice.call(arguments,0):[],function(t,e,r){r.prototype=t.p" - "rototype;var n=new r,o=t.apply(n,e);return Object(o)===o?o:n}(InTimezone,[{}" - ",this].concat(slice.call(t)),function(){})},e.prototype.during=aropt(functio" - "n(t,e,r){return new During(r,this,t,e)}),e.prototype.date=function(){var t;r" - "eturn t=1<=arguments.length?slice.call(arguments,0):[],function(t,e,r){r.pro" - "totype=t.prototype;var n=new r,o=t.apply(n,e);return Object(o)===o?o:n}(RQLD" - "ate,[{},this].concat(slice.call(t)),function(){})},e.prototype.timeOfDay=fun" - "ction(){var t;return t=1<=arguments.length?slice.call(arguments,0):[],functi" - "on(t,e,r){r.prototype=t.prototype;var n=new r,o=t.apply(n,e);return Object(o" - ")===o?o:n}(TimeOfDay,[{},this].concat(slice.call(t)),function(){})},e.protot" - "ype.timezone=function(){var t;return t=1<=arguments.length?slice.call(argume" - "nts,0):[],function(t,e,r){r.prototype=t.prototype;var n=new r,o=t.apply(n,e)" - ";return Object(o)===o?o:n}(Timezone,[{},this].concat(slice.call(t)),function" - "(){})},e.prototype.year=function(){var t;return t=1<=arguments.length?slice." - "call(arguments,0):[],function(t,e,r){r.prototype=t.prototype;var n=new r,o=t" - ".apply(n,e);return Object(o)===o?o:n}(Year,[{},this].concat(slice.call(t)),f" - "unction(){})},e.prototype.month=function(){var t;return t=1<=arguments.lengt" - "h?slice.call(arguments,0):[],function(t,e,r){r.prototype=t.prototype;var n=n" - "ew r,o=t.apply(n,e);return Object(o)===o?o:n}(Month,[{},this].concat(slice.c" - "all(t)),function(){})},e.prototype.day=function(){var t;return t=1<=argument" - "s.length?slice.call(arguments,0):[],function(t,e,r){r.prototype=t.prototype;" - "var n=new r,o=t.apply(n,e);return Object(o)===o?o:n;\n" - "}(Day,[{},this].concat(slice.call(t)),function(){})},e.prototype.dayOfWeek=f" - "unction(){var t;return t=1<=arguments.length?slice.call(arguments,0):[],func" - "tion(t,e,r){r.prototype=t.prototype;var n=new r,o=t.apply(n,e);return Object" - "(o)===o?o:n}(DayOfWeek,[{},this].concat(slice.call(t)),function(){})},e.prot" - "otype.dayOfYear=function(){var t;return t=1<=arguments.length?slice.call(arg" - "uments,0):[],function(t,e,r){r.prototype=t.prototype;var n=new r,o=t.apply(n" - ",e);return Object(o)===o?o:n}(DayOfYear,[{},this].concat(slice.call(t)),func" - "tion(){})},e.prototype.hours=function(){var t;return t=1<=arguments.length?s" - "lice.call(arguments,0):[],function(t,e,r){r.prototype=t.prototype;var n=new " - "r,o=t.apply(n,e);return Object(o)===o?o:n}(Hours,[{},this].concat(slice.call" - "(t)),function(){})},e.prototype.minutes=function(){var t;return t=1<=argumen" - "ts.length?slice.call(arguments,0):[],function(t,e,r){r.prototype=t.prototype" - ";var n=new r,o=t.apply(n,e);return Object(o)===o?o:n}(Minutes,[{},this].conc" - "at(slice.call(t)),function(){})},e.prototype.seconds=function(){var t;return" - " t=1<=arguments.length?slice.call(arguments,0):[],function(t,e,r){r.prototyp" - "e=t.prototype;var n=new r,o=t.apply(n,e);return Object(o)===o?o:n}(Seconds,[" - "{},this].concat(slice.call(t)),function(){})},e.prototype.uuid=function(){va" - "r t;return t=1<=arguments.length?slice.call(arguments,0):[],function(t,e,r){" - "r.prototype=t.prototype;var n=new r,o=t.apply(n,e);return Object(o)===o?o:n}" - "(UUID,[{},this].concat(slice.call(t)),function(){})},e.prototype.getIntersec" - "ting=aropt(function(t,e){return new GetIntersecting(e,this,t)}),e.prototype." - "getNearest=aropt(function(t,e){return new GetNearest(e,this,t)}),e}(TermBase" - "),DatumTerm=function(t){function e(t){var r;return r=e.__super__.constructor" - ".call(this),r.data=t,r}return extend(e,t),e.prototype.args=[],e.prototype.op" - "targs={},e.prototype.compose=function(){switch(typeof this.data){case\"strin" - "g\":return'\"'+this.data+'\"';default:return\"\"+this.data}},e.prototype.bui" - "ld=function(){if(\"number\"==typeof this.data&&!isFinite(this.data))throw ne" - "w TypeError(\"Illegal non-finite number `\"+this.data.toString()+\"`.\");ret" - "urn this.data},e}(RDBVal),translateBackOptargs=function(t){var e,r,n;r={};fo" - "r(e in t)hasProp.call(t,e)&&(n=t[e],r[util.toCamelCase(e)]=n);return r},tran" - "slateOptargs=function(t){var e,r,n;r={};for(e in t)hasProp.call(t,e)&&(n=t[e" - "],void 0!==e&&void 0!==n&&(r[util.fromCamelCase(e)]=rethinkdb.expr(n)));retu" - "rn r},RDBOp=function(t){function e(){var t,r,n,o,p;return o=arguments[0],r=2" - "<=arguments.length?slice.call(arguments,1):[],p=e.__super__.constructor.call" - "(this),p.args=function(){var e,o,p;for(p=[],n=e=0,o=r.length;e0&&r.push(intsp(t)),Object.keys(e).length>0&&(r.length>0&&r.push(\", " - "\"),r.push(kved(translateBackOptargs(e)))),r},shouldWrap=function(t){return " - "t instanceof DatumTerm||t instanceof MakeArray||t instanceof MakeObject},Mak" - "eArray=function(t){function e(){return e.__super__.constructor.apply(this,ar" - "guments)}return extend(e,t),e.prototype.tt=protoTermType.MAKE_ARRAY,e.protot" - "ype.st=\"[...]\",e.prototype.compose=function(t){return[\"[\",intsp(t),\"]\"" - "]},e}(RDBOp),MakeObject=function(t){function e(t,r){var n,o,p;null==r&&(r=20" - "),o=e.__super__.constructor.call(this,{}),o.optargs={};for(n in t)if(hasProp" - ".call(t,n)){if(p=t[n],\"undefined\"==typeof p)throw new err.ReqlDriverCompil" - "eError(\"Object field '\"+n+\"' may not be undefined\");o.optargs[n]=rethink" - "db.expr(p,r-1)}return o}return extend(e,t),e.prototype.tt=protoTermType.MAKE" - "_OBJ,e.prototype.st=\"{...}\",e.prototype.compose=function(t,e){return kved(" - "e)},e.prototype.build=function(){var t,e,r,n;r={},e=this.optargs;for(t in e)" - "hasProp.call(e,t)&&(n=e[t],r[t]=n.build());return r},e}(RDBOp),Var=function(" - "t){function e(){return e.__super__.constructor.apply(this,arguments)}return " - "extend(e,t),e.prototype.tt=protoTermType.VAR,e.prototype.compose=function(t)" - "{return[\"var_\"+t]},e}(RDBOp),JavaScript=function(t){function e(){return e." - "__super__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.t" - "t=protoTermType.JAVASCRIPT,e.prototype.st=\"js\",e}(RDBOp),Http=function(t){" - "function e(){return e.__super__.constructor.apply(this,arguments)}return ext" - "end(e,t),e.prototype.tt=protoTermType.HTTP,e.prototype.st=\"http\",e}(RDBOp)" - ",Json=function(t){function e(){return e.__super__.constructor.apply(this,arg" - "uments)}return extend(e,t),e.prototype.tt=protoTermType.JSON,e.prototype.st=" - "\"json\",e}(RDBOp),Binary=function(t){function e(t){var r;if(t instanceof Te" - "rmBase)r=e.__super__.constructor.call(this,{},t);else{if(!(t instanceof Buff" - "er))throw new TypeError(\"Parameter to `r.binary` must be a Buffer object or" - " RQL query.\");r=e.__super__.constructor.call(this),r.base64_data=t.toString" - "(\"base64\")}return r}return extend(e,t),e.prototype.tt=protoTermType.BINARY" - ",e.prototype.st=\"binary\",e.prototype.compose=function(){return 0===this.ar" - "gs.length?\"r.binary()\":e.__super__.compose.apply(this,arguments)},e." - "prototype.build=function(){return 0===this.args.length?{$reql_type$:\"BINARY" - "\",data:this.base64_data}:e.__super__.build.apply(this,arguments)},e}(RDBOp)" - ",Args=function(t){function e(){return e.__super__.constructor.apply(this,arg" - "uments)}return extend(e,t),e.prototype.tt=protoTermType.ARGS,e.prototype.st=" - "\"args\",e}(RDBOp),UserError=function(t){function e(){return e.__super__.con" - "structor.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermTy" - "pe.ERROR,e.prototype.st=\"error\",e}(RDBOp),Random=function(t){function e(){" - "return e.__super__.constructor.apply(this,arguments)}return extend(e,t),e.pr" - "ototype.tt=protoTermType.RANDOM,e.prototype.st=\"random\",e}(RDBOp),Implicit" - "Var=function(t){function e(){return e.__super__.constructor.apply(this,argum" - "ents)}return extend(e,t),e.prototype.tt=protoTermType.IMPLICIT_VAR,e.prototy" - "pe.compose=function(){return[\"r.row\"]},e}(RDBOp),Db=function(t){function e" - "(){return e.__super__.constructor.apply(this,arguments)}return extend(e,t),e" - ".prototype.tt=protoTermType.DB,e.prototype.st=\"db\",e}(RDBOp),Table=functio" - "n(t){function e(){return e.__super__.constructor.apply(this,arguments)}retur" - "n extend(e,t),e.prototype.tt=protoTermType.TABLE,e.prototype.st=\"table\",e." - "prototype.compose=function(t,e){return this.args[0]instanceof Db?[t[0],\".ta" - "ble(\",intspallargs(t.slice(1),e),\")\"]:[\"r.table(\",intspallargs(t,e),\")" - "\"]},e}(RDBOp),Get=function(t){function e(){return e.__super__.constructor.a" - "pply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.GET,e.p" - "rototype.mt=\"get\",e}(RDBOp),GetAll=function(t){function e(){return e.__sup" - "er__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=pro" - "toTermType.GET_ALL,e.prototype.mt=\"getAll\",e}(RDBOp),Eq=function(t){functi" - "on e(){return e.__super__.constructor.apply(this,arguments)}return extend(e," - "t),e.prototype.tt=protoTermType.EQ,e.prototype.mt=\"eq\",e}(RDBOp),Ne=functi" - "on(t){function e(){return e.__super__.constructor.apply(this,arguments)}retu" - "rn extend(e,t),e.prototype.tt=protoTermType.NE,e.prototype.mt=\"ne\",e}(RDBO" - "p),Lt=function(t){function e(){return e.__super__.constructor.apply(this,arg" - "uments)}return extend(e,t),e.prototype.tt=protoTermType.LT,e.prototype.mt=\"" - "lt\",e}(RDBOp),Le=function(t){function e(){return e.__super__.constructor.ap" - "ply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.LE,e.pro" - "totype.mt=\"le\",e}(RDBOp),Gt=function(t){function e(){return e.__super__.co" - "nstructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermT" - "ype.GT,e.prototype.mt=\"gt\",e}(RDBOp),Ge=function(t){function e(){return e." - "__super__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.t" - "t=protoTermType.GE,e.prototype.mt=\"ge\",e}(RDBOp),Not=function(t){function " - "e(){return e.__super__.constructor.apply(this,arguments)}return extend(e,t)," - "e.prototype.tt=protoTermType.NOT,e.prototype.mt=\"not\",e}(RDBOp),Add=functi" - "on(t){function e(){return e.__super__.constructor.apply(this,arguments)}retu" - "rn extend(e,t),e.prototype.tt=protoTermType.ADD,e.prototype.mt=\"add\",e}(RD" - "BOp),Sub=function(t){function e(){return e.__super__.constructor.apply(this," - "arguments)}return extend(e,t),e.prototype.tt=protoTermType.SUB,e.prototype.m" - "t=\"sub\",e}(RDBOp),Mul=function(t){function e(){return e.__super__.construc" - "tor.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.MU" - "L,e.prototype.mt=\"mul\",e}(RDBOp),Div=function(t){function e(){return e.__s" - "uper__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=p" - "rotoTermType.DIV,e.prototype.mt=\"div\",e}(RDBOp),Mod=function(t){function e" - "(){return e.__super__.constructor.apply(this,arguments)}return extend(e,t),e" - ".prototype.tt=protoTermType.MOD,e.prototype.mt=\"mod\",e}(RDBOp),BitAnd=func" - "tion(t){function e(){return e.__super__.constructor.apply(this,arguments)}re" - "turn extend(e,t),e.prototype.tt=protoTermType.BIT_AND,e.prototype.mt=\"bitAn" - "d\",e}(RDBOp),BitOr=function(t){function e(){return e.__super__.constructor." - "apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.BIT_OR" - ",e.prototype.mt=\"bitOr\",e}(RDBOp),BitXor=function(t){function e(){return e" - ".__super__.constructor.apply(this,arguments)}return extend(e,t),e.prototype." - "tt=protoTermType.BIT_XOR,e.prototype.mt=\"bitXor\",e}(RDBOp),BitNot=function" - "(t){function e(){return e.__super__.constructor.apply(this,arguments)}return" - " extend(e,t),e.prototype.tt=protoTermType.BIT_NOT,e.prototype.mt=\"bitNot\"," - "e}(RDBOp),BitSal=function(t){function e(){return e.__super__.constructor.app" - "ly(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.BIT_SAL,e" - ".prototype.mt=\"bitSal\",e}(RDBOp),BitSar=function(t){function e(){return e." - "__super__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.t" - "t=protoTermType.BIT_SAR,e.prototype.mt=\"bitSar\",e}(RDBOp),Floor=function(t" - "){function e(){return e.__super__.constructor.apply(this,arguments)}return e" - "xtend(e,t),e.prototype.tt=protoTermType.FLOOR,e.prototype.mt=\"floor\",e}(RD" - "BOp),Ceil=function(t){function e(){return e.__super__.constructor.apply(this" - ",arguments)}return extend(e,t),e.prototype.tt=protoTermType.CEIL,e.prototype" - ".mt=\"ceil\",e}(RDBOp),Round=function(t){function e(){return e.__super__.con" - "structor.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermTy" - "pe.ROUND,e.prototype.mt=\"round\",e}(RDBOp),Append=function(t){function e(){" - "return e.__super__.constructor.apply(this,arguments)}return extend(e,t),e.pr" - "ototype.tt=protoTermType.APPEND,e.prototype.mt=\"append\",e}(RDBOp),Prepend=" - "function(t){function e(){return e.__super__.constructor.apply(this,arguments" - ")}return extend(e,t),e.prototype.tt=protoTermType.PREPEND,e.prototype.mt=\"p" - "repend\",e}(RDBOp),Difference=function(t){function e(){return e.__super__.co" - "nstructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermT" - "ype.DIFFERENCE,e.prototype.mt=\"difference\",e}(RDBOp),SetInsert=function(t)" - "{function e(){return e.__super__.constructor.apply(this,arguments)}return ex" - "tend(e,t),e.prototype.tt=protoTermType.SET_INSERT,e.prototype.mt=\"setInsert" - "\",e}(RDBOp),SetUnion=function(t){function e(){return e.__super__.constructo" - "r.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.SET_" - "UNION,e.prototype.mt=\"setUnion\",e}(RDBOp),SetIntersection=function(t){func" - "tion e(){return e.__super__.constructor.apply(this,arguments)}return extend(" - "e,t),e.prototype.tt=protoTermType.SET_INTERSECTION,e.prototype.mt=\"setInter" - "section\",e}(RDBOp),SetDifference=function(t){function e(){return e.__super_" - "_.constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoT" - "ermType.SET_DIFFERENCE,e.prototype.mt=\"setDifference\",e}(RDBOp),Slice=func" - "tion(t){function e(){return e.__super__.constructor.apply(this,arguments)}re" - "turn extend(e,t),e.prototype.tt=protoTermType.SLICE,e.prototype.mt=\"slice\"" - ",e}(RDBOp),Skip=function(t){function e(){return e.__super__.constructor.appl" - "y(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.SKIP,e.pro" - "totype.mt=\"skip\",e}(RDBOp),Limit=function(t){function e(){return e.__super" - "__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=proto" - "TermType.LIMIT,e.prototype.mt=\"limit\",e}(RDBOp),GetField=function(t){funct" - "ion e(){return e.__super__.constructor.apply(this,arguments)}return extend(e" - ",t),e.prototype.tt=protoTermType.GET_FIELD,e.prototype.mt=\"getField\",e}(RD" - "BOp),Bracket=function(t){function e(){return e.__super__.constructor.apply(t" - "his,arguments)}return extend(e,t),e.prototype.tt=protoTermType.BRACKET,e.pro" - "totype.st=\"(...)\",e.prototype.compose=function(t){return[t[0],\"(\",t[1],\"" - ")\"]},e}(RDBOp),Contains=function(t){function e(){return e.__super__.constru" - "ctor.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.C" - "ONTAINS,e.prototype.mt=\"contains\",e}(RDBOp),InsertAt=function(t){function " - "e(){return e.__super__.constructor.apply(this,arguments)}return extend(e,t)," - "e.prototype.tt=protoTermType.INSERT_AT,e.prototype.mt=\"insertAt\",e}(RDBOp)" - ",SpliceAt=function(t){function e(){return e.__super__.constructor.apply(this" - ",arguments)}return extend(e,t),e.prototype.tt=protoTermType.SPLICE_AT,e.prot" - "otype.mt=\"spliceAt\",e}(RDBOp),DeleteAt=function(t){function e(){return e._", - 65472) + std::string( - "_super__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt" - "=protoTermType.DELETE_AT,e.prototype.mt=\"deleteAt\",e}(RDBOp),ChangeAt=func" - "tion(t){function e(){return e.__super__.constructor.apply(this,arguments)}re" - "turn extend(e,t),e.prototype.tt=protoTermType.CHANGE_AT,e.prototype.mt=\"cha" - "ngeAt\",e}(RDBOp),HasFields=function(t){function e(){return e.__super__.cons" - "tructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermTyp" - "e.HAS_FIELDS,e.prototype.mt=\"hasFields\",e}(RDBOp),WithFields=function(t){f" - "unction e(){return e.__super__.constructor.apply(this,arguments)}return exte" - "nd(e,t),e.prototype.tt=protoTermType.WITH_FIELDS,e.prototype.mt=\"withFields" - "\",e}(RDBOp),Keys=function(t){function e(){return e.__super__.constructor.ap" - "ply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.KEYS,e.p" - "rototype.mt=\"keys\",e}(RDBOp),Values=function(t){function e(){return e.__su" - "per__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=pr" - "otoTermType.VALUES,e.prototype.mt=\"values\",e}(RDBOp),Changes=function(t){f" - "unction e(){return e.__super__.constructor.apply(this,arguments)}return exte" - "nd(e,t),e.prototype.tt=protoTermType.CHANGES,e.prototype.mt=\"changes\",e}(R" - "DBOp),Object_=function(t){function e(){return e.__super__.constructor.apply(" - "this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.OBJECT,e.pro" - "totype.mt=\"object\",e}(RDBOp),Pluck=function(t){function e(){return e.__sup" - "er__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=pro" - "toTermType.PLUCK,e.prototype.mt=\"pluck\",e}(RDBOp),OffsetsOf=function(t){fu" - "nction e(){return e.__super__.constructor.apply(this,arguments)}return exten" - "d(e,t),e.prototype.tt=protoTermType.OFFSETS_OF,e.prototype.mt=\"offsetsOf\"," - "e}(RDBOp),Without=function(t){function e(){return e.__super__.constructor.ap" - "ply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.WITHOUT," - "e.prototype.mt=\"without\",e}(RDBOp),Merge=function(t){function e(){return e" - ".__super__.constructor.apply(this,arguments)}return extend(e,t),e.prototype." - "tt=protoTermType.MERGE,e.prototype.mt=\"merge\",e}(RDBOp),Between=function(t" - "){function e(){return e.__super__.constructor.apply(this,arguments)}return e" - "xtend(e,t),e.prototype.tt=protoTermType.BETWEEN,e.prototype.mt=\"between\",e" - "}(RDBOp),Reduce=function(t){function e(){return e.__super__.constructor.appl" - "y(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.REDUCE,e.p" - "rototype.mt=\"reduce\",e}(RDBOp),Map=function(t){function e(){return e.__sup" - "er__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=pro" - "toTermType.MAP,e.prototype.mt=\"map\",e}(RDBOp),Fold=function(t){function e(" - "){return e.__super__.constructor.apply(this,arguments)}return extend(e,t),e." - "prototype.tt=protoTermType.FOLD,e.prototype.mt=\"fold\",e}(RDBOp),Filter=fun" - "ction(t){function e(){return e.__super__.constructor.apply(this,arguments)}r" - "eturn extend(e,t),e.prototype.tt=protoTermType.FILTER,e.prototype.mt=\"filte" - "r\",e}(RDBOp),ConcatMap=function(t){function e(){return e.__super__.construc" - "tor.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.CO" - "NCAT_MAP,e.prototype.mt=\"concatMap\",e}(RDBOp),OrderBy=function(t){function" - " e(){return e.__super__.constructor.apply(this,arguments)}return extend(e,t)" - ",e.prototype.tt=protoTermType.ORDER_BY,e.prototype.mt=\"orderBy\",e}(RDBOp)," - "Distinct=function(t){function e(){return e.__super__.constructor.apply(this," - "arguments)}return extend(e,t),e.prototype.tt=protoTermType.DISTINCT,e.protot" - "ype.mt=\"distinct\",e}(RDBOp),Count=function(t){function e(){return e.__supe" - "r__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=prot" - "oTermType.COUNT,e.prototype.mt=\"count\",e}(RDBOp),Union=function(t){functio" - "n e(){return e.__super__.constructor.apply(this,arguments)}return extend(e,t" - "),e.prototype.tt=protoTermType.UNION,e.prototype.mt=\"union\",e}(RDBOp),Nth=" - "function(t){function e(){return e.__super__.constructor.apply(this,arguments" - ")}return extend(e,t),e.prototype.tt=protoTermType.NTH,e.prototype.mt=\"nth\"" - ",e}(RDBOp),ToJsonString=function(t){function e(){return e.__super__.construc" - "tor.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.TO" - "_JSON_STRING,e.prototype.mt=\"toJsonString\",e}(RDBOp),Match=function(t){fun" - "ction e(){return e.__super__.constructor.apply(this,arguments)}return extend" - "(e,t),e.prototype.tt=protoTermType.MATCH,e.prototype.mt=\"match\",e}(RDBOp)," - "Split=function(t){function e(){return e.__super__.constructor.apply(this,arg" - "uments)}return extend(e,t),e.prototype.tt=protoTermType.SPLIT,e.prototype.mt" - "=\"split\",e}(RDBOp),Upcase=function(t){function e(){return e.__super__.cons" - "tructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermTyp" - "e.UPCASE,e.prototype.mt=\"upcase\",e}(RDBOp),Downcase=function(t){function e" - "(){return e.__super__.constructor.apply(this,arguments)}return extend(e,t),e" - ".prototype.tt=protoTermType.DOWNCASE,e.prototype.mt=\"downcase\",e}(RDBOp),I" - "sEmpty=function(t){function e(){return e.__super__.constructor.apply(this,ar" - "guments)}return extend(e,t),e.prototype.tt=protoTermType.IS_EMPTY,e.prototyp" - "e.mt=\"isEmpty\",e}(RDBOp),Group=function(t){function e(){return e.__super__" - ".constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTe" - "rmType.GROUP,e.prototype.mt=\"group\",e}(RDBOp),Sum=function(t){function e()" - "{return e.__super__.constructor.apply(this,arguments)}return extend(e,t),e.p" - "rototype.tt=protoTermType.SUM,e.prototype.mt=\"sum\",e}(RDBOp),Avg=function(" - "t){function e(){return e.__super__.constructor.apply(this,arguments)}return " - "extend(e,t),e.prototype.tt=protoTermType.AVG,e.prototype.mt=\"avg\",e}(RDBOp" - "),Min=function(t){function e(){return e.__super__.constructor.apply(this,arg" - "uments)}return extend(e,t),e.prototype.tt=protoTermType.MIN,e.prototype.mt=\"" - "min\",e}(RDBOp),Max=function(t){function e(){return e.__super__.constructor." - "apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.MAX,e." - "prototype.mt=\"max\",e}(RDBOp),InnerJoin=function(t){function e(){return e._" - "_super__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt" - "=protoTermType.INNER_JOIN,e.prototype.mt=\"innerJoin\",e}(RDBOp),OuterJoin=f" - "unction(t){function e(){return e.__super__.constructor.apply(this,arguments)" - "}return extend(e,t),e.prototype.tt=protoTermType.OUTER_JOIN,e.prototype.mt=\"" - "outerJoin\",e}(RDBOp),EqJoin=function(t){function e(){return e.__super__.con" - "structor.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermTy" - "pe.EQ_JOIN,e.prototype.mt=\"eqJoin\",e}(RDBOp),Zip=function(t){function e(){" - "return e.__super__.constructor.apply(this,arguments)}return extend(e,t),e.pr" - "ototype.tt=protoTermType.ZIP,e.prototype.mt=\"zip\",e}(RDBOp),Range=function" - "(t){function e(){return e.__super__.constructor.apply(this,arguments)}return" - " extend(e,t),e.prototype.tt=protoTermType.RANGE,e.prototype.st=\"range\",e}(" - "RDBOp),CoerceTo=function(t){function e(){return e.__super__.constructor.appl" - "y(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.COERCE_TO," - "e.prototype.mt=\"coerceTo\",e}(RDBOp),Ungroup=function(t){function e(){retur" - "n e.__super__.constructor.apply(this,arguments)}return extend(e,t),e.prototy" - "pe.tt=protoTermType.UNGROUP,e.prototype.mt=\"ungroup\",e}(RDBOp),TypeOf=func" - "tion(t){function e(){return e.__super__.constructor.apply(this,arguments)}re" - "turn extend(e,t),e.prototype.tt=protoTermType.TYPE_OF,e.prototype.mt=\"typeO" - "f\",e}(RDBOp),Info=function(t){function e(){return e.__super__.constructor.a" - "pply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.INFO,e." - "prototype.mt=\"info\",e}(RDBOp),Sample=function(t){function e(){return e.__s" - "uper__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=p" - "rotoTermType.SAMPLE,e.prototype.mt=\"sample\",e}(RDBOp),Update=function(t){f" - "unction e(){return e.__super__.constructor.apply(this,arguments)}return exte" - "nd(e,t),e.prototype.tt=protoTermType.UPDATE,e.prototype.mt=\"update\",e}(RDB" - "Op),Delete=function(t){function e(){return e.__super__.constructor.apply(thi" - "s,arguments)}return extend(e,t),e.prototype.tt=protoTermType.DELETE,e.protot" - "ype.mt=\"delete\",e}(RDBOp),Replace=function(t){function e(){return e.__supe" - "r__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=prot" - "oTermType.REPLACE,e.prototype.mt=\"replace\",e}(RDBOp),Insert=function(t){fu" - "nction e(){return e.__super__.constructor.apply(this,arguments)}return exten" - "d(e,t),e.prototype.tt=protoTermType.INSERT,e.prototype.mt=\"insert\",e}(RDBO" - "p),DbCreate=function(t){function e(){return e.__super__.constructor.apply(th" - "is,arguments)}return extend(e,t),e.prototype.tt=protoTermType.DB_CREATE,e.pr" - "ototype.st=\"dbCreate\",e}(RDBOp),DbDrop=function(t){function e(){return e._" - "_super__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt" - "=protoTermType.DB_DROP,e.prototype.st=\"dbDrop\",e}(RDBOp),DbList=function(t" - "){function e(){return e.__super__.constructor.apply(this,arguments)}return e" - "xtend(e,t),e.prototype.tt=protoTermType.DB_LIST,e.prototype.st=\"dbList\",e}" - "(RDBOp),TableCreate=function(t){function e(){return e.__super__.constructor." - "apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.TABLE_" - "CREATE,e.prototype.mt=\"tableCreate\",e}(RDBOp),TableDrop=function(t){functi" - "on e(){return e.__super__.constructor.apply(this,arguments)}return extend(e," - "t),e.prototype.tt=protoTermType.TABLE_DROP,e.prototype.mt=\"tableDrop\",e}(R" - "DBOp),TableList=function(t){function e(){return e.__super__.constructor.appl" - "y(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.TABLE_LIST" - ",e.prototype.mt=\"tableList\",e}(RDBOp),SetWriteHook=function(t){function e(" - "){return e.__super__.constructor.apply(this,arguments)}return extend(e,t),e." - "prototype.tt=protoTermType.SET_WRITE_HOOK,e.prototype.mt=\"setWriteHook\",e}" - "(RDBOp),GetWriteHook=function(t){function e(){return e.__super__.constructor" - ".apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.GET_W" - "RITE_HOOK,e.prototype.mt=\"getWriteHook\",e}(RDBOp),IndexCreate=function(t){" - "function e(){return e.__super__.constructor.apply(this,arguments)}return ext" - "end(e,t),e.prototype.tt=protoTermType.INDEX_CREATE,e.prototype.mt=\"indexCre" - "ate\",e}(RDBOp),IndexDrop=function(t){function e(){return e.__super__.constr" - "uctor.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType." - "INDEX_DROP,e.prototype.mt=\"indexDrop\",e}(RDBOp),IndexRename=function(t){fu" - "nction e(){return e.__super__.constructor.apply(this,arguments)}return exten" - "d(e,t),e.prototype.tt=protoTermType.INDEX_RENAME,e.prototype.mt=\"indexRenam" - "e\",e}(RDBOp),IndexList=function(t){function e(){return e.__super__.construc" - "tor.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.IN" - "DEX_LIST,e.prototype.mt=\"indexList\",e}(RDBOp),IndexStatus=function(t){func" - "tion e(){return e.__super__.constructor.apply(this,arguments)}return extend(" - "e,t),e.prototype.tt=protoTermType.INDEX_STATUS,e.prototype.mt=\"indexStatus\"" - ",e}(RDBOp),IndexWait=function(t){function e(){return e.__super__.constructor" - ".apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.INDEX" - "_WAIT,e.prototype.mt=\"indexWait\",e}(RDBOp),Config=function(t){function e()" - "{return e.__super__.constructor.apply(this,arguments)}return extend(e,t),e.p" - "rototype.tt=protoTermType.CONFIG,e.prototype.mt=\"config\",e}(RDBOp),Status=" - "function(t){function e(){return e.__super__.constructor.apply(this,arguments" - ")}return extend(e,t),e.prototype.tt=protoTermType.STATUS,e.prototype.mt=\"st" - "atus\",e}(RDBOp),Wait=function(t){function e(){return e.__super__.constructo" - "r.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.WAIT" - ",e.prototype.mt=\"wait\",e}(RDBOp),Reconfigure=function(t){function e(){retu" - "rn e.__super__.constructor.apply(this,arguments)}return extend(e,t),e.protot" - "ype.tt=protoTermType.RECONFIGURE,e.prototype.mt=\"reconfigure\",e}(RDBOp),Re" - "balance=function(t){function e(){return e.__super__.constructor.apply(this,a" - "rguments)}return extend(e,t),e.prototype.tt=protoTermType.REBALANCE,e.protot" - "ype.mt=\"rebalance\",e}(RDBOp),Sync=function(t){function e(){return e.__supe" - "r__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=prot" - "oTermType.SYNC,e.prototype.mt=\"sync\",e}(RDBOp),Grant=function(t){function " - "e(){return e.__super__.constructor.apply(this,arguments)}return extend(e,t)," - "e.prototype.tt=protoTermType.GRANT,e.prototype.mt=\"grant\",e}(RDBOp),FunCal" - "l=function(t){function e(){return e.__super__.constructor.apply(this,argumen" - "ts)}return extend(e,t),e.prototype.tt=protoTermType.FUNCALL,e.prototype.st=\"" - "do\",e.prototype.compose=function(t){return t.length>2?[\"r.do(\",intsp(t.sl" - "ice(1)),\", \",t[0],\")\"]:(shouldWrap(this.args[1])&&(t[1]=[\"r(\",t[1],\")" - "\"]),[t[1],\".do(\",t[0],\")\"])},e}(RDBOp),Default=function(t){function e()" - "{return e.__super__.constructor.apply(this,arguments)}return extend(e,t),e.p" - "rototype.tt=protoTermType.DEFAULT,e.prototype.mt=\"default\",e}(RDBOp),Branc" - "h=function(t){function e(){return e.__super__.constructor.apply(this,argumen" - "ts)}return extend(e,t),e.prototype.tt=protoTermType.BRANCH,e.prototype.st=\"" - "branch\",e}(RDBOp),Or=function(t){function e(){return e.__super__.constructo" - "r.apply(this,arguments)}return extend(e,t),e.prototype.tt=protoTermType.OR,e" - ".prototype.mt=\"or\",e}(RDBOp),And=function(t){function e(){return e.__super" - "__.constructor.apply(this,arguments)}return extend(e,t),e.prototype.tt=proto" - "TermType.AND,e.prototype.mt=\"and\",e}(RDBOp),ForEach=function(t){function e" - "(){return e.__super__.constructor.apply(this,arguments)}return extend(e,t),e" - ".prototype.tt=protoTermType.FOR_EACH,e.prototype.mt=\"forEach\",e}(RDBOp),Fu" - "nc=function(t){function e(t,r){var n,o,p,c,u;for(o=[],n=[],u=0;u0&&this._responses.push(e):this._res" - "ponses.push(e),this._outstandingRequests-=1,e.t!==this._type&&(this._endFlag" - "=!0,null!=this._closeCb))switch(e.t){case protoResponseType.COMPILE_ERROR:th" - "is._closeCb(mkErr(error.ReqlServerCompileError,e,this._root));break;case pro" - "toResponseType.CLIENT_ERROR:this._closeCb(mkErr(error.ReqlClientError,e,this" - "._root));break;case protoResponseType.RUNTIME_ERROR:this._closeCb(mkErr(util" - ".errorClass(e.e),e,this._root));break;default:this._closeCb()}return this._c" - "ontFlag=!1,this._closeAsap===!1?this._promptNext():this.close(this._closeCb)" - ",this},e.prototype._getCallback=function(){var e,t;return this._iterations+=" - "1,e=this._cbQueue.shift(),this._iterations%this.stackSize===this.stackSize-1" - "?t=function(t,r){return setImmediate(function(){return e(t,r)})}:e},e.protot" - "ype._handleRow=function(){var e,t,r;return t=this._responses[0],r=util.recur" - "sivelyConvertPseudotype(t.r[this._responseIndex],this._opts),e=this._getCall" - "back(),this._responseIndex+=1,this._responseIndex===t.r.length&&(this._respo" - "nses.shift(),this._responseIndex=0),e(null,r)},e.prototype.bufferEmpty=funct" - "ion(){return 0===this._responses.length||this._responses[0].r.length<=this._" - "responseIndex},e.prototype._promptNext=function(){var e,t,r;for(null!=this._" - "closeCbPromise&&(e=this._getCallback())(new error.ReqlDriverError(\"Cursor i" - "s closed.\"));null!=this._cbQueue[0];){if(this.bufferEmpty()===!0)return voi" - "d(this._endFlag===!0?(e=this._getCallback())(new error.ReqlDriverError(\"No " - "more rows in the cursor.\")):this._responses.length<=1&&this._promptCont());" - "switch(r=this._responses[0],1===this._responses.length&&this._promptCont(),r" - ".t){case protoResponseType.SUCCESS_PARTIAL:this._handleRow();break;case prot" - "oResponseType.SUCCESS_SEQUENCE:0===r.r.length?this._responses.shift():this._" - "handleRow();break;case protoResponseType.COMPILE_ERROR:this._responses.shift" - "(),e=this._getCallback(),e(mkErr(error.ReqlServerCompileError,r,this._root))" - ";break;case protoResponseType.CLIENT_ERROR:this._responses.shift(),e=this._g" - "etCallback(),e(mkErr(error.ReqlClientError,r,this._root));break;case protoRe" - "sponseType.RUNTIME_ERROR:this._responses.shift(),e=this._getCallback(),t=uti" - "l.errorClass(r.e),e(mkErr(t,r,this._root));break;default:this._responses.shi" - "ft(),e=this._getCallback(),e(new error.ReqlDriverError(\"Unknown response ty" - "pe for cursor\"))}}},e.prototype._promptCont=function(){if(!this._contFlag&&" - "!this._endFlag&&this._conn.isOpen())return this._contFlag=!0,this._outstandi" - "ngRequests+=1,this._conn._continueQuery(this._token)},e.prototype.hasNext=fu" - "nction(){throw new error.ReqlDriverError(\"The `hasNext` command has been re" - "moved since 1.13. Use `next` instead.\")},e.prototype._next=varar(0,1,functi" - "on(e){var t;if(null!=e&&\"function\"!=typeof e)throw new error.ReqlDriverErr" - "or(\"First argument to `next` must be a function or undefined.\");return t=f" - "unction(e){return function(t){return e._cbQueue.push(t),e._promptNext()}}(th" - "is),Promise.fromNode(t).nodeify(e)}),e.prototype.close=varar(0,1,function(e)" - "{return null!=this._closeCbPromise?this._closeCbPromise.isPending()?this._cl" - "oseCbPromise=this._closeCbPromise.nodeify(e):this._closeCbPromise=Promise.re" - "solve().nodeify(e):this._endFlag?(this._closeCbPromise=Promise.resolve().nod" - "eify(e),this._responses=[],this._responseIndex=0):this._closeCbPromise=new P" - "romise(function(e){return function(t,r){return e._closeCb=function(o){for(e." - "_responses=[],e._responseIndex=0;e._cbQueue.length>0;)e._cbQueue.shift();ret" - "urn e._outstandingRequests=0,o?r(o):t()},e._closeAsap=!0,e._outstandingReque" - "sts+=1,e._conn._endQuery(e._token)}}(this)).nodeify(e),this._closeCbPromise}" - "),e.prototype.each=varar(1,2,function(e,t){var r,o;if(\"function\"!=typeof e" - ")throw new error.ReqlDriverError(\"First argument to each must be a function" - ".\");if(null!=t&&\"function\"!=typeof t)throw new error.ReqlDriverError(\"Op" - "tional second argument to each must be a function.\");return o=this,r=functi" - "on(o){return function(n,i){return null!=n?\"No more rows in the cursor.\"===" - "n.message?\"function\"==typeof t?t():void 0:e(n):e(null,i)!==!1?o._next(r):\"" - "function\"==typeof t?t():void 0}}(this),this._next(r)}),e.prototype.eachAsyn" - "c=varar(1,3,function(e,t,r){var o,n,i,s;if(null==r&&(r={concurrency:1}),\"fu" - "nction\"!=typeof e)throw new error.ReqlDriverError(\"First argument to eachA" - "sync must be a function.\");if(null!=t)if(\"object\"==typeof t)r=t,t=void 0;" - "else if(\"function\"!=typeof t)throw new error.ReqlDriverError(\"Optional se" - "cond argument to eachAsync must be a function or `options` object\");if(!(r&" - "&\"number\"==typeof r.concurrency&&r.concurrency>0))throw new error.ReqlDriv" - "erError(\"Optional `options.concurrency` argument to eachAsync must be a pos" - "itive number\");return n=[],s=function(t){var r,o,n,i;return e.length<=1?i=P" - "romise.resolve(e(t)):(n=!1,r=!1,o=void 0,i=Promise.fromNode(function(i){var " - "s;return s=e(t,function(e){return n=!0,r?i(e):o=e}),void 0!==s?i(new error.R" - "eqlDriverError(\"A two-argument row handler for eachAsync may only return un" - "defined.\")):n&&i(o),r=!0})),i.then(function(e){if(void 0===e)return e;throw" - " new error.ReqlDriverError(\"Row handler for eachAsync may only return undef" - "ined, or a Promise that resolves to undefined.\")})},o=function(e){return fu" - "nction(){return null!=e._closeCbPromise?Promise.resolve().then(function(e){t" - "hrow new error.ReqlDriverError(\"Cursor is closed.\")}):e._next().then(funct" - "ion(e){return n.length=12&&(s=th" - "is.buffer.readUInt32LE(0)+4294967296*this.buffer.readUInt32LE(4),o=this.buff" - "er.readUInt32LE(8),this.buffer.length>=12+o);)t=this.buffer.slice(12,o+12),r" - "=JSON.parse(t),this._processResponse(r,s),n.push(this.buffer=this.buffer.sli" - "ce(12+o));return n},t.prototype._delQuery=function(e){return delete this.out" - "standingCallbacks[e]},t.prototype._processResponse=function(e,r){var t,o,n,s" - ",i,u,c,l,a,p,f,h;if(a=e.p,null!=this.outstandingCallbacks[r])if(p=this.outst" - "andingCallbacks[r],t=p.cb,h=p.root,o=p.cursor,l=p.opts,s=p.feed,null!=o){if(" - "o._addResponse(e),o._endFlag&&0===o._outstandingRequests)return this._delQue" - "ry(r)}else if(null!=t)switch(e.t){case protoResponseType.COMPILE_ERROR:retur" - "n t(mkErr(err.ReqlServerCompileError,e,h)),this._delQuery(r);case protoRespo" - "nseType.CLIENT_ERROR:return t(mkErr(err.ReqlDriverError,e,h)),this._delQuery" - "(r);case protoResponseType.RUNTIME_ERROR:return n=util.errorClass(e.e),t(mkE" - "rr(n,e,h)),this._delQuery(r);case protoResponseType.SUCCESS_ATOM:return e=mk" - "Atom(e,l),Array.isArray(e)&&(e=cursors.makeIterable(e)),null!=a&&(e={profile" - ":a,value:e}),t(null,e),this._delQuery(r);case protoResponseType.SUCCESS_PART" - "IAL:for(o=null,f=e.n,i=0,u=f.length;i }.\");return null!=this._closePromise?this._closePromi" - "se.nodeify(t):(this.closing=!0,n=(null==s.noreplyWait||s.noreplyWait)&&this." - "open,this._closePromise=new Promise(function(e){return function(r,t){var o;r", - 65486) + std::string( - "eturn o=function(o,n){return e.open=!1,e.closing=!1,e.cancel(),null!=o?t(o):" - "r(n)},n?e.noreplyWait(o):o()}}(this)).nodeify(t))}),t.prototype.noreplyWait=" - "varar(0,1,function(e){var r,t;return this.open?(t=this.nextToken++,r={},r.ty" - "pe=protoQueryType.NOREPLY_WAIT,r.token=t,new Promise(function(e){return func" - "tion(o,n){var s;return s=function(e,r){return e?n(e):o(r)},e.outstandingCall" - "backs[t]={cb:s,root:null,opts:null},e._sendQuery(r)}}(this)).nodeify(e)):new" - " Promise(function(e,r){return r(new err.ReqlDriverError(\"Connection is clos" - "ed.\"))}).nodeify(e)}),t.prototype.server=varar(0,1,function(e){var r,t;retu" - "rn this.open?(t=this.nextToken++,r={},r.type=protoQueryType.SERVER_INFO,r.to" - "ken=t,new Promise(function(e){return function(o,n){var s;return s=function(e" - ",r){return e?n(e):o(r)},e.outstandingCallbacks[t]={cb:s,root:null,opts:null}" - ",e._sendQuery(r)}}(this)).nodeify(e)):new Promise(function(e,r){return r(new" - " err.ReqlDriverError(\"Connection is closed.\"))}).nodeify(e)}),t.prototype." - "cancel=ar(function(){var e,r,t,o;t={t:protoResponseType.RUNTIME_ERROR,r:[\"C" - "onnection is closed.\"],b:[]},r=this.outstandingCallbacks;for(e in r)hasProp" - ".call(r,e)&&(o=r[e],null!=o.cursor?o.cursor._addResponse(t):null!=o.cb&&o.cb" - "(mkErr(util.errorClass(t.e),t,o.root)));return this.outstandingCallbacks={}}" - "),t.prototype.reconnect=varar(0,2,function(e,r){var t,o;return null!=r?(o=e," - "t=r):\"function\"==typeof e?(o={},t=e):(o=null!=e?e:{},t=r),new Promise(func" - "tion(e){return function(r,t){var n;return n=function(o){return e.constructor" - ".call(e,{host:e.host,port:e.port,timeout:e.timeout,authKey:e.authKey},functi" - "on(e,o){return null!=e?t(e):r(o)})},e.close(o,n)}}(this)).nodeify(t)}),t.pro" - "totype.use=ar(function(e){return this.db=e}),t.prototype.isOpen=function(){r" - "eturn this.open&&!this.closing},t.prototype._start=function(e,t,o){var n,s,i" - ",u;if(!this.open)throw new err.ReqlDriverError(\"Connection is closed.\");i=" - "this.nextToken++,s={},s.global_optargs={},s.type=protoQueryType.START,s.quer" - "y=e.build(),s.token=i;for(n in o)hasProp.call(o,n)&&(u=o[n],s.global_optargs" - "[util.fromCamelCase(n)]=r.expr(u).build());if(null==o.db&&null==this.db||(s." - "global_optargs.db=r.db(o.db||this.db).build()),null!=o.noreply&&(s.global_op" - "targs.noreply=r.expr(!!o.noreply).build()),null!=o.profile&&(s.global_optarg" - "s.profile=r.expr(!!o.profile).build()),null!=o.noreply&&o.noreply||(this.out" - "standingCallbacks[i]={cb:t,root:e,opts:o}),this._sendQuery(s),null!=o.norepl" - "y&&o.noreply&&\"function\"==typeof t)return t(null)},t.prototype._continueQu" - "ery=function(e){var r;if(!this.open)throw new err.ReqlDriverError(\"Connecti" - "on is closed.\");return r={type:protoQueryType.CONTINUE,token:e},this._sendQ" - "uery(r)},t.prototype._endQuery=function(e){var r;if(!this.open)throw new err" - ".ReqlDriverError(\"Connection is closed.\");return r={type:protoQueryType.ST" - "OP,token:e},this._sendQuery(r)},t.prototype._sendQuery=function(e){var r;ret" - "urn r=[e.type],void 0!==e.query&&(r.push(e.query),null!=e.global_optargs&&Ob" - "ject.keys(e.global_optargs).length>0&&r.push(e.global_optargs)),this._writeQ" - "uery(e.token,JSON.stringify(r))},t}(events.EventEmitter),pbkdf2_cache={},Tcp" - "Connection=function(e){function r(e,t){var o;if(!r.isAvailable())throw new e" - "rr.ReqlDriverError(\"TCP sockets are not available in this environment\");r." - "__super__.constructor.call(this,e,t),this.ssl?(this.ssl.host=this.host,this." - "ssl.port=this.port,this.rawSocket=tls.connect(this.ssl)):this.rawSocket=net." - "connect(this.port,this.host),this.rawSocket.setNoDelay(),this.rawSocket.setK" - "eepAlive(!0),o=setTimeout(function(e){return function(){return e.rawSocket.d" - "estroy(),e.emit(\"error\",new err.ReqlTimeoutError(\"Could not connect to \"" - "+e.host+\":\"+e.port+\", operation timed out.\"))}}(this),1e3*this.timeout)," - "this.rawSocket.once(\"error\",function(e){return function(){return clearTime" - "out(o)}}(this)),this.rawSocket.once(\"connect\",function(r){return function(" - "){var t,n,s,i,u,c,l,a,p,f,h,d,y,w,_,b,v,m,E;return m=new Buffer(4),m.writeUI" - "nt32LE(protoVersion,0),y=new Buffer(4),y.writeUInt32LE(protoProtocol,0),w=ne" - "w Buffer(crypto.randomBytes(18)).toString(\"base64\"),r.rawSocket.user=e.use" - "r,r.rawSocket.password=e.password,void 0===r.rawSocket.user&&(r.rawSocket.us" - "er=\"admin\"),void 0===r.rawSocket.password&&(r.rawSocket.password=\"\"),i=\"" - "n=\"+r.rawSocket.user+\",r=\"+w,p=JSON.stringify({protocol_version:protoVers" - "ionNumber,authentication_method:\"SCRAM-SHA-256\",authentication:\"n,,\"+i})" - ",h=new Buffer(\"\\0\",\"binary\"),r.rawSocket.write(Buffer.concat([m,Buffer(" - "p.toString()),h])),v=1,f=0,a=0,_=\"\",b=\"\",n=\"\",s=\"\",t=0,E=function(e," - "r){var t,o,n,s,i;for(i=[],n=Math.min(e.length,r.length),t=o=0,s=n;0<=s?os;t=0<=s?++o:--o)i.push(e[t]^r[t]);return new Buffer(i)},d=function(e,r,t){" - "var o,n,s,i,u,c,l;if(n=e.toString(\"base64\")+\",\"+r.toString(\"base64\")+\"" - ",\"+t.toString(),pbkdf2_cache[n])return pbkdf2_cache[n];for(i=crypto.createH" - "mac(\"sha256\",e),i.update(r),i.update(\"\\0\\0\\0\1\"),l=i.digest(),c=l,o=s" - "=0,u=t-1;0<=u?su;o=0<=u?++s:--s)i=crypto.createHmac(\"sha256\",e),i.upd" - "ate(c),c=i.digest(),l=E(l,c);return pbkdf2_cache[n]=l,l},u=function(e,r){var" - " t,o,n,s,i,u,c;for(n=void 0,c=r,u=void 0,e.length===r.length&&(n=e,u=0),e.le" - "ngth!==r.length&&(n=r,u=1),s=Math.min(n.length,c.length),t=o=0,i=s;0<=i?oi;t=0<=i?++o:--o)u|=n[t]^c[t];return 0===u},l=function(e,t){return 10<=e&&" - "e<=20?r.emit(\"error\",new err.ReqlAuthError(t)):r.emit(\"error\",new err.Re" - "qlDriverError(t))},c=function(e){var y,m,R,S,g,T,k,C,A,O,q,U,D,P,L,I,H,N,x,Q" - ",F,B,M,j,K;for(r.buffer=Buffer.concat([r.buffer,e]),q=0,H=r.buffer,A=D=0,L=H" - ".length;DprotoVersionNumber||a0},n.prototype.thro" - "wLater=function(t,e){if(1===arguments.length&&(e=t,t=function(){throw e}),\"" - "undefined\"!=typeof setTimeout)setTimeout(function(){t(e)},0);else try{this." - "_schedule(function(){t(e)})}catch(t){throw new Error(\"No async scheduler av" - "ailable\\n\\n See http://goo.gl/m3OTXk\\n\")}},l.hasDevTools?(u.isStatic&" - "&(u=function(t){setTimeout(t,0)}),n.prototype.invokeLater=function(t,e,r){th" - "is._trampolineEnabled?i.call(this,t,e,r):this._schedule(function(){setTimeou" - "t(function(){t.call(e,r)},100)})},n.prototype.invoke=function(t,e,r){this._t" - "rampolineEnabled?o.call(this,t,e,r):this._schedule(function(){t.call(e,r)})}" - ",n.prototype.settlePromises=function(t){this._trampolineEnabled?s.call(this," - "t):this._schedule(function(){t._settlePromises()})}):(n.prototype.invokeLate" - "r=i,n.prototype.invoke=o,n.prototype.settlePromises=s),n.prototype.invokeFir" - "st=function(t,e,r){this._normalQueue.unshift(t,e,r),this._queueTick()},n.pro" - "totype._drainQueue=function(t){for(;t.length()>0;){var e=t.shift();if(\"func" - "tion\"==typeof e){var r=t.shift(),n=t.shift();e.call(r,n)}else e._settleProm" - "ises()}},n.prototype._drainQueues=function(){this._drainQueue(this._normalQu" - "eue),this._reset(),this._drainQueue(this._lateQueue)},n.prototype._queueTick" - "=function(){this._isTickUsed||(this._isTickUsed=!0,this._schedule(this.drain" - "Queues))},n.prototype._reset=function(){this._isTickUsed=!1},e.exports=new n" - ",e.exports.firstLineError=a},{\"./queue.js\":28,\"./schedule.js\":31,\"./uti" - "l.js\":38}],3:[function(t,e,r){\"use strict\";e.exports=function(t,e,r){var " - "n=function(t,e){this._reject(e)},i=function(t,e){e.promiseRejectionQueued=!0" - ",e.bindingPromise._then(n,n,null,this,t)},o=function(t,e){this._setBoundTo(t" - "),this._isPending()&&this._resolveCallback(e.target)},s=function(t,e){e.prom" - "iseRejectionQueued||this._reject(t)};t.prototype.bind=function(n){var a=r(n)" - ",u=new t(e);u._propagateFrom(this,1);var c=this._target();if(a instanceof t)" - "{var l={promiseRejectionQueued:!1,promise:u,target:c,bindingPromise:a};c._th" - "en(e,i,u._progress,u,l),a._then(o,s,u._progress,u,l)}else u._setBoundTo(n),u" - "._resolveCallback(c);return u},t.prototype._setBoundTo=function(t){void 0!==" - "t?(this._bitField=131072|this._bitField,this._boundTo=t):this._bitField=this" - "._bitField&-131073},t.prototype._isBound=function(){return 131072===(131072&" - "this._bitField)},t.bind=function(n,i){var o=r(n),s=new t(e);return o instanc" - "eof t?o._then(function(t){s._setBoundTo(t),s._resolveCallback(i)},s._reject," - "s._progress,s,null):(s._setBoundTo(n),s._resolveCallback(i)),s}}},{}],4:[fun" - "ction(t,e,r){\"use strict\";function n(){try{Promise===o&&(Promise=i)}catch(" - "t){}return o}var i;\"undefined\"!=typeof Promise&&(i=Promise);var o=t(\"./pr" - "omise.js\")();o.noConflict=n,e.exports=o},{\"./promise.js\":23}],5:[function" - "(t,e,r){\"use strict\";var n=Object.create;if(n){var i=n(null),o=n(null);i[\"" - " size\"]=o[\" size\"]=0}e.exports=function(e){function r(t,r){var n;if(null!" - "=t&&(n=t[r]),\"function\"!=typeof n){var i=\"Object \"+a.classString(t)+\" h" - "as no method '\"+a.toString(r)+\"'\";throw new e.TypeError(i)}return n}funct" - "ion n(t){var e=this.pop(),n=r(t,e);return n.apply(t,this)}function i(t){retu" - "rn t[this]}function o(t){var e=+this;return e<0&&(e=Math.max(0,e+t.length))," - "t[e]}var s,a=t(\"./util.js\"),u=a.canEvaluate;a.isIdentifier;e.prototype.cal" - "l=function(t){for(var e=arguments.length,r=new Array(e-1),i=1;i32&&this.uncycle()}function r(t,e" - "){for(var r=0;r=0;--a)if(n[a]===o){s=a;break}for(var a=s;a>=0;--a){var u=n[a];if(e[i]!==u)" - "break;e.pop(),i--}e=n}}function o(t){for(var e=[],r=0;r0&&(e=e.slice(r)),e}f" - "unction a(t){var e;if(\"function\"==typeof t)e=\"[function \"+(t.name||\"ano" - "nymous\")+\"]\";else{e=t.toString();var r=/\\[object [a-zA-Z0-9$_]+\\]/;if(r" - ".test(e))try{var n=JSON.stringify(t);e=n}catch(t){}0===e.length&&(e=\"(empty" - " array)\")}return\"(<\"+u(e)+\">, no stack trace)\"}function u(t){var e=41;r" - "eturn t.length=0;--n){var o=e" - "[n].stack;void 0===r[o]&&(r[o]=n)}for(var n=0;n0&&(e[a-1]._parent=void 0,e[a-1]._length=1),e[n]." - "_parent=void 0,e[n]._length=1;var u=n>0?e[n-1]:this;a=0;--l)e[l]._length=c,c++;return}}}},e.p" - "rototype.parent=function(){return this._parent},e.prototype.hasParent=functi" - "on(){return void 0!==this._parent},e.prototype.attachExtraTrace=function(t){" - "if(!t.__stackCleaned__){this.uncycle();for(var s=e.parseStackAndMessage(t),a" - "=s.message,u=[s.stack],c=this;void 0!==c;)u.push(o(c.stack.split(\"\\n\")))," - "c=c._parent;i(u),n(u),p.notEnumerableProp(t,\"stack\",r(a,u)),p.notEnumerabl" - "eProp(t,\"__stackCleaned__\",!0)}},e.parseStackAndMessage=function(t){var e=" - "t.stack,r=t.toString();return e=\"string\"==typeof e&&e.length>0?s(t):[\" " - " (No stack trace)\"],{message:r,stack:o(e)}},e.formatAndLogError=function(t," - "e){if(\"undefined\"!=typeof console){var r;if(\"object\"==typeof t||\"functi" - "on\"==typeof t){var n=t.stack;r=e+d(n,t)}else r=e+String(t);\"function\"==ty" - "peof l?l(r):\"function\"!=typeof console.log&&\"object\"!=typeof console.log" - "||console.log(r)}},e.unhandledRejection=function(t){e.formatAndLogError(t,\"" - "^--- With additional stack trace: \")},e.isSupported=function(){return\"func" - "tion\"==typeof j},e.fireRejectionEvent=function(t,r,n,i){var o=!1;try{\"func" - "tion\"==typeof r&&(o=!0,\"rejectionHandled\"===t?r(i):r(n,i))}catch(t){h.thr" - "owLater(t)}var s=!1;try{s=b(t,n,i)}catch(t){s=!0,h.throwLater(t)}var a=!1;if" - "(m)try{a=m(t.toLowerCase(),{reason:n,promise:i})}catch(t){a=!0,h.throwLater(" - "t)}s||o||a||\"unhandledRejection\"!==t||e.formatAndLogError(n,\"Unhandled re" - "jection \")};var y=function(){return!1},g=/[\\/<\\(]([^:\\/]+):(\\d+):(?:\\d" - "+)\\)?\\s*$/;e.setBounds=function(t,r){if(e.isSupported()){for(var n,i,o=t.s" - "tack.split(\"\\n\"),s=r.stack.split(\"\\n\"),a=-1,u=-1,l=0;l" - "=u||(y=function(t){if(f.test(t))return!0;var e=c(t);return!!(e&&e.fileName==" - "=n&&a<=e.line&&e.line<=u)})}};var m,j=function(){var t=/^\\s*at\\s*/,e=funct" - "ion(t,e){return\"string\"==typeof t?t:void 0!==e.name&&void 0!==e.message?e." - "toString():a(e)};if(\"number\"==typeof Error.stackTraceLimit&&\"function\"==" - "typeof Error.captureStackTrace){Error.stackTraceLimit=Error.stackTraceLimit+" - "6,_=t,d=e;var r=Error.captureStackTrace;return y=function(t){return f.test(t" - ")},function(t,e){Error.stackTraceLimit=Error.stackTraceLimit+6,r(t,e),Error." - "stackTraceLimit=Error.stackTraceLimit-6}}var n=new Error;if(\"string\"==type" - "of n.stack&&n.stack.split(\"\\n\")[0].indexOf(\"stackDetection@\")>=0)return" - " _=/@/,d=e,v=!0,function(t){t.stack=(new Error).stack};var i;try{throw new E" - "rror}catch(t){i=\"stack\"in t}return\"stack\"in n||!i?(d=function(t,e){retur" - "n\"string\"==typeof t?t:\"object\"!=typeof e&&\"function\"!=typeof e||void 0" - "===e.name||void 0===e.message?a(e):e.toString()},null):(_=t,d=e,function(t){" - "Error.stackTraceLimit=Error.stackTraceLimit+6;try{throw new Error}catch(e){t" - ".stack=e.stack}Error.stackTraceLimit=Error.stackTraceLimit-6})}([]),b=functi" - "on(){if(p.isNode)return function(t,e,r){return\"rejectionHandled\"===t?proce" - "ss.emit(t,r):process.emit(t,e,r)};var t=!1,e=!0;try{var r=new self.CustomEve" - "nt(\"test\");t=r instanceof CustomEvent}catch(t){}if(!t)try{var n=document.c" - "reateEvent(\"CustomEvent\");n.initCustomEvent(\"testingtheevent\",!1,!0,{})," - "self.dispatchEvent(n)}catch(t){e=!1}e&&(m=function(e,r){var n;return t?n=new" - " self.CustomEvent(e,{detail:r,bubbles:!1,cancelable:!0}):self.dispatchEvent&" - "&(n=document.createEvent(\"CustomEvent\"),n.initCustomEvent(e,!1,!0,r)),!!n&" - "&!self.dispatchEvent(n)});var i={};return i.unhandledRejection=\"onunhandled" - "Rejection\".toLowerCase(),i.rejectionHandled=\"onrejectionHandled\".toLowerC" - "ase(),function(t,e,r){var n=i[t],o=self[n];return!!o&&(\"rejectionHandled\"=" - "==t?o.call(self,r):o.call(self,e,r),!0)}}();return\"undefined\"!=typeof cons" - "ole&&\"undefined\"!=typeof console.warn&&(l=function(t){console.warn(t)},p.i" - "sNode&&process.stderr.isTTY?l=function(t){process.stderr.write(\"\x1b[31m\"+" - "t+\"\x1b[39m\\n\")}:p.isNode||\"string\"!=typeof(new Error).stack||(l=functi" - "on(t){console.warn(\"%c\"+t,\"color: red\")})),e}},{\"./async.js\":2,\"./uti" - "l.js\":38}],8:[function(t,e,r){\"use strict\";e.exports=function(e){function" - " r(t,e,r){this._instances=t,this._callback=e,this._promise=r}function n(t,e)" - "{var r={},n=s(t).call(r,e);if(n===a)return n;var i=u(r);return i.length?(a.e" - "=new c(\"Catch filter must inherit from Error or be a simple predicate funct" - "ion\\n\\n See http://goo.gl/o84o68\\n\"),a):n}var i=t(\"./util.js\"),o=t(" - "\"./errors.js\"),s=i.tryCatch,a=i.errorObj,u=t(\"./es5.js\").keys,c=o.TypeEr" - "ror;return r.prototype.doFilter=function(t){for(var r=this._callback,i=this." - "_promise,o=i._boundTo,u=0,c=this._instances.length;u=0)return s[t]}var s=[];return n.prototyp" - "e._pushContext=function(){r()&&void 0!==this._trace&&s.push(this._trace)},n." - "prototype._popContext=function(){r()&&void 0!==this._trace&&s.pop()},t.proto" - "type._peekContext=o,t.prototype._pushContext=n.prototype._pushContext,t.prot" - "otype._popContext=n.prototype._popContext,i}},{}],10:[function(t,e,r){\"use " - "strict\";e.exports=function(e,r){var n,i,o=e._getDomain,s=t(\"./async.js\")," - "a=t(\"./errors.js\").Warning,u=t(\"./util.js\"),c=u.canAttachTrace,l=u.isNod" - "e&&(!!process.env.BLUEBIRD_DEBUG||\"development\"===process.env.NODE_ENV);re" - "turn l&&s.disableTrampolineIfNecessary(),e.prototype._ignoreRejections=funct" - "ion(){this._unsetRejectionIsUnhandled(),this._bitField=16777216|this._bitFie" - "ld},e.prototype._ensurePossibleRejectionHandled=function(){0===(16777216&thi" - "s._bitField)&&(this._setRejectionIsUnhandled(),s.invokeLater(this._notifyUnh" - "andledRejection,this,void 0))},e.prototype._notifyUnhandledRejectionIsHandle" - "d=function(){r.fireRejectionEvent(\"rejectionHandled\",n,void 0,this)},e.pro" - "totype._notifyUnhandledRejection=function(){if(this._isRejectionUnhandled())" - "{var t=this._getCarriedStackTrace()||this._settledValue;this._setUnhandledRe" - "jectionIsNotified(),r.fireRejectionEvent(\"unhandledRejection\",i,t,this)}}," - "e.prototype._setUnhandledRejectionIsNotified=function(){this._bitField=52428" - "8|this._bitField},e.prototype._unsetUnhandledRejectionIsNotified=function(){" - "this._bitField=this._bitField&-524289},e.prototype._isUnhandledRejectionNoti" - "fied=function(){return(524288&this._bitField)>0},e.prototype._setRejectionIs" - "Unhandled=function(){this._bitField=2097152|this._bitField},e.prototype._uns" - "etRejectionIsUnhandled=function(){this._bitField=this._bitField&-2097153,thi" - "s._isUnhandledRejectionNotified()&&(this._unsetUnhandledRejectionIsNotified(" - "),this._notifyUnhandledRejectionIsHandled())},e.prototype._isRejectionUnhand" - "led=function(){return(2097152&this._bitField)>0},e.prototype._setCarriedStac" - "kTrace=function(t){this._bitField=1048576|this._bitField,this._fulfillmentHa" - "ndler0=t},e.prototype._isCarryingStackTrace=function(){return(1048576&this._" - "bitField)>0},e.prototype._getCarriedStackTrace=function(){return this._isCar" - "ryingStackTrace()?this._fulfillmentHandler0:void 0},e.prototype._captureStac" - "kTrace=function(){return l&&(this._trace=new r(this._peekContext())),this},e" - ".prototype._attachExtraTrace=function(t,e){if(l&&c(t)){var n=this._trace;if(" - "void 0!==n&&e&&(n=n._parent),void 0!==n)n.attachExtraTrace(t);else if(!t.__s" - "tackCleaned__){var i=r.parseStackAndMessage(t);u.notEnumerableProp(t,\"stack" - "\",i.message+\"\\n\"+i.stack.join(\"\\n\")),u.notEnumerableProp(t,\"__stackC" - "leaned__\",!0)}}},e.prototype._warn=function(t){var e=new a(t),n=this._peekC" - "ontext();if(n)n.attachExtraTrace(e);else{var i=r.parseStackAndMessage(e);e.s" - "tack=i.message+\"\\n\"+i.stack.join(\"\\n\")}r.formatAndLogError(e,\"\")},e." - "onPossiblyUnhandledRejection=function(t){var e=o();i=\"function\"==typeof t?" - "null===e?t:e.bind(t):void 0},e.onUnhandledRejectionHandled=function(t){var e" - "=o();n=\"function\"==typeof t?null===e?t:e.bind(t):void 0},e.longStackTraces" - "=function(){if(s.haveItemsQueued()&&l===!1)throw new Error(\"cannot enable l" - "ong stack traces after promises have been created\\n\\n See http://goo.gl" - "/DT1qyG\\n\");l=r.isSupported(),l&&s.disableTrampolineIfNecessary()},e.hasLo" - "ngStackTraces=function(){return l&&r.isSupported()},r.isSupported()||(e.long" - "StackTraces=function(){},l=!1),function(){return l}}},{\"./async.js\":2,\"./" - "errors.js\":13,\"./util.js\":38}],11:[function(t,e,r){\"use strict\";var n=t" - "(\"./util.js\"),i=n.isPrimitive;e.exports=function(t){var e=function(){retur" - "n this},r=function(){throw this},n=function(){},o=function(){throw void 0},s" - "=function(t,e){return 1===e?function(){throw t}:2===e?function(){return t}:v" - "oid 0};t.prototype.return=t.prototype.thenReturn=function(t){return void 0==" - "=t?this.then(n):i(t)?this._then(s(t,2),void 0,void 0,void 0,void 0):this._th" - "en(e,void 0,void 0,t,void 0)},t.prototype.throw=t.prototype.thenThrow=functi" - "on(t){return void 0===t?this.then(o):i(t)?this._then(s(t,1),void 0,void 0,vo" - "id 0,void 0):this._then(r,void 0,void 0,t,void 0)}}},{\"./util.js\":38}],12:" - "[function(t,e,r){\"use strict\";e.exports=function(t,e){var r=t.reduce;t.pro" - "totype.each=function(t){return r(this,t,null,e)},t.each=function(t,n){return" - " r(t,n,null,e)}}},{}],13:[function(t,e,r){\"use strict\";function n(t,e){fun" - "ction r(n){return this instanceof r?(h(this,\"message\",\"string\"==typeof n" - "?n:e),h(this,\"name\",t),void(Error.captureStackTrace?Error.captureStackTrac" - "e(this,this.constructor):Error.call(this))):new r(n)}return l(r,Error),r}fun" - "ction i(t){return this instanceof i?(h(this,\"name\",\"OperationalError\"),h" - "(this,\"message\",t),this.cause=t,this.isOperational=!0,void(t instanceof Er" - "ror?(h(this,\"message\",t.message),h(this,\"stack\",t.stack)):Error.captureS" - "tackTrace&&Error.captureStackTrace(this,this.constructor))):new i(t)}var o,s" - ",a=t(\"./es5.js\"),u=a.freeze,c=t(\"./util.js\"),l=c.inherits,h=c.notEnumera" - "bleProp,p=n(\"Warning\",\"warning\"),f=n(\"CancellationError\",\"cancellatio" - "n error\"),_=n(\"TimeoutError\",\"timeout error\"),d=n(\"AggregateError\",\"" - "aggregate error\");try{o=TypeError,s=RangeError}catch(t){o=n(\"TypeError\",\"" - "type error\"),s=n(\"RangeError\",\"range error\")}for(var v=\"join pop push " - "shift unshift slice filter forEach some every map indexOf lastIndexOf reduce" - " reduceRight sort reverse\".split(\" \"),y=0;y0&&\"function\"==typeof arguments[e]){t=arguments[e];" - "var n}for(var i=arguments.length,o=new Array(i),s=0;s=1?[]:_,c.invoke(a,this,vo" - "id 0)}function a(){this._init$(void 0,-2)}function u(t,e,r,n){var i=\"object" - "\"==typeof r&&null!==r?r.concurrency:0;return i=\"number\"==typeof i&&isFini" - "te(i)&&i>=1?i:0,new s(t,e,i,n)}var c=t(\"./async.js\"),l=t(\"./util.js\"),h=" - "l.tryCatch,p=l.errorObj,f={},_=[];l.inherits(s,r),s.prototype._init=function" - "(){},s.prototype._promiseFulfilled=function(t,r){var n=this._values,o=this.l" - "ength(),s=this._preservedValues,a=this._limit;if(n[r]===f){if(n[r]=t,a>=1&&(" - "this._inFlight--,this._drainQueue(),this._isResolved()))return}else{if(a>=1&" - "&this._inFlight>=a)return n[r]=t,void this._queue.push(r);null!==s&&(s[r]=t)" - ";var u=this._callback,c=this._promise._boundTo;this._promise._pushContext();" - "var l=h(u).call(c,t,r,o);if(this._promise._popContext(),l===p)return this._r" - "eject(l.e);var _=i(l,this._promise);if(_ instanceof e){if(_=_._target(),_._i" - "sPending())return a>=1&&this._inFlight++,n[r]=f,_._proxyPromiseArray(this,r)" - ";if(!_._isFulfilled())return this._reject(_._reason());l=_._value()}n[r]=l}v" - "ar d=++this._totalResolved;d>=o&&(null!==s?this._filter(n,s):this._resolve(n" - "))},s.prototype._drainQueue=function(){for(var t=this._queue,e=this._limit,r" - "=this._values;t.length>0&&this._inFlight1){var n,i=new Array(r-1),o=0;for(n=0;n0" - "&&\"function\"!=typeof t&&\"function\"!=typeof e){var n=\".then() only accep" - "ts functions but was passed: \"+a.classString(t);arguments.length>1&&(n+=\"," - " \"+a.classString(e)),this._warn(n)}return this._then(t,e,r,void 0,void 0)}," - "e.prototype.done=function(t,e,r){var n=this._then(t,e,r,void 0,void 0);n._se" - "tIsFinal()},e.prototype.spread=function(t,e){return this.all()._then(t,e,voi" - "d 0,p,void 0)},e.prototype.isCancellable=function(){\n" - "return!this.isResolved()&&this._cancellable()},e.prototype.toJSON=function()" - "{var t={isFulfilled:!1,isRejected:!1,fulfillmentValue:void 0,rejectionReason" - ":void 0};return this.isFulfilled()?(t.fulfillmentValue=this.value(),t.isFulf" - "illed=!0):this.isRejected()&&(t.rejectionReason=this.reason(),t.isRejected=!" - "0),t},e.prototype.all=function(){return new d(this).promise()},e.prototype.e" - "rror=function(t){return this.caught(a.originatesFromRejection,t)},e.is=funct" - "ion(t){return t instanceof e},e.fromNode=function(t){var r=new e(h),n=k(t)(b" - "(r));return n===w&&r._rejectCallback(n.e,!0,!0),r},e.all=function(t){return " - "new d(t).promise()},e.defer=e.pending=function(){var t=new e(h);return new j" - "(t)},e.cast=function(t){var r=_(t);if(!(r instanceof e)){var n=r;r=new e(h)," - "r._fulfillUnchecked(n)}return r},e.resolve=e.fulfilled=e.cast,e.reject=e.rej" - "ected=function(t){var r=new e(h);return r._captureStackTrace(),r._rejectCall" - "back(t,!0),r},e.setScheduler=function(t){if(\"function\"!=typeof t)throw new" - " l(\"fn must be a function\\n\\n See http://goo.gl/916lJJ\\n\");var e=u._" - "schedule;return u._schedule=t,e},e.prototype._then=function(t,r,i,o,s){var a" - "=void 0!==s,c=a?s:new e(h);a||(c._propagateFrom(this,5),c._captureStackTrace" - "());var l=this._target();l!==this&&(void 0===o&&(o=this._boundTo),a||c._setI" - "sMigrated());var p=l._addCallbacks(t,r,i,c,o,n());return l._isResolved()&&!l" - "._isSettlePromisesQueued()&&u.invoke(l._settlePromiseAtPostResolution,l,p),c" - "},e.prototype._settlePromiseAtPostResolution=function(t){this._isRejectionUn" - "handled()&&this._unsetRejectionIsUnhandled(),this._settlePromiseAt(t)},e.pro" - "totype._length=function(){return 131071&this._bitField},e.prototype._isFollo" - "wingOrFulfilledOrRejected=function(){return(939524096&this._bitField)>0},e.p" - "rototype._isFollowing=function(){return 536870912===(536870912&this._bitFiel" - "d)},e.prototype._setLength=function(t){this._bitField=this._bitField&-131072" - "|131071&t},e.prototype._setFulfilled=function(){this._bitField=268435456|thi" - "s._bitField},e.prototype._setRejected=function(){this._bitField=134217728|th" - "is._bitField},e.prototype._setFollowing=function(){this._bitField=536870912|" - "this._bitField},e.prototype._setIsFinal=function(){this._bitField=33554432|t" - "his._bitField},e.prototype._isFinal=function(){return(33554432&this._bitFiel" - "d)>0},e.prototype._cancellable=function(){return(67108864&this._bitField)>0}" - ",e.prototype._setCancellable=function(){this._bitField=67108864|this._bitFie" - "ld},e.prototype._unsetCancellable=function(){this._bitField=this._bitField&-" - "67108865},e.prototype._setIsMigrated=function(){this._bitField=4194304|this." - "_bitField},e.prototype._unsetIsMigrated=function(){this._bitField=this._bitF" - "ield&-4194305},e.prototype._isMigrated=function(){return(4194304&this._bitFi" - "eld)>0},e.prototype._receiverAt=function(t){var e=0===t?this._receiver0:this" - "[5*t-5+4];return void 0===e&&this._isBound()?this._boundTo:e},e.prototype._p" - "romiseAt=function(t){return 0===t?this._promise0:this[5*t-5+3]},e.prototype." - "_fulfillmentHandlerAt=function(t){return 0===t?this._fulfillmentHandler0:thi" - "s[5*t-5+0]},e.prototype._rejectionHandlerAt=function(t){return 0===t?this._r" - "ejectionHandler0:this[5*t-5+1]},e.prototype._migrateCallbacks=function(t,r){" - "var n=t._fulfillmentHandlerAt(r),i=t._rejectionHandlerAt(r),o=t._progressHan" - "dlerAt(r),s=t._promiseAt(r),a=t._receiverAt(r);s instanceof e&&s._setIsMigra" - "ted(),this._addCallbacks(n,i,o,s,a,null)},e.prototype._addCallbacks=function" - "(t,e,r,n,i,o){var s=this._length();if(s>=131066&&(s=0,this._setLength(0)),0=" - "==s)this._promise0=n,void 0!==i&&(this._receiver0=i),\"function\"!=typeof t|" - "|this._isCarryingStackTrace()||(this._fulfillmentHandler0=null===o?t:o.bind(" - "t)),\"function\"==typeof e&&(this._rejectionHandler0=null===o?e:o.bind(e)),\"" - "function\"==typeof r&&(this._progressHandler0=null===o?r:o.bind(r));else{var" - " a=5*s-5;this[a+3]=n,this[a+4]=i,\"function\"==typeof t&&(this[a+0]=null===o" - "?t:o.bind(t)),\"function\"==typeof e&&(this[a+1]=null===o?e:o.bind(e)),\"fun" - "ction\"==typeof r&&(this[a+2]=null===o?r:o.bind(r))}return this._setLength(s" - "+1),s},e.prototype._setProxyHandlers=function(t,e){var r=this._length();if(r" - ">=131066&&(r=0,this._setLength(0)),0===r)this._promise0=e,this._receiver0=t;" - "else{var n=5*r-5;this[n+3]=e,this[n+4]=t}this._setLength(r+1)},e.prototype._" - "proxyPromiseArray=function(t,e){this._setProxyHandlers(t,e)},e.prototype._re" - "solveCallback=function(t,r){if(!this._isFollowingOrFulfilledOrRejected()){if" - "(t===this)return this._rejectCallback(i(),!1,!0);var n=_(t,this);if(!(n inst" - "anceof e))return this._fulfill(t);var o=1|(r?4:0);this._propagateFrom(n,o);v" - "ar s=n._target();if(s._isPending()){for(var a=this._length(),u=0;u0&&t._cancellabl" - "e()&&(this._setCancellable(),this._cancellationParent=t),(4&e)>0&&t._isBound" - "()&&this._setBoundTo(t._boundTo)},e.prototype._fulfill=function(t){this._isF" - "ollowingOrFulfilledOrRejected()||this._fulfillUnchecked(t)},e.prototype._rej" - "ect=function(t,e){this._isFollowingOrFulfilledOrRejected()||this._rejectUnch" - "ecked(t,e)},e.prototype._settlePromiseAt=function(t){var r=this._promiseAt(t" - "),n=r instanceof e;if(n&&r._isMigrated())return r._unsetIsMigrated(),u.invok" - "e(this._settlePromiseAt,this,t);var i=this._isFulfilled()?this._fulfillmentH" - "andlerAt(t):this._rejectionHandlerAt(t),o=this._isCarryingStackTrace()?this." - "_getCarriedStackTrace():void 0,s=this._settledValue,a=this._receiverAt(t);th" - "is._clearCallbackDataAtIndex(t),\"function\"==typeof i?n?this._settlePromise" - "FromHandler(i,a,s,r):i.call(a,s,r):a instanceof d?a._isResolved()||(this._is" - "Fulfilled()?a._promiseFulfilled(s,r):a._promiseRejected(s,r)):n&&(this._isFu" - "lfilled()?r._fulfill(s):r._reject(s,o)),t>=4&&4===(31&t)&&u.invokeLater(this" - "._setLength,this,0)},e.prototype._clearCallbackDataAtIndex=function(t){if(0=" - "==t)this._isCarryingStackTrace()||(this._fulfillmentHandler0=void 0),this._r" - "ejectionHandler0=this._progressHandler0=this._receiver0=this._promise0=void " - "0;else{var e=5*t-5;this[e+3]=this[e+4]=this[e+0]=this[e+1]=this[e+2]=void 0}" - "},e.prototype._isSettlePromisesQueued=function(){return(this._bitField&-1073" - "741824)===-1073741824},e.prototype._setSettlePromisesQueued=function(){this." - "_bitField=this._bitField|-1073741824},e.prototype._unsetSettlePromisesQueued" - "=function(){this._bitField=1073741823&this._bitField},e.prototype._queueSett" - "lePromises=function(){u.settlePromises(this),this._setSettlePromisesQueued()" - "},e.prototype._fulfillUnchecked=function(t){if(t===this){var e=i();return th" - "is._attachExtraTrace(e),this._rejectUnchecked(e,void 0)}this._setFulfilled()" - ",this._settledValue=t,this._cleanValues(),this._length()>0&&this._queueSettl" - "ePromises()},e.prototype._rejectUncheckedCheckError=function(t){var e=a.ensu" - "reErrorObject(t);this._rejectUnchecked(t,e===t?void 0:e)},e.prototype._rejec" - "tUnchecked=function(t,e){if(t===this){var r=i();return this._attachExtraTrac" - "e(r),this._rejectUnchecked(r)}return this._setRejected(),this._settledValue=" - "t,this._cleanValues(),this._isFinal()?void u.throwLater(function(t){throw\"s" - "tack\"in t&&u.invokeFirst(v.unhandledRejection,void 0,t),t},void 0===e?t:e):" - "(void 0!==e&&e!==t&&this._setCarriedStackTrace(e),void(this._length()>0?this" - "._queueSettlePromises():this._ensurePossibleRejectionHandled()))},e.prototyp" - "e._settlePromises=function(){this._unsetSettlePromisesQueued();for(var t=thi" - "s._length(),e=0;e=this._leng" - "th&&this._resolve(this._values)},s.prototype._promiseRejected=function(t,e){" - "this._totalResolved++,this._reject(t)},s.prototype.shouldCopyValues=function" - "(){return!0},s.prototype.getActualLength=function(t){return t},s}},{\"./util" - ".js\":38}],25:[function(t,e,r){\"use strict\";function n(t){return t instanc" - "eof Error&&f.getPrototypeOf(t)===Error.prototype}function i(t){var e;if(n(t)" - "){e=new h(t),e.name=t.name,e.message=t.message,e.stack=t.stack;for(var r=f.k" - "eys(t),i=0;i2){for(var o=arguments.length,s=new Array(o-1),a=1;a=this._length){for(var n={},i=this.l" - "ength(),o=0,s=this.length();o>1},e.prototype.props=function(){return s(this)},e.props=function(t){retur" - "n s(t)}}},{\"./es5.js\":14,\"./util.js\":38}],28:[function(t,e,r){\"use stri" - "ct\";function n(t,e,r,n,i){for(var o=0;o=this._length&&this._resolve(this._values)},n.prototype." - "_promiseFulfilled=function(t,e){var r=new i;r._bitField=268435456,r._settled" - "Value=t,this._promiseResolved(e,r)},n.prototype._promiseRejected=function(t," - "e){var r=new i;r._bitField=134217728,r._settledValue=t,this._promiseResolved" - "(e,r)},e.settle=function(t){return new n(t).promise()},e.prototype.settle=fu" - "nction(){return new n(this).promise()}}},{\"./util.js\":38}],33:[function(t," - "e,r){\"use strict\";e.exports=function(e,r,n){function i(t){this.constructor" - "$(t),this._howMany=0,this._unwrap=!1,this._initialized=!1}function o(t,e){if" - "((0|e)!==e||e<0)return n(\"expecting a positive integer\\n\\n See http://" - "goo.gl/1wAmHx\\n\");var r=new i(t),o=r.promise();return r.setHowMany(e),r.in" - "it(),o}var s=t(\"./util.js\"),a=t(\"./errors.js\").RangeError,u=t(\"./errors" - ".js\").AggregateError,c=s.isArray;s.inherits(i,r),i.prototype._init=function" - "(){if(this._initialized){if(0===this._howMany)return void this._resolve([]);" - "this._init$(void 0,-5);var t=c(this._values);!this._isResolved()&&t&&this._h" - "owMany>this._canPossiblyFulfill()&&this._reject(this._getRangeError(this.len" - "gth()))}},i.prototype.init=function(){this._initialized=!0,this._init()},i.p" - "rototype.setUnwrap=function(){this._unwrap=!0},i.prototype.howMany=function(" - "){return this._howMany},i.prototype.setHowMany=function(t){this._howMany=t}," - "i.prototype._promiseFulfilled=function(t){this._addFulfilled(t),this._fulfil" - "led()===this.howMany()&&(this._values.length=this.howMany(),1===this.howMany" - "()&&this._unwrap?this._resolve(this._values[0]):this._resolve(this._values))" - "},i.prototype._promiseRejected=function(t){if(this._addRejected(t),this.howM" - "any()>this._canPossiblyFulfill()){for(var e=new u,r=this.length();r0},e.prototype.i" - "sRejected=t.prototype._isRejected=function(){return(134217728&this._bitField" - ")>0},e.prototype.isPending=t.prototype._isPending=function(){return 0===(402" - "653184&this._bitField)},e.prototype.isResolved=t.prototype._isResolved=funct" - "ion(){return(402653184&this._bitField)>0},t.prototype.isPending=function(){r" - "eturn this._target()._isPending()},t.prototype.isRejected=function(){return " - "this._target()._isRejected()},t.prototype.isFulfilled=function(){return this" - "._target()._isFulfilled()},t.prototype.isResolved=function(){return this._ta" - "rget()._isResolved()},t.prototype._value=function(){return this._settledValu" - "e},t.prototype._reason=function(){return this._unsetRejectionIsUnhandled(),t" - "his._settledValue},t.prototype.value=function(){var t=this._target();if(!t.i" - "sFulfilled())throw new TypeError(\"cannot get fulfillment value of a non-ful" - "filled promise\\n\\n See http://goo.gl/hc1DLj\\n\");return t._settledValu" - "e},t.prototype.reason=function(){var t=this._target();if(!t.isRejected())thr" - "ow new TypeError(\"cannot get rejection reason of a non-rejected promise\\n\\" - "n See http://goo.gl/hPuiwB\\n\");return t._unsetRejectionIsUnhandled(),t." - "_settledValue},t.PromiseInspection=e}},{}],35:[function(t,e,r){\"use strict\"" - ";e.exports=function(e,r){function n(t,n){if(c(t)){if(t instanceof e)return t" - ";if(o(t)){var l=new e(r);return t._then(l._fulfillUnchecked,l._rejectUncheck" - "edCheckError,l._progressUnchecked,l,null),l}var h=a.tryCatch(i)(t);if(h===u)" - "{n&&n._pushContext();var l=e.reject(h.e);return n&&n._popContext(),l}if(\"fu" - "nction\"==typeof h)return s(t,h,n)}return t}function i(t){return t.then}func" - "tion o(t){return l.call(t,\"_promise0\")}function s(t,n,i){function o(t){l&&" - "(l._resolveCallback(t),l=null)}function s(t){l&&(l._rejectCallback(t,p,!0),l" - "=null)}function c(t){l&&\"function\"==typeof l._progress&&l._progress(t)}var" - " l=new e(r),h=l;i&&i._pushContext(),l._captureStackTrace(),i&&i._popContext(" - ");var p=!0,f=a.tryCatch(n).call(t,o,s,c);return p=!1,l&&f===u&&(l._rejectCal" - "lback(f.e,!0,!0),l=null),h}var a=t(\"./util.js\"),u=a.errorObj,c=a.isObject," - "l={}.hasOwnProperty;return n}},{\"./util.js\":38}],36:[function(t,e,r){\"use" - " strict\";e.exports=function(e,r){function n(t){var e=this;return e instance" - "of Number&&(e=+e),clearTimeout(e),t}function i(t){var e=this;throw e instanc" - "eof Number&&(e=+e),clearTimeout(e),t}var o=t(\"./util.js\"),s=e.TimeoutError" - ",a=function(t,e){if(t.isPending()){\"string\"!=typeof e&&(e=\"operation time" - "d out\");var r=new s(e);o.markAsOriginatingFromRejection(r),t._attachExtraTr" - "ace(r),t._cancel(r)}},u=function(t){return c(+this).thenReturn(t)},c=e.delay" - "=function(t,n){if(void 0===n){n=t,t=void 0;var i=new e(r);return setTimeout(" - "function(){i._fulfill()},n),i}return n=+n,e.resolve(t)._then(u,null,null,n,v" - "oid 0)};e.prototype.delay=function(t){return c(this,t)},e.prototype.timeout=" - "function(t,e){t=+t;var r=this.then().cancellable();r._cancellationParent=thi" - "s;var o=setTimeout(function(){a(r,e)},t);return r._then(n,i,void 0,o,void 0)" - "}}},{\"./util.js\":38}],37:[function(t,e,r){\"use strict\";e.exports=functio" - "n(e,r,n,i){function o(t){for(var r=t.length,n=0;n=u)return c.resolve();var l=a(t[o++]);if(l instanceof e&&l._i" - "sDisposable()){try{l=n(l._getDisposer().tryDispose(r),t.promise)}catch(t){re" - "turn s(t)}if(l instanceof e)return l._then(i,s,null,null,null)}i()}var o=0,u" - "=t.length,c=e.defer();return i(),c.promise}function c(t){var e=new v;return " - "e._settledValue=t,e._bitField=268435456,u(this,e).thenReturn(t)}function l(t" - "){var e=new v;return e._settledValue=t,e._bitField=134217728,u(this,e).thenT" - "hrow(t)}function h(t,e,r){this._data=t,this._promise=e,this._context=r}funct" - "ion p(t,e,r){this.constructor$(t,e,r)}function f(t){return h.isDisposer(t)?(" - "this.resources[this.index]._setDisposable(t),t.promise()):t}var _=t(\"./erro" - "rs.js\").TypeError,d=t(\"./util.js\").inherits,v=e.PromiseInspection;h.proto" - "type.data=function(){return this._data},h.prototype.promise=function(){retur" - "n this._promise},h.prototype.resource=function(){return this.promise().isFul" - "filled()?this.promise().value():null},h.prototype.tryDispose=function(t){var" - " e=this.resource(),r=this._context;void 0!==r&&r._pushContext();var n=null!=" - "=e?this.doDispose(e,t):null;return void 0!==r&&r._popContext(),this._promise" - "._unsetDisposable(),this._data=null,n},h.isDisposer=function(t){return null!" - "=t&&\"function\"==typeof t.resource&&\"function\"==typeof t.tryDispose},d(p," - "h),p.prototype.doDispose=function(t,e){var r=this.data();return r.call(t,t,e" - ")},e.using=function(){var t=arguments.length;if(t<2)return r(\"you must pass" - " at least 2 arguments to Promise.using\");var i=arguments[t-1];if(\"function" - "\"!=typeof i)return r(\"fn must be a function\\n\\n See http://goo.gl/916" - "lJJ\\n\");t--;for(var s=new Array(t),a=0;a0},e.prototype._getDisposer=function(){return thi" - "s._disposer},e.prototype._unsetDisposable=function(){\n" - "this._bitField=this._bitField&-262145,this._disposer=void 0},e.prototype.dis" - "poser=function(t){if(\"function\"==typeof t)return new p(t,this,i());throw n" - "ew _}}},{\"./errors.js\":13,\"./util.js\":38}],38:[function(t,e,r){\"use str" - "ict\";function n(){try{return T.apply(this,arguments)}catch(t){return C.e=t," - "C}}function i(t){return T=t,n}function o(t){return null==t||t===!0||t===!1||" - "\"string\"==typeof t||\"number\"==typeof t}function s(t){return!o(t)}functio" - "n a(t){return o(t)?new Error(v(t)):t}function u(t,e){var r,n=t.length,i=new " - "Array(n+1);for(r=0;r1||e.length>0&&(1!==e.length||\"constructor\"!==e[0])||R.test(" - "t+\"\"))return!0}return!1}catch(t){return!1}}function f(t){function e(){}e.p" - "rototype=t;for(var r=8;r--;)new e;return t}function _(t){return S.test(t)}fu" - "nction d(t,e,r){for(var n=new Array(t),i=0;i10||t[0]>0}(),O.isNode&&O.toFastProperties(process);try{throw new " - "Error}catch(t){O.lastLineError=t}e.exports=O},{\"./es5.js\":14}]},{},[4])(4)" - "}),\"undefined\"!=typeof window&&null!==window?window.P=window.Promise:\"und" - "efined\"!=typeof self&&null!==self&&(self.P=self.Promise);\n" - "}).call(this,require('_process'),typeof global !== \"undefined\" ? global : " - "typeof self !== \"undefined\" ? self : typeof window !== \"undefined\" ? win" - "dow : {})\n" - "},{\"_process\":\"/rethinkdb/build/external/admin-deps_2.0.4/node_modules/re" - "thinkdb-webui/node_modules/browserify/node_modules/process/browser.js\"}],\"" - "/rethinkdb/build/packages/js/proto-def.js\":[function(require,module,exports" - "){\n" - "module.exports={VersionDummy:{Version:{V0_1:1063369270,V0_2:1915781601,V0_3:" - "1601562686,V0_4:1074539808,V1_0:885177795},Protocol:{PROTOBUF:656407617,JSON" - ":2120839367}},Query:{QueryType:{START:1,CONTINUE:2,STOP:3,NOREPLY_WAIT:4,SER" - "VER_INFO:5},AssocPair:{}},Frame:{FrameType:{POS:1,OPT:2}},Backtrace:{},Respo" - "nse:{ResponseType:{SUCCESS_ATOM:1,SUCCESS_SEQUENCE:2,SUCCESS_PARTIAL:3,WAIT_" - "COMPLETE:4,SERVER_INFO:5,CLIENT_ERROR:16,COMPILE_ERROR:17,RUNTIME_ERROR:18}," - "ErrorType:{INTERNAL:1e6,RESOURCE_LIMIT:2e6,QUERY_LOGIC:3e6,NON_EXISTENCE:31e" - "5,OP_FAILED:41e5,OP_INDETERMINATE:42e5,USER:5e6,PERMISSION_ERROR:6e6},Respon" - "seNote:{SEQUENCE_FEED:1,ATOM_FEED:2,ORDER_BY_LIMIT_FEED:3,UNIONED_FEED:4,INC" - "LUDES_STATES:5}},Datum:{DatumType:{R_NULL:1,R_BOOL:2,R_NUM:3,R_STR:4,R_ARRAY" - ":5,R_OBJECT:6,R_JSON:7},AssocPair:{}},Term:{TermType:{DATUM:1,MAKE_ARRAY:2,M" - "AKE_OBJ:3,VAR:10,JAVASCRIPT:11,UUID:169,HTTP:153,ERROR:12,IMPLICIT_VAR:13,DB" - ":14,TABLE:15,GET:16,GET_ALL:78,EQ:17,NE:18,LT:19,LE:20,GT:21,GE:22,NOT:23,AD" - "D:24,SUB:25,MUL:26,DIV:27,MOD:28,FLOOR:183,CEIL:184,ROUND:185,APPEND:29,PREP" - "END:80,DIFFERENCE:95,SET_INSERT:88,SET_INTERSECTION:89,SET_UNION:90,SET_DIFF" - "ERENCE:91,SLICE:30,SKIP:70,LIMIT:71,OFFSETS_OF:87,CONTAINS:93,GET_FIELD:31,K" - "EYS:94,VALUES:186,OBJECT:143,HAS_FIELDS:32,WITH_FIELDS:96,PLUCK:33,WITHOUT:3" - "4,MERGE:35,BETWEEN_DEPRECATED:36,BETWEEN:182,REDUCE:37,MAP:38,FOLD:187,FILTE" - "R:39,CONCAT_MAP:40,ORDER_BY:41,DISTINCT:42,COUNT:43,IS_EMPTY:86,UNION:44,NTH" - ":45,BRACKET:170,INNER_JOIN:48,OUTER_JOIN:49,EQ_JOIN:50,ZIP:72,RANGE:173,INSE" - "RT_AT:82,DELETE_AT:83,CHANGE_AT:84,SPLICE_AT:85,COERCE_TO:51,TYPE_OF:52,UPDA" - "TE:53,DELETE:54,REPLACE:55,INSERT:56,DB_CREATE:57,DB_DROP:58,DB_LIST:59,TABL" - "E_CREATE:60,TABLE_DROP:61,TABLE_LIST:62,CONFIG:174,STATUS:175,WAIT:177,RECON" - "FIGURE:176,REBALANCE:179,SYNC:138,GRANT:188,INDEX_CREATE:75,INDEX_DROP:76,IN" - "DEX_LIST:77,INDEX_STATUS:139,INDEX_WAIT:140,INDEX_RENAME:156,SET_WRITE_HOOK:" - "189,GET_WRITE_HOOK:190,FUNCALL:64,BRANCH:65,OR:66,AND:67,FOR_EACH:68,FUNC:69" - ",ASC:73,DESC:74,INFO:79,MATCH:97,UPCASE:141,DOWNCASE:142,SAMPLE:81,DEFAULT:9" - "2,JSON:98,ISO8601:99,TO_ISO8601:100,EPOCH_TIME:101,TO_EPOCH_TIME:102,NOW:103" - ",IN_TIMEZONE:104,DURING:105,DATE:106,TIME_OF_DAY:126,TIMEZONE:127,YEAR:128,M" - "ONTH:129,DAY:130,DAY_OF_WEEK:131,DAY_OF_YEAR:132,HOURS:133,MINUTES:134,SECON" - "DS:135,TIME:136,MONDAY:107,TUESDAY:108,WEDNESDAY:109,THURSDAY:110,FRIDAY:111" - ",SATURDAY:112,SUNDAY:113,JANUARY:114,FEBRUARY:115,MARCH:116,APRIL:117,MAY:11" - "8,JUNE:119,JULY:120,AUGUST:121,SEPTEMBER:122,OCTOBER:123,NOVEMBER:124,DECEMB" - "ER:125,LITERAL:137,GROUP:144,SUM:145,AVG:146,MIN:147,MAX:148,SPLIT:149,UNGRO" - "UP:150,RANDOM:151,CHANGES:152,ARGS:154,BINARY:155,GEOJSON:157,TO_GEOJSON:158" - ",POINT:159,LINE:160,POLYGON:161,DISTANCE:162,INTERSECTS:163,INCLUDES:164,CIR" - "CLE:165,GET_INTERSECTING:166,FILL:167,GET_NEAREST:168,POLYGON_SUB:171,TO_JSO" - "N_STRING:172,MINVAL:180,MAXVAL:181,BIT_AND:191,BIT_OR:192,BIT_XOR:193,BIT_NO" - "T:194,BIT_SAL:195,BIT_SAR:196},AssocPair:{}}};\n" - "},{}],\"/rethinkdb/build/packages/js/util.js\":[function(require,module,expo" - "rts){\n" - "(function (Buffer){\n" - "var convertPseudotype,err,errorClass,mkAtom,mkErr,mkSeq,plural,protoErrorTyp" - "e,protodef,recursivelyConvertPseudotype,slice=[].slice;err=require(\"./error" - "s\"),protodef=require(\"./proto-def\"),protoErrorType=protodef.Response.Erro" - "rType,plural=function(r){return 1===r?\"\":\"s\"},module.exports.ar=function" - "(r){return function(){var e;if(e=1<=arguments.length?slice.call(arguments,0)" - ":[],e.length!==r.length)throw new err.ReqlDriverCompileError(\"Expected \"+r" - ".length+\" argument\"+plural(r.length)+\" but found \"+e.length+\".\");retur" - "n r.apply(this,e)}},module.exports.varar=function(r,e,t){return function(){v" - "ar o;if(o=1<=arguments.length?slice.call(arguments,0):[],null!=r&&o.lengthe){if(null!=r&&null==e)throw new err.ReqlDriverCompileEr" - "ror(\"Expected \"+r+\" or more arguments but found \"+o.length+\".\");if(nul" - "l!=e&&null==r)throw new err.ReqlDriverCompileError(\"Expected \"+e+\" or few" - "er arguments but found \"+o.length+\".\");throw new err.ReqlDriverCompileErr" - "or(\"Expected between \"+r+\" and \"+e+\" arguments but found \"+o.length+\"" - ".\")}return t.apply(this,o)}},module.exports.aropt=function(r){return functi" - "on(){var e,t,o,n;if(e=1<=arguments.length?slice.call(arguments,0):[],t=r.len" - "gth-1,n=e[t],null!=n&&\"[object Object]\"!==Object.prototype.toString.call(n" - ")&&(n=null),o=e.length-(null!=n?1:0),t!==o)throw 1!==t?new err.ReqlDriverCom" - "pileError(\"Expected \"+t+\" arguments (not including options) but found \"+" - "o+\".\"):new err.ReqlDriverCompileError(\"Expected \"+t+\" argument (not inc" - "luding options) but found \"+o+\".\");return r.apply(this,e)}},module.export" - "s.toArrayBuffer=function(r){var e,t,o,n,u;for(e=new Uint8Array(new ArrayBuff" - "er(r.length)),t=o=0,n=r.length;o \x88\xa5\x12\xfc\0\0\0\xa8IDATx\xda\x62\xbcp\xe1\x42\5\3\3\x43;\3" - "n\xf0\x1f\x88s\xf4\xf5\xf5\xa7\xa1K01\x10\6\x8c@<\xf5\xd1\xa3G+\xd0%X`\f111\6" - "III\f\x9dO\x9f>ex\xf3\xe6\r\xc3\xfb\xf7\xef\xc3\x41|99\xb9\b\\6w@m\x82\x63\xa0" - "\xc6J\x98$\xc8\0\xa0\vV\x92\xe2l\x14\0\x34 \xec\xd9\xb3g\xf3P\x9c\x8d\v\0\3\xaa" - "\3\xcal\x87y\x81\x91\x91\x31\x11\xc8\xdf\x45\xac\xcd\x1d\xe8^\0\x99K\xb2\xb3" - "I\x8d\xaaQ\xcd\xd4\xd2\fO$\xaf^\xbd\2\x61P\xe\xab\xa0\x8b\xcd\f\xff\xff\xff\a" - "\xe1\x8a\xff\xa4\x83v\x80\0\3\0\xc8\x8ap\xe0\x8a[\x83\x1c\0\0\0\0IEND\xae\x42" - "`\x82", - 1078 ) }, - { "/images/cog-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x11\0\0\0\x11\b\6\0\0\0;mG\xfa\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 5\xd6L\x8e\0\0\2\x15IDATx\xda" - "\x8cSMK\x1bQ\x14}\x93\x99\xc4\x42\"\t\xf9 &1\xc6\x85.\f\x15\x12\x1b\x91\xea\xa6" - "\x14\xff\x80\v7.\xfdh\xd7\x85\xee\v*\xfe\0\xb7\n" - "V\xda\x95+\xff\x80\xe0\xc6\x8d\xc9\x42\x14ta!d\x93\x90o\xd1\xa8\xc1|\xf5\x9c" - "8/\x8c\x83\x62\x1e\x1c\xe6\xde\xfb\xee\xbb\xef\x9e\xf3\xee(\xc9\x64R\xbc\xb1" - ">\0\xa7\x80\v\x98\1r\xaf%%\x12\ta1\xf8*0`\xf0\x97\x80I \x1c\f\6/\x91\x1c\xd0" - "\xe3\x9a\x8e\xde\xd2\f\5N\x80)EQ\xfet:\x9d,\xec\x35\x99T(\x14\\\xb5Z\xed\0\x66" - "\n" - "X\5\xfe\xf9|\xbe\xb9\x62\xb1\xf8\xc0}E\xa7\xc3\"U`P\xf4\xb9\xe2\xf1\xf8\xb9\xaa" - "\xaa\xd3\x30\x9f$\x9d\x16\xb0\xd7o\1\x8f\xc7#P\xe0\xa3\xae\xdb\vn7\xd2\0%\x11" - "\b\4\x84\xd7\xeb\xed\xfa\xa5RI\xe4r9\1\x9a\xcf\xdcU6.\xfe\xa6R\xa9[)\xac\xf\xf8" - "\1|\x97\x45X\0\x62\xb6l6\xdb/\x80\xc2\x8e\xb8\xdd\xee#\xb9_.\x97\x45&\x93\x19" - "c\xaa\xec\x84\xcf\x38jn\x17k\3\x37\xad\x1b\xc2\x8b@\xa5\xcb\xbd\xd5\x12\x10u" - "\x16\x1d]\xc1\x9d`'\xf7\x66\xce\xa4\x83\xb5\xff\x9e\x36V\xab\xd5\xc9&Xd:\x1c" - "\xeg\x11\xe8mR\3\xac\x8e\xe9\xccjo\xa0\xa0I$\x12\x11\xd1h\x94\xee\x1d\x8b<\xfa" - "\xfd\xfe\x31\xbb\xdd~(\x93(b:\x9d\xde\x85\x39\4\x84\0\xd2\xda\x92\xfb\x14\x1c" - "sR\xb5X,\xdf\xe0~\xea\xbe\xe\xb8?r\x80\x64\x12_\1\xe2\xcd\xbf\x35\xea\x8d\x46" - "\x83\x9f\xdf\x38\xb7\x63\x1c{\x8a\xb0\xdc\xef\x9cT*\x15\x16\x1a\x91\xbe,B\xfe" - "\x17\x14\x94\xad\x86\x42!a\xd4H\xd3\xb4\xee\xb3\x83\xb6\0\5\xe1p8\x18[@\x17." - "\xf3\xb0}\xc5(\x1f#\xe9\x33\a\xa9\xd9l:\xf3\xf9\xfc\2\x37x\x18\x45\x8a\x30wq" - "\x81\x1d\x39+\xb0\xcf(\xaa\xb9H\x1b\x9b_\xf0\xb5\x81k\x9d#\x8d\xdb\xae\x11\x1b" - "\xd6\xe7\x66\x1b\xf1M\xfd\xf7\xff\xc9q\x81\xdf\xa6&/~i\6\xf1\xa9\xebn=\x16\x8b" - "\x8d\xeb\xc3\xe8\xa6\x90\x86\xbc\x86\xf1\xdc\x7f\1\6\0WZ\xa1\x93\xf1\x1b\xd6" - "\x9a\0\0\0\0IEND\xae\x42`\x82", - 1443 ) }, - { "/images/green-light.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\v\0\0\0\v\b\6\0\0\0\xa9\xacw&\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \x94w\b\xef\0\0\1\x34IDATx\xda" - "\x84\x8f\xbbJCA\x14\x45\xcf\xcc\xdc\x98\xa7\n" - "&\xe1\"\xd8\4\xecl\x84\xe0\x17XED-\xc5\x42\x10THm\xa1\x9f\xe0g\x88\x36\xa6\x11" - "\v\xc5hc!Dl\x14\x11\xc5\x42\x8c\x85J\x12\x42nr_I\xbcs\xe6\x38>\x1a\x85\x90\5" - "\x1b\x36\x9bu\x8a\xc3\xe0\x97|qa\\\b\xb1\xa9kNg\x14\b*DtlW\xbd\xed\xbd\xe5\xd3" - "\xb2\xde\x88}\x89\xebG\xb3\x33\"$\n" - "\xba&\xe0?\4\x8e\xd7l/\xed.\x9e\x9d\xb0\xf9\x9d\xa9L\xdaL\xdd\xe9\x39\xe\xbd" - " p\x1f/\x9e\xb3\x46l8\xb2\xf5\xa1\xba\xbd\xc5\x1f\x12\xe6\xc4\xc8\x86\x81\x1c" - "s\n" - "\x15\xf4\x83\x45i\xda\xf0;\x9ei\x84\x44_9\xe8\xca\xb4\xe1XN-\x96\x8c\x8c\xf5" - "\x93]\xab]7\xec\xaa_\xe4\x43\xb4\xca\x38\xeb\xfd\x9f\"h\xbdy\xe7\"\x95\x8d>\xa0" - "\xc4\x15\x88\x63\x18\x41\2\xd2\xdfH\x94\xe0\xbf\6N\xfd\xca\xcf\x8b\xda\xa5\xdf" - "LNF\xefU\x9b\xe6\x90\xe1\0r\t\n" - "\xf4Y\xa0\x63\x13\4\xef\xe0\xb8/\xc1Z\xb9\xd0\xba\xf9\xfe\xacV\xf2\x9f\6\x33" - "\xe1\3\xae\x44\x42\xd9\xc2T\x16\x8f\xa0\xc5+\xb2\1\x87\x8d\xdbN\xbe\xbc\xdf\xbc" - "\xd6Z\xe7S\x80\1\0\xae\x64\x95\t2\xed\xb0u\0\0\0\0IEND\xae\x42`\x82", - 1218 ) }, - { "/images/status_panel-icon_2-error.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0/\0\0\0/\b\6\0\0\0s'\x93\xac\0\0\0\x19tEXtSoftware\0\x41\x64" - "obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \xdc\xc5\x37\xcf\0\0\1\xf8IDA" - "Tx\xda\xec\xdaM/\3\x41\x18\a\xf0\x7fw\xb5M(\xbb\x15oK\xb9\x12_\x80#\a\xa4\a'" - "\xbe\1\x91\x88\x84\v\a\x82\4\t\"\xc2\x17\xf0\xf2\1\xc4M\xb8y9\x90p'\xc2\x41\x10" - "Y\x84\xaa\xa2\xa8\xb6kf\x1b\x15\xaa\x95\xac\xc8\xce$\xf3$\x93}\xd2l\xb2\xbfN" - "\xff\xd9\xd9\xd9\xd4q\xeco\xf2\3\x98'C\3\x1f\xa5\x93\xd1\xa6--\xafI\x9c\xc1i" - "i\x92\xa2\xae\x90\xa3_\xe2\fnV\xfc>hN\xba\4~K\xe3\x19\xf\x81\x17x\x81\x17x\x81" - "\xff\xb5\xb2\xfe\xfb\2\xeYFvM-\xdc\xd5\xd5\x66\xffzx\x88\xc7\xadM\xf6\xf1\xce" - "2\x1f\x8a\a\x87\xe0\xf4\xf9\x12\xcb\xfa\xd3\x13\x1e\x37\xd6\xd9\x9fyYQ\xa0ML" - "B\xf6z\x93\xf0\xcb\xe1\x41\xbc\x1e\x1d\xb1\x9fy\xa5\xa5\xf5\x13\x1e\xe\xff\b" - "\x97<\x1e\x36\xf1\x34\xe7\x1f\x15\xd5\xf5\x14\xb8\xac\xaa(\x9d\x9a\x46Va!{x\x1a" - "\x9b\x64\xff\rH\xe1\xda\xf8$\x9c\xe5\xe5\xa4\xf7\xb2\x97\xf9\xd8\xdd\x1d\xa4" - "\x9c\x9c\4\x36/\xf\xb9\r\x8d\x88^_#\xf6\xf0\x80\xa2\xde>\x13n\xfe*\xb7\x37l\xe1" - "M\xb4\x11\xff\xf2YAwO\xcay4J\xb1@\x80\x9d\xd8Px\xc9\xc8\x18\x99\xd9\x8a\x8c\xe7" - "\x19\xd1(\2\xf3s\xec\xdc*?\xe0\xee\xca\xca\xcc\x91\n" - "\x85p3;\x83\x97\x83}6\xf0\xe9\xe0\xb1`\x10\x81\xc5\5\xb2`\x95\xc1\xe1v#rr\x82" - "\xf0\xce\x36\xe2\xcf\xcfl,R\x99\xe0\xfa@?\xde\xce\xcf\xd8|0\xb3\v\xfeg\xbc\x9d" - "\xf0?\xe1\xed\x86[\xc6;\\.\2\x1f\xb5\x15n\x19\xef\xa9\xab'\xf0*[\xe1\xd6\x63" - "c\x18\xb6\xc3-\xe3\xe9N(\xbc\xb7k\xf6\x91\xb3S[\xe0\x96\xef\xf3\x46$\x82\xab" - "\xb1Q3\xfb\xb4\xe7r\3n'\\\xbc\xfa\x10x\x81\x17x\x81\x17x\x81\x17\xf8\x34x\x9d" - ";\xb4\xaa&\xf1\xed\x92\xa2\xc4y\x81\xcb\xf9\xf9P;\xbbh{I\x1f\x89W\x8b\xe7\x16" - "\x9a\x91\xf8\3\x45\t'\xdf\xe1\x82\x8c\x8ew\1\6\0>\xbc\xc6\xee\2\xb0#[\0\0\0\0" - "IEND\xae\x42`\x82", - 1414 ) }, - { "/images/body_bg_tile.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x32\0\0\0\x32\b\2\0\0\0\x91]\x1f\xe6\0\0\0\x19tEXtSoftwar" - "e\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 " - " 0}Y\x8b\0\0\a\x86I" - "DATx\xda\x64\xd9\xd9r\x13K\x10\x45Q\xabm\x83\xf9\xff\xbf\x84 xd\xb0\x8do\xb6" - "\x96\xd8\xce\v\xfd\xa0h\x95\xaar\x1eN\x96.\xdf\xbe}\xbb\xbb\xbb{}}=\x8e\xe3r" - "\xb9\xcc\xfb\xdb\xdb\xdb|z\x9f\xf5\xf9\xfa\xf0\xf0\x30\xef///\xf3\x39\xef/\xd7" - "\xe7\xc3\x87\xfsdV~\xff\xfe=/\xb3\xad#\xf7\xf7\xf7\xf3\xf2\xeb\xd7\xaf\xd9\x83" - "\xe0,\xce\x41;\x7f\xfc\xf8\xf1\xf4\xf4\x34\x14\x90\x42\x1c\x9dy\xbc\x1f\xf8\r" - "!D\x87\xd6\xbc\f\x95y\xff\xf9\xf3\xe7\xfd\xf5\x99\xf7\xe7\xe7g2a3/d\x9a\xb3\xf3" - "2\x9fsd\x88\"\x85\xc7\xe3\xe3\xe3\xdb\xf5!\x93\xc5\xd9\xf0\xf1\xe3G\xa4\xbe\x7f" - "\xffn}6\f\xfd\xa1\x33_Ir\x8c\x1c\xf3\x36\xbb\xe7\xfc\x1c\xa3_\xb2\xce\xca\x9c" - "\x99\xf3\xc3#\x9d\x66}\xcbw\xd3\xefj\xec\xecJ\x9a\xd9<\xf4\xb7=\xf2\xc3\xec\xf9" - "\xf4\xe9\x93\xc5\xd9<\xc7\xdb?\xf4\x8f\x91\xc3>\xa4y\x90m\x99}V\xc6\xe6\xf3\x39" - "\xbb\xc7\xfe\x1c\xc1~\xd9|\xc7\0K\xcc\x66\xcc\xd8\x66\x9e\x39Kg1\x90\2\x11\x9c" - "#Cg\xf6\x9f\x1e\x88\xee\x10\xcd\x36s^$\r\xef\xb1\x93_\xd9\6\xb9\xf1\xef\xac\xe3" - "=\xfc\x38\xce~\xee\xf6\xcc)\x8c\xc9J\xb7\xe3\xfaX,:y\xac\xf5\xcb\xe7\xcf\x9f" - "\xc5\x81\x90\xf2\x43\xf6 \xc4|\xf5\xde:\xbb\xa2\x95\x7f\xd9\xa3=\xb3.\x8a\xe7" - "\x9d\xbf\x86\xfe\xf8\b/n\xd9\xf1*\x8a|=9EW~\xf9\x61h\xb1\xff\xec\x1e\xdb\x8c" - "\x1f\xbd\x17\xcb\xd2\x82\x8d\xa9\x34\xfb\x99g\x9er-\xcf\x32\xff\xc8\xc7*\\\xcc" - "\x96\x99S\x84\x9d\xc6\xe4\x88\x9d_\4\x8f\x1c\xbd\x31\x1b\xf9\xd0\xda\xca\x89" - "\xbf-A\xfal\a\xe5\xe8\xa1\x30\x36K\x94l6{\xc6\xde(\x1f\xce\xf\xef\x92\xb9\x94" - "\xc9/RO\x9eK7\xbep\xa4\xe8\xd9\xfe-\xc2\xb2\xdf\xe5\xfaX\xc9?\xb2O\xd8\xcdW>" - "9\xc4\x41\xb5n\xe\x90O\tQ\xf7\x92ol`\xdb\xae\x84#_{\xf8\xb7\xb0\xc5\x9b\xb5\x66" - "\x8f\x97\xa1S\xa4\xcb\x15\n" - "\x13\xae<;5s\x9e\xc8s\xbe\x33\xed\xb3N\xe9j\x15o\xcay\xf2\x91\x38O\x91\tK/\xf2" - "\x86\xfd\xc8\xda~\x86$\xc3\xe9\x90:F\xfa\xcd\xbb\x98\x15\xa7\x62\xcb\x99r\xb3" - "NRN\x8d|(\xb2\x62\x1d\xac\x38\xe3\xfd-G:Kj\xecn\xbc\xfe\xcdR\r\xe7\xaf\x9e\x45" - "\xdd\x1a\x8bJ\xd6\xa9]\xf9v\x9e\x46\x39~\"\xcf\xa2\xfa\xd9W\xac)pT?\xc4\x96\xb0" - "\x88\x93z\xa1\xf6\xa8\x84\xc5\x90SrS,\x16\xf\x64R{gQ~H\x82\xd9_#\xa9~\xcaq\2" - "\xd9\x7fp\xb9\x30/\xe1Y\xa2\x32\x91/\xca\x8f\xfc\x98\x8f*\4L\xe2\xb8\xaf\xe2" - "o$\x93\x37\x8aS\xf5\x96sf}\xdb\xe2\xbdi\vgDG\xf6\r6b\xa0\xd6\xd1ux\xa4+3s\a\f" - "b\xddO\xe4\x96\xd7\x8e\xab\xe1\xac\xc0\x8f\xf9\xd4\xfe\x1bN\n" - "3\r\xa1\xa1\x9bm\x82S\xe2\xbd\x92\x98\r\xe6\xac\xc5Z\x93uG\xd4\x61j\x8cL\xe1" - "%\xde\b;Hj\xf4oi\xae\xb7\xe4\x38u\xbf\xdci\xdf\xec\x19Yi\xe3\xbc(,\x9b\x82\3" - "\xd5?\xda\x62/\xe6\xf8\4\x46(U\xeb+\xbb\a\x1e|\xcc\x1djt=$\xde\x83)\xc8\x64\x45" - "p\xe0q\xf9\xf3\xf0`U\xcaNYy\xb7\x9e\xe2\x64|R\x95\xaf\xbf\xcd\xfb\xe8\x7fZGN" - "\xa2\xa5\"\x10\xb1`\x9f}\xd3_\xc3\xbb\xa5}`\xb2\x66\xa5\n" - "\4\x43\x84\4{\xccW\xb1\x18\xd4\xd1\x33\xec!\xbd\x38\xb9\xe1\xad\x9a\xe5\xae\xf5" - "\xa2\x84\x36\xf4 \x13i\xb2\x13\xb9\x61\xc3\xaa\x65\xbe\xc0\x89\x83\x8a\xd7\x10" - "\xd1\xc6\x64\0\x88\x38\xb9\x45\x1b\x18.\2\x98\x34'&G\x18\x37(\x92\x32\xf2\x8b" - "\xa2\xf5\x96\x12\5\xb5\xe1\a\3k\xe1\x61\x16\xf4\xab\xef\xe4&\xe2\x43\xfd\x8b" - "\x19\x1c\x46\xab\x1eR\xb2\xd8\x99L\xf2\xabTEqcCz\xea}\xdcZ\x1d\x1f\x99\xf8\xa1" - "\x9c@\xc7\xca\xa1\x84\xcc\x97\x89\xeb\xb2\x63^\xf8\xbe)\xa8~\x1c@\xa8/\xc1\xae" - "$\xa8k\xa5^\xf1@\xd6\xcc\xe9\xa5\n" - "\xacv\x9aG\xde[5\xdcH\xa1\xd0\x66u\xbfZj\xd6\xd8sX8\x91\x1f\xfb\x15\x1b\x82\xe2" - "\xba\x87\"\xa1\xfcz}\xd8\xd5\x11\x35\xef\x64\x1d~\n" - "{\xa4\x62\xf0\x92=\xc8*w\xea\x83\xe5J\xee\xd6\n" - "\xf5\xb8\xf0\xf7\xae\xfb\x8f\xd7G]@S\xbb\xcc-G\xc3\x9d\xf8\xaf\xb8\xf5\x33\x37" - "SEx\xd2\xcf\x36\x31[\x9d\xdc\xb3\x46\a\xad\xd3\x64\xd8\x87\x97\xd8\xbe\xf0-^" - "U\xc4# \xdfl\x98\xef\xb2\x16\x1f\xd5\xe3\xaaICW\xf6\xc1\b~\x85\x19sYr\xcf\xfa" - "\xc4\t\xc3\x34\x43\xec{\0,\xde{\xa2\x19\xbf\xb9\xbb\x81\xacr\x80h\xe\xd2\xaf" - "\xfcJ\x1aq\xb9q&U\x8b\x95\xa6v\b\xa7 a{\xb1Q\xe7>Yc3\xe7\xd1M\xa6\xea\x61=q_" - "\x96\x8c=\xc4\x32\xac!\xe9\xf6\xe0\x9a\x94\x8eL^[1\xf7\xd2\x99\x9dh\xcb\xcb\xa4" - "\xa3\xca\xc3\xef\xe9\x99\xce{&+\a\xc5\xe2\xcd\x30\xa1\xa2l\x9e\xf6\xd3+" - "\x93X\x7f\xa8\xd4U\xc7\xd5O+;\x1f\xd5q\x82\xc2\xf8\xd5[\x1c\xe5\x81u\xc8\xf1" - "6\xbf\xe0\xdd&\xfc\xf6\x8d\n" - "L\"~\xc3\xcd\xe1O\xc7\xf9\xb4\xb9\x37\0\xb8\xd1\xacX\xdc\xf7JpX~\xe4\x8a[\n" - "\a\x17}\xd2\xafj^\xad\xfk\xab\xa5RD}W\x1a\xd2\x38%+K\xfcR\xf\x90\x43{\x98\x66" - "\xbf\x92\xe6\xcc\xb0\xec\xb1\1\xe0\xec\xc6\x9b\x9d\x9a\xf7\xf9\xa5{\x8e\xee/" - "U\x93Y\t\xe\x14\xc2\xcd\x83\x1b\xbb\x12\xabk\xc1\xf2Q\f\x9c\xb1\x15\xf4\x9b\xef" - "\x1b\xbe\x35\x93\xed\xf9]\xbd\x90\xe7\xeeN\xb3.|\x16n\xd9\xf3{\xb3g\xf9\xef\xe1" - "5=\xbbZx\xbb\x87\xee.\xa9\xf9x\xdf\xc9pD\x13\x62 \xae\x18\xb7\x93\xcd\x42\xf7" - "].l{l\x8c\x9fO\xf7L\x1a\x9e\xbb]\b\xca\xcc\x8d\xef\xb2S\xf2\xe9\xe5\xeaV\x98" - "\xa2\xc8\xabX\xec\xa4\t\x8e\x9b\xdf\xcb\x8f\x9dwd\2n\xf5\xae\x1b,\xc8\x12\xdd" - "\x85n\xcc\xb4\xeb\x8a\xa8/\xb9\xba\x17\xd9\xf7R{\x9e\x41S-\xed\xfew\xf7\x92\xb0" - "\xa1k\x88\xae\xcd\xcf\x8c\xa9\x13uif\xee\xae\x18\x16\x13\xa5\5W\x8a\xcd\xee\x61" - "\xc4u\xb2\x36[\xd7\xf\xfc\xd7\"\x98\xdc\x91&\xfa\x9e\xffn\xa1\xe2?\x9f\x36\x35" - "OVHv\xa5`g\xe2v\xa9\x14\xca\xeb\xf2\xbc\xb2\xdc\xdf\x13\xfcX\\v\x1f\xd1\xbc\xbf" - "\xef\xa7\xdf\xff\xf3\xd9\xf3x\xb5\xa4\xffW\xf6?M\xbb\xa9w\xff[)\xb7\x1e>\x91" - "\xe6\xf3\x42\xa6\xfe\xbf\xa8\xc6\xee.\xd9\x9d\xf9\xa9\x33\x7f\xff\x8b\xb7\x9a" - "\xe3\x18\xa6|\xaegg|\x9f\xe1\xdb\r\xc2\xba\x93\x89~f\xae&\xf3>\x1c\x9b\x94\x97" - "\xaf_\xbf\xe6\xbe\6\xe8\x1d\x46\xa1\xb3\xee\x61\x32U\x1b\xe0\x88\xee\xc3\x9b" - "\xa1\x37\x38n}\xdf\x9d\x66\xce\x8d_\xde\x87\x9f\xee\xbb\xe2\x17\xcb\xac\xd5<" - "\xc8\x36\xb2\xb2\xfb\x8b\x46\x42wkh\x92\xa6{\xde\xad\xcf\xf6\xf5\xfb\xfd\xd1" - "\x9f\xbc>\x9aO\x82~\xfe#\xd9S+\xba\x84S\xb7\xc2\x99\x1b\x8f\x97\xb3\xd5wi\xdf" - "\xfdL1\x10\xc6\xcc\xb9\xbb\x63\xce\xe7\x7f\2\f\0~\xf\xdc\xae@e`]\0\0\0\0IEND" - "\xae\x42`\x82", - 2836 ) }, - { "/images/warning-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x16\0\0\0\x13\b\6\0\0\0\x94y\xfd\x88\0\0\0\4gAMA\0\0\xaf\xc8" - "7\5\x8a\xe9\0\0\0\x19tEXtSoftware\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\1\x1d" - "IDATx\xda\xacT\xb9\x11\xc2\x30\x10\x94\xcc\xd3\x16m\x98\x36\xc8i\1r\2R*0)!\x14" - "@B\1\x44\xd0\0\x8f\x11{\xcc\x1e#\x8c\x64\x89ggn8\x8b\xd5\xcezu\xb2\x31\x80s\xae" - "\xb5\x80\xb1\xd0X\xd2'aU\xb8\x95\x64\xedM\xb9\x14/R\xc2I\2\x44+\x8anY\xd2W?;" - "\x86\xb0#\a\xadU\xc7\xc6{\x83\xcf\x1d\x43h\xc7v\xea-k\xbfK\6\x1d\x39\xb0\1\xdd" - "\xd5\xde\x9a\xa2\xe6\x7f\x83\x8f\xa3\x80\xdb\x33~z\x12\x81\xb7\xf6\xe2\auA\xf5" - "\xb3\xa3\x80\xc0\x44\x44Q\x87\x46\xd6\xbe\x83\3\x39\x93l\xc7:^\r\xb7\xa1\x43" - "s\xb1\xf1+\2nW\xdc\xbc\xce\xb8\akrWI\xc7\xfa\xca@\x11\x1a\xbb\xc0\x98\xe9\xe5" - "\xb1Q\xc7\xd8|d;2\xf9P\xee\x31\x38n@\xc9\xbcN-\xdf\x8c\x18N\xdc[\xbe\x45\1\xb7" - "W\xb4\x1d\xff\xc0\x32\xa3\xf0\xfR\xe6\xbb\xfb\x8c\2\x9b\xe6\"\x8a\xda\x9b\xef" - "\xb1\xa7\xc6\xdc\x34\xbeX&\xe6\x36rAb\xae\x1fTq\xbc\xe1\xc3\xd2\xfc\xe\xd5\xd8" - "\xd8\xc6m\xfa\x1b$\xe8\x19j\xc8\xe0\xff\1\xc9yq\x17`\0\xf5\x10\xa1\x45\xad#\xe1" - "v\0\0\0\0IEND\xae\x42`\x82", - 395 ) }, - { "/images/cog_16x16.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\x10\b\6\0\0\0\x1f\xf3\xff\x61\0\0\0\x19tEXtSoft" - "ware\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\0tIDAT8\xcb\x63`\xc0\r\x12\x80\xf8" - "?\x14'0\x10\1\f\xd0\x14\x9eG2\xe0<\x9a\xc1\6\xd8\x34\xbf\x87*\6\xd1\xeb\x91\x34" - "\xc3\xf0z45\6\xb8\x9cK,\xc6\xf0\xd6{\x12\x34\xbf\xc7\x16\6\xeb\xb1\x38\xd9\1" - "\x8a\xb1\xc9\xa1\x38\xff<>\5\x38,8\xf\xf3\6\x36'\x1a`1\xc0\1\x87Z\xca\r\xa0\xd8" - "\v\x84\2\xd1\x80P R%\x1a)NH\x14'e\x8a\x33\x13\xd9\xd9\x19\0\xd0\x12\x91\x81\x62" - "Cj\xcd\0\0\0\0IEND\xae\x42`\x82", - 210 ) }, - { "/images/ajax-loader.gif", std::string( - "GIF89a\x10\0\x10\0\xf2\0\0\xff\xff\xff\0\0\0\xc2\xc2\xc2\x42\x42\x42\0\0\0\x62" - "bb\x82\x82\x82\x92\x92\x92!\xff\vNETSCAPE2.0\3\1\0\0\0!\xfe\x1a\x43reated wi" - "th ajaxload.info\0!\xf9\4\t\n" - "\0\0\0,\0\0\0\0\x10\0\x10\0\0\3\x33\b\xba\xdc\xfe\x30\xcaIk\x13\x63\b:\b\x19" - "\x9c\aN\x98\x66\tE\xb1\x31\xc2\xba\x14\x99\xc1\xb6.`\xc4\xc2q\xd0-[\x18\x39\xdd" - "\xa6\a9\x18\f\aJk\xe7H\0\0!\xf9\4\t\n" - "\0\0\0,\0\0\0\0\x10\0\x10\0\0\3\x34\b\xba\xdc\xfeN\x8c! \x1b\x84\f\xbb\xb0\xe6" - "\x8a\x44qBQT`1\x19 `LE[\x1a\xa8|\x1c\xb5u\xdf\xed\x61\x18\a\x80 \xd7\x18\xe2" - "\x86\x43\x19\xb2%$*\x12\0!\xf9\4\t\n" - "\0\0\0,\0\0\0\0\x10\0\x10\0\0\3\x36\b\xba\x32#+\xca\x41\xc8\x90\xcc\x94V/\6\x85" - "c\x1c\xe\xf4\x19N\xf1IBa\x98\xabp\x1c\xf0\n" - "\xcc\xb3\xbd\x1c\xc6\xa8+\2Y\xed\x17\xfc\1\x83\xc3\xf\x32\xa9\x64\x1a\x9f\xbf" - "\4\0!\xf9\4\t\n" - "\0\0\0,\0\0\0\0\x10\0\x10\0\0\3\x33\b\xba\x62%+\xca\x32\x86\x91\xec\x9cV_\x85" - "\x8b\xa6\t\x85!\f\4\x31\x44\x87\x61\x1c\x11\xaa\x46\x82\xb0\xd1\x1f\3\x62R]\xf3" - "=\x1f\x30\x38,\x1a\x8f\xc8\xa4r9L\0\0!\xf9\4\t\n" - "\0\0\0,\0\0\0\0\x10\0\x10\0\0\3\x32\b\xbar'+J\xe7\x64\x14\xf0\x18\xf3L\x81\f" - "&v\xc3`\\bT\x94\x85\x84\xb9\x1ehYB)\xcf\xca@\x10\3\x1e\xe9<\x1f\xc3&,\x1a\x8f" - "\xc8\xa4R\x92\0\0!\xf9\4\t\n" - "\0\0\0,\0\0\0\0\x10\0\x10\0\0\3\x33\b\xba \xc2\x90\x39\x17\xe3t\xe7\xbc\xda\x9e" - "0\x19\xc7\x1c\xe0!.B\xb6\x9d\xcaW\xac\xa2\x31\f\6\v\x14sa\xbb\xb0\x35\xf7\x95" - "\1\x81\x30\xb0\t\x89\xbb\x9fm)J\0\0!\xf9\4\t\n" - "\0\0\0,\0\0\0\0\x10\0\x10\0\0\3\x32\b\xba\xdc\xfe\xf0\t\x11\xd9\x9cU]\x9a\1\xee" - "\xdaqp\x95`\x88\xdd\x61\x9c\xdd\x34\x96\x85\x41\x46\xc5\x30\x14\x90`\x9b\xb6" - "\1\r\4\xc2@\x10\x9b\x31\x80\xc2\xd6\xce\x91\0\0!\xf9\4\t\n" - "\0\0\0,\0\0\0\0\x10\0\x10\0\0\3\x32\b\xba\xdc\xfe\x30\xcaI\xab\x65\x42\xd4\x9c" - ")\xd7\x1e\b\b\xc3 \x8e\xc7q\xe\4\x31\x30\xa9\xca\xb0\xaeP\x18\xc2\x61\x18\aV" - "\xda\xa5\2 ub\x18\x82\x9e[\x11\x90\0\0;\0\0\0\0\0\0\0\0\0", - 673 ) }, - { "/images/arrow_right.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\n" - "\0\0\0\n" - "\b\6\0\0\0\x8d\x32\xcf\xbd\0\0\0\x19tEXtSoftware\0\x41\x64obe ImageReadyq\xc9" - "e<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \x9d\xefn\x8a\0\0\0{IDATx\xda\x62\xfc\xff\xff?\3\x31" - "\x80\tD\\\xbf~}\x1d\x10\xab\x10T\b\4\x81@|\x15\xa8\xb8\a\x88\5\xb0)d\4Y\r\x94" - "D\xb6\xff\r\x10\xd7\3\xf1,MM\xcd?\xe8&\"\3\x11 \x9e\n" - "\xc4\x17\x81\6x\xe0S\b\3l@\xcc\t\xe3\xb0`Q\xf0\1\x88[\x80x2\xd0\xea_\xd8\x14" - "\x82\xdc\x33\v\xe4>\xa0\x82\x37\xe8\xba\x61\n" - "w\0q1P\xc1\x35\\\xee`$6\xc0\1\2\f\0\x39\xc1*\x81\x9e\x15g\x86\0\0\0\0IEND\xae" - "B`\x82", - 1033 ) }, - { "/images/loading-feed.gif", std::string( - "GIF89a\x10\0\x10\0\xf2\0\0\xff\xff\xff\0\0\0\xc2\xc2\xc2\x82\x82\x82\0\0\0\0" - "\0\0\0\0\0\0\0\0!\xfe\x1a\x43reated with ajaxload.info\0!\xf9\4\0\n" - "\0\0\0!\xff\vNETSCAPE2.0\3\1\0\0\0,\0\0\0\0\x10\0\x10\0\0\3\x1b\b\xba\x32\x34" - "bI\xe9\xc8\x9b\xd8j<\xeb\xe5\x8c\3\x81\x64i\x9eh\xaa\xael\xeb\xba\t\0!\xf9\4" - "\0\n" - "\0\1\0,\0\0\0\0\x10\0\x10\0\0\3\x1c\b\xba\xdc\xe\x62\x90!\x1e\x93\x64\xda\x95" - "\xfbV\x98\xf6\x45S\xf5\x9dh\xaa\xael\xeb\xbeo\2\0!\xf9\4\0\n" - "\0\2\0,\0\0\0\0\x10\0\x10\0\0\3\x1c\b\xba\xdc\xfeP\x89\x41\x86\x88\x8a\x92\x8a" - "\xc1\xfe\x9d\xc6\x61Suuh\xaa\xael\xeb\xbei\2\0!\xf9\4\0\n" - "\0\3\0,\0\0\0\0\x10\0\x10\0\0\3\x1b\b\xba\xdc\xfe\x30\xca\xc9\xc4 C\xccK\xf0" - "\xec\xe0\6\xe\x93\x85iT\xaa\xael\xebN\t\0!\xf9\4\0\n" - "\0\4\0,\0\0\0\0\x10\0\x10\0\0\3\x1b\b\xba\xdc\xfe\x30\xcaI\xab\xad\x62\x90!\xa6" - "&\xdb\4\x8e\xde\x38L\xd9\xd6]l\xeb*\t\0!\xf9\4\0\n" - "\0\5\0,\0\0\0\0\x10\0\x10\0\0\3\x1a\b\xba\xdc\xfe\x30\xcaI\xab\xbd\x38\xeb)\6" - "\x19\xc2\xe4\x11\xdf\x44\x9e\xe2\x39p\x1e\xd8$\0!\xf9\4\0\n" - "\0\6\0,\0\0\0\0\x10\0\x10\0\0\3\x1a\b\xba\xdc\xfe\x30\xcaI\xeb\x14\x83\f13\xd1" - "\xd3'v\xe2pe\x9b\xa5\xael\xdb$\0!\xf9\4\0\n" - "\0\a\0,\0\0\0\0\x10\0\x10\0\0\3\x1b\b\xba\xdc\xfe\x30\x46\x31\xc8\x10\x12Tb3" - "\xff\xd9\xd6I\x94\x85\x65h\xaa\xael\xeb\xb2\t\0!\xf9\4\0\n" - "\0\b\0,\0\0\0\0\x10\0\x10\0\0\3\x1c\b\xba\xdc\xe\x62\x90!\x1e\x93\x64\xda\x95" - "\xfbV\x98\xf6\x45S\xf5\x9dh\xaa\xael\xeb\xbeo\2\0;\0\0\0\0\0\0\0\0\0", - 529 ) }, - { "/images/resolve_issue-danger_icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\x10\b\6\0\0\0\x1f\xf3\xff\x61\0\0\0\x19tEXtSoft" - "ware\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0" - " \x80Xk\xdd\0\0\0" - "\xd0IDATx\xda\x62|S\x90\xc5\x80\3\x30\2q/\x94]\f\xc4\xff\xb1)b\xc1\xa3y*\x10" - "gB\xf9\x1c@\x9c\x8d\xcd\x10&\"43@\xd9S\xa1rx\r\xc0\xa6\x19\xd9\x90i\xe8\x86\x30" - "\xe1\xd1\xfc\t\x88\xe5\x81\xd8\tIM\6\xba!,xl\xfe\0\xc4\x8f\xb0\xb8$\3J\x83\x42" - "\xff?\vT\xf3\x34$\tb\0\xdc\x10\x90\1=$jF6\xe4\x1b\x13\x1e\5\\\xd0\x30\x62\xc3" - "g\n" - "\xc8\5%P\xc5\xe8\xae\x10\x81\x86\3\x33\xe\xbd\x33@z\x99\xa0\x89#\v*\x80\f@\xb1" - "\xa0\3\xc4>84\xc3\3\x91\1\xc9\x10\xe4\0\xfa\5\xc4O\xb0x\1\xae\x19=)\xa3\x1b\x82" - "\xcd\v(\x9a\xb1\xe5\5tCx\xf1i\xc6\x95\x17\xb0\x85\tV\xcd\xf8r#\xcc\x90\x1fP~" - "\x11\xae\xec\f\x10`\0|\b55\b\x9a\xdf\xb0\0\0\0\0IEND\xae\x42`\x82", - 1118 ) }, - { "/images/status_panel-icon_4-error.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0/\0\0\0/\b\6\0\0\0s'\x93\xac\0\0\0\x19tEXtSoftware\0\x41\x64" - "obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \xb7\x30\x45*\0\0\2\xb2IDATx\xda" - "\xec\x9aMk\x13\x41\x18\x80\xdf\xdd|t\x93l\xc8\xd2\xd2\xea&\x91\xa6\x14z\x88\x87" - "\xa4\x98T\n" - "\x15L\5\x43\xe\x82x\xd1\xbb\"\x88\x7f\xc9\xfa\aJ\xf\xad\xad\xa2'\xbd\xd5\x34" - "\xb1\x87\x42\x15\x92\x8a\x87\xd6Xz\xb1\x86|\x87]gfw\xa7Y\xda\x14\x35!\xc9\xe0" - "\xbc\x30\xec\xccn\xe\xcf\xcc<\xf3\xce\x30\x44(d\xd2\x19\0XAE\5\x36\xa2\x84\xca" - "cuu\xed\x8d\xc8\x18\x38\xeU\f(\x9b\xe8\x99\x11\x19\3'\xa1\x9d\xfe$\x83.\2\xbb" - "\xa1\xb2\f\xf\x1c\x9e\xc3s\xf8\xff\r^\xf4\xfb\xc1\x15\n" - "\xb1\t\x8f\xc1\xbd\v7\x19\x86O$Y\x85\xf\xc3X4\n" - "\xa2\xcf\xc7 |0\b\x82\xd3\t\x9e\xf9y\6\xe1\xc3\xd7\xc8\xd3\x9bX`\f^\x10\xc0\xa5" - "\x1a\aSO\xe2\6i3\3\xef\x9c\x9c\4\xc1\xed&uG@\x81\xb1\xb9\xb9\xae\xbfuGfhGG\2" - "\xde\x15\xe\xdb\xda\x97\x65\x1d\x39\x95\x42\xb3\x93\x1c!\xf8\xa0}s\xf2&\xbbx" - "/\x8a \xdfN\xa1\xef\xfd\x83w\xf6#\xc7\xdb\xd4\x98\x9d%*\xb5ONl\xef=\xb1\x38\x38" - "\xc6\xc7\x41\x92\x65\xa2\x99\xdel\x8e\xc0\xc8[\xf0\xba~\xe9\xe8\xcb\xa9\x65\x63" - "}#pO,6\"\xda\x84\f\xe7[\xa5\x12\xb4\xe\xf\xcd\xac\x63WC\x94<\xe0[\\<\x9b\x85" - ">y\xdf\x13<\x1e\x45\xac\b\x8e\xe6\x41\x11\xaa;YS\x91\x18\xcd@d&\x10\xb8 I\x7f" - "\xb4\xa8\a\6O\x16\xab\x99\xd7\x1b\x85\2Ts;\x17\xaa!/\xdf\xb1\xa9\xe5\x9c\x9a" - ":\x97\xa5\6\xf\xdf\xb1X\x1b\xc5\2\x34\xf6\xf7\x41\xabTlj8&&hG\xea{{T\xad\xae" - "Yip#\x1f\xa4#\xda,\x16\x41o\xb7\xa1\x96\xcf\xdb\xe0pz\xb4\x66\xe7tc\x9d\xaa\xd5" - "\xfuz\x83\x37\xa7\xbe\xf5\xfd\b\xb4Z\x8d\xd4\xab\xd9\x8ft\xe7uG\"4\xcb\xb4\x8f" - "\x8f\xc9\x37K-r\n" - "E\vy\xb8\xce\x9b\xbe[Q\xfd\x84\x46^\xd3H]y\xf8\b\xdc\xd3\xd3\xa4\xfekk\x93\xcc" - "\x90\xa5\x16>\x85J\xf1\xf8\xf0\x9d\xc7\xbe[\xa1\x95\xcbP\xff\xf2\x99\xd4}K\xb7" - "\x8cw\xf5\x1a\x94\xdf\xbd\x35\f\xc3j\xed\xee\x9aj\xf5\xa6\xce_\xef\xb0\x33\xaf" - "\xb6\xce\xbd\xc3\xbew\6\xf6Z\x8a^\xa7\xed\xca\xfb\xft!\x93\xef\xb9,\xea\xd8\x12" - "\xf8\xef\xa6I\xd1[-\xf8\xf6\xe0\xfe\x10\x8e\xc4H\x91\xe6\xc1W;\xfc\xf6\xf6Y\3" - "\xa9\x82\x17jg\xd4ry\xdb\x8e,\xb8\\\xff\xb6\xcf\x14\x32i\x9d\xc5k\xfuu\x8d\xdf" - "\xdbpx\xe\xcf\xe1\x39<\x87\xe7\xf0\x1c\xfe\"\xf8\x12s\xd0\x8a\x42\xe1\x9f\x88" - "\x81\x80\xc6\n" - "8\xbe\xb8R\x9e=\xc7\xd5\x1f\xf8<\xff\xfa\xca\x8b\x97\xf7\xc0\xf8\3\xc5UF\xfa" - "p\x84\xca\xd3\xdf\2\f\0\xd0\xee\xc1\xe1\x9a\"\x1a\x43\0\0\0\0IEND\xae\x42`\x82", - 1600 ) }, - { "/images/resolve_issue-clock_icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\xf\0\0\0\xe\b\6\0\0\0\xf0\x8a\x46\xef\0\0\0\x19tEXtSoftwa" - "re\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 X\x15\xd0\x97\0\0" - "\1@IDATx\xdat\xd3?(Ea\x18\xc7\xf1\xf7\x1cw\xb0\xc8@FI\xfe\xa4\xc8r\xef\x62\x63" - "\x92\xd1\xc0\xa6X\xd8t\x15\3\x16\x65\xb2q\x19$L\6\6\x46\x36u\x8a\x14\x83\x32" - "(w\x91\x8d\xa4\xec\xd7\x9f\xefS\xbfSOo\xf7\xbe\xf5\xe9\xbc\xe7}\x9e\xf7\x9c\xf3" - "\xbc='\xc9\xb2,Dc\0s\x18\x41;>q\x8b#<\xfb\xc4\x42\x34\xdf\xc2\"R\xb7\xde\xa7" - "\a-a\x1b+\xa8\5\x97\x94\xe0\x14\x65\5\xf6\x30\x86~]w\xb5^V^\xe2\xdf<\x8bI|c\x1c" - "wZo\xc1\v\xaeq\x82+\xe5YY\x87\xa9\x9e\xb2\xa6\xe4\5\xb7\xd1\xc6#:4\xb7\xf5y\xcd" - "\xd7m\x9fm\x1e\x46\x37\xde\xf4I\xf1\x99\x34\xbb\xfb\x33\xe5ua(U]6\x1e\xf0\x17" - "m~\xd7\1\x36\xe9\xde\xe2\xf7\x9a\xf7\xfaS\x8d\x37\6\xd5?\x88K\xb4\xc5\x41\xdb" - "\\\xd5\xbc\x94\x9f\xa2\x1b_\x98\xd0W\xcd(^R\xacZ\xd0\xa1X\x1d\x9d\x98\xaaS\xf7" - "\xfV5\x9fV\xde+\x9e\xec\xcd\xbf\xd8Pp\x1f\xc5P\x7f\x14\x15\xb7\xb1ie\xe6\x35" - "\x1f\xe3\x1c\xad\xb8\x41\5\xa3\xe8\xd1\xb5\xa2u\x8b_\xa8UC\xe2z\xbbQ{\xe6\xc3" - "\xbep\a\xcby{\xfa\xde\xae\xa9\x7f\xf\xdc\x8f\x61\r\xf2\xd1\xe8\xc7\xf8\x17`\0" - "\xce\x32H\x81\\\x15\x62\x31\0\0\0\0IEND\xae\x42`\x82", - 1230 ) }, - { "/images/book_alt_16x16.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\x10\b\6\0\0\0\x1f\xf3\xff\x61\0\0\0\x19tEXtSoft" - "ware\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\0KIDAT8\xcb\x63```h\0\xe2\xff\x64" - "`8\xf8Om\3\xf0\1\a4\xb5\xe7)1\0\xa4Y\0\x97\1\xe\x44\x88\xcf\xc7\xe7\5\a\"\xc5" - "\xe7\xe3\x32@\0\xaa\x18\x19\xa3\x1b\0\x17'7\f(\x8e\x85Q\3hi\0\xc9\x99\x89\xa2" - "\xec\f\0so\x90u`\x8dNc\0\0\0\0IEND\xae\x42`\x82", - 169 ) }, - { "/images/navbar-active.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x83\0\0\0>\b\2\0\0\0\x87\xab?\x9a\0\0\0\x19tEXtSoftware\0" - "Adobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \x81\xd3um\0\0\t'IDATx\xda\xec" - "\\[r\xdb\x38\x10\\Jr\x92S\xe4\xfe'\xcc\x8f-y)\x11\6G\xd3\xf\x8c,\xa8v?\xe2\xaa" - "\xa4h\x98\4\x81\xc6`^=\xe0\xf2\xfb\xf7\xef\x7f\xfe\xfe\xfc\xf~N\xeb\xbf\xf7\xf7" - "\x8f\xe3\xf1\xf8\xf9\xf9\xb9,\xcb\xf9|^\xaf\xd7\xc6\xf5\xd7\xcb\xe5r8\x1c\xd6" - "\xc6\xf5\xd7\xde\xbe]c{\xbc\x7f\xbbV\xf7\xaf\xd7\xeb\x45\x7fW\x7f\xafzW\xec\xdf" - "\xf4i\xfa_\x7f\xa5\xcf\xd2\x39\xaa\xfb\xe9\x98ga\xf5\xf6v\xba\xae\xc4\xf6\xe7" - "\xe\xc7\xd6\x45\x1fJ\x7f\x8c\xc2\xd1\x87\xd5\xef\xc1il\xf7\xf4g\xe3\x34\xe2\x32" - "\xc4\xfe\xd5T\xe9\xf2 \x94\x14\xb2\xeq\x12\x97\xad\xbd\xb2l\xfd\xbd\xaf\xc0\xea" - "\xd0w\a\xae$\xc2\xbau\x11\x87\x1e%%=\xbb\r\xbdK\x93\x1aV\xec\x87.U\x1f\x43\x84" - "#.m\x1a\x7fz\x16\xfb\xdf\x96\xad?\xab\xc6\x80\x38\xa0(\xcc\xc2\xaai'\x84\xc6" - "\xaf*JP\xda\xbctg\xf4\x61)\b\xa8R\xa2\xd2\x17\xdf\x9b\xc6L\x95\x89\xdaUI\x14" - "\xe6\xb8\x9b\xd3\xbc\xfcR=\x89\xd5\xb5\xc3\xd5w\xba=>0\x8f" - "F\xb1\xe0\xeb\x87\xa6L\xf5\x1f\xdb\xa3\x14\x63\x9f^\xd1\x61?\xc6\x66\xa4\xa5" - "\xc2\xa5\xa5R<\x17\xab\xf5Um\x1ci\xe5\xa9\x8e\x43\x93\x85\xf7\xfb\r\x1b\xa5&" - "\xf6Ouq\xd7\xe9\xbdO\x84\x8f*1\xba\f\xd4\x1elpGi\r\xd0,j\xbe\xaf\xc0j\xb7\x13" - "\xc9\xe2\xd3\xcdN\xf5l\xf4\x46\x92\xd3\x96V\x9e\xee\fe\xbe\xa8.\xc6{\x8crK\n" - "!\x8a\2\xdd=\tJj\xcf\x92\xe7\x33\x11\xabl'\xbc\xd9\xa1\x30Q\xbdO%\x82\xe\x97" - "z8\bY\x97\xdc.\xc5\xb8\xb4\x43/%\xee\x30\n" - "\xf7\xd0\x96T\x94\xe4\xf7\xb0j{\xc2o\xb4\xe1\x35\xbd\x1f=%\xd5\x8f\n" - "\x97|\x88\xa7\x14\x94\xeag\xe8\xe1P\xbb\x62L\xfd\\\xac\xda\x9e\xa0\xe6\v\xcd" - "\x8b\xda=\xe8\x83\xa3\xa7\x14=\x93\xca\x92\xa0\xf7\x82vB9\x9a\xd8\xf\x86\x8d" - "J\x11\xa5\xfe\xa3\xadR\xcb\x33\v+\xbe\xda\xa8\xa3\x8d\xd3\x66\xcc\x66\x82 y#" - "\tb\xda?\xdd\1h\xc6\xd1\xae$\x9d\x8e\x66\x99\x46\xc8\xc9\\\xf\xa5~\x16VM;%kN" - "\xbd\5\x93\xf3\x31\x89\x8d\xb8$\xa8\x34\x94\xde\x1c*4\x95\x14\xa9\xbb\6\6V\xf4" - "\xbe\xd0\\O\xc7\xeap8.\xb7\\\xecRI\xb7\xa9\xcdX\xf4\xf1+:\xd4\x87i>UW\xb1\1\xaa" - "\xcfg\xc6?\5\xab\xb5\x99\xe4\x9d\xa8\x44$]\x89zSy\x1a\xe8My\xf3U\x8f~\xfd\x18" - "\xe8\x38\xbf\xb7\ff\x9c\xb3\xb0j\xda)\r\5\xaf\x87\x89\xe2\4=\x8d-\xd2\xa6\x1e" - "faU\xd2{8%\x93\x16\xc4{\x94\x44\x17#\xeaYX\xb5=\xf1\x8d,\xa3\xf2\xd3\rg\x10}" - "\xf9\x14+([\x85\x1e\x17\x9a\xdf\x18^Q\xf'ybQ\xd7Ss\xaa\x12\x33\xde\xa9}\x12\xab" - "\xb6'h\xbe\xdew\x87\x1e\x88\x89\x96Q\t\xf8\x9d\x94\xbc\xe\x45\xb3\x98{\x94$z" - "\xdb\xa3\xc2\x31\x33\x97\x89X\x9d\xba\x9d\xa0\xf9\x19\xf4|LL\xa0\x42*\x8c-\x8c" - "d\xf9\b<\xb6+\xc5\x42S\x87T)\xf9%T\t\xed\xd7`\xf5y\xf5\x9d\x96\xe5\x30\x64\xb8" - "\x86\x84\xa5q\xd4\xbc\ab\xcc\xa6I\xe7\xd5I\xd9g\bT\xdf\xe7\x44\xac\xd6Q\x9c|" - "\xce\xdd@F\xe3\6\x15\xe6\x18\xe2\x93\xee\x92\xd4N\xa9\xa1\xa2s\xec\x39soZMb|" - "6V\xcb\x61\xb3\x13*@W\xd1/\xa6\xccX\xce}Q\xaf\xac\x8a" - "D&.\xa1r@M\"]\xed\xb0$\xc5*\xb6\x98\x85U\xcb;a\xce\x9d\xb2\x66\x94\xe1\x42\t" - "2\x14|%\x94S\x89nO\xca*\x92\x95\xde\x1f\x13\x18\x86\xa3\xa6\n" - "\xf3\x45X\xed\xfc\x84\xf2pp#\xa7\n" - "\tcr\xd3\xeb\xd5\xb4\xa3\x92\x31\\4\xc6\x10\x98\x17\x32U#\x8a\3\x37\xf9+\xef" - "\x94\xcf\xc5\xaa\xf1\xd8\xab\xef\x34,\x17\x1b:\xa9\x43\xaf\xa3\xa2\xa0|\xf9\xda" - "0\x8c\x32uM\x86\xa0}t^\xaf\xc0\xaa\xf9NE'R\xe9q\x1a\xe6\x14\x33\xaf\x95hVM[\x85" - "W\x8f.\x7f\xa5,a\xc8\xd6=\x89\x15\xc9;\xa1\4\xa1'\xa0L1\x9a\x35#\xb9\x95P\x8e" - "&\x18p\f\x9e\x32\xa2\x34\xd1\x43\xa2\xa3\x62\x9a\x89X5;a(\x1d\x84\x95V\xf3\xa9" - "\f(\xe5\t\x94\x33\xaa\x38\0tpU\xa9\0\x95\xc4\x98\xd7R\xe\xa5\xc9\4\x63T\xff\n" - "\xacv\x1e\xdb\x64()\x1b\x45%\xcbg1\x87\xd2=\xcc\v\x19\x8a?B`\xb2\xbf^\x8a\x87" - "\xd9\x81!\xdf\xf0m\xac.\x97\xf3\xd5\x62\x1f\xem\x94H:\xa6l\xe5v\xf-f\xe9\xb0" - "n-]\"\xd2\xf5\xa6\xdf{?x\xdd\xabN\xfax\xb6\xeb\xd8\x8e}\xaa\x32\xb2\xd8?\xbd" - "\x87\xce\x17\xc7I\xdb'b\xb5[lZ\xdc\x98\x82r*\xf5\xc9\x94\xad?\x9e\x89\xeb\xf7" - "\xe0p\xfbu\x94\xac\xf5\xffJ\xf6\x94zJJ9\xe0\xbc\xa8\xf7\xa5\xe6\xf2\x1a\xac\xee" - "\xf3N)\x1d\x36\x64\xc4\xb0\x1et\x18\xc1z\xf\a\xf5l\xbd\0\x12s;J\xa1\xa9\x1aV" - "S\xa9\xe7\x1d\xee\xe7\xb1\xba\xabw\xf2\x45\x8c\x15\x66\xad\x9e\x85U\\\x85I\x86" - "\x1bO\x8c*%\xb5\x1b\x8a\xa5\5\xbe\xfa}:V\xe7\xf3\a\xe1'\xd4\x94\xe8++\x14M\x85" - "\xe0T\xc4\xaa\"S\x8bN\xa4\xcf\xfeV*\xc9\x87\xb1\xd1\x14\xac\xc8\xf9\t\xe3Q\xd0" - "\x8ak\xb5\xf2\xc3S@\x95\xe4\xf9\x43;\xc3\x13\x9c\x18\xbd'E\xa1\x98\xbbJ\xd5\xf7" - "\xf3X\xed\xd9\x8e\xe3q[\xd5\xad\x8bu\x88+4W3\x12\xdbo\x8f\x35\xf3\xb2\xb5\xf7" - "\xebx\x7f\xbfV\xf7\xa7\xf6\xf4.\xda\x7f|\xf6\xde\x44\xef\x63\xbb-\xe7\xf8]_0" - "\xf1\xf7\x62\x9f\xea\xfe\xb9X\xadkt\xf8\xb2\x13\xebs\xdd\xe2/_\x8f]\xdb\xbf^" - "\xb9\xad\xf6\xb2\xb5o\xab\xb8\r\xfd\xd6\xdd~\xdd\x9f\xbd\xad\xf6\xdeO\x7f\x16" - "\xda;\xc4{{\xec?={\xef\x38\xa6\x10\xb2=\xdb\xfb\xb9\xb5\xc7\x31\xf7\xb9,\xf7" - "c^\xbe\xc6s\xd7g\xf0\x94\xda\xfd/\xc2j\xd7N\x8aQz\x94\"\xf7L\x9c\xdf\xc8\x15" - "\xddj\xd2\x82\x95\xc8y\xc8%T\xe\t\xbc\2+\x99w2\xd3\xab\x94y\xa5!z\xea\xff\xa1" - "\x8a\xf1\xa1\xa3L\x89X\xea\xb0\xd2y=t\x1aj\"V{\xde\x89\xd6\b\x15%K\x9d&\xc2\x61" - "\xa5\n" - "p\n" - "\xd9\xb0*\xc2p\x1e\x43\x9e\0\tT_\xc0\x99rJ4[<\5\xab\xb6\x12\xbe\xe0^\x15\x92" - "x\xaf\6\xf3?x\x14\xd0ldS\x83\x84\xa9@\x15Q\xd7\xc9T:/\x95\xce{\5V\xcdwz{\xfb" - "Q\xd9\xec\x95\x62\x19\x95\x94\xf6\xc7 +\xf5\xa9>\x84\x1cR@\xc5y=T\x9c\x39\x11" - "\xab\x8f\x8f\xf7\x43\xd1_.\x12\x8a\x34\xc7\x42\xf5\xb2\x39\x97W1\xa1\xb4\xc0" - "\x12\x13\xe0\xe6L\x83?\xecU?\xab\x31\5\xab\x66\xb1\x8d\x17\x61\xce\xbe\xa9\x94" - "\x9c)\xcfR\xe7\x1e*\a\xb9\xd4\xf1\x93\x8a\x39\x1d\x96\xaf\r+\x13U\xa9\xc3,\xac" - "$?Q\xa9\x1d\xaa\x14U\xe\xe1\x33\x85\x8eJ\xc9\x18\xae\xc2Wn+\xf*\xee$c\x9f\x86" - "\xe\xeb\x33X].\xe7\xf6\x37u\xce e.MV5\x9du\x88\xd7\xa6.V\xd5,y\x86K\x1d\xf6R" - "\f\xa0\xc9\4\xd3\3g\xb4\x18\a]\x83\x89X\xe5x\xc2l(\x7f\xec\xf0\xdbg\xe\xfc\xe7" - "P\x8c\x97\x65\xe\x90)}\xed\x8f\xe9\x9b\x33\xd5\x86\x87\x98\x85U\xf3\x9d\x8e\xc7" - "\x93\x62\xa6\"#\x16\x99\xb2\xc4:)\xd6l\xc8\x88\xe1\xb3\xc8\x88\xa5{\xe8\xf7\x30" - "R\xff\x89z\x8a\xef\xad\xb0o\xb1\x9d\xb2o\xd3\xb1jYq*e\xc8X\xa9\xd2+s\xfe\xd9" - "0n\x95\xf3\aTZ\x93\x64\x15k\x96\xbc\xd4\xf7qR\x85\x86N\xf6t\xacV\x9b}\xea\xcc" - "0\xdd\xec\x9e\x46/\x16\xb5\xab<\x8f\xbf\xbf^\x82\x36,\xa0\x37\x1f\4\x32\x31\x10" - "-Y{\x11V{],\x9d\x46\xd2\xb9&\xc5\xa6\xf4 \x1eY\xac\x97'S&\xe\x8f\xbc\x63y2\xc6" - "+\xc3$J\x82\xa9\x12\x97\xcc\xc5*\xc7\x13\x95\x90\xa4^\x19G\x8f\x8a\xa0\4\x15" - "\xb3\xa4\xaap\xa6\x18[\x14+\xfb\xea\x9f\xcd\x9a\x8b\xd5\x9ew\x1a\x16\xb8\xab" - "\xcc\xa5\x89\x15\x94\x62\xa1\xa5\x63\x98\"L\x1fk\xa0\xe+\xfd\x30\4]\xe6\x61\5" - "{\xfc\xc0\x9d\x8a\xf6}\t\xf6\x33X\xf1sv\xca\xdc\xd1/\xd4\xa8\xca\x38U\xb1\xe1" - "\xbf\b3\xac\x98\xf3\xdc\xc3\x43Ilu\xe2\xc8\xef\x86\xd7\x61uR\x87\xa8*\xdf,2\xdf" - "\xea\x30\xa9\x37_w4<\xcb\xf6\xd0\xf1wC\xfdW\xbe`\xa9\n" - "\x82\xa6\x63\xd5\xe2\x89\xd3\xe9M\xf9\xdd\xa9\n" - "\x8f\xfa\xfe\xaa\xb2\xf\xfdw\x15\x37`\xff\xe8\x8f\xd3\xf7\xa6\xc8\x16\xe3\xf" - "\xe5\xbfW*\x13+\x15\x7f\xb3\xb0\xfa\xf8x\xdf\xcf\x63\xd7?yH\x8f\vR\xff\xda\x9c" - "3\xa0\x9f\xcd\xf2\xdf\x94\xf5uG\xf}\n" - "\x18\x1d\xcd\r\x1a\xcc\x41yBw\x16V\xebJ\xdc\xd5\xc5VjL}\xf4H\xeb\x62S\x14:\x8c" - "l\xbd\x99\xf5\xb5\xb0X\xe5G\xc7\xec\x9f\xad\xef\x98YX\xb5\xba\xd8?\x7f\xfe\xd0" - "\x8d\xe3\x83{T\bu(U\xd2\xc2@6Ll\x14\xa1\x31\xe3\xf4\x10\x1b\x91z\x1e\xab\x9f" - "?\x7f\\W\xe2\xd7\xaf\x9f\x7f?\xb0\xfe\x9f\xff\xfc+\xc0\0 \xe3\x41\x64\xb8\xa9" - "#\x90\0\0\0\0IEND\xae\x42`\x82", - 3253 ) }, - { "/images/list-vert-dash.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\1\0\0\0\b\b\6\0\0\0\x38\x1a\x95\x41\0\0\0\x19tEXtSoftware" - "\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 " - " \x11\x7f\x1a\x93\0\0" - "\0\"IDATx\xda\x62\xd8\xb0\x61\x83\x31\x13\3\3\x43\a\x88`\0\x13\f\xff\xff\xff" - "g@p\xc1\4@\x80\1\0\x9c\xe6\5\xd5\xb7\xb4I>\0\0\0\0IEND\xae\x42`\x82", - 944 ) }, - { "/images/status_panel-icon_3.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0/\0\0\0/\b\6\0\0\0s'\x93\xac\0\0\0\x19tEXtSoftware\0\x41\x64" - "obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 b\x93\x44\xca\0\0\2\xf6IDATx\xda" - "\xec\x9a\xcbk\x13Q\x14\xc6\xbf\x99I&\x93L\xd2$m\x93\x34}\x18\xdb*mu\xa1\v\x17" - "\6\xc5\xa2\x14\xa1X\xc1\x85V\\\b\xa5\x16q\xe5\xc6\x95 \xae\xfc\x13\x44W\xa2\x88" - "\xd0\xa5.\xaaR\x15T\xb0]\b\xa2\xf8\xa0\xb5TBi\xd3\xa6\xa9i\xda<\x9ai^\xf5\xce" - "\x8d\xa9]\xf8\xa0\x93::p\xf\x1c\x66\x92\xcc\x9d\xfc\xee\x99\xf3\x1d\xce\t\xe1" - "\xcc\x37\xc7\xbb\1\xdc\"\xee\x87\x31l\x8e\xf8\xb9\xf8\xd9\xc6\xc7\xbc\xc1\xc0" - "U\xf3{%a\x88\x1c\xbby\x83\x81S\x8b*\5\x1at\x1e\xc6\x35\xbf\x91\xe1\xc1\xe0\x19" - "<\x83g\xf0\f\xfe\x8f\x66\xaa\xf4\6\1\x87\x19\a\xea$\4}\x12v\xb9-h\xb4\x9b\xe0" - "\x14y\xeak\xe4\xf3\x44\xb6\x88\xafJ\1\xe1t\x1e\xef\x63\xab\x18\x8d(\x18\x89\x64" - "\x10\xcd\x14*\x86\xe7Hc\xb6\xa6\x65\x61\x7f{\x15z[\x1d\xe8\xac\xb7j\xfa\xe2'" - "\xd3+\xb8\xfd\x39\x81\a\xa1\x94\xa6\xf5\xa4\x31\xd3\x16\xf9\xc1\xae:\x9ch\xb6" - "\xd3\xf3\x84\x92\xc7\xbb\xd9\4\xc6\xa3iL/)\x88\xad\x64\x91\xce\x16\xa8s\x1c`" - "3\vpXL\xa8\x95\xcd\xd8\xee\xb6\xa1\xdd+cO\xbd\3G\x9bl\xd4\a'\x93\xe8\x7f>\xaf" - "O\xda\xdc\x39\xec\xa3\xe0\xcb\4\xfa\xee\x9b\x30^\x85\xe2(\x14\x7f\xfe\xf0\xd6" - "\xc8\xdb\xe5\x8d\x44\x92\xab\xf8\x18Iah\f\x90L<\xbav\xd6\xe2\xf4^?\xce\xecp`" - "\x96\xa4\xd4\x95\xd7\xb1\xbf/\xd8\x9e\x80L\x8f\xd7\x9eM\xe2\xe5\x97\xc5_\x82" - "\xff\xce\x94|\x91l\"\x8a\x1b\xa3S\xf4u_[\x95>\xd5\x46\x36\x97\x96\b\x85%R\xad\x82\1\x17.u6\xeb\xf_\xce\xf5yR\2U\x7f\x41" - "*\x8fV\xc1\xea\xde\xdb\x30\xc1\x32\xc1\x32\xc1\x1a@\xb0j\x13\xb5\xd5\x82\r%r" - "\xfa\xf4\xf3\xbb\xdd\"\x86{\x1a\xd6\xfb\x11\xb5%\xae\x44\xb0\vd(\xe9~\x18\xc6" - "\xa7xv\xd3\xfd\xbc\xe6\x61\x64\xa0\xc3\x89\x93-v\xcd\xc3\xc8\xd3\x19\x32\x8c" - "\x8c'p\xbf\x82\x61\x44\x33|\xd9\x9a\xd5\x31\xd0/a\xbf\x8f\x44\xd5%\xd2\x31\xd0" - "EF\xc0*\xb1\x94\x91\xcb\x1b\xc6\xc0\xf\x64\f\x1c\xd9\xa2\x31P\xf3$\xb5\xd1\x42" - "\xc9\x1c\xf5{\x13I\xf6\xeb\1\x83g\xf0\f\x9e\xc1\x33x\6\xcf\xe0\xff\a\xf8\x39" - "\xa3\x41{\xad\xc2:\xfc\x80G\xe2\x8b\x46\1\xf7\xdb\4\\\xf\xba\xd5\xd3\x88\xda" - "U>\x9a\x38U\x7f\x1c\xa5?P\xd4\x19\x64\xf\x61\xe2\xe7\xbf\t0\0\xd2jd\xd3\xca'" - "\xff\xf1\0\0\0\0IEND\xae\x42`\x82", - 1668 ) }, - { "/images/graph-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x14\0\0\0\x12\b\6\0\0\0[\xd0\xfe\x10\0\0\0\x19tEXtSoftwar" - "e\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 " - " \xce\x81:.\0\0\1\x31" - "IDATx\xda\xac\xd3;K\3Q\x10\x86\xe1\xcd\xe2%D\xf1\x82M\xc0\xd2\xc2V\b\xa8\xa0" - "\x8d\xf9\a6v\x16^\t(6\xfa\x1f\xd2\n" - "^:A,D\xd1V\4[Q\b\x8a`@\"(\x88\"\x82\x65\"\x88\x61\xf1\xf2\xe\xcc\xc2\x61\xc9" - "\x8a\xb2s\xe0)v\x93|9\xe7\xccL\xea\xf4\xbc\xd4\xeey\xde\x17\xde=\x83\xe5\xe3" - "\x1a\x15\xb4Z\4\x36\xa1\a\x9dH\xa3n\x11h\xb5\xba\x10\xf8\x46\x61\3\xb8\x94\xd3" - "Z\4N\xe2\f}R\xd8$\x81-\xd8\xc0\xe\x32\xfa.\x88\xbb\xc3\xacV\xfd\x31\xe6\xf3^" - "\x1c\x62X\x9f\xa5\xe5\x16Puw(\x95\x9e\xc2\t\x9eq\x83\x45\xa4\"ac\xb8r\xc2n1\x84" - "\xedh\x95\xef\xd1\xec<\xb7\x61\r\xe3\x98\xc6\x13\x96Qt~w\x80\x19\xbc\x35j\x9b" - "0\xec\5\xfb\x18\xc1 \xf2(k\x15\xf3\xfa\x1d\xe9\xd7\x15\xac\xff\xd6\x87{z\xc1" - "r\xe4O\xbd\x43\xd9\xe1\x1c:\x9c\xb0\aL\xe8\x1f\x34\x1c\xbdp\x15p\xac\x61\xe1" - ".\xe6\x31\x8b\xf}w\x84\\\\\xd8_'eK\x8f<\x8aU|[\x8c\xde\x85\xfa\xd7,\xf7\xe3." - "\xe1\xd4\xd4$\xf0U{\xb0\x64\x30\x86\xbb\x12\xb8\x84Mt\x1b\4\xd6\x7f\4\x18\0\xf8" - "\2<\xdcue\x81\xc2\0\0\0\0IEND\xae\x42`\x82", - 1215 ) }, - { "/images/status_panel-icon_3-error.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0/\0\0\0/\b\6\0\0\0s'\x93\xac\0\0\0\x19tEXtSoftware\0\x41\x64" - "obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 l\xebYn\0\0\2\xbdIDATx\xda\xec" - "\x9a\xcbk\x13Q\x14\xc6\xbf\xcc$a\x92L\x93\x18\xf2\x14\xd2\x9a\x9d\x34\xb4(](" - "V+Uh\x9a\x85\xfa\a\x88\xab\xaa E\x17R!\xad\xd0\xee\xc4\x85\x88\xee\x35\xe8\xba" - "tQ\x10\xab\x42,\x8aue\xbb(6]\x14[S\xab\t4)I\x9b\x17y:s\x83\xb3\xb2\x91<\x9c\x30" - "p\xf\x1c\x66\x42&\xc9\xef>\xbe\xf3\xb8\x44\xb5\xe1\xf7\xf9\1<\x17\xdc\5\x65X" - "T\xf0\x31\xd7\xec\xdc\2\xa3\x30p\xd1\\\x8c\xc9\xfcJ\xb8\xfa\x19\x85\x81\x13\xab" - "\xa4\x92\x64\xd2\x19(\xd7\\J\x86\a\x85\xa7\xf0\x14\x9e\xc2S\xf8\x7f\x9a\xba\xe5" - "/\xb0;\xc0y\xbd\xe0z{\xa1\xe9\xee\x86\xdaj\3\x63\x30\x10G\xb5\x8aJ6\x8b\xf2~" - "\n" - "\xe5x\2\x85\xadM\xe4\xc3\x61\xe4\xd7\xbe\xa2\x9cLv\xe\xbe\xcb\x37\n" - "~\xe8<\xb8\xfe\xfe\xc3\x1fR\xa9\xa4\x81h\\G\xc1\xf5\xf5\xc1x\xf9\n" - "y+\xb7\xbc\x8c\x83wo\x91\xf9\xbc$/\xbc}r\n" - "\x86\x33\x83\xe4\xbe\x9cJ!\xb7\xb2\x82\xfcz\x18\xc5\xed\bJ\xbbqT2i\xc1\x33\x12" - "<\xdb\x65\4k\xb3\x42\xeb\xf1\b+\xe4\x85\xee\xc4I\xe8\6\6\x88\xa7\x17\x17\xb1" - "\xfb\xf8\x91<\xf0\xb6\x89{\4\xbc\x9cJb/\x18\x44\xe6\xe3\aTK\xa5\xbf?,n\x9bt\x9a" - "x1\xfa\v\xf9\xd5U\xec\xcf\xcf\x83\xe1t\xe0}#8r\xf5\x1a\xf8\xe1\x61\x94\x13q\xec" - "\xbd|\xf1\xff\5\xab?u\x9a\\c\xd3\xd3H\xbf\xf\x1d\xe^\xaf*\xcc\xe7\xc8 \xe2O\x9f" - "\x90\xd7\xfc\x88O\x9eh\xc3p\\m;\xb3l\x1b\x62]\xed\xe7Y\xa3Q\xdeP\xe9\x98\x99" - "!K\xaeR7.\x1b\x95\x30\1\xa2p\xad\xb7\xeft&\xda\xb0&3lw'`\x19\xbb!D\x8e/\x82`" - "\xd7Q\xfc\xb1]_\xb0\xc7<$\xac\x8a\x82\x65\xf4\xfa\xce\x85\xcaJ.Kb\xb7\xc6\xed" - "\6\x7f\xe1\"\xf1\x46\xac\xb8\xb3#\xe4\b;TZ\xad\xfc\xf0\xd1@\0\x85\xcdoP;\x9d" - "\xb5$u\\LR\xeeZ\x92\xe2yif\xc5\x15\x90\x92\xd4\xf7-!A\xadII\xca\x30x\x16\xf6" - "\xc0\xa4\xfc\xf0\x7f\4[\x8a\xc5\x90\x16=\x14jZ\xb0\xb2\xd7\x36T\xb0T\xb0T\xb0" - "\n" - "\x10l)\x91h\xbb`\x8b\xb1hs\x13\xb8\xe1\xf7U\x1b\xf9\x80\xb6\xa7\a\xce\a\xf\xa5" - "z\x84\x94\xc4-\bV\xacNc\xf7\xa7P\x88\x44\x1a;.\x9b\x9dk\x1c^jFF\xfd\xe0\xcf\r" - "\xd5oF\xea\x98\xd8\3\x90\x66\x64\xe9Ssg}\2|\xd3{\xfe\xe0\xcd\2\xf1V\4\xdb\xf1" - "\x1e\xb6%\xc1\xd2\xa3\xf\n" - "O\xe1)<\x85\xa7\xf0\x14\x9e\xc2\xb7\x1f>\xaa\x38h\xb3Y\x82\xbf\xce\x98L\x15\xa5" - "\x80\xb3\x16\v\xcc\xb7\xc6\xc5\xdb\x98XU\xbev<\v^B\xed\xf\x14N\x85\x8c\xe1\xa7" - "\xe0\x37\x7f\v0\0\x15XgE\x97\b\xf4\x63\0\0\0\0IEND\xae\x42`\x82", - 1611 ) }, - { "/images/bars-icon_white.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\r\b\6\0\0\0\xa0\xbb\xee$\0\0\0\x19tEXtSoftware\0" - "Adobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \x9a\x1b\x8e\r\0\0\0HIDATx\xda" - "b\xfc\xff\xff?\3)\x80\x91\x91\xd1\x18\x85O\xaa\1@\x80\xa2\x81\x89\x81\x42\xc0" - "\b\xc4(N\2\xba\xe8,).\0y\xe1?\x16\x43\xe9\xe7\5\x8a\r`!6\xbap\x85\r\xbe\x30 " - "J|\xe0\xc3`\xe0\r\0\b0\0\x97\xd7\x1b;g\xbaN\xe2\0\0\0\0IEND\xae\x42`\x82", - 982 ) }, - { "/images/status_panel-icon_2.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0/\0\0\0/\b\6\0\0\0s'\x93\xac\0\0\0\x19tEXtSoftware\0\x41\x64" - "obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \x10[\xd5\xd6\0\0\2\bIDATx\xda" - "\xec\xdaMK\x1b\x41\x18\a\xf0\x7fv\xd3u\x93V\x93\xd0\x66\x63|\xc1\x8b\x44=y\xb2" - "\xd0\x1ez\xf0P\x1aH{\xaa\xd8K\xe9\x41\xf1\vx\x10\xfc\x1c\x42\xbd\b=\t\xf1$\xbe" - "\xa2\x97\xdeJ\3\xbd\x15\xaa\xd8j!\x10\x37\xa4i\xa4n\xacK^6\xce\xac$T%)l\b;\3" - "\xf3\xc0\x90'a \xbfY\xfe\xb3\xbbY\xe2\xb9\xf7\xfe\x30\xe`\x85\x8c(\xf8(\x9d\x8c" - "\x99\xb3\xb7\3\xbb\x12gpZQM\x95\xb7\xc8k\\\xe2\fnW\xce\xac\xda\a]\2\xbf\x15\xe5" - "\x19\xf\x81\x17x\x81\x17x\x81\xffoy;\xfe\5\x92\a\x89\xa1\xfbx\x1aQ\xed>\x95\x33" - "\x91\xfc\x61\xb0\x8f\x8f\5\x14\xac=\xef\xc5HP\xb1\xdf\xff)YX\xfdn\xb0\x7f\xe4" - "\x1f\xa9\x32\xf6\x13\xfd\x88\xf8\xe5\6<\xb1s\x8a/\xbfL\xf6\x33??\x1ej\xc0\xcf" - "\x9b\xc0\x83]\x12\x9bx\x9a\xf3z\x9d\x9c\x97\xef\xc0\x35\x9f\x8c\x8f\xaf\6\x30" - "\xf8\xc0\xcb\x1e>\xec\x93\x1b\xfdm \x85\xef\x91H\x8d\x92\xbd\xa0\xfd\x33\x8f" - "\x99\xccg\xffV\x10P\xae\x37\xea\x43\x92\xffw#=H\x17\xcb(\x98\x16>LFl8\xad\xd3" - "\x8b*[\xf8\x80\"\xc1\xaa\xdd\xfcl\xf9\x99vg\x1e\x8d\x92N\x16\xc9Ll(|3\xde\x87" - "\xb1\x90\xd2r^\x89\xacn\xe1s\x9e\x9d\r[\x87Ohj\xcby\xbf\xcd*\xa6\xf7u|\xca\xb6" - "w\xda\xf4v\x1a\x9e\xbb\xac\x62\x31\x95G\x8c\x64\xdc'{\xf0\xb5P\xc2\xfa\xcf\"" - "\x8c\xb2\xc5\xc6\x45\xaa\x15\xfc\xc5v\6\ag%6o\xcc\xdc\x82\xb7\x8dw\x13\xde\x16" - "\xdem\xb8\x63\xbc\xcf\xeb\xc1\x86\xcbp\xc7\xf8\x37\xc3\xddx\xec\x32\xdc\x31\xbe" - "v\xeb\xea\xe9\6\xdc\x31>yl`;}a\xf7\xdf\b\xd8\r\xb8\xe3\xf3\xfc\x65\xa5\x86\xd7" - "{\xba\x9d}\xdas\xf9\3\xdcM\xb8x\xf4!\xf0\2/\xf0\2/\xf0\2/\xf0M\xf0:o\xe8\xfa" - "#B\x8a\x9f\r\xab\x92\xc5\v<\xea\x97\xb1\xf4$D\xdb,\xbd%\xde\x39\x9a\xea{\x89" - "\xeb?P\xf4r\xb2\x86\f\x19sW\2\f\0\x34\x8b\xc6\xc6\x9b\x42k\xd4\0\0\0\0IEND\xae" - "B`\x82", - 1430 ) }, - { "/images/clock-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\xe\0\0\0\r\b\6\0\0\0\x99\xdc_\x7f\0\0\0\x19tEXtSoftware\0" - "Adobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \xd5\4\xfe\xba\0\0\1\x31IDA" - "Tx\xda|\xd2/HCQ\x14\xc7\xf1\xbd\x39\xa6\x41\a\x9b\x61\x36\xff\xe0\x1f\xd0\xb2" - "0\x19\x88\x82\xb8\xa0\xc1\x62\xd1`1X\x86\x45,K\v\x82\x88\x98\f&\xd3\xca\x86M" - "\xd4`\x10\x83\xf8X\x13\xfc\x83(\xf8\a\4Q\xd0\xc1\x8a\xe8P\xd4}\xcf\x38\xf.\x97" - "\xcd\v\x9f\xc7}\xef\x9e\xdf\xbb\xbc{\x9e\xe3\xba\xae\xcf\x1a\x43\x98\x41\xf~" - "q\x8d\x1c\xce\xcc\"\xbf\x31\xf\x61\a\xc7\xe8\xc3\5\xae\x30\x88Sl\xa1\xd1\xe\6" - "q\x80n\xc4\x30\x81\x66|`\x14#\x18\xc7\x36\x1c\x33\x98\x46\a\xc6t\x17\x19\x61" - "Du^@R\xd7\xe7\xbc\xa0X\xc0\n" - "\xde|\xf5\xc7\x1d\x36\x91\xf2\x82\xbd\xfa\xe6\xfd\x1a\xc5\xf2\xdd]\xe8\x44\3" - "\xf6\x10G\x93\4[\xb5\xe8\xd5\n" - "}b\x16\xf7x\xc0\x92\xd6\xc8\x37\x46\2\\\x9e\xb4\xb0\x1d\xb7\x46p\x11\xab\xc6" - "\xfd\x33\x12\xf8\x46Qv|\xc4\r\xa6\xad\x1d\xdfu'OY\xfb{\x82/\xefT\xd7\xf5\x64" - "\a\xfe\x39\x9c\x61\xcc\x63\xcdlG\x16\xbb\x38\xc2\x94\x15\xf0k\v\xa4\xcf\x1b\x38" - "\x94\x87\1]\xfc\xd3\xc5\x65\xe4\xf1\x82s\r\xc9\x9f\xd3\x82\x8c\6\xab\xc3\xa9" - "\xf1\xaf\xb6\x61\x12\xfd\xf8\xc1\xa5\xb6\xaa\x64\x16U\4\x18\0\xd8@?t\x83M\x86" - "\f\0\0\0\0IEND\xae\x42`\x82", - 1215 ) }, - { "/images/cog_white_16x16.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\x10\b\6\0\0\0\x1f\xf3\xff\x61\0\0\0\1sRGB\0\xae" - "\xce\x1c\xe9\0\0\0\6\x62KGD\0\xff\0\xff\0\xff\xa0\xbd\xa7\x93\0\0\0\tpHYs\0\0" - "\v\x13\0\0\v\x13\1\0\x9a\x9c\x18\0\0\0\atIME\a\xdd\4\x19\x13\x16\x13<\xa3\xf2" - "\xa4\0\0\0\xa2IDAT8\xcb\xad\x93\xd1\r\xc3 \x10\x43\x1f\x99\x80n\xc2\b\xcd&lH" - "F\xc8\6\x64\x13\xb2\x81\xfb\3*\xba\x92\x36Ub\x89\x1f\xceg\xd9w\0\a\x90\x14\xf5" - "F\xe4\x17$\x85\x9e()w\2\xd9\b\x87Qs\xa9\xe4\")\xe9\x13\xc9p\xc2\x91\xdd\xb3\x88" - "\0\xae\x13)\x80\xe7\x1cv\xe7\xdc\3`\xea.WCZ\x80\xb9\x9e\xc5\xd4Vk?\xdb\xbc\x83" - "!\xdb\xb9\x64I\x91\x83|a \xf0\x1c\x11'\xae\xe2r\x84/\x84Tm\x87Q\xad\xf5\xdd\xb3" - "\xc6j\xc5\xff\x91\xdc\x37\xfbm\x88\x1b\xb0\x37\xf5\xc1\xde\xdb\xbb\xe8\x39\xdb" - "}\x9f\xe9\xcaw~\1\x31\3\x93\xfe\xf3\xb3\4]\0\0\0\0IEND\xae\x42`\x82", - 290 ) }, - { "/images/icon-pause.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\xe\0\0\0\xe\b\6\0\0\0\x1fH-\xd1\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\0\xc6IDATx\xda\x9cRQ\r\xc3 \x14$(\xa8\4$T\2\x12" - "\xea\0\tT\2\xeZ\x14,U\xd0:\xa8\x14\xea\0\x1c\x30\x8e\1\xe9\x1a\xb6\xb2]r<\xc2" - "\xbb{\x8f\x8f#\xe4\x1d\xe7_\2\f\0\xa0\x19\6\xf7]\x83\xeaR\0\0\0\0IEND\xae" - "B`\x82", - 284 ) }, - { "/images/live-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x11\0\0\0\x11\b\6\0\0\0;mG\xfa\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 V^\xcfW\0\0\3\xfIDATx\xdatT{" - "HSa\x14\xbf\xf7\xee\xed\x9c\x13\xc7LkY>\xf0\xb5\xd2l\xa8!\xe2+\"\xd4\n" - "\xd2\xa8(*#\xca\x1e\xa4\xe6?\xbe\x82\xc2\x34\xa3\xc4\xca\x92\xa8\xc8\a\xfdS\x94" - "fQaEZZ0\xb5\x34\x64*\xa2\xe2\x34\x1f\v\x1f\xd3\xb9|ln\xb7\xdf\a\x1b\xc8\xb0\v" - "?\xce\xfd\xce\x39\xdf\xef\x9e\xef|\xbfs\xe9k\xea\x8b\xd4\x1a\x8f\f\x88\a6\xda" - "\xd6\xbf\x81&`\xda\x31\xf1rd9\xc5u\xf0\xf9\2\xb7\x80\4.\xc3\xfb\xee.\xf2\xb4" - "\xf2\x39\x42\x89\xd1lp\x9bY\x9a\xac\x61h\xa6i\xc5j\xce\x44|p\xf5\xa6\xd5$I@\x95" - "\xab@\xf6\xf4\xf4\xd6\x1c\x8e\x80#\x8c\xb5}\xbd\x83\4\x97-K\xda\x36\xdd\x97]" - "\xea\x89\xa6n\xf\xb1\xe2\xc2\xb0\xa1\xff\x89#\xc9\xe\xa0:N\x91\\\x12\xbd\x61" - "w.K\xb1\xe5\xe5\x9dW*\f&\xbd\xd2\x16\xff\t\xb4\xa0t\x91T\xe0V\xfai\xf8\xd5\xa3" - "XE\x92\xf4\xeb\xe8\xfb\x32\x12\xa4\xd1\x13>l\x8fR\xa6z\xb0\xdf\xef\x44N\xbb\xae" - "\xf9\xec\x87\xe1\x97\xf9\xf0\xa9h\x9aY\xa4)\x9a\xb1\xb2\x16\x81\x80#\xea[a\xcd" - "{,\xd6\x95\x81`\xd9\xf6\xecq\xa3\xf6\x66\x9a\x32;\xde\x99\xe7\xd2\x42*9\x86\xe4" - "~\x10\x9c\x1b\x35\xe\x65\x82\xe0.\xfa\x31}$\xf0|\xaf\x97\xc4\x37\bq\xe3\xf8\xdf" - "\x11K\x83\xf6\x45\xc0\xdc\xf2Lw\xdc\xa6\xe4\xe8wC\xcf\xca\\\xf8\xae\x87{\xa7" - "\x7f\xd5\x86{\xc4(\x18$\xa5\x86\xaf\x8b\x19\x82\xed\xab\xee\xbe\xad\x42q\xe2" - "\x8c\xb0\x42\xf9\xbci\x8es\xbd-k\xa6\xa8\x35\x63\xa1RSz\x10\xe5'\xa0\xa9\xcc" - "\x9f\x85\xb1\xcf\x38\x96\xdc`\x9a-\xd2L\xff \xa7\x39NH\xc2#=\xe3\xfdIS\x81\3" - "\x9b]\xfc\xc6\x9c\xb8\x62\x61\xfd@\x8d\xd4\xcaZ\xdd\xe0s\x17\xf3$o|\xa5\x41N" - "X?\xef\xd7k\xc4\xf0\xe5\2\xed $'I%$2\x94\x16\n" - "\xdb\fx\xc8\x9d\xd6{\xc1\xd6\xa3\xb9|{\xf7-\xac\x85\xe4=6[M\xa3\xa8\x80\xb8R" - "\x80IT&\x81\r%\xc1\xa9\5\xb3\x91\x88k\n" - "\xd0\x1aMs\xce\xb6\xf7\x32;\t\x8eK\x8c\x10\xf0\x11pEFX\x85M\x90z@NH:F\xe6\a\xe7" - "\xc9\2x;\xb9\x38\x61\xc6\r\x10\xd1}\xb3\x93xK\3(TQ\x81W\x7f_i\xa0\xev\x1c\x88" - "\xc2\5h\b\x11!\xa9l\xd5\x35-\xdb\xb4R\x90\x1e\x92\x7f\x9f\xc3p\xfd\xd0\xe0\x62" - ";\t\xf4@\xf1\x18\xfe\xc7\xf4\x90\xbc\x88\xbd>G\x97\xe0\xaa\5Nm\x91\xa9\x88\x10" - ";\bI\x1d\x97\xe6\x41\x99\xec\x19\"L\xe8\xa2\xa0\xb8-+\xf\xeb(;\t\xd4I\xf5\xe9" - "\xbb\xee=\xec*1\xe1\xeb\xdb\x66\x97g\x88\x8e\xd4\x89\xde\x87\x88\4*\x89\xd8\xc8" - "\x10\x91.c#U\x84+%\xfbZI+V\xcf\x87\x42\xe2M\xa5\5_\xda\x89x\xa3m\xf0hR9p\x83" - "^c\x8a\xf7\1\xaf\xd7\x1a\xed\x14\xbf\x93=u\3UJ\xc7)f\x1c\xf2\b{!\xf5\x9fG\xad" - "k\f.\x88\xb8\x93\xe8\xe8w\xfc\x15\xb0`\xe\x83\xed$\xf7\xff\x1f\xae\xab\xc8i\xc0" - "\xb1X\xbb\xe3\x9f\0\3\0\xbd\0*\6\xdbI\xf7x\0\0\0\0IEND\xae\x42`\x82", - 1693 ) }, - { "/images/status_panel-icon_1.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0/\0\0\0/\b\6\0\0\0s'\x93\xac\0\0\0\x19tEXtSoftware\0\x41\x64" - "obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 `\xad)\xe4\0\0\1\x12IDATx\xda" - "bd\x9d~\xc3\x93\x81\x81\x61.\x10K2\f\r\xf0\x1c\x88\x93\xdf\xc7\xcalg\x1a\x62" - "\xe\a\1I1\xe\xe6-@\xda\x93i\x88\x39\x1c\f^\xfd\xf8\v\xet&\x86\xa1\v$\x87\xb2" - "\xe3\x19\x46\x1d?\xea\xf8Q\xc7\x8f:~\xd4\xf1\xc3\xdb\xf1,\xe4h\xba\x11!\xcf " - "\xcf\xcbJ\x15\a\xdc\xfd\xf4\x9b\x41g\xe5\x43\xfa\x85<\xb5\x1c\xe\2\xca|\xac\xf4" - "\ry\x18\xe0\x9c}\x87l\xbd\xfclL\f/\xe2\x95\x86\x66\x9a\xff\xfc\xfb\xdf\xd0\xcd" - "\xb0\xff\xfe\x8f\x16\x95\xa3\x8e\x1fu\xfc\xa8\xe3G\x1d?\xeax\x1a\x36\xf\xbe\xa7" - "\xaa\f\xbd\x90\xbf\xf9\xe1\x17\xd5\x1cp\xf1\xedO\xfa\x86\xbc\xc1\xeaGC?\xd9\x10" - "\x9b\x9c(i}\x8e\x66XJ2/\xb2\x1c\x35\x63\x61\x34\xe4i\x99\xaeGk\xd8Q\xc7\x8f:" - "~\xd4\xf1\xa3\x8e\x1fu\xfc\xa8\xe3\a\xc2\xf1\xcf\x87\x9a\xa3\xc5\x38\x99\xe1" - "\x8eO\x11\xe5`\xfa\x37T\x1c.\xc9\xc5\xcc\x30\xc5R\x10\xc4|\1j\x98m\xbb\x15*\xe5" - "\xcb\0Y@!1D\xfc\xf0\x14\x88\xd3\0\2\f\0\xe1U4\xb0+\xc7\xb5\x83\0\0\0\0IEND\xae" - "B`\x82", - 1184 ) }, - { "/images/list-horiz-dash.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\b\0\0\0\1\b\6\0\0\0\xe3\0\xef\x43\0\0\0\x19tEXtSoftware\0" - "Adobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \xa8\xbf\b'\0\0\0\x1aIDATx\xda" - "b\xdc\xb0\x61\xc3n\6\4X\xed\xef\xef?\v\x89\xcf\0\x10`\0vq\4\xff\xfb\r\x84\xd7" - "\0\0\0\0IEND\xae\x42`\x82", - 936 ) }, - { "/images/fullscreen_16x16.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\x10\b\6\0\0\0\x1f\xf3\xff\x61\0\0\0\x19tEXtSoft" - "ware\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\0ZIDAT8\x8d\x63```\xf8\x8f\5\xcfg" - "\xc0\4\xf3q\xa8%J3>C0\4\f\xf0\x18`@\x8c\1\xefq\x18\x62\0\x95\xc3j\xc0|4\5\xef" - "\xb1\x18\x80,g\x80\xe4\x1d\x14?\xc3\fI\xc0\x62@\2\x16\xd7\xe1\v\xaf\x11\5(\xe" - "D\x8a\xa3\x91*\t\x89\xaaI\x99\xe2\xcc\x44Rv\6\0\xe3~i\x15\n" - "\x93\x96.\0\0\0\0IEND\xae\x42`\x82", - 184 ) }, - { "/images/server-icon-removed.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x1b\0\0\0\x14\b\6\0\0\0|\x82\x46\x80\0\0\0\6\x62KGD\0\0\0" - "\0\0\0\xf9\x43\xbb\x7f\0\0\0\tpHYs\0\0\v\x13\0\0\v\x13\1\0\x9a\x9c\x18\0\0\0" - "\atIME\a\xde\f\x10\x15(\xe\xd1\1\x8fW\0\0\2;IDATH\xc7\xb5\xd5K\x88Oa\x18\xc7" - "\xf1\xcf\x9f\xd1\xd9\xb9\x34n\xcd\x42(do)\xd2I\xae\x1b\x94\x8cX\xd0\x14G.\x85" - "\\&r\x8d\\B.s\\\xc6\xc6lPh\x8a\xd8\x9c\xa5\x85\xc2Z\"\x92\xe4\x36\x99\tq\x98" - "16\xef\xa9\xd3\xf4'\x7f\xc6\xb3:\x9d\xf7}\x9e\xef\xf3\xfe\x9e\xe7}^j\xb4<\x89" - "\x97\xe6I\xfc\x30O\xe2\x65\xb5\xfaVj\x80\xac\xc4\1\x8c*\xfd\xee\xc0\xd6(\xcd" - ".\xf6\v,O\xe2U\xd8\x8f\xe1\xbf\xd9\xf6\b\xc7\xa2\x34\xbbP3,O\xe2\n" - "\xd6\x61\x37\x86\xd5\xa0T'vEiv\xf2\x8f`y\x12\xaf\xc5>\f\xad\xb1$?0 |w\xe1`\x94" - "f\x87\xaa\xc2\xf2$\x9e\x87#\x98\x8c\xef\x18T\xac\x45i\xf6'r\xf7V\xf9\xfd\tG\xa3" - "4\xdb\3\x95<\x89\xc7\xe1*\xa6T\xc9\xf0_a\x85}\xc6\xac\1Q\x9a=\xc3j\xdc\x46\xe1" - "\xd0\xab\xff\xec\x15VDiv\xb7,c\x84i8\x8d\x89%`\xe5/!/\xb1\1\xb7\xa3\x34\xfbR" - "\xc8\x38\6-\x98\x8d\x37\xd8\x86\x8f\x38\x83\x86\0\xad\5\xf8\2\xeb\x31\x38\x34" - "Z\3\xda\xb0\xbd\xe\xdf\x30\x30\4l\xc0%\xbc\x43\x33\xde\xe2\x1c\x46\xf7\x91\xb6" - "\x1a\xfc\x19\xd6\x86\x18\xe7\x31\xb2\x10\r_\x91\x97\x65\x1c\x85V\xcc-5H\av\xe1" - "y\x90w,zBr\x85=\xc1\x1aL\xc0^\xd4\x97\x1a\xed\x16\x9a\xa2\x34{\xab\b\x9a'q\x13" - "\xee\x87K9\x1e\xed\x61s}\x80\xb4\xe1\x38\x66\xe0i8\xe5\x63L\xc7)\\\t\xb2\xd7" - "\a\xbf\xf6\x10\xa7\v\xf7\xf2$^\\\xd4lj\xc8`p\xc8\xa8\x1b\xd7\xb1\5G\xb1\xa0t" - "\x92\xae\x30\x1f[\xc2i\x9a\x31$\xac\xf5\xe0\6\x36\xe3\x10\x16\xa1\xae\xd4\x91" - "3\xcb\x32n\xc2\x8e\xd2\xe4\xe8\t\x19n\xac\xe2\\\x96\xb2\x1b\xd7\xb0\xb5Jr\x9d" - "\xd8\x13\xa5\xd9\x89_\x8d\xab\xf5}f\xe2\xf\xdc\f\x1d\xb6\x1fK\2\xb4\x1b\x97\x43" - "\x82'1\xbfT\xeb\xf\xd8\xddwFV~3\x11V\x87\xd6\x1d^\x82\xde\t\x1d\xb7\x30H}\n" - "sJ\x90\xf7\xd8\x19\xa5\xd9\xd9\xbf}b\x9a\x42\x9d\x46\x94\xa6\xcb#L*A\xde\xa1" - "9J\xb3\xd6\xfez<\x97\xe3p\xb8s\x85\xbd\xc6\x96(\xcd\xda\xfc\xf\xcb\x93\xb8\x31" - "O\xe2\ay\x12\x37\xd6\xea\xfb\x13\x94\xf1\xbb\xe9\x85\xc3\x90\xbd\0\0\0\0IEND" - "\xae\x42`\x82", - 686 ) }, - { "/images/pencil-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x11\0\0\0\x11\b\6\0\0\0;mG\xfa\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \xe5\x61\xce\xad\0\0\1\x8bID" - "ATx\xda\x62\\\xb2\x64\t\3\t@\x15\x88\xb7\1\xf1] \xe\2\xe2o\xd1\xd1\xd1\f,$\x18" - " \0\xc4\xfb\x81X\x1a\x88U\4\5\5\x1f\xcb\xc8\xc8\xa8\3\xd9o\x98H0\xe4\3;;\xfb" - "\f\x18\xe7\xfd\xfb\xf7\x42/^\xbc\xb8\tdr\x11\x63\x88\n" - "\x10\xef\3\xb9\xe0\xe7\xcf\x9f-\n" - "\n" - "\n" - "\x9b\x61\x12\xdf\xbf\x7f\x17\2Rk\x99\x88\x30\0\xe4\5G..\xae\xdb\xda\xda\xda\xc6" - "\xf\x1e<\xf0\3\x19\4\xe4\x33\x38\x39\x39\x81\xc3\x89\x89\b\3\x64@\x9co\xdf\xbe" - "q>|\xf8\xf0\x38\xcc ww\xf7\x1a^^\xde'@)O&b\f\x80\x81/_\xbe\xb0\xbe|\xf9\xf2\x30" - "0F\xa4\xd7\xaf_\xdf\n" - "\x14\xd2\6\xe2\xdbL\xc4\x1a\0\2 /XYYq\2\x99\xe0t\xb1t\xe9\xd2O \x9a\x89\x14\3" - "\\\\\\\x18\x80^x\n" - "\xe4\xa6!\xcb\x31\x91\x61\x80#\xd0\5\xb7q\x19\xc2\xc7\xc8\xc8\xc8K\xaa\1\x30" - "C\x98\x81\xd8\4\x88\xcf\xa9\xab\xabg\xb1\xb1\xb1\xfd'\xc5\0\x98!z@\xbc\ah@\xfc" - "\x8d\x1b\x37\x96)))\xc5\x80\f\"\xd6\0\x10\0\xe5\x1d+ \xe6\xbfw\xef\xde|\xa0\x41" - "L@\x83\xe6kjj\xfeWQQ\x99\xe\x34\xe0\v!\3`\x86\xd8\x80\x18\xbf\x7f\xff\x66\4\x1a" - "4WKK\xeb\xff\xb5k\xd7\x16\x18\x19\x19\xdd\0%\rB\6\xc0\f\x91\x97\x90\x90x\b\xcc" - "\x91\xf2\x62\x62\x62\x8c\2\2\2\x13\f\r\r\xaf\0\x35\x9f!6g\x82\xbd\xe3\xec\xec" - "|\fH\x83\xf0q >\2\xc4\x97H)d\0\2\f\0\x7f\x46\x8d\xce\xc7k\xa1V\0\0\0\0IEND\xae" - "B`\x82", - 1305 ) }, - { "/images/copy_to_clipboard_16x16.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\x10\b\3\0\0\0(-\xfS\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\0\x33PLTE\xb0\xb1\xb1\xd7\xd8\xd8\xeb\xeb\xeb\xc4" - "\xc5\xc5\xce\xce\xce\xe6\xe7\xe7\xfa\xfa\xfa\xbf\xc0\xc0\xba\xbb\xbb\xf0\xf0" - "\xf0\xc9\xc9\xc9\xb5\xb6\xb6\xdc\xdd\xdd\xd3\xd3\xd3\xe1\xe2\xe2\xf5\xf5\xf5" - "\xff\xff\xff\x7f\xd8 #\0\0\0\x11tRNS\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff\xff\xff\0%\xad\x99\x62\0\0\0\x66IDATx\xda|\x8fK\x12\x80\x30" - "\bC\x83@k\xfdr\xff\xd3\x8a\xc5V\xbb\x31\x1b\x86\x37\fI`f\v\x9a\x38\x19\x1c\xb0" - "L!AN\x15\x90\x85\xc8o\6P\x88\x11\xe0\xd4\xf7\xa6\2\x82|\x81\xaa\xc0\xdf=\x80" - "\x9a\xe5\\\2\xe4\x1e\x42\2\xe8\xce\x1c{\xea?\x8e\xc9\xe7\x36\xba\xac:\xda\x9a" - "\xd9?h\xe5\xeez\x18\xea{\x9aK\x80\1\0U\x16\b\x11\x39W(3\0\0\0\0IEND\xae\x42`" - "\x82", - 288 ) }, - { "/images/layers-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x11\0\0\0\x11\b\6\0\0\0;mG\xfa\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 E\xae\x31 \0\0\1%IDATx\xda\x62" - "\xbcp\xe1\2\3\xe`\xf\xc4\rP6\x88>\x88M\x91\xbe\xbe>\3\x13\xe\xcd\xfb\x81\xf8" - "\0\x10;@\xf1\1\x16\x16\x96\xf3P9\f\xc0\x84G3\n" - "\xf8\xf3\xe7\x8f\1.\xc3\x98\bi&\xc6\x30\x90!\x8c@\1\x41\6\x12\1\a\a\x87\x81\x82" - "\x82\xc2\4\x90\x41, S\xb5\xb5\xb5\v>}\xfa\x34\xe1\xf5\xeb\xd7\xfa_\xbe|\xc1\xab" - "\x99\x87\x87\x87\x41\\\\\x1cL\3\xc1G\x90#\x18\x91\x63\a\x18\xd2\xe\xb8\fC\xd6" - "\xfc\xff\xff\xff\x83\x8c\x8c\x8c\r\x17/^<\0\x8a\x1d\x90K4\x81\x38\x18\x84\x81" - "\x82\xdf\x81t\5P\xe2\a\xcc\x30\x90\1\x30\xcd@\xb1\vW\xaf^\xfd\r\f\x17\x90\x1e" - "\x7f ~\v\xc4\x97\x41.q`bb\xea\xfa\xf7\xef\x9f)\xccV`\x18\xdd\0*,\4\x19\6J#\x1f" - "?~\x14|\xf2\xe4\xc9\x7f\xa0\x98>L\r77\xf7o!!\xa1\r@\xdc\f\xf6\xe.o@\r{\adZ\xe1" - "\b\x13P\2l\x18\r\x13*\x85\tL3,L\x18\xb1\x15\5\xc8\x86!\x87\t\xb2\x66\xe4\xa2" - "\x80\x11Oy\2\x36\f\xb9LA\xd6\x8cl\b@\x80\1\0\xdc\x9b\3s\xf4\xf0\xf2M\0\0\0\0" - "IEND\xae\x42`\x82", - 1203 ) }, - { "/images/clock-icon_alt.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\xe\0\0\0\xe\b\6\0\0\0\x1fH-\xd1\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \xe\x1a\x9b\xbb\0\0\1!IDATx\xda" - "|\x92O+Da\x14\x87\xdf\x99\"\xe6S\x18\xc9\x62\xccl\x94k\xcfV\xcd\x46!\x9a\x32" - "3M\xb2\xe2\x8b\xf0\5\x64\x45\x92\xd9L\xac-\xec\x10+w\x14\x62/\xebIc\xc1s\xea" - "wuzs\x9dzz\xff\x9d_\xe7\xbc\xe7\x9c\x42\x9a\xa6\xc1\xd9\x18\xac\xc1\x12L\xeb" - "\xee\t.\xe0\4>3\xc7\x82\x13\xce\x42\x17\xca\xe1o{\x83\x65\xb8\xb7\x43Q\x97\t" - "\\I\xd4\x87\x36\xd4\x44[we\xf9$YDK\xcf\xc2N\xc2\x31l\xc2W\x14m\4\xe\x61\3^\xa1" - "j\x11W%2q\xd3\x89\xbe\x45\xd0]\v\x1e\xe4\xbb\x62\xc2\xba\x1e\xf7\x61\x18\xf2" - "m(\x1f\xb3\xba\t+:\xdc\xe4\b&\xdc\xfeVk\xa5\xf8O\x84\x33\xad\aV\v\x97~\xc8\xaa" - "\xda\xd7~.\x12n\xc3;,B\xc7U\xdf\xec\xd1\x84=\x1dva\xd4\t?`K\x95\xee\xeamGo=k" - "\xc7\xb8\xab\xd6QTY\xdf\xeK\xb9\xa1\x41\x98\xc9&g\x1e.\xa1\xa4\xb6\xec\xb9\x62" - "%\xca\xa6\n" - "\3X\x80\xebx\xe4Na*\xa7X/\xea\xf9]<\xab\x41\xffX\x8f\x86\xfc\x19\xce\xf5\xd7" - "\xdf>\xff\b0\0\x89\xdcL\xff\x87\xa4\4]\0\0\0\0IEND\xae\x42`\x82", - 1199 ) }, - { "/images/trash_stroke_16x16.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\x10\b\6\0\0\0\x1f\xf3\xff\x61\0\0\0\x19tEXtSoft" - "ware\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\0NIDAT8\xcb\x63`\xc0\4\r@\xfc\x1f" - "\an` \0\4\xf0h\x86\x61\f\xf0\x9f\x42L\xb9\1\xe8.!\4\bz\x85*\6\xbc\x87\x62\6," - "|\xfa\x18\x30\x1a\6\x14z\xa1\1O>A\xceh\n" - "\xe8\n" - "\xf6\x93\x90|\xf7\xe3\xb2\x61?\x91\x9a\5`\x9a\0\x46\x61{\xe5\xc4\x15R\xe9\0\0" - "\0\0IEND\xae\x42`\x82", - 172 ) }, - { "/images/status_panel-icon_4.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0/\0\0\0/\b\6\0\0\0s'\x93\xac\0\0\0\x19tEXtSoftware\0\x41\x64" - "obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 'n?\v\0\0\2\xc9IDATx\xda\xec\x9a" - "\xcfo\x12Q\x10\xc7\x87\xfd\5,\x14J#Mi\1M\xa5\xd5\x44M4\x9e\xea\xc9\xc6\x83\xa5" - "\a\xfz\xf1Gz0V\xff\6\xff\x10\x9b\xc6\x83z\xf6^\xad\x9ax\xf1`b\xa2V\x13\x63\x30" - "\x91\x44\x8b\xdajm(--,\x14\xf0\xcd[\x1e,Tbt\t\xf0\xe2\x9b\xe4\x65\x87\x85\x84" - "\xcf\x9b\xf9\xce\xcc[\x82\x43\x9dO\xc4\1\xe0\xeY!\xe0\xc3V\xc8\xba\x96\x9e\t" - "/J\x9c\x81\xa3\x85\6]\xf2\2\xb9\xc6%\xce\xc0\xa9\xfd\xc8\x97h\xd0%\xe0\xd7\x42" - "<\xc3\x83\x80\x17\xf0\2\xfe\x7f\x83\xf\x38\x65\x18\xf3\xab|\xc2\x8f\xf7\xab\x30" - "\xbd\xdf\xc3'\x88G=\xbd\3\x1fk\x1aN\xad\xe0\x30\x39\x17\x63}0\x15i_Q+\xf6" - "{|c\xe4\x8f\xefsB\xc4\xab@*\xbb\xdbp\x7frD\x87\x90\xae\xd0i\xecV\x1c\x90\xdb" - "\xadt?\xf2\xecXP\xf9\x43\xf4/\x13\xc9\xa0\x61}\x9c\x1e\xd6{C6\xacM&3E\xf8\xb0" - "Q\xa0~\xb3\x34\xbc\xaa\4\xe7\xe\xd4\x37\xd4.\xe9\xd8\x82\xc7(b\xb7\x41{\xb3n" - "\xc0\xa3\xe5\x1d\xea\x63\x64Q\x1a\xcc\x10\xdc\xa3\xd4\xbfj\xaaME+\xd9\x95\f\xeb" - "\xeb/\xd7\xf2\xb0\x98\xda\xa6>\x82[\xa5q\xa5*\x19&\xad(\xa9\x89\xe6.\xd5q\xf8" - "\x98\xa5X\x97\xd6\fx\xbe\x9a\x87L\xa1\xdc \x8d\x61\x92\x19\xb6\x91g\xdfruiE\xf5" - "n\xc3\x9b\xc5Z&!]\xfai@\x91\x38OR;\rE\x8b\xed\x91\x65g\xee\xdd\x46MZ\xed\x38" - "J\xd8\x82g\xa9\xff\xb8Y\x80\xad\xa2\x19\xf1\a\xcb\xa6t\xb0]\x1e%\x13\x95u\x99" - "O[E\xfa\x1e\x93\xd6\4\x39\x85\x62!w/\xf2\xd5Q\xff\x9aH\x86\x19\x46\xbeT\x15\xf7" - "\xcd\x13\3p$`n\xf0\xf6\xfb\f\xcd\x10\x93\x96\x46\xd2\x31\x39\xe2\xee\x1e<>A\xa1" - "\xbd\xb2\xc0\xa7\x8d\x12\xbc\xf8\x9e\xa7\xfe\x85Q/\xbd\x66IV\xee%6\xa9\x8f\xd2" - "z\xfa\xa5=\xd2\xf9\xeb\t\x9b\xbb\x1e\xdbs\xf\xf5n\xb5\x87\x44\x1e\xf8p\xc2\xec" - "~2[+d4\x94\xcey\xb2\xb1\xab\x87}t\x19$U\xfdw\x93\x9d\x1fR(\x91\xb7\xeb\x8d\xf0" - "\v\x9f\xb7k~\xa5Z\xa8V{L\xa4U\xb6\x8c\x64\xa7\xfco\xe7h\x87:\x9f\xb0\x7f\xc8" - "\xe8\x82\xa5g\xc2\xe2w\x1b\1/\xe0\5\xbc\x80\x17\xf0\2^\xc0\xff\xe~\x85\x37\xe8" - "A\xb7\\\x83\x9f\r\xba\xa4\x32/\xe0!]\x86[\x13\1tW\xf1T\x89'\xb4i0\xff@1\xc4\xc9" - "\x1e\xbe\x92u\xe3\x97\0\3\0\xd0:\xbf\\\xac\x65\xa5-\0\0\0\0IEND\xae\x42`\x82", - 1623 ) }, - { "/images/yellow-light_glow.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\x10\b\6\0\0\0\x1f\xf3\xff\x61\0\0\0\x19tEXtSoft" - "ware\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0" - " \xf5\x9b\xd7\x89" - "\0\0\2nIDATx\xda|S\xbbn\x13\x41\x14\xbd\xfb\xf6\x33\x8emR\x84\xc8\x89\x1c\5\xd3" - " \1\x12\5T\4\x44\x91\xef\b\r%\x7f@I\x8d\4-\xb4\x88\x82_\xa0\x81\6\xea \xa4\b" - "\x14\x1b\xbf\x62\xc7\x9e\xf5z\xbc\xaf\xd9\xd9\xe5\xde\xcd:r\x10\x61\xa4#\x8d" - "ft\xee\xdcs\xce\x1d\xc5\xfb\xf8\bV\x96\x82P\x11z\6-;\x97\x88(C\x8cH\x96\4}\x85" - "LD\3Q@\x94\x10\x45\x84\x99\xdd\x85\x88\5\x82#\\\x84\xc8\n" - "]\x14 \xb2\x85\xa8H\x19oMf\x8b\x43!\xe4\x93$I\xb6\xd3\xb6\x14\xa5\xa3\xaa\xda" - "\xa7|>\xf7\xd6\x34\xd4\xdf\5K\xb3\xf1\x38\xa0\"z\xd6\x36\xbd\\q\xbd\xf0.s\xdc" - "7\xe5\x9d\a\xcdk\x9b\xb7\xc0(\xd6\xb1\xd9\4\4\x1f\xb5\xf8\xf0\xa8\xe5\xb4\xbf" - "\x1e$\x9a\xf1\x9c\xa9\xfa\xb7\xad\xba\x35\xa1\"j\xf6z\x81^&\xf2\xfa\xde\xe3\x66" - "}o\x1fL+\xf\x8a?\5\xc5\x1b\x81\x89N\xd4\x1aw\xa0\xda\xbc\xbf\3\x32|\xc5=\xd1" - "<\xee\xbb$S]\x9aU\xa2\xb6\xcb\x8d{\xcdJ\xe3\x36\xc4\xde\0\x12\x81\x92\x63\x94" - "\x1a\xcbL\xa4\6\xe5\x8d\6\x44\xbe\xbd\x1dv\x7f\x1cN\xb9\xfa\xb2=\xf2\xf9\xd2" - "\xb8\"i.n\xec\x42\xe2\x9e\"F\xd8\xdc\f\xad\xc2\"\xd2?\a\xed\xf1\xacP\xae\x81" - "\xae&\xf\x17\x81\xac\xfe\x1cz\x96\x9eI0\xd1\xb0]\xc3\x30!\xf1\xcf\x30,o5\xa9" - "K\xcb\x34\x34\xd0\x14\xd8^\xf8\xb2\x14\x88\xd8\xd4/\xdd\6\f\3s\xb0\xed\b\xae" - "\\(\tK\xcbH&\xea\xcc\x95\xa9\a\x94g\x88Q\xfd\n" - "\x9c\x41\xcb\xd4\x64\xea\xfcU+\xf4]\x88\x63\xe8\xa3\4\xe1\xb8\x91\xd4\xb3\xa1" - "XP\xce.\xeb\xb7\xcc\xea\xfa\x7f\v\xb8\xce\x19\xb8!|q\\9\xe7\x9e\f\xf5l \xe9\x38\x42x\0" - "\0\2pIDATx\xda|\x93\xcbj\x14\x41\x14\x86Ow\xaa{2\xf7\x89\x64\x93\b\t18J \xa2" - "\x10\bd\xe3\5\x91\xbc\x83;]G\xdc\xb9\xf4\r\xdc\t\xae\xd4\xa5\xf8\x10\xba\xd1" - "\x95\x97\x85\x90\x65\x62\x32\x31^2\xdd\xd3=}\x99\xae\xae\xaa\xae\xf2\x9cIG\x86" - "\x90X\xf0\x41S\xcd\x7fn\xff)\xcb\x7f\xb4\5\x13\xc7\x42l\x84\x95L\x95\xf7\5\xa2" - "J4bN\4lBLB\a\xa9!\r\xa4\x8e\xb8\xe5?\x81\xa4H\x82\x8c\x10Y\6\xfa\x17\x80\xc4" - "\x15\xa4m\n" - "u\x91\xfb\xfe\3-\xe5]\xd0z\xe1\xf8\xaf\xdd\x33\x8c\xbd/Z\xad\x97\xae\xe3\x1e" - "T\x19\v\xf1\x36\xa7 \xac,\x9b\x32\xb7U\x9a\xde\xc8\xc3\xe0yuuu\xa9\xb2|\x19\xa6" - "\xda-0ZC\x11\4]\xb1\xbb\xd3M\xb7\xb7\x37\xe3Z\xedq\xe0V>\xcd\x37\x1a>\5\x99z" - "\xb2\xbeN}60\xf3\"\xf7\xbc\xd7\xb5\xb5\xb5\xa5\xda\xb5\xeb`\xe9\2L\x1c\x63\xd1" - "\tX\xc6\0\x9b\x9d\5\xdbq;\xe6\xf0p#\xb4\xecw\x81\x10\xd1\x85\xe9\xe9q\0\xeas" - "\x86{\xfd\xad\xe9+W\xefUWV@\xf\2\xd0\x61\b\x1a\xc5&\xcb\xc0p>\x86\x35\x9b\x18" - "\f\xda\xda\xf7\xac#\xad?+\xad\x13V\x96_\xa7\x9e\x9d\xf9\x39\xd0}\xft4\4#%\x9c" - ">4zV\xaf\x43\x45\x9b\x9b#\xa3^|\x1f\xe\x8fX9@\x17\av\xc9\xb6m(\6\x83q60\6\xce" - ":\x96\x45\xde\x9a\x85T\xaa\x46\xae\n" - "w\xd2\x46\xd0\xc3\bt\x8cN)\5\xe7\x1d\x1a*\xed\5\x96oG\"\xb7Y\xe9\xa7@\xabvU\xdf" - "\xebZR\x9c\x9b}\x1c@\xe4\xb4M?GR\xca(\x17\5+\x97\"%\x9f\xe5\xc0\xeb:n\xe5\xbf" - "\1$:\x13\x1b\xf8\x18\v\x11\xa7R\n" - "V\xaeg\xa2\x9b\xadWb\xe0oB\xb5\xba\xc8\\\xf7L\xb1\xc2\xd9p\xce\xf\xbf\x66\xfc" - "m\x98\xe7\xc1\x80\xf3\xdc.[\xc8\x1c\xd7\xed\x8dpI\xf2,\xeb\x89\x34\x85\x82\xac" - "\x13\x62\f}\v\xb4\x34\xe3\xfc\xc7\x37!\x9f\xee%\xf1\x1e\xb6\x10\xe2\x1eHV\xba" - "#h=i\xc3\xfe\x18\xb8\xdf\xe2\xd9\xc3\x8aT\xb7p\xdasp\\\xe2o,\xfb\xc3\x97\x8c" - "\xbf\xd9O\xe2}?\xcb~%B&\xcf\xeelhk\xe2\x35\x8e\x1f\xd3N\x18\x36\x43\xcegFJ\xcd" - "`\x8f\r\x9a\x36\r\x8cz\xa6\xb2)s\xc0s\x14\xdf\x96\xa7_\xe3\xd8\x8d\xe5N'\xe8" - "EQr\x10\xc5}\xf4\xd9!\xab\xb8R\x1a\x45\x39\xf5LeS\xe6\x13\xd1_\1\6\0\x1b\xd9" - "\x86%\x8al\3\xa7\0\0\0\0IEND\xae\x42`\x82", - 1534 ) }, - { "/images/bar-line-graph-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\x10\b\6\0\0\0\x1f\xf3\xff\x61\0\0\0\x19tEXtSoft" - "ware\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0" - " {h\x9f\x41\0\0\0" - "\xd8IDATx\xda\x62\xbcp\xe1\2\3\5\x80\x93\x89\x12\xcd@\xbc\x81\x85\4\r\xcc@\xcc" - "\ae\xb3\1\xf1\" ~M\xac\v\xe4\x80\xf8\b\x10\xdf\a\xe2\xbb@|\x1d\x88\x9f\3q<6\3" - "X\xa1\xb6\xc1@\0\x10\x9f\6\xe2U@,\b\xc4\x42P\x9c\0\xc4\x7f\xd1\xbd \n" - "\xc4\xeb\x80X\f\x88[\x80\xd8\x10\x88\x83\x80\xd8\x1f\x88O`s\x1a\xb2\1\xba\xa0" - "@\1\xe2\x65@\xbc\x1f\x88\xeb\x81\xf8\x1d\x10\x1b\0\xf1\a4}\xbb\x61\fFh4\xfa\0" - "\xf1< \xce\a\xe2\xe5\x44\x84\xc9\x7f\x64\x17\x94\2q\x1e\xd4\x90S84\xec\x46\x62" - "\xbb\"K\x80\\p\xeH\xfb\1\xf1\x13\x62l\4\xe9\x41w\x81-\x10\x7f%\xd6\x46t\xc0\x88" - "#)\xe3\xb4\x11\x9b\vH\xb2\x11\x97\v\x88\xb6\x11\x9dOIfB\xf1\2\xba\xb3\x89\xe6" - "\3\4\x18\0\xe1\x88\x34\x96\xcd_8\xae\0\0\0\0IEND\xae\x42`\x82", - 1126 ) }, - { "/images/list-square-bullet.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\a\0\0\0\a\b\2\0\0\0K0\xc0\x84\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \x95.\xd0|\0\0\0\x17IDATx\xda" - "b\xbc}\xfb\x36\3\6`b\xc0\6\xe8+\n" - "\x10`\0\x8a\xf7\2\x9f\f\t\x92\xb2\0\0\0\0IEND\xae\x42`\x82", - 933 ) }, - { "/images/server-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x1b\0\0\0\x14\b\6\0\0\0|\x82\x46\x80\0\0\0\x19tEXtSoftwar" - "e\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 " - " !\xb5\xdc+\0\0\2'I" - "DATx\xda\xac\x96\xddk\xcdq\x1c\xc7\xcfY.vGN-\xb5\x92\xb9\x98\x1ay\xaai\xf3\xbc" - "ay\xa8\xb9\xc2\x95[\xbbX\xad\xac\xfc\5\x44\x1ev\xb1\x45\x39\x17\x34Q\x14\x17" - "\xa6\xd0h\xb2i\x99\x46\x8b\x94P&%5\xae\x46\x84\x93y}\xeau\xea\x97pv\x1e~\xf5" - "\xea{~\x9f\xdf\xe7\xf3~\xff\xce\xf7\xe1sN\xfa\xec\xb3O\xa9\"\xafU\xb0\x1f.\xc2" - "D1\x85UE\xe4\xae\x81\1x\2\a\x1d\a\x8cW\xccl-\xdc\x82G\xd0\xei\xe3i\xef#~\xdb" - "\xbc\x92\xcd\x36\xc1\x10\x8c\xc2\x8e\2:\xdb\xcd\x1b\xb2n\xd6\x66[a\4\xee\x43" - "k\x91\xeb\xd9j]\xd4o\xfb\x9fY\xbc\xfd\x43\xb8\v\x1bR\xe5]Q\x7fG\xbd\x9dI\xb3" - ":\x18w]\x9aR\x95\xbd\x42\xef\xa6\xfaua6\t\a\xe0:\xccT\xd8lF\xdd\xd0\x9f\xccO" - "c\x9c\x97\xbd\xb0\2\xae\xc2\xaf\x32M\xa2\xfe\x1a\xacTw\"?\x8d\x19\x38\f\x1f\xe1" - "\b\x9c\x80\xe5p\xb9\4\xd3\xc8\xbf\x62\xfd\xf1\x84n\x8c\x99\x30\xfb\1_\x1d\xdb" - "\x9d\xdf\x93\xd0\a\rp\tr\5Lr\xe6-\x85^_x\\\xbd\x9f\xf0-\xf4\xc3\xec\x33\x1c\x83" - "\xc5p\b\xa6\x12;3\f\xb3\x9a\xf6\xff\xc5\x34g\xbc\xc1\xbc\xde\xc4\xe\x9cR/6\xe0" - "\xd1\xf0\xa9J\xec\x9a\xf3\x30\rK\xa0\x1b>@\x1bVHhN\x11\xdd\xe1\x31\xec.\xe7o\xc1o\1\6\0\t\xb8\x96" - "p\x9f\x9c[\xfd\0\0\0\0IEND\xae\x42`\x82", - 1461 ) }, - { "/images/query_round_trip-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\x10\b\3\0\0\0(-\xfS\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\0-PLTE\xa0\xa0\x9f\xe7\xe7\xe7\xb8\xb8\xb7\xb2" - "\xb2\xb1\xed\xed\xed\xe1\xe1\xe1\xf9\xf9\xf9\xca\xca\xc9\xd5\xd5\xd5\xac\xac" - "\xab\xbe\xbe\xbd\xa6\xa6\xa5\xcf\xcf\xcf\xdb\xdb\xdb\xff\xff\xffg\xa4\xaaK\0" - "\0\0\xftRNS\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\0\xd4\xdc" - "\x98\xa1\0\0\0mIDATx\xda\\\x8fQ\xe\x80\x30\bC\xcb\x80m\xce\x8d\xfb\x1fWD\xa3" - "h\x7f /)-\xb0\x9f\xf0l\xb5\xd7/ \xb0\4\xa0QB\f\xec\xcb\x81\"i8\xd8\xc0J\xa7:" - "\xd0N\v@\xd7\r\xd9\xd5\xac\xcd\x17\4\f\xa3XJ\2\xa6\x1bu\x93\x17x\xa3\6\xf4\xb8" - "\xab\xec\x19\x66%\a\xab\x83\xe5\x93\xafr\x83\xa2\xbapJB\xfa\xeb\xf7\xed\xad\x43" - "\x80\1\0\x96\x92\b\x96\x84\x93\x1c\x89\0\0\0\0IEND\xae\x42`\x82", - 287 ) }, - { "/images/server-icon_server-assignments.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x13\0\0\0\x13\b\6\0\0\0rP6\xcc\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 h\xc2\xa8\t\0\0\1]IDATx\xda\xbc" - "\xd4\xcd+\4q\x1c\xc7q\xa3\\\xb4\x12'\x7f\x84\x13\x37\x8f\xbb\x1c\x1c\\\xdd$$" - "q\x90(\xe\x1c\xb4\xb9\xb8(\a\x8a ~=\x13\xbe\0\0\2#IDATx\xda\x62" - "\f\x98w\x9a\1\bT\x81\xb8\3\x88]\x80\x98\x8f\x81>\xe0\x13\x10\xef\1\xe2\n" - " \xbe\xcd\2$\xd4\x81\xf8\4\x10\v0\xd0\x17\x80<\x1c\4\xc4N@l\xc1\4$\xda\6\xc0" - "\x11\xc8\0\x64w\x1b\x13\x34:\6\x1a\xb8\x31\xd1\x31M\xe0\3p" - "\x81\xc5\xc2\xc4\xc4\x30\xf3\xc4\x63\x86\xbf\xff\xff\x83\xd3\1\xa8\x1e\1\x45" - "\t5\0QQ\3*\x17@\xc0_[\x8c\xe1\xcc\x93\x8f\f\xfd\x87\x1e\x30\xf0s\xb0\x30\xec" - "\xbc\xf9\x86j\xf5\x14\x41\x87p\xb2\x32\x33l\xba\xfa\x8a\x81\x97\x9d\x85\xc1\x1a" - "XH\x89q\xb3\x31\xbc\xff\xf6\x9b\xe1\xc4\xc3\xfT\xad\xb5\t:\xc4\0X\x7f\xf8\1\x43" - "\xa2y\xcf]p\b\\\x7f\xf5\x85\xe1\xff\x7f\6\xaa\3\x82\xe\x39\xf9\xe8#\xc3q\xa8" - "\xef\xaf\xbd\xfc\x32p-\xb4\x7f\xb4\xf0\xfeh\xe3y\xa8\x39\xe4\xd3 p\xc7\x17&\xe8" - "\xd0\xc0@\x83] \x87T\3\xf1\x87\1t\4\xc8\xeej\x90\x43n\x80\xc6'@59\x10\x7f\xa6" - "\xa3\3>C\xed\4\xd9}\3 \xc0\0n{\x95\x8e\xbe\xb6n'\0\0\0\0IEND\xae\x42`\x82", - 1457 ) }, - { "/images/db.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\t\0\0\0\f\b\6\0\0\0\xb0\\\x97\xa3\0\0\0\4gAMA\0\0\xaf\xc8" - "7\5\x8a\xe9\0\0\0\x19tEXtSoftware\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\1\x63" - "IDATx\xda\x62\xbcz\xe1H\xee\x91\x63'c\xae]\xbb\xa9\xfa\xf9\xcbW6FF\xc6\xff@\xc0" - "\xc8\xcb\xc3\xfdKKK\xfd\xb6\x8d\x95\xf9\2\xc6\xac\x8c\xa4\xf?~\xfc\x64\x61\0" - "\2\x98\2\x18\r\x12\xe3\xe0`\xff\xc3\2R\0\x12\4\x62\xa0\"\xb0\xd2\xff \x12\xa8" - "\6\xac\x10$\xcf\x64gcy\x16\xa4\xe0\xdf\xbf\x7f\x8c\x7f\xff\"0\x88\xfR\f\x96?" - "\xb4ws\x9f\x8c\x8c\xd4\xe9\xdb\xb7\xef\xb9\xbdx\xf9R\xe9\xdb\xf7\x1f<\\\x9c\x1c" - "_\xc4\xc5\xc5\xee\xa9()\x1ex\xfe\xe2\xa5>3#\x13\xd3\xda\x8f\x1f?\xea\x8a\x8b" - "\x8b\xde\xd7PW\xddk\xa0\xaf\xb3^XH\xf0\xd1\xb7o\xdf\x45\x8f\x1e;\x19\xb1\x63" - "\xf7~\x17\x14\x87\x63\3 \x87\x33O\xeao\xe7\6\xb2\x45\xde\xbe{'\0t\v\x13L\x92" - "\x8d\x8d\xf5\xaf\xbe\x9e\xce]\xfw\xe7%\x8c=\x1d\x8d\x37|\xbc\xdc\xfay\xf9x\x1e" - "\x7f\xf8\xf0Q\xf9\xcb\x97o\xc2<<\\/\5\4\xf8\x1f~\xfe\xf4\x45v\xcb\xb6]\x85\x8c" - "I\t\xd1_\xd8\xd9\xd9\xfe*+*<\x17\x11\x11~),,\xf8\xfc\xed\xdb\xf7\x92o\xde\xbc" - "\x15\xbf{\xff\x81\xe4\xcf\x9f\xbf\x98\x19\x97.\x9c\xb1g\xef\xfe\xc3\x16\xb8\xdc" - "\xe4\xech{\x82\xf1\xdb\x87\xc7Z\xa7\xcf\x9cO?t\xe4\xb8\xff\xe3'O\x85\xfe\xfd" - "\xfb\xf\xf4\x30\xe3\x7fY\x19\xe9w\xc0\x30\xdahjb8\x13 \xc0\0.\x82\xb3\xb3\x42" - "\xb1\xbdK\0\0\0\0IEND\xae\x42`\x82", - 465 ) }, - { "/images/push-arrow-left-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x11\0\0\0\t\b\6\0\0\0\xd4\xe8\xc7\f\0\0\0\x19tEXtSoftware" - "\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 " - " \x89$\\\xf\0\0\0\xe5" - "IDATx\xda\x62\\\xb2\x64I\5\3\3\x43;\x10w\0q%\3*\xb0\4\x62\x13 \x9e\xcc\x80\3" - "DGG30\xc1\x38\xda\xda\xda\x15@\x81v$\xf9T \xde\xcf\xcf\xcfo\n" - "\x14\xcf\x65\xc0\3\x98\xb0\x88\xb1\1\xf1\f \x9e\5\xc4\xec\xd2\xd2\xd2Q@Z\x8a" - "hC>~\xfc\xc8\3\xb2\x1d\x88\xd3\x61\x62\xd7\xae]c^\xbat)\xc8\xcb\xff\xb1`\xb0" - "\xcbY\x90\r\xf9\xff\xff?\3\x33\x33\x33\xdb\xdf\xbf\x7f\x19\x88\1\xa0 \xc0\x30" - "D@@\xe0\x8b\x8b\x8bK\xe8\x8d\x1b\x37N<|\xf8P\x1c\xaa\x90\xc1\xc0\xc0\0\xaf\x61" - ",\xe8\2;w\xee|\0\fHy\x1e\x1e\x9e\xdd\xd7\xaf_\xb7\x85\n" - "w\0\xbdT\x89+vX\xb0I\0\x35\xfc\4Rv&&&\x9d@/\x16\x11\xf2\x16(`;\x80\xa6Ubs\xf2" - "\x99\x33g\xca\x35\x34\x34\xec\x81\xccg\xf8\f\1\b0\0\x1f\tG0\x16\x66\f\xca\0\0" - "\0\0IEND\xae\x42`\x82", - 1139 ) }, - { "/images/thinker_on_hill.svg", std::string( - "\n" - "\n" - "\n" - "image/svg+xml", - 7180 ) }, - { "/images/bars-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x11\0\0\0\r\b\6\0\0\0Oy\x85\x1a\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 >\xc7\xf5\x61\0\0\0MIDATx\xda" - "b\xbcp\xe1\2\3\x89\xe0?2G__\x9f\x91\x89\x81r\xf0\x9f\x1a\x86\x30\xb0\xa0;\xf" - "\b\x18I5\4\xc3%@?\xfe\xa7\xd8\x10r\0\xd5\xc2\x84\xa8\xa8\xc4\x17VD\xbb\4_X\r" - "\x9e\x30\xa1Z\xc0\x32\xe2\xf0/\xd1\xe2\0\1\6\0!\xe1\xe\xdb^\xea\n" - "2\0\0\0\0IEND\xae\x42`\x82", - 987 ) }, - { "/images/server-icon-added.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x1b\0\0\0\x14\b\6\0\0\0|\x82\x46\x80\0\0\0\6\x62KGD\0\xff" - "\0\xf2\0\xf2\xd6\xd4H}\0\0\0\tpHYs\0\0\v\x13\0\0\v\x13\1\0\x9a\x9c\x18\0\0\0" - "\atIME\a\xde\f\x10\x15/#\xdb\x9f\x45\xe5\0\0\2SIDATH\xc7\xad\xd5\xdd\x8b\x8f" - "i\x18\a\xf0\xcfo\x8c\xf6\xccK\xc3\x92\3\xa1\x96\x9c{\xce\x44\xe\x36\xcb\xe3\4" - "%\xd4s\xb0\x9a\"Y\n" - "\x19&\x1b\x96\x14;\r\xe3-\xaf'n\xb5(4\xe5\x89\x7f\xc0\x81\x1e\x7f\x80\x44$\xc9" - "\xbb\xd1\xd2.\x86\xd9\x93\xfb\xa7\xbb\xe9\x37\xecOs\x1d=\xdd\xf7s]\xdf\xeb\xfa" - "\xde\xdf\xfb{\xd7\x34\x19\x45\x95\xaf\xc4\x66t\x87\xac<\xd7Ln\xad\t\x90U\xd8" - "\x8b\t\xc9\xf2Kt\x84\xac<3,`E\x95\xaf\xc6\x1e\x8c\x83\x90\x95\xb5\x64o ~\xde" - "\x8e\x93\x9ej\x1a\xac\xa8\xf2\x1a~\xc3N\x8cM\xf7\x86\0\xabG\x1fv\x84\xac<\xd4" - "\xa8nK\3\xa0ux\x85\x9e\xc1@\xdf\x88\xcf\x18\x83\x9e\xa2\xca\xfb\x8a*\xef\x18" - "r\xb2\xa2\xcas\xfc\x89\x99\xf8\x88\x91\x8d\xa6\xf9\n" - "\xdd\3\r\x96\xdf\xa2+d\xe5.\xa8\x15U>\x15\x17\x31+\xe9\xb0\x65(\xea\x9a\4\xab" - "\xc7;\xcco\tYy\x1fkp\r\xf5\x84\1\xc3\x17\x8f\xf1k\xc8\xca\x1b)\x8d?`\xe\x8e`" - "z\2XkB i<\xc2\6\\\vY\xf9\xf\xb4\x16U>\x19\xc7\xf0\v\x9e\x62+\xfe\xc6QL\x8a\xa0" - "\xb5&&y\x88\xf5\x18\x85n\x9c/\xaa<`[+>`D,8\tg\xf1\x1c\x9dx\x86\x13\x98\xf8?\xa8" - "\xbd\x8fu\xb1\xc6I\xfc\x18\xd7\xdf\xe3_\xbcO)\x99\x80\xd3X\x98\b\xe4%v\xe0\x41" - "\xa4w\n" - ">\xc5\xe6\xeaq\x17k\xf1\x13\xfe@[\"\xb4\x12\xed!+\x9f}\xb9gE\x95\xb7\xe3V\xbc" - "\x94\xd3\xd0\x1b\x7fn\x8b \1\a0\xf\xf7\xe2\x94w0\x17\x87q!\xd2\xde\x16\xf3zc" - "\x9d\x37\xb8YT\xf9\xb2\xba\xf4g\xc7\xe\x46\xc5\x8e\xfaq\x19[\xd0\x85\xc5\xc9" - "$o\xa2?\x1e\x8b\xd3tbt\xdc\xfb\x84+\xd1\xa4\xf7\x61)Z\x13\x45\xfe\x9c\xd2\xb8" - "\t\xdb\xa3\v\xd4\x93{\xb1\xb1\x41rJe?.\xa1\xa3\x41s}\xd8\x15\xb2\xf2`Co,\xaa" - "|\xfd O\xfc\x8c\xabQa{\xb0<\x82\xf6\xe3|l\xf0\x10\x16%g\xfd\x1a;\a{d\xed+\x8e" - "\xb0\6\xbb\xebn\x1f\x41\xafG\xc5-\x89T\x1f\xc6\x82\4\xe4\5~\xfYy\xfc{\x9f\x98" - "\xf6xN\xe3\x13w\xb9\x8d\x19\t\xc8st\x86\xac<=\\\x8fg\x81\xfd\xf1\xce\xd5\xe3" - "\t\xb6\x84\xac\f\xc3\xfaR'\xa0+\xa2\xe2\xba\x42V\xfe\xd5L\xee\x7f\x82\xbc\xc8" - "\xdeU\x88\x17S\0\0\0\0IEND\xae\x42`\x82", - 710 ) }, - { "/images/red-light_glow_small.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\xf\0\0\0\xf\b\6\0\0\0;\xd6\x95J\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 ^^\x8bu\0\0\2\x32IDATx\xdalS" - "\xcbn\xd3@\x14\xbd\xae\x9d\xf8\x11\xe2\x36\xa8\xa5{\n" - "l\xc9\xa2\b\x11\x10\xec\xc8\x1f\xf0\5\b\xc1\2\xfe\xa0\x8b\xd2\x1f`\xdb=H\xfd" - "\r\xb6\x95(\x94\x96UCW\xad\x84\x43]\x13?\xe7\xe1\x99\xe1\xde`K\x91\xc8HG#k\xe6" - "\xdc\xb9\xe7\x9ck+~\xf7\x16\x16\xd6\n" - "\xc2\x46\x38\r,\x84\x41\xd4\r\x14\x42\xb7\x97\x9d\x66\xb7\x1a\xa2\x8b\xe8!\xfa" - "\b\xbf)D\x84\n" - "\x91!\n" - "\4o\n" - "\x18g\xe1\xc5\0\x31\xa8\x8b|(\xb2\xec\xb5\xa9\xebm0f\0\x96\xf5\xc7\xd8\xf6\x17" - "\xe5\a\xfb\xe0\xfb_W]7\xc6{%\x15u\x16^\x1c\xb0\x38~\xa1\xca\xe2}o4\xf2\xdd\xdb" - "[`\xb9.\xe8\xa2X\xe7\xe7?\xc7\xe5\xe1\xe1\x33.\xf8\xde\x85\x1f\x1c\xb8\xb6\x1d" - "m\4\x41\xd5j\xec\xc9<\x1f\"q7\x1c\x8f}o\xeb\xe\0\xab@O#l6\5ws\x13n\x8c\x46^\x97" - "\xf3\x9d\xba,\x1e&\x8c\x85\xc4k\x8d\xe9\xcb<{\x13l?\b\x9c\xfe*\xa8_\x11\x18$" - "c\xeb\xa8\f\xfd\xb2,\xbc\xe9@p\xf7\x9e\x17N&/'\xda\x9c\xfc\xb8\xbaJ[\xb2O\x1a" - ";\xb7\x36@\xfd\x9e\x82\xce\xd1\x1b\xa5\x16S\x98[\xbe\xe2\xb9\xd0\x31z\x98\t\xb5" - "\x96\v\xe9\xb6q\xd8\xf8\xc2\x1a\x94\xd8j\x92\x80\x11\2\x96-\v\xbb@\x9d\xa1\xd4" - "\xba\x9br\xee\x38MQE\xae\xaa\x38^7\5\xa6\xa1\xf5R2\xc9\xc0\x93\x94\xd5\xb5)d" - "=o\x99\xc2\xaf\x30\x8e#1\x8d\x9e;\xb6\xfdO\xe7\x92\x85\xa6\2\3\x38\xcd\x84\xa8" - "f\x9c\xcb\x96\x9c)\xdf\xdf\x17Q\xf4\x14\xc2\xd0\xb3\x1d\xe7?\xa2\x42)\"\xcf\xd9" - "\x99\x94\x9f\x12\xc6\x13\xa1\x14_i&\xa8\0?8b]w\x8f\xa7)\xa3\x17\x34\x63`8\x9f" - "\xef\xf4\xcd\x91x\xa9\xcd\x87\xe3Y\xf6-\xae\xaa\4\x39\xc2iF\x8d\xd3\xe4\\\4\xc1" - "A\6\xe6<\x94\xf5\xab\x8e\x10\xf7\xc9\x1c<\xccp6O\xce\x84\xfc\xf8=\xcd\x8e\xaf" - "\x19\xbb\xe4J\xcdv\x9f<\xae\xdb\xfe\xa8@I\x93Sv\xba\x9f\x31\xc7S\x8a\x83\\%s" - "H#\xb5\x8a:\xaf\v)\xd3\x9b\x9e'\x17\x7f\x8c\xb9\xe3\x34r\bA\3@9R\x1c\xe4*\x99" - "C\x1a\xb1\xb8\xd8\x19=\xaa[\x1f\xfe\n" - "0\0UFX\xdf\xe8\xd9\x9e\xd4\0\0\0\0IEND\xae\x42`\x82", - 1472 ) }, - { "/images/fullscreen_exit_16x16.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\x10\b\6\0\0\0\x1f\xf3\xff\x61\0\0\0\x19tEXtSoft" - "ware\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\0_IDAT8\xcb\x63```H\0\xe2\xf7@l\xc0" - "\x80\0\xf3\x19\x30\1\xb2\x98\1T\xfH/\x98\xf1\x1f\xc9\x90\xf9P>:\xf8\xf\x95\x33" - "@\xd3\x83\"\x80\x8c\xb1\x19\x80\x8cQ\\m@\x86\1\6\xe8\xfe#\xd5\x80\xf9\xf8\x34" - "\x13\x63\xc0\x7f\x1c\x81=\"\1U\2\x91\xa2h\xa4\x38!Q\x9c\x94)\xceL\x14\x65g\0" - "\xe3~i\x15\x95\xc0#\x7f\0\0\0\0IEND\xae\x42`\x82", - 189 ) }, - { "/images/status-panel_bg_tile.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x32\0\0\0\x32\b\2\0\0\0\x91]\x1f\xe6\0\0\0\x19tEXtSoftwar" - "e\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 " - " \x8e\xd4\x32^\0\0\a" - "bIDATx\xda\x64\xd9QV\xe3J\fE\xd1\xd7\x86n\x98\xff\4\x99\6\x34\xd0<%\xdb\x9ch" - "\5\x7f\x64\xd9\xe5\xb2J%]IW\x95_///\xbf\xae\xd7\x7f\xd7\xeb\xfd\xfd\xfd\xf1\xf1" - "\xd1\xe3\xbf\xeb\x35\x8f\x1f\x1f\x1f\xc7q\xcc\xfd\xc3\xc3\xc3\xd7\xd7\xd7\xbc" - "\x9d\xdf\x19\x99\xf1\xcf\xcf\xcf\xa7\xa7\xa7&\x9b\x36\x9f\x90\x66pn\x8e\xeb\x35" - "73?\xe1\xbe%\x9c\xe\t9F\x8fy\xfe\xb8^t\x9a%\xe7\x33\xcb\xcf\xa3\x65\xe8\x34\x82" - "\xfe\xfe\xfd\xdb\xf7\x33g\xe4Zx~\x8d'\xc1\xf8\xdc\xcf\b\x9d^__GHk\xcf\xb7\xb6" - "7\xaf\xb6\xae\x17\xb5\xfe\xfc\xf9\x33\xcfL\x92\x44\xba\x92;\xf7\xb6;\xaf\xe6" - "\x9b\xe7\xe7g\x8f\xcd\xb1+6f-k|^\xaf\xdf\xbf\x7f\xcf\x87\x33\x38\xf3\xe7[Z\xce" - "xJ\xcf>\xdd\xcc\xef(\xcd\xf6\a\x8f\x8c\xdc\xf9&\xb9#\xcb\xa4\x91\x65O\x16\xcb" - "_\xc6\x99\xa1\x8d\xd2\xd5z\x9c\x45\x8e%yv\6\xd9\x8c\x97\xe7\xd5\xcc!|\xbe\xa5" - "\xc9\x65\xb2w,9\xd7\xe8\x1e\b\xec/\v\xcf\xef\f\x8e\x44\xfa\xf1r\xc8\x98\xdfY" - "\x8f\xed\x89\xe6t\xdf&\xff\xed\xed\x8d\xccp\xd2r\xf6@\xef\x8b,x\xe4\xf2y\x97" - "\v|?\x83#\xcb^\xd9\x8f\xd0\x99\x66\xdc\xb7#\xc7\xbd\x8b\xa7\xec\xc1\xc2\xe0;" - "zS\x1a\x34\xc1\0\x1e,:2g\xcf\a\xbf\xe4\xe3y=\xb2\xe6\x1dY_\xd7\v\xaeG\xdc\xd6" - "\xdb\x1a\x36\x93\xbf`\xd1\xb7\xa3\xd9\xc8)\xc6\xa1*\xec\x86W+\xc2\xc9\xfc\xc2" - "\xdf\xc1\x11\x33o\xd6\b\xd7\x17}W<\x9f\xd1\xf1\xed\b*\x96GX\xa5mX\xa3\\S\x9c" - "\x8a*P\x86'q\xc7\x9c\xbe\x82\xb9\xd3w\xf3\xbcu\xea]\xf3\xf8\x8e\x66\xec\xca\xa7" - "\xe1\xba\xb0\x95~f\xce\xf8\xd4\xb8\xf4\xc6\xa2>g\xd7\x91\x30r\xb2\xb1\xb5\x46" - " ?\xdcr\x86\xabL8ri\f[\xf4+G\xc0\xc7]\x18\xb6\xc9\x19\x9c\x39\xd9\xe9\xe1z\xd9" - "\6\xcd\xdcg\xcb|=#a\xfd\xf0\x9a\x85\t\x12S\xa1\x95O\xa9\b\x1f\xb3\x8c\xf9\xe4" - "f\xa7\xf2\x96\xe8\x66\xe3\xadz0(W\xc3\xab\xfd\xcc\xba\xf6s\3P~\x99Q9L\xac\xb6" - "'\x16\xba\xcb\x43\xf2\x42\x19\x84\xc6\x65\xda\xe2\x89\x96\x41;]\x81\x92\x15\xad" - "K\xf2\x91k\xdc\xc0\xa9{\xb1\x46\x89\xd0j\xa3\xbb\x86\xda\3\xfc\x16\1\xe5\xbf" - "\xf2\"\x81\x33\x9f\x43I\v9\xd5\xc0\xd3\xae\xe2\x96*\xb0,z\xe7wlk+p0\xe2X.\x89" - "\xbb*\xd3\6\xb0\xc4\1\a\xc1J\xca\5\x98\x19/o\xcd[\xf3\x61\xeb,>\xbe\x61s\xcb" - "\xab-;W\x95\xf7\xb3\x81{6\x80Y\x19?\xf\xb2_\xf1\x91\xefl\xb5\x1a_~\xa6\xd3\xcc" - "?\xdaG\xd4\x65\x63\b\f\3\xe6\xe6'\xa3G5x\xe6\xcf=\xb3\xcd\xb8\x1c\x1b\xae\xad" - "\x17\xde\xe5\xf4\xe2I}\xdc\xf9\xf9\x8cq\x1a\xec\x18\xe1S8\xdd\x15\xadGve\x15" - "\x1c\xc4\x96\xe2^\xe5\xa7r2;\x91i?;\x1f\xf1\x89\xdau\xe6\x88|\x9c~\x8c\f[\xf9" - "\x8b\xfb\xd5,\xfe*\v\xdc\xc5J\xf5\xbe\x38p3s\xd4\xd0l\x19\xd3\xaa\"\xc7M\xe\xb1" - "f\x7f{s\xc6\x45@u\x10\x1f\x8a\x9a\xa6\xdf\xfc\xb2Sq\xa3\x64\x95\2\x8a\t;\x8f" - "cE}\xc3\x9c\xb5\x1e\xcb\x90\xdb\x65\x39knp\x95\xad\x81\xdfrL\xf5_\x8e\x8dw\xd0" - ">\\\xa7\x37\xfcy\xe5\xde\xb7\xf0~\x16\x1f\xfc$\0m^*\xb5\xa6\xe2\xce\x11\x38x" - "d\x9f\xfb\xf8\x9a\x91\xe2\b\x96/\xee\xe8\xca\x39\xfc\x1e\fF'\xdf\x1e|1\xdf\xec" - "\xfa\xc0\xaa\x33\xc3\x9e\xd4\xc7\x8d\xa1\x38\xbb\xfbr\x18,\xdbwq\x13W#'\x8a\x31" - "~\xb4.\x9bI\xe9\xf3\xa8W86/cLr\xf5\2w9\xc6\xda\xe0\x62?\xe6\x94)h9\xfb\6#\x9c" - "n\xc7\x38\x84\xd0\t6\xe2p\xee\xad\xf8\xa8\x1a\x86\xfa\x30[\n" - "\x8e\xfb\xe2X\xe1\xacv\xa8\xcc^\xfd\xfe\xfa\xbe\xe4R#9d\xe4\x8f-\x8a\xa4;F~\xc6" - "fe\xce\xb3\\G\xad\xea\x9a\xd9\xf9\x82\xcd\xd9X\xbe\xb0*\xdf\xc5\x91\xaa\xe8\xf1" - "O1(\x9f\xe1\xae#\xa4\xfa\x8dg\x9f\\\xbe:-\4\x9aT\xfdom\x18\xe7;t`\x1e\xe5t5X" - "\x1c\xe9%-\x1c\1\xe4\x13>\xcd\xe3\2(^\4\xec\xf5\a\xc7\xa8?\x93\x64\x11\xefx\xa7" - "\x1a\f\x9bU\xf1\xb8\x7f\xde\xafW\xa3_\xfb\x91\2\xa3'\x8d\xabW\xc5~\xf5'\xcc\xdd" - "\x8a\xf\x9b\x17\x89\xd5\xa9r\x8f\x35\x36_`T>m\\\x1e\xb2\x9e\xfb\xe8x\xdc\x7f" - "\x1f\x17\xe0\xe\xe2\x94\xedO\xbe\5\x95\xb5M\xbb\x37\xafW\xb6' \xe5\x8bZ\f\xf7" - "\xbb&\x96\xc1\xd9ilP\t\t\3q\xff\xb8\xe4\xc9K\xd9\x45.\xe5\xec\xfc\b\xd1\xe1\xa0" - "|\xd1\xc1\x44\xb6)V`EW\xbdk\6\xce)\x85\x16\x13\x62\x36rQ\xceS\xa7/{\x90\x84v" - "\a\xac\xcf\x64\f\xb1\x99\\\xfe\2;;\xeb\x1e\xc0I\xbf+\xaf\x64\xb2\xb1%:C\x88>" - "\x80\x97=\\h\x92\xe7rc\x9d\2T\xf1Q\xd8\xd2[\x96S\xf2\x91\x9c\x8e\xc7\xee\xd3" - "\x91\xe2\xa6\2\x15\xb7\xee\x98\xa9\xf3\x81\xc8\xdc\xd9\x62\xc4}\xd9\xa3,U\xff" - "\xb4W\xaa\x9b\xc5\x8cG'y\xa1\xa6\x39\xbbv\xdcUc\x17\x17\a\xf3\2h\3\xfa\xe4\x45" - "\x15\xe9Yl3\x8dz\xdf\xeaZqPZ\x8f[\x97\t\xeb\xdf\xab?\xd1\xa9\xea\x15T\xcd\xda" - "\xd1\xf1\x9a\v\xe3\x17\\A\xb7\xab\x64\xe1L\xa7L\x1d\xe7$W\x9e\v\x10wg>}\x9b\xa7" - "L\xd0;\xd8\x83\xfa\x13.\xebo;s\xb8\xe5\xa1z\xf3\xfa\x96\xcd\xeb\xe5\xfal.l\xb7" - "\x99\xcb|\xf2\xa4\xde\xa6\xa2\xb9\xf9\xf4\xc6SV\xec\xdcKD\x1f\xf5\b\xbb\xad\xc3" - "X\xe0\fB\xe3np=kXO\xafq;;\xf8\xe6\xcas\xa3\x7f\xe4\x8dN\x94\x44\xcc\xae\1\xe5" - "\5\xf1\xde\xe7G1\xc2\xce\xbb\xd7S\x9a\xeaU\xe0\x80\x13w\x9f\xe3\xec\x89~\xa5" - "\xf2\x62\xa8\\\rO\xf5v\xe4\xc4u\xf3\xcf%5F \xab\xb5i\xd6\xb9\xc5\xac}\xe3\x8d" - "\xdf\xbc[\xff\x64\3\xe5^\xbe\xe8\x1cJ\x9c&'>\xbdY\xc9np\xaa\xc5\xb7\xf3\x93}" - "\xa6\xb3I}>\x82!~\xd9<[x\x87\xc2\xf2\x85zU\xb5\xce\xc6\xfa\xdeZ\xa0M\xb3\xa8" - "{\xb1\xd6\xdd\x39X}K\xa7gQ\xef}\xe\x9bO7>\xe2g\xfd\x66K{./Zk\x9fm\x1b\xa7\xd3" - "\xc9\xb7Z\x9e\xcd\x95\x14\xe7\x30\xbb\5\xaa>f\xce\xce\x92`@o-\xbfT\xbb\x9c_l" - "\x8e\x64\xe\xee\x1a.q\xaf\xce\x8bnd\xa8s\x1c\x36\xfg\xfb\f\xb7<\xd9yI\xff\6\x94" - "S\xacTr/\xc0\xab\xf7\xf1\xad\xea\xf]\xf7\xd9\xd1\xd1s\a\r\x9d\xf5\xc4\x1d\xc4" - "\xc4>\x1b\xdaP`W&t\x16\\dTa\xc4\x41=E\xe7\x86;\xe5\x9e\xf9\xdd\xdf\x33\xbb\xc7" - "\xa7\\=O\xf6#N\x9d\x61\xa7\xb8^\xe7\x19\xb6W\x9e\xec\x8c\xb4\xfe'6a\xf%p&\x90" - "\xb7\x62\x97\x37\x84\xb2G\xb5v\x9f]\xc1S(\xbe\xe3O\xee;\xe3\x64\xc2t\xe2/\x1e" - "'\x7f\x1f\xb7\xd4\xb7\xc5\xc3N'\xfa\x97\xcb\x43u\xaa~5\x1e\xfc\xd3\x8f\xeaZ\x80" - "(\3\xa5_\xffm\x95\x9fv\xfR\1\xd8\xd8:{W\xf9\x43\x8e\xd9\xfc\xbd\xbe[^\xd8\xfd" - ">\xb3\xc5\x33;\3\xda\xbd\xff\xae\xb3\xd5\xc1\xfel\xea\x8c\xf8\xee,\r\x97?\xdb" - "WIR\xac\x82\xce>\xeb\xae\xd7+\xe\x30\xe3M\x8f\xfa\xcf\x82-%\x88\x9d\x17:\x87" - "\xaa\xc7\xffyF\xa2\x7f\x8c\xb6\x9c\xe7\x44~\xeb\r\xfb\xff\xf2g<\xcb\xb7\xc1\xb1" - "\x1d\xd7\x33\xc6\xdf\xeflV\xde\xba\x8b'Ng\x97\xd4\xfa_\x80\1\0\x38\x94\x98\x66" - "0\xf2u\xfc\0\0\0\0IEND\xae\x42`\x82", - 2800 ) }, - { "/images/icon-play.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\xe\0\0\0\xe\b\6\0\0\0\x1fH-\xd1\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\0\xc3IDATx\xda\xa4R\1\r\xc4 \fl^\1\x12\x90\x30" - "\tH\xc3\1\x41\xc1g\n" - "\x98\3$ \1\tH\xe0{\4\b0\xf2\x9f\xec/\xe9\xb6\xb4wmaG4Cq8\x8e\xbc\x84\xab\xb5" - "-\xde\x42\x88\xac\xb5\xce)\xa5\xdc\x10\x63,9\xd4\x98\x63V\x91\x39\x8e\xa3\x90" - "F\xd1\b\xd4\xc0\x19\xc5\n" - "\xddP\0\xbc\xf7\x85\x10\x42\xd8\x8a\xeb\xe4\xb2\xb6\xc3*\r\x10\xe2\\ \x18\x63" - "nbp\xeb\x99\xa9O\x1b\x85-\x94RS\x1dG\xa9\x35\x9a:\xae\xc2\xddt\xe4^\x9c\xa4\xa7" - "\x98n\xf2\xd7\xaa\xf8.\x13\xf9qYko\xdd\xb0\t\xafG\xdc\x88\xa4\x94=\x7f\x9e'^" - "\xd7_\xbf\xa3\xb8\xe6\x89\1\xba{\x9a\xe5\xd6\xeb\xff\x66\xb9\xad\xc9+yk\xf2\x8f" - "\0\3\0&\xcbia\xf7\3\x38\xde\0\0\0\0IEND\xae\x42`\x82", - 289 ) }, - { "/images/status_panel-icon_1-error.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0/\0\0\0/\b\6\0\0\0s'\x93\xac\0\0\0\x19tEXtSoftware\0\x41\x64" - "obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \x83\xdf\xbfk\0\0\1\n" - "IDATx\xda\xec\xda\xcb\n" - "\x82P\x10\6\xe0\xe9\x64+\x17\x8a\x8b\xec\4\x11=MH\xf8\x14\xdd\x88^\xa9\\G\xd1" - "\xaa\xdb\xc3\x44\xb5\xb1lY\x9b\x16\5\x96\x1a\x45\xcb(5&\xfe\x81\x41\x41\x84\xef" - "\x8c\x33z\x16\x66\x96V\xd5\"\xa2~\x90\x92x\x84\x17\x64]\x8e\xc6\v\xc1\f\x1e\x86" - "\x14\x9a>\r\x8e\x96`\6\x8f\xc2?\x1e\xa2\xa2\v\xe2\x1b\x92\x33\x9e\x80\a\x1ex" - "\xe0\x81\xffo\xbc\xf2\xc9M\xa5\xbe\x43J\xde\x8c\5p\xf1v\xe4\xb6\x9a\xe9U>.x\x18" - "9YL\xb7\xf2\x8f\xd8\xd8\xb5\xcf\xfbUU\xa9<\x18\xf2\xecy\xfftb<\xb0\xd7+^\x95" - "\xc0\3\xf<\xf0\xc0'\xb7=\xa8Lf\xfc*\x7fq\xdd\xd8\0\xe7\xf5*\xdd\xca\xbb\x9d\x36" - "\xff\xb6y\xb7\x9d\xbe\xd9}b`\xbf\x19\xde\xd7kq>\5T>\xc9\xbe\xc6\x17\x16x\xe0" - "\x81\a\x1ex\xe0\x7f\x81\xf7\xd8\xa1u\xfd\x89o\bM\xf3\xb9\xc0\xb3\x86\x41z\xa7" - "\x1b\x9e\xee\xc3\x8d\xd9\xdc\xec\x39\x36\xdd\x7f\xa0(0Y\xc3\x36\xc8\xd6M\x80" - "\1\0\xfa\xb9\x34\xbf:\xb4\xa0\0\0\0\0\0IEND\xae\x42`\x82", - 1176 ) }, - { "/images/grid-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x11\0\0\0\x11\b\6\0\0\0;mG\xfa\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \xc1U^\xb6\0\0\0pIDATx\xda\x62" - "\xbcp\xe1\xc2\a\6\6\6~\6\b\xf8\b\xc4\2P6Q\xe2\xfa\xfa\xfa\2LH\2 \xc0\xf\x14\xfc" - "\0\x63\x13#\xe\x32\x94\x89\1\x13\xf0\x33`\a8\xc5\x99\xa0\xce\3\3\x66\x66\x66" - "dI\xa2\xc5Y\x80X\x10\xe8\xd4\xf7H~\xfe\b\xa5\x89\x16g\4\6,\3%\0h \3\xcc;\xff" - "\xa1\xf8\3\x9a\xb3\x89\x12\x1f\x8d\x9d\xd1\xd8\x19Z\xb1\3\x10`\0~\x83Sc\xe5G" - "\xd5\xee\0\0\0\0IEND\xae\x42`\x82", - 1022 ) }, - { "/images/green-light_glow_small.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\xf\0\0\0\xf\b\6\0\0\0;\xd6\x95J\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \x1a\x99o\xa2\0\0\2YIDATx\xda" - "l\x93Mk\x13\x41\x18\xc7\x9f\xd9\x9d}\xc9\x36\xd9&5\xb4\x36\x14\x11QA\x90R\xa4" - "=\x15\x14\x41\xf4\xe0\xc5\x93\x17=z\xd0\x8b\x17\xf1\v\xd8\x93\x37\xf^\xfa\1\4" - "?\x83'\5o\xda*Z\x8aZl\xb5\xd6\xc4\xb4\x35\x9bL6;\xbb;o\xce\x84\x8d\4\xed\xc0" - "\x8f\x61\x61\x9e\x97\xff\xffy\x16\xdd\xdeZ\x84\xb1\x63il\r.@\x1a\xa5\xe1\5\x42" - "#G\x8fqq\xa3\"\xd0\xd3Lh*\x9aR\x91\xc8\4PM_3\xd0\x64\x45\2\x85\xc7*\6\x9aZ\xf7" - "G\x7f\xa1\xf3\x8d\xdc\xcd)_TB\xd5,\x1bu\xb1\x87\xd7\xaa\x8d\xf2je\xa6\xfc\xce" - "\r\xed\xdf\xfa]b\x92\xe2\xb1\x8a\xb5\xe6\x87\xc3\x9b\xa4\x19?\xba|\xe6z\xe9\xec" - "\xccy\xf0q\t\xe2\x9c\xd4?\xb7\x37\xae\xbd\xfe\xfa\xe2R\x12\xe5+Ss\x93\xcfm\x17" - "\xb5\xfd\xba\x43\xed\xf9\xfb\r\x93 \x8cv\xfbK\x9d\xed\xde\xd3\x1b\xf3\xb7\x82" - "\x93\xd3\xa7\x61\xc0\tD\xd9\1P\x11\x43%\ba\xba\xd2\xc0_\xf6\x36\x97\x41\xa0\x35" - "\v\xe1\x96?\x85\xd3\x91\x31\x95\xe8;\xb9\xb7|\xeaJ\x10\x96\xab\xb0Gv`\xc0\xfa" - "\xc0$\xd3nIm\x88\5\x8e\xe3\xc0\xb9\xc6\xbc\xbfu\xb0y\xc7\xb1\xdc\x8f\xd1'JF\xc1" - "%\xa3q\xba:\v\xcdx\x17zY\a\xb8\xe4\xf0\xef\xf1|\x17\4\x13\v,\x16U6\x90\xdeh\x1c" - "\xb6\x36\xa7\x1a\xebV\xf\xe9/H9\x85\xa3\x8eR\xca\x10J\xae\xdc\x9cp\x8c\x8b\x39" - "\n" - "d\xa1\xee~\xdc\xac\x13\x16\x81P\xe2\xc8`.8 \x84\x88H\xa5\xe2T\xe[6\xfdQ\xc7\xc7" - "\xeb\xad\xee\xdeU\xf0Lvydp\x12'`[\xf6\6#\x92\x32\"\xd8(\xb8?9[^\xdd\xdfn]\f\xc2" - "\xc0\xb7\\\xf4\x7f\xd5\\\0\xed\xa7\xa9\xa7\x82g\xb4\xc7#\x91\xcb\xcc*6h\x10\x1e" - "/\xafOL\5+\3\x92\xa4\xa6\2\xcd\xe9P\xbb\xb9\x87\xdf\xbd\x34um\xefI\xd6V\xef\xb3" - "\xe\x8ftLn\xe6l4K\xdb\xb3\x38\x46\xce\xe\x12\xe8-\xa3\xe2\x98\xa0rRP\xe5\xa8" - "\x1cz\x96\xb4\xdf\x38\xd2\x7f\x9c\xb7\xd0\xcb\xac\xcb\x7fj\xbd\xd1\x85\x87sl" - "\xb4\x9e\x46\x64\x62\x36'\xa8\5\xaf\xb0\xe5n\x98q\x18W\x8d\x39\x46\xa3iU\xeb" - "\xec\xb0\x44\x10\xaf\x8a\xd9\xf8\x8f\x31t\xdc\xac\x9c&7\v`\xe6h\xc6\x61\\5\xe6" - "\f5\xba(_zp\xe2\xef\2\xfc\x11`\0\xe3\xccSPu\xd6%p\0\0\0\0IEND\xae\x42`\x82", - 1511 ) }, - { "/images/resolve_issue-details_icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\x10\b\6\0\0\0\x1f\xf3\xff\x61\0\0\0\x19tEXtSoft" - "ware\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0" - " d\xa8|\f\0\0\0\xe6" - "IDATx\xda\x62\x88" - "\2\x19\xa0G\x81\1zL\xd0\xa8#\x17\xfc\4\x19p\x91\2\3n\x80\fXA\x81\1+@\6L\a\xe2" - "[dh\xbe\xf\xc4\xb3@\6\xfc\0\x62o\x90\x7fH\xd0\xfc\x1a\x88}\x80\xf8+,%J\3\x31" - ";\x91\x9a\x8f\0\xb1\x19\x10_C\xce\v\rH\n" - "@\xb9\xeb.43\xc9@\xc5\x9e\1\xf1q ^\xe\xc4;\xd0\x33\x93=\x10;\0\xf1\x41\xa8\x41" - "\aH\t\b\x90\1\x8eP|\x80\x9ch\0\b0\0\xbf\xf5(\xb0\5\n" - "\xe9\xa4\0\0\0\0IEND\xae\x42`\x82", - 1140 ) }, - { "/images/disk-slot-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x11\0\0\0\r\b\6\0\0\0Oy\x85\x1a\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \x94\xf9R\xb3\0\0\0\xbdIDATx" - "\xda\x9c\x92\x41\xe\x82\x30\x10\x45\al\\\xbb\xedM\xf0\0\x1c\xc0\x8dz\1\x12\x45" - "w\xde\xe\xe\x80\x17i\n" - "\xcbn\x9atA\xeb\x8c\x61\f\xc1M\xdb\x97\xfc\xb4\x94\xf4\xf5\x93Rt]\a\v\x15\x66" - "\x80\x38\x8e\x98\x37M\xea\xba\x86r\xf5\xe2\x1e)\0)\xe5\x80\x9b+~f\x89\xc4\\c" - "%\xd3\x34\x81\xb5\xf6\xb5\x95\x34\x98}\xac\xc4{\xfZ\xeb\x13\xb6\x91,\xa1\xcd" - "7HD)\xb5\x9b\xe7\xb9\x65\xc9y\xf9\x9c$\x9cs0\x8e\xe3\x93J\x90\xe4\1\x99`\x9b" - "\3\xe\x97r\xb9\xda,\x8c\x31\x34\xb4\x82\xef:\x87\xbe\xef\xbf\xff\x97\xd8,\xa4" - "P`\2M\xc4z\x11\x1b\x85\4I\xe0\x83\xc5\xaa\x45\xc8h\xf3\x93\xa4\x36\xf8\xe3#\xc0" - "\0\xaa!<\n" - "\xdah\x81\xb9\0\0\0\0IEND\xae\x42`\x82", - 1099 ) }, - { "/images/red-light.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\v\0\0\0\v\b\6\0\0\0\xa9\xacw&\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 `\xf4\x1a\xd7\0\0\1\x35IDATx" - "\xda\x62\x64\x80\x82\x37M\x8d*\xcc,,\xe5@\xa6'\x10K\xfe\xff\xff\xff\xc5\xbf\xff" - "\xff\xb7>~\xf7\xae\xc3\xb0\xaf\xff>P\xec?#H\xe1\xcb\xbaZ/6V\xd6\x95@&\xf\3\x1a" - "\0j\xfa\xfc\xfa\xf3\xa7(\xf5\xee\xde\xed\xccWbc\x14\5\x84\x84\xf6\x33\xfc\xfb" - "\xc7\3\xc4\f\x18\xf8\xff\x7fv.V6?mv\xd6\xd5,B\"\"\x15\xff\x7f\xff\xe6\x66\xc0" - "\xfx\xcc\x94\x94\x8bYX\x99\x18=\x19~\xff\x62 \4\xf8\x39\xd8\x9dY~}\xf9\"\xce" - "\xce\xc1\x41P\xf1\x9fo\xdf\x44Y\xde\xbc|\xf5JJRR\x86\x90\xe2W\xcf_\xbc\x61z\xf4" - "\xf0\xc1\x8e??\xbe\x33\xfc\a:\5\x17\6\xc9\xdf~\xf4p?\xd3\xebo\xdf:\xae\xdd\xb8" - "\xf1\xf9\xef\x8f\x1f\f\f\xbf~a\xe0?\xdf\xbf\x33\x80\xe4\xf7\xbd~3\x85y\xe3\xdb" - "w\x1f\x1cy\xb8\xaf~|\xff\xde\x8f\x89\x81\x81\x8d\x95\x89\x89\x81\xe9\xff\x7f" - "\x86\x9f?\x7f\x32\xbc~\xfb\x96\xe1\xc6\x83\a\x9f/\xbe{\x97\xda\xf9\xf8\xc9yF" - "\xa8\x93\x98\xdb\x95\x14\xd4\xe4\xd9\xd9\xcbY\x19\x99\xdc\x98\x18\x19\x44\xfe" - "\xfdgx\xf5\xfd\xdf\xdf\xbd\xc7>}\x9e\x38\xe3\xd9\xf3\a@5?\0\2\f\0\xac\xe3\xab" - "\xb2\x8f\xbbYj\0\0\0\0IEND\xae\x42`\x82", - 1219 ) }, - { "/images/disk-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\f\b\6\0\0\0k\xe7=\x81\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \xe1.}\xbe\0\0\0vIDATx\xda\x62" - "\xbcp\xe1\2\3\x14X\0\xf1q\6\xe2\x80%\x10\x9f\0\x31\x98\x90\4\x33\x19\x88\aY0" - "\6\xcc\0I \x8e \xc1\x80p\xa8\x1e\xb8\1\xa9@\xcc\x46\x82\1 \xb5\xe9\x30\3@\x9c" - "\f\6\xd2\x41\x1aH/\xc8\x80\x30\x98sH\4 =\xe1 \3\xb2\x19\xc8\aYL\xd0\xe8#\x17" - "X\xb0@\x19\x8c\x64\x1a\xf0\x9f\x89\\\x8d\xe8\xe9\x80T\0w1\v\xba\x89\xa4\2\x80" - "\0\3\0\xbd\t\r\x15t\xd5\xf3\x85\0\0\0\0IEND\xae\x42`\x82", - 1028 ) }, - { "/images/icon-magnifying_glass.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x11\0\0\0\x11\b\6\0\0\0;mG\xfa\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 uPgU\0\0\2hIDATx\xda\x9c\x92" - "\xcbk\x13Q\x14\xc6'\x93wh\x92\x99!!\x91\xc4*\x15*>\x92\x42\x44\xea\3\xcc\"\x8a" - "\r\x82R\4\x15\"\x81\x82\x1bW\xe2J]\xf8\xf\xf8Z*\xe8\x42\x88\x16u\x93\x90\xb6" - "n\xa4\n" - "\n" - "EKP\xac\x31\v;\x8b\x34\xb1$\x99\x94\x38I\xa7\xa1\x86L\x1d\xbf\x9bNK\x18\x1b(" - "\x1e\xf8\xcd\xb9\xf7\xce\x9do\xee\xf9\xee\xd1\xc5\x62\x97)5hp\1\\\2\xc3\x80\3" - "U0\3\x1e\x83w\xd4\x16\x31>\xfe\xbc\xf3!\t\x17\x98\6/\xc0\x39\xe0\5&\xe0\a\x17" - "\xc1\xdbv\xbb\x9dT\xd7\xfe\t\3\x30\x82)\xf2wEQ\xda\x82P\xf9\x90\xcf\xe7\xbfJ" - "\xd2\x8a\xc4\x30Nf``\xcf\xb0\xcb\xe5:b4\x1aG%I\x9a\x9a\x98HGq\xfa\x35\xad\xc8" - "\xd5\r\x81L&\xf3\x90\xe7\xe7y\xcc\xe7\xc0\xfc\xd2Ru\x95\xe7\xf9\xbep8|\xcd\xef" - "\xdfy\xc3n\xb7\x9f\n" - "\x85\xe]\xc7\xbb{Z\x91\x38\x19T\xab\xd5\x19U \x89?\x95\xbb\xf6H\xe0&\xca\x39" - "\x81\xd3\x1cu\xbb\xdd\xe7\xe1\xc3K\xecY\xec\x36s?\x19T*\x95\x1fHY\x8d\xc0\x66" - "@ M\xb2\xd5ju#\xed\xed~GDV\xd7\x37\x19\x88iE\xaaw\xf4\x91\a\xca\xfe\x83\xc4h" - "E2d\xe0\xf3\xf9\x42\xaa\xc9\xbd.`\xb4S\x9b\xb4\xbc@\xb4\xb4\"\xf\xc8\xc0\xe9" - "d\x86\"\x91\x93\xb7z\b<\2\ap\n" - "9\x97\xcb}$\xd5k7\xbci\xb5Z\t\xb3\xd9\x1c\xf7z\xbd\x63\x30\xf0\x30\xeaO`\xbd" - "\4v\x83\x18\xd8G6\x97J\xa5iA\x10H\3~\xd7\x8aP\x10\x18\xab\xd5j2\xc7qq\b\x1c\xc4" - "\xd2\x9d\xadj\xf2x<\xc7\2\x81\xc0\xb3`ph\xb9{]\x1f\f\6;~\xa5R\xc9\xc9\x66\xb3" - "\xf9\x19\xbe\xd9\x14\x85j\xc9\xb2\xdc\xc0\xfcg\xb9\\&m\xff\x1b\xb7\xb2\x83\xa6" - "i3\xae\x38*\x8a\xf5\x39\xccI;P\xd9\xec\xb7\xf5\x93\x90\xc0\xd5\x12\xb3^\xa3\a" - "\xde#\xf\xaa\xado&\xb7g2\x99\x84\x91\x91\xe8]4[\x84\xa6\xf5\x36\x96\x65S\xa2" - "\xf8\xeb\n" - "\xcbr\x89\xcdr\xba\3\x62+H_\xb4\xeb\xc5\x62\xe1\x34\x8cM;\x1c\x8e\x33:\x9d\xce" - "\xc8\x30\xecSQ\x14I\xcf\xdc\xa7\xa9mF\x7f\xff\xae\xb5\x42\x61\xe1l\xa3\xd1H\x91" - "9\x84h\x9c\xec\x36\x31}\xc3\x93m\5\x8cU,\x16\xcb\xabz\xbd>\xa8\xd7\xeb}\xb3\xb3" - "\x9f\x9e@\xd4\x66\xa0\xfe#\x18\x86\x89\xc1\xbb\xe3\xaaw\x93\x7f\5\x18\0\xa0\xc3" - "\xe2\xba\x95\t\xb5~\0\0\0\0IEND\xae\x42`\x82", - 1526 ) }, - { "/images/resolve_issue-resolved_icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\xe\b\6\0\0\0&/\x9c\x8a\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 +\xc4\x84N\0\0\0\xf5IDATx\xda" - "bL\xee\fg \x11p\2\xb1!\x10\x1f\a\xe2\xffL$j\xe6\2\xe2\x9d@|\x14\x88\xa7\0\x31" - "#\v\t\x9a\x39\x80x#\x10\xdb\x42\xf9Y@\xfc\x96X\x17\xb0\1\xf1\x1a vA\x13W!\xc6" - "\0\x66 ^\6\xc4\xdeh\xe2g\x81\x38\x13\x66\x80\x1e\x10\xef\5\xe2\x30,\x9a\x17\2" - "q0\x9a\xf8\x65 \xf6\0\xe2\x8f\xa0\x30\xd0\5\xe2=@,\n" - "\xc4\xf6@\xcc\2\xb5\x91\x11\x88g\2q4\x9a\xe6\x9bP\xaf\xbc\x61\x80*\xee\x85j\x86" - "\xd9\xb8\b\xca\xb6\0\xe2\x64\x34\xcdw\xa1\x9a_\xc1\4@\6L\3\x62G(\x1b\x66\xc8" - "R,a\xf1\4\xaa\xf9\t\xb2 (\f6\0q$\x10\xff\xc1\x13\x90\xcf\xa1\x96<@\x97\x80\5" - "\xe2\x1a<\x86\xbc\6\x62W \xbe\x83\xcd\x64\xe4h\xc4\x66\bL\xf3U\\NCO\x89k\xa0" - "\xf4r ~\xf\xd5|\x11_\"\xc1\x96\x94\xd7@m\xfc\4\xc4O\t\xa5\x32\x80\0\3\0\xf9\xd7" - ".\x8e\x19\x96N\x1d\0\0\0\0IEND\xae\x42`\x82", - 1155 ) }, - { "/images/bars-icon_server-assignments.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x11\0\0\0\xe\b\6\0\0\0\xc9\xed\xf7\xb4\0\0\0\x19tEXtSoftw" - "are\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0<" - "?xpacket begin=\"\xef\xbb\xbf\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?> \x93\xb7z\xc5\0\0" - "\0TIDATx\xda\x62 \1\x18\xc5\xb1\0" - "\0\2\x87IDATx\xda|\x93\xbdk\x14\x41\x18\xc6\xdf\x99\x9d\xdb\xcb\xdd&\xb9\\$ " - "\xf1\x92#bNL\x11\xd4\x88\1S\xd8\4\xc9\xff\xa0\x95\xbd\x62i)\xa4\xb2\xb4\x10\xac" - "\xb4\xb7\xb7\xd5\xca\"(F$*\"(\x9a\xbb\\n/\xb9\xaf\xfd\x9e\xaf\xf5\x9d\x63\x13" - "\xce`\\\xf8\x15;\xc3\xf3\xbc\xf3\xbe\xcf\f\xb9\xf3\xfd\x1a\x8c|\4\xa1\b\xcb\xb0" - "\xb2u\x85\xc8\f\x8d\xa4G\2\x36\"6\xc2\x1cRD\xc6\x11\a\xb1\xb3=\x8e\4\x88\x8f" - "\x84\x88\xc8\x8c\x8e\r\x8c\x38\x8f\x94\x44$\xcf\x35?\x1d\xdc\x8d=\xbe\xae\xa5" - "\x9e\x1fn2\xfa;\xef\xd8of\x16\xa7\x9f\xdb\xe\xdb\x65\5\xda\xc3\xe5\xc4\x98\xb0" - "\xec\xd8\xa6r\xa9\xdf\xf0\xaf\xb4\xbev\x9e^\x9d][\xb8\xb4\xb8\fe\xe7\f\xe8T\xc1" - "\x81\xef\xd6\xbe\xb5vj\x1f?lmLWJ\xf\x9c\xe9\xc2\xbb\xe2Y\xfb\xd0\x98\xb0\xac" - "z\xd1T6\xe2\xb5\xea\xfa\xc2\xe5\xb9\xeb\xe0\x8b\1\xb8\x61\3\x84\x12\xc3#^\x9c" - "]\2\x42\xa0\xba]\xdfzbQ\xeb\xb6\x8ct2\xb9\x30&\xac\xe5\xfb\xb3\xa6\xcfrc\xdb" - "\xbd\xb7T^\xb9\xb5<\xbf\x82\xc2=8\x8cZ\xd0\xe7=4\xea\x43 =\bE\0\x13\x85IH5\x94" - "\xdc\xc3\x16\x61\xd4~\xafU\xea\xb3\xec\xf8N<\xe0\xeb\x95J\x15\xf6\xfd:t\xe2\x36" - "V\xe6\x38\xea\x14\xfe\x8e\x88\x80St@\xb4\xc5M\x19\xeag\xde\xaf\xc4=j\xc1VB\x9f" - "'\x8c\f\xab\x9bj'\xc5\xa3Yi\xad\xe7\x65\xa4\xc6U\x92\xda\xa3\x31\x42\x37v\xa1" - "\x9ft@h\1\xa7}(\x1e\xde\v-S*\xf4\xa5\xb6\xf6\x1e\xa6\xb2z\x84\xa9\xe3'\xd8{\xe0\x30" - "Q\x14\xd9 \bZ\xce\xb9Y\xe0\xbc\xb5\xf6\x87\x31\xe6\x83\6\x8a\xa9T\xea\\\x1c\xc7" - "&\xd9\xf1z\x10\4\x1f\xcb\x36\xf7\xa0\r+YM\xab\x19\xc7\x9f\xbe\6\x41\xdb\x39g" - "\xba\xce\xf2\xf9\xeb\x85\x42\x61\x42\x44\xeet\x9c\xdc\xecu\xe2\x9c\x9b\x99\x9b" - "i\xaf\xbc\xa8@\x9a\x38\xfe^\xfdv*\x93\xc8\xed\x39V\x1a\x38\b\x8c\xefxl\xbdN:" - "\x82\xfb\t\x15\x91\xb2R\xaa\xeb\x44\xf5:\1\xb6\x80\x65\xdf\xf7\xb7\xd7_o\xde" - "\x7f\xfa\xf8\xee\xf4\x99\x62\x8bK\xd7nS\x18/\x12\x45\x91\xadV\xab-\xe7\xdc\x31" - "\xe0\t\xb0\x36\xd4\xc9\xf2\xa3W\x9b'\x9b?\xa7\xdf\x36\xb6\xb8\xb2\x61\xc3`\xf3" - "\xc8\xaf\x41N\xd4\xa0\x63\xbc\xd9h\xf1\xf2\xf9=9\xbd/j\x9e\xbdxcO&\xeb\xf5{'" - "\x7f\xae\xfa\x7f\x9ct\xe1\xc4I\xdf\x12\x91\xf9z\xbdn\xc3\x30\x94\x30\f\xa5\xd1" - "hHR%\x11\x19\xfa\x41\xfb\x86%\xe0P\xf8\x37\x44);\xb7\x97\x38K<\0\0\0\0IEND\xae" - "B`\x82", - 619 ) }, - { "/images/arrow_down.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\n" - "\0\0\0\n" - "\b\6\0\0\0\x8d\x32\xcf\xbd\0\0\0\x19tEXtSoftware\0\x41\x64obe ImageReadyq\xc9" - "e<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \x11~\r\x88\0\0\0wIDATx\xda\x62\xfc\xff\xff?\3\x31\x80" - "\5\x44\\\xbf~};\x90\xf2\xc0\xa1\x66\xbd\xa6\xa6\x66\x10\x13\x94S\f\xc4\x7f\xb0" - "(\xfa\5\xc4\x65 \6X!P\xc7\x35 5\v\x8b\xc2\xc9@\xb9;p\x85PP\xf\xc4\x1f\x90\xf8" - "o\x80\xb8\5\xc6\x81+\4\xea\x44\x91\0i\4\x8a}\xc0P\b\xb3\n" - "\x88\x41Va8\x85\x11=x\x80!\x10\b\xa4\xbe\3M\xdb\x81W!.\xc0\xc4@$\0\b0\0S\x8c" - "#R\x92\xd3\x18\a\0\0\0\0IEND\xae\x42`\x82", - 1029 ) }, - { "/images/resolve_issue-message_icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\x10\b\6\0\0\0\x1f\xf3\xff\x61\0\0\0\x19tEXtSoft" - "ware\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0" - " \xac\x96\xc2\xaf" - "\0\0\0mIDATx\xda\x62.y\x16\2\x16h\2\xf1* \xd6\xc1\xe7\2\x46\x34\f\3\t@|\6\x88\xbf\1\xb1\n" - "\x16u`\x8c/\f\xe6\0q\x1f\x10[\3\xf1]b\xf2\2\x32x\b\xc4\xd1@|\x94\x9cXX\6\xc4" - "\xfa\xc4h\6\1\x80\0\3\0\x83\xdb\x16\x38\x95\xcfH\xf4\0\0\0\0IEND\xae\x42`\x82", - 1019 ) }, - { "/images/globe-icon_white.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\x10\b\6\0\0\0\x1f\xf3\xff\x61\0\0\0\x19tEXtSoft" - "ware\0\x41\x64obe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0" - " \xc8I\xd6M\0\0\1" - "NIDATx\xda\x84\xd3;K\3\x41\x10\xc0\xf1\xbb\x8b\b\x8a\xa8\xc5]#>P\xb0\xf0\xd1" - "\xd8\4\xb5H\xa1ha\x9f\xca\x42\v?\x85\xf8\x39\xf4\n" - "\xb5\x90\x14\xb6>\b6V\x8a\"\xf6V\n" - "\xb9h\xa7\x88u\x12r\xfeW\xe6\x64\x98\x9c\xf1\xe0\a\xd9\xdd\x99\xd9\xbd\xc9\xad" - "\x9f\xa6\xa9\x97\xf3\fc\5\x13\x32\xae\xe3\x1a_\x1d\x91\xae\x80\x12\xe1\0\x8d" - "\xb4\xf3i\xc8Z\xa4s|u\x82YT\xd5\xaew\"\xc0\2J( \xc1\6\x9e\xf4\tB\xd4\x64\xa7" - "6b\xec`\t3X\xc3\x34n%\xa6&9\xbf\5\x8e\xd4Q\xcf\xb0\x8e+\xf9\xed\x92\3\xf4\xa2" - "\xf\x37\x12w\x9c\x15\b\xd5;?b\xc8\xf4\x65\x1e\xfd(a\xf\xbbhIN\xe8\2\xca\x92\xdc" - "\xc4\x98I\xf6\x64\xf7-\x14P\x95\xb8\x17\xc9)\xbb\6MI\xd3\x9e\xf1\x9a\xf3\x97" - "\xb6q\x81m,\xa3G\xe5L\xba\2M\x19\x8c\"\xc4HN\x91O\xdc\x63\xd0\xcc\xb7\2\xb5\xeb" - "\0\xde\x65\xbci\2\xdd\xae'\xf0\xcd|\xdd\x36\x31{\x1e\xa4\xebY\x1f\x8a\x7f|X\x91" - ";\xc1\a*\xa6r\x11\x8b\xa6\xf\xa7&\xa6\xf2sb\xf5!%f\x87}Ys'y\xc3\xb9ZK\xec\x87" - "\xe4\xcc\xe5\x14\x39\xc4\xa5\x99K$\xd6\xb3\5\xb2\xcb\x14w\xb9Lq\xb7\xcb\x64\xaf" - "\xf3*\xc6\xff\xbb\xce\xdf\2\f\0\xed\xc0\x1c\x10\xa2\xd4\x64]\0\0\0\0IEND\xae" - "B`\x82", - 1244 ) }, - { "/images/eye-icon_white.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\v\b\6\0\0\0v\xe2\r9\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\3$iTXtXML:com.adobe.xmp\0\0\0\0\0 \x87%.\xf\0\0\1\xa5IDATx\xda" - "\\\x92\xc1K\2\x41\x14\xc6w\xb3\xb6\xb6\x42\xdd$b)q\3\v\2\xf\x92\x87\xe]\x82\xe" - "\xd1\xa9\x83\x10\xd4_`\xd0\xc5\xe8\xd4M\xbav\xe8\x14T\xb0\x1e\xb2\x8b\a;\t\x15" - "\x9d\3\x8f\xa2\x82(\xab\x64\v\6\n" - "\v\xca\xaa,\xb6\xdb\x37\xe2\xd0\xd4\x83\x1fox\xf3\xbdy\xf3\xde\f\xef\x38\xeG" - "\x8d\x87\xc1y\x80\x1bL\3\1\xd8`\0\xba\xc0\x80\xfe\x9b\x63l\x92I\xf6H\x92\x14" - "H&\x93\xbb\xe1px\x1bkY\x14\xc5\x45l\xf5L\xd3l\x94J\xa5\x42\"\x91x\x85\xae\x82" - "\xd8\x97\x43+\x8f\xfdJ&\x93\x89\xf7\xfb\xfdO\xe7\xd7r\xe0\f\x9c\x80GR\x19\x66" - "\x16\x8b\xc5[\xbf\xdf\x1f\"\xc5G\xb9\xa8\xb2\\\xadV\x1f\x9c\xbfv\x15\x89\x44" - "\xa6 R\xc0:\x98\x43l\xcf\xb6m\x8blv:\x9d|4\x1a\xdd\"\x17\xe7\xca\xe5\xf2=\x9b" - "iYV\r\xce\x15\x8b\xc5\x42\xba\xaeg\r\xc3\xc8\xa9\xaazD\xaa\xa1\x95\x1b\xaa\xc3" - "!\5\xb4\xaap8u\xc0\x1e\xd0n\xb7U\"n\xb5Zw4\x86\xd6\xc8\xa1\\\xbd^?`\xb5\x95J" - "E\x9d\x80\xbf\x66\xa7\xda\xeb\xf5\x66\xc8kx\xbd\xde.\x8d\t\x82\x30$\xbe\xd9l" - "\xce\xb2\xda`0xL\x86(\xe2\x16\x39\xa6ZC\x96\xe5\0\x96\v\x80\xcc\xe6\r\x90~\xdd" - "\x9a\xa6\xa5\xfe\xcd\xear\xf4\n" - "c\xf1;\x8d\x62\xa8)T\xdd@\xd2\x12 O\xb9\x9aN\xa7O\xe9\x10\xe9\xa0\1\xcf\x33\x1f" - "\x89L\xfd\2\x9c\x83y\xdc$_\xab\xd5^\6\x30\x45Q6}>\xdf>\xe2.\xf0\1\xe2\xe0i\xf4" - "\x7f\xd8\x9f\x38\x36\t\x1c\x82\x1d\xb0\6H\xdf:\xd0\xc0\x33\xc8\x82!\x15\xff\b" - "0\0PKbI\xc1\xbc^'\0\0\0\0IEND\xae\x42`\x82", - 1331 ) }, - { "/images/query_shard_access-icon.png", std::string( - "\x89PNG\r\n" - "\x1a\n" - "\0\0\0\rIHDR\0\0\0\x10\0\0\0\x10\b\3\0\0\0(-\xfS\0\0\0\x19tEXtSoftware\0\x41" - "dobe ImageReadyq\xc9\x65<\0\0\0-PLTE\xb8\xb8\xb7\xcf\xcf\xcf\xa0\xa0\x9f\xf3" - "\xf3\xf3\xa6\xa6\xa5\xac\xac\xab\xdb\xdb\xdb\xb2\xb2\xb1\xca\xca\xc9\xe1\xe1" - "\xe1\xe7\xe7\xe7\xd5\xd5\xd5\xbe\xbe\xbd\xc4\xc4\xc3\xff\xff\xffT~ `\0\0\0\xf" - "tRNS\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\0\xd4\xdc\x98\xa1" - "\0\0\0QIDATx\xda\x94\x8fK\xe\xc0 \b\5\xe1\x81Z?\xed\xfd\x8f\xab,L\xc1\x9d\xb3" - "!L\xc2$\xd0w@{\xd2!\x80K!\rh\xe2\x44\xc7\xa2\xfb\x93\2\x94\xd0\x10U\x89\xd1Z" - "w\xf4\xa1\x80\xad\xec\x30\xc7\xff\x1f:8\x8a\xf4\x9aH>\xb1\x96\xcc\x81<\5\x18" - "\0\xe3s\t\x15.I\x11i\0\0\0\0IEND\xae\x42`\x82", - 259 ) }, - { "/js/ZeroClipboard.swf", std::string( - "CWS\xe\x95\f\0\0x\xda\x8dV\xcdr\xdb\xc8\x11\x9e\1\1\f\0\x92\x12I\xc9\x94,\xff" - "\xc1\xb6\xd6\xb2\xb4\x90HQ^\xefZ\x96\xb5\x96I\xc9\x96-\x89\x1bK\xb2\x37^\xdb" - "\xcb!0\x10\x61\x83\0\3\x80\xfaIj+\xae\xad\xca#\xe4\x98*\xe7\x92Sny\x80<@*)H\xae" - "$\xf7\x1cRy\x85\\\xe2\x34@\x9a\x14\xf7'\x15\x14\xd1\xe8\xfe\xa6\xbb\xa7\xbb\xa7" - "\x1b\xe0!\xe2\xff\x81\xd0\xd0\xef\x10\x1a\xc7\xa8\x92=\x8b\x10\xfa\xe5\xc8\x9f" - "0BK\x9e\x61.>\xae\xac\xa9\x87M\xdb\xf1\x17\x41\xba\x33\xd5\b\x82\xd6\x62\xa1" - "ppp0w\xb0\x30\xe7z{\x85\xf9[\xb7n\x15\x8a\xa5\x42\xa9\x34\v\x1a\xb3\xfe\x91\x13" - "\xd0\xc3Y\xc7\xbf:\xb5\x1c;\xa8\x30_\xf7\xacV`\xb9\x8e\x1a\xc9\xb4\xee\xb6\x83" - ";SS]\xaf\x86\xdes\xdaj{v\xec\xd2\xd0\v\xcc\x66M\xe6\4~a~n\x1e\x1c\x19\xfa\xa2" - "\xe9zM\x1a,\xd3V\xcb\xb6t\x1a\xb9+\x1c\xce\xfa\rW\x7f}@\xf7\xd9\xaciS\xbf\xb1" - "T\xe8+F6\x81\x15\xd8ly\xc5p\xebL]\xb3\xd9\xa1zC]\xe9\xdb\xc7\xda\x1d\x95H\xd9" - "\xe8\a\xba|*M\x1aY\xcf\xe9n\xb3\xd0\xf2\\\xa3\xad\x43L&\xb8\x8a\x8dO\x9b\x44" - ".Z\xed\xbam\xf9\r\xe6-\xb7\x9d\xd7\x8e{\xd0\xd9\xa2\x8f\x46:\xba\xc7h\xe0\xe" - "j|\xc0\xa2u\x9b:{m\xba\xc7\x96W\xb7\xe2\xb5\x9e\x1c\xc7H\3\xb6\\\xd5\3u\xa1\xa8" - "\xa9\xa5\xe2\xfc\x42'\x8c\b]*|\xa7\xda]\4\xep\x19U2\xffJ,\xa1\x32\xf7\xfe\xfd" - "\xfbgJ\2NX\x84\x9b\x17\x7f\xfb\f\xc5\x97\xf0\x9b\x7f+\xf7\xe0\xc4\xff\xa2\xe8\x8d" - "BXO=hNf\xf4uG!\xbe\xef\x81R\x93\5\xb4\x42\3:u\xddl;z<\x17\xd7\xe3\xa2i*\xf5\xf6" - "|\xa0\xfd\f\xa7\xd5_(7T\xd5\x63?k[\x1e\xbb\xfe\xd5\xa9\xa5\x17\x9a\xda\xb7\x1f" - "\b*2\x9aV\xe1\xfa\xe1POo6}[\x11U\xf5\x1bxp\xdfL\xffh@\xa7\xca\x1c\xf9\xd6Tu\x9f" - "z\x83\xde\xd5;\xbd Ok\xdfV\xa6\xfe\xcf\x30:\xdd\xf3\x84y>\xec\xdfm%\xff\xc8\xf" - "X3U\xa6-Z\xb7l+\xb0\x98O\xf6;\x1a\"\xb5\x83G\xec\xe8rwP\xfb\x43\xba\xb2\xbdP" - "(\x15\x8b\x37\v\xf5\xb6\x65\a\x96\x93\xec\xb8j\a\x96\xed\x8f\xe\x34\xd8\x62\xa7" - "\xc1&\a\xc1J\xe7\xd9i\xe9\xb2\v/3\xcb\x61\xde\xa5\x41\xa5u'`\x1e\x85R\xed\xb3" - "\x8e\xe2\xb9\xff\xe1\xe4\xfc\xe9\xb9X\x8cG\xa2\xdf\rD\xf<\x1b\x12\x91\xfc\x86" - "eF\x19\t+\x9eG\x8f\4?\x80\x91\xe7v6\4j[{\xeq\xdcm\x9d\xdaL\xf6#\n" - "\xd5\x65\xca\x86K\r\xe6\xad;\xa6\xcb{\xae\x1b(vOVZ4\x1aV\x88\xd0\x1f\n" - "\xbc\x36\x94\xd0\xa8z\xd6\x9e\xe5\xf8\xa2\x1f\xf!~.@xV\xc0=\x7f\xce\xbfr-\ak" - "\xd2\x36\xd3\xdbP\x8c\xa3$\xb5m\xf7\xa0\xe2\x36!k!z\xd5\x1e)\x9d\xf9\x8b\xf6" - "L\xc3\xe0\xf4GE\xda\x83\xcenX\xba/\xd7\x19\x38_\xb3l[\x89\xa3~j\x19\x41#\x19" - "\xb3\xf\x98\xb5\xd7\b$\xc3\xa3\a\x8f\xa1\x12\x90L\xab\x41%j\x18\xe5\x86\x65\x1b" - "Byc\xbd\xfc(\3\x62\\\x94\r\vB\x85Z+\x9b\xd5\xdd\xed\xd5\xaf\xabOV\x1f\xcb]vw" - "\xa7\vV\xaaO\xb7\xa4\xe\xbb\xfb\xc5P\xb7\xb0\x87\x90\xaa\x43\xed\xecj\x97\x89" - "O\xc7\xa4:KF;AFu\xaa\xbf\xe6\xa3\2\x89\xdb\x9d\x8e\x82)\xef\x35\xa4\4]\xd3\xb2" - "\xa1\\|\x10\xbd\xe\xe0]J\x1f\x43'\xb3\xa8n\xc2\x41\x94\x8b\xd8\x88\xd3\xe0\xa1" - "\xf6\xf6\xf0w\xce/=p\xd4\xd9\xef\x35\x46\xfe\x87\xfb)\x97\xc7\xf9\x44^\x19\x97" - "\5\x94\x9f\x12\xd0\x18\x96>\xcek\x13\xf2\xc4\xec\xc4\xdc\x44\x61\xa2\x38\x31" - "\x9f\x7f\xca\r\t\x98H2\xa7\xf0\xc9Tzh8S%\x98#\x1cO\xb0@\xb0H0!X\"X&\x89$\x11" - "RDH\x13\x61\x88\b\xc3\x44\xc8\x10!K\x84\x1c\x11\x46\x88\x30J\xf0\x19\x82\xf3" - "\4\x8f\x11\x61\x9c\bg\x89p\x8e\b\xe7\x89p\x81\x88\xd7\t\x9e&xF\x9e\xc1\4\x97" - "\xe4\x12\xd0\5y\1\x9f\x83\xe7\r\x82?!\xf8S\x82o\x91\xc4\"\xc1\xb7\t^\"\xf8\x8e" - "\xbc\x8c\xe5\x15,\x97\x31\x11W\t^#\xf2}y\b\xcb\xc3\xa0\xff\x80\xe0u\x82\x1f\x12" - "\xfc\x88\xe0\r\x82\x37\t\xde\"\x18\x82\xfd\x82\xe0\x9f\x10\xfc\x98\xe0m\x82w" - "\b\xde%\xf8\t\xc9~I\xf0O\x89\xf8\x15\xc1\xcf\x89\xbc\x42\xe4\x32\xc1\x35\x82" - ")\xc1u\xc2\xe9$a\x90\4#\t3\x1d}V8\xcc\x61$\xf5\xbe\x32\x38\xc1\xfd\x18\xe1#\"" - "\x80\x45\x42\x14#\xca\xe3\xf\x36P>\x1eqi\x19!\t\xc1w\x8c\xc7\x38\t4\x95J\xc5" - "4\x8dQb\b#~\x18#!\x83\x91\x98\xc5\x88\xe4`\xcf\x11\x8c\xe4Q\x8c\x94\x33\x18%" - "\x11\x9fG\x98\x9f\xc0\x63\x88\xe3\xd1\x38\x82\xbd\xce\x66\x13\x18\xa7\x30\x81" - "]\xd2(\x81%y&,\xbe\x84\xb3\xa3\xf9\x97\x63\xda\x45\xed\xd2[M}\xab]~\xab]yK\xc7" - "^\x8ekW\xb5\xc9\xb7\xdaG\xb1\x38~\x1f\x42\xe2!\xa6\x9d\xb0\xa8\xfeJ\xe7\xb5k" - "\xb5\t\xf3\xdc.~\x83\xff~\x9c\xcb!\xf4\xee\xd8\").attr(t);thi" - "s.setElement(r,false)}else{this.setElement(i.result(this,\"el\"),false)}}});" - "e.sync=function(t,r,s){var n=T[t];i.defaults(s||(s={}),{emulateHTTP:e.emulat" - "eHTTP,emulateJSON:e.emulateJSON});var a={type:n,dataType:\"json\"};if(!s.url" - "){a.url=i.result(r,\"url\")||M()}if(s.data==null&&r&&(t===\"create\"||t===\"" - "update\"||t===\"patch\")){a.contentType=\"application/json\";a.data=JSON.str" - "ingify(s.attrs||r.toJSON(s))}if(s.emulateJSON){a.contentType=\"application/x" - "-www-form-urlencoded\";a.data=a.data?{model:a.data}:{}}if(s.emulateHTTP&&(n=" - "==\"PUT\"||n===\"DELETE\"||n===\"PATCH\")){a.type=\"POST\";if(s.emulateJSON)" - "a.data._method=n;var o=s.beforeSend;s.beforeSend=function(t){t.setRequestHea" - "der(\"X-HTTP-Method-Override\",n);if(o)return o.apply(this,arguments)}}if(a." - "type!==\"GET\"&&!s.emulateJSON){a.processData=false}if(a.type===\"PATCH\"&&k" - "){a.xhr=function(){return new ActiveXObject(\"Microsoft.XMLHTTP\")}}var h=s." - "xhr=e.ajax(i.extend(a,s));r.trigger(\"request\",r,h,s);return h};var k=typeo" - "f window!==\"undefined\"&&!!window.ActiveXObject&&!(window.XMLHttpRequest&&(" - "new XMLHttpRequest).dispatchEvent);var T={create:\"POST\",update:\"PUT\",pat" - "ch:\"PATCH\",\"delete\":\"DELETE\",read:\"GET\"};e.ajax=function(){return e." - "$.ajax.apply(e.$,arguments)};var $=e.Router=function(t){t||(t={});if(t.route" - "s)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,argumen" - "ts)};var S=/\\((.*?)\\)/g;var H=/(\\(\\?)?:\\w+/g;var A=/\\*\\w+/g;var I=/[\\" - "-{}\\[\\]+?.,\\\\\\^$|#\\s]/g;i.extend($.prototype,u,{initialize:function(){" - "},route:function(t,r,s){if(!i.isRegExp(t))t=this._routeToRegExp(t);if(i.isFu" - "nction(r)){s=r;r=\"\"}if(!s)s=this[r];var n=this;e.history.route(t,function(" - "i){var a=n._extractParameters(t,i);n.execute(s,a);n.trigger.apply(n,[\"route" - ":\"+r].concat(a));n.trigger(\"route\",r,a);e.history.trigger(\"route\",n,r,a" - ")});return this},execute:function(t,e){if(t)t.apply(this,e)},navigate:functi" - "on(t,i){e.history.navigate(t,i);return this},_bindRoutes:function(){if(!this" - ".routes)return;this.routes=i.result(this,\"routes\");var t,e=i.keys(this.rou" - "tes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:" - "function(t){t=t.replace(I,\"\\\\$&\").replace(S,\"(?:$1)?\").replace(H,funct" - "ion(t,e){return e?t:\"([^/?]+)\"}).replace(A,\"([^?]*?)\");return new RegExp" - "(\"^\"+t+\"(?:\\\\?([\\\\s\\\\S]*))?$\")},_extractParameters:function(t,e){v" - "ar r=t.exec(e).slice(1);return i.map(r,function(t,e){if(e===r.length-1)retur" - "n t||null;return t?decodeURIComponent(t):null})}});var N=e.History=function(" - "){this.handlers=[];i.bindAll(this,\"checkUrl\");if(typeof window!==\"undefin" - "ed\"){this.location=window.location;this.history=window.history}};var R=/^[#" - "\\/]|\\s+$/g;var O=/^\\/+|\\/+$/g;var P=/msie [\\w.]+/;var C=/\\/$/;var j=/#" - ".*$/;N.started=false;i.extend(N.prototype,u,{interval:50,atRoot:function(){r" - "eturn this.location.pathname.replace(/[^\\/]$/,\"$&/\")===this.root},getHash" - ":function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:\"\"" - "},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._wantsH" - "ashChange||e){t=decodeURI(this.location.pathname+this.location.search);var i" - "=this.root.replace(C,\"\");if(!t.indexOf(i))t=t.slice(i.length)}else{t=this." - "getHash()}}return t.replace(R,\"\")},start:function(t){if(N.started)throw ne" - "w Error(\"Backbone.history has already been started\");N.started=true;this.o" - "ptions=i.extend({root:\"/\"},this.options,t);this.root=this.options.root;thi" - "s._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=!!th" - "is.options.pushState;this._hasPushState=!!(this.options.pushState&&this.hist" - "ory&&this.history.pushState);var r=this.getFragment();var s=document.documen" - "tMode;var n=P.exec(navigator.userAgent.toLowerCase())&&(!s||s<=7);this.root=" - "(\"/\"+this.root+\"/\").replace(O,\"/\");if(n&&this._wantsHashChange){var a=" - "e.$('