Add event listeners to RocksJava (#7425)
Summary: Allows adding event listeners in RocksJava. * Adds listeners getter and setter in `Options` and `DBOptions` classes. * Adds `EventListener` Java interface and base class for implementing custom event listener callbacks - `AbstractEventListener`, which has an underlying native callback class implementing C++ `EventListener` class. * `AbstractEventListener` class has mechanism for selectively enabling its callback methods in order to prevent invoking Java method if it is not implemented. This decreases performance cost in case only subset of event listener callback methods is needed - the JNI code for remaining "no-op" callbacks is not executed. * The code is covered by unit tests in `EventListenerTest.java`, there are also tests added for setting/getting listeners field in `OptionsTest.java` and `DBOptionsTest.java`. Pull Request resolved: https://github.com/facebook/rocksdb/pull/7425 Reviewed By: pdillinger Differential Revision: D24063390 Pulled By: jay-zhuang fbshipit-source-id: 508c359538983d6b765e70d9989c351794a944eemain
parent
b99fe1ab74
commit
6528ecc800
@ -0,0 +1,43 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// This file implements the "bridge" between Java and C++ for
|
||||
// rocksdb::EventListener.
|
||||
|
||||
#include <jni.h> |
||||
|
||||
#include <memory> |
||||
|
||||
#include "include/org_rocksdb_AbstractEventListener.h" |
||||
#include "rocksjni/event_listener_jnicallback.h" |
||||
#include "rocksjni/portal.h" |
||||
|
||||
/*
|
||||
* Class: org_rocksdb_AbstractEventListener |
||||
* Method: createNewEventListener |
||||
* Signature: (J)J |
||||
*/ |
||||
jlong Java_org_rocksdb_AbstractEventListener_createNewEventListener( |
||||
JNIEnv* env, jobject jobj, jlong jenabled_event_callback_values) { |
||||
auto enabled_event_callbacks = |
||||
ROCKSDB_NAMESPACE::EnabledEventCallbackJni::toCppEnabledEventCallbacks( |
||||
jenabled_event_callback_values); |
||||
auto* sptr_event_listener = |
||||
new std::shared_ptr<ROCKSDB_NAMESPACE::EventListener>( |
||||
new ROCKSDB_NAMESPACE::EventListenerJniCallback( |
||||
env, jobj, enabled_event_callbacks)); |
||||
return reinterpret_cast<jlong>(sptr_event_listener); |
||||
} |
||||
|
||||
/*
|
||||
* Class: org_rocksdb_AbstractEventListener |
||||
* Method: disposeInternal |
||||
* Signature: (J)V |
||||
*/ |
||||
void Java_org_rocksdb_AbstractEventListener_disposeInternal(JNIEnv*, jobject, |
||||
jlong jhandle) { |
||||
delete reinterpret_cast<std::shared_ptr<ROCKSDB_NAMESPACE::EventListener>*>( |
||||
jhandle); |
||||
} |
@ -0,0 +1,502 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// This file implements the callback "bridge" between Java and C++ for
|
||||
// rocksdb::EventListener.
|
||||
|
||||
#include "rocksjni/event_listener_jnicallback.h" |
||||
|
||||
#include "rocksjni/portal.h" |
||||
|
||||
namespace rocksdb { |
||||
EventListenerJniCallback::EventListenerJniCallback( |
||||
JNIEnv* env, jobject jevent_listener, |
||||
const std::set<EnabledEventCallback>& enabled_event_callbacks) |
||||
: JniCallback(env, jevent_listener), |
||||
m_enabled_event_callbacks(enabled_event_callbacks) { |
||||
InitCallbackMethodId( |
||||
m_on_flush_completed_proxy_mid, EnabledEventCallback::ON_FLUSH_COMPLETED, |
||||
env, AbstractEventListenerJni::getOnFlushCompletedProxyMethodId); |
||||
|
||||
InitCallbackMethodId(m_on_flush_begin_proxy_mid, |
||||
EnabledEventCallback::ON_FLUSH_BEGIN, env, |
||||
AbstractEventListenerJni::getOnFlushBeginProxyMethodId); |
||||
|
||||
InitCallbackMethodId(m_on_table_file_deleted_mid, |
||||
EnabledEventCallback::ON_TABLE_FILE_DELETED, env, |
||||
AbstractEventListenerJni::getOnTableFileDeletedMethodId); |
||||
|
||||
InitCallbackMethodId( |
||||
m_on_compaction_begin_proxy_mid, |
||||
EnabledEventCallback::ON_COMPACTION_BEGIN, env, |
||||
AbstractEventListenerJni::getOnCompactionBeginProxyMethodId); |
||||
|
||||
InitCallbackMethodId( |
||||
m_on_compaction_completed_proxy_mid, |
||||
EnabledEventCallback::ON_COMPACTION_COMPLETED, env, |
||||
AbstractEventListenerJni::getOnCompactionCompletedProxyMethodId); |
||||
|
||||
InitCallbackMethodId(m_on_table_file_created_mid, |
||||
EnabledEventCallback::ON_TABLE_FILE_CREATED, env, |
||||
AbstractEventListenerJni::getOnTableFileCreatedMethodId); |
||||
|
||||
InitCallbackMethodId( |
||||
m_on_table_file_creation_started_mid, |
||||
EnabledEventCallback::ON_TABLE_FILE_CREATION_STARTED, env, |
||||
AbstractEventListenerJni::getOnTableFileCreationStartedMethodId); |
||||
|
||||
InitCallbackMethodId(m_on_mem_table_sealed_mid, |
||||
EnabledEventCallback::ON_MEMTABLE_SEALED, env, |
||||
AbstractEventListenerJni::getOnMemTableSealedMethodId); |
||||
|
||||
InitCallbackMethodId( |
||||
m_on_column_family_handle_deletion_started_mid, |
||||
EnabledEventCallback::ON_COLUMN_FAMILY_HANDLE_DELETION_STARTED, env, |
||||
AbstractEventListenerJni::getOnColumnFamilyHandleDeletionStartedMethodId); |
||||
|
||||
InitCallbackMethodId( |
||||
m_on_external_file_ingested_proxy_mid, |
||||
EnabledEventCallback::ON_EXTERNAL_FILE_INGESTED, env, |
||||
AbstractEventListenerJni::getOnExternalFileIngestedProxyMethodId); |
||||
|
||||
InitCallbackMethodId( |
||||
m_on_background_error_proxy_mid, |
||||
EnabledEventCallback::ON_BACKGROUND_ERROR, env, |
||||
AbstractEventListenerJni::getOnBackgroundErrorProxyMethodId); |
||||
|
||||
InitCallbackMethodId( |
||||
m_on_stall_conditions_changed_mid, |
||||
EnabledEventCallback::ON_STALL_CONDITIONS_CHANGED, env, |
||||
AbstractEventListenerJni::getOnStallConditionsChangedMethodId); |
||||
|
||||
InitCallbackMethodId(m_on_file_read_finish_mid, |
||||
EnabledEventCallback::ON_FILE_READ_FINISH, env, |
||||
AbstractEventListenerJni::getOnFileReadFinishMethodId); |
||||
|
||||
InitCallbackMethodId(m_on_file_write_finish_mid, |
||||
EnabledEventCallback::ON_FILE_WRITE_FINISH, env, |
||||
AbstractEventListenerJni::getOnFileWriteFinishMethodId); |
||||
|
||||
InitCallbackMethodId(m_on_file_flush_finish_mid, |
||||
EnabledEventCallback::ON_FILE_FLUSH_FINISH, env, |
||||
AbstractEventListenerJni::getOnFileFlushFinishMethodId); |
||||
|
||||
InitCallbackMethodId(m_on_file_sync_finish_mid, |
||||
EnabledEventCallback::ON_FILE_SYNC_FINISH, env, |
||||
AbstractEventListenerJni::getOnFileSyncFinishMethodId); |
||||
|
||||
InitCallbackMethodId( |
||||
m_on_file_range_sync_finish_mid, |
||||
EnabledEventCallback::ON_FILE_RANGE_SYNC_FINISH, env, |
||||
AbstractEventListenerJni::getOnFileRangeSyncFinishMethodId); |
||||
|
||||
InitCallbackMethodId( |
||||
m_on_file_truncate_finish_mid, |
||||
EnabledEventCallback::ON_FILE_TRUNCATE_FINISH, env, |
||||
AbstractEventListenerJni::getOnFileTruncateFinishMethodId); |
||||
|
||||
InitCallbackMethodId(m_on_file_close_finish_mid, |
||||
EnabledEventCallback::ON_FILE_CLOSE_FINISH, env, |
||||
AbstractEventListenerJni::getOnFileCloseFinishMethodId); |
||||
|
||||
InitCallbackMethodId( |
||||
m_should_be_notified_on_file_io, |
||||
EnabledEventCallback::SHOULD_BE_NOTIFIED_ON_FILE_IO, env, |
||||
AbstractEventListenerJni::getShouldBeNotifiedOnFileIOMethodId); |
||||
|
||||
InitCallbackMethodId( |
||||
m_on_error_recovery_begin_proxy_mid, |
||||
EnabledEventCallback::ON_ERROR_RECOVERY_BEGIN, env, |
||||
AbstractEventListenerJni::getOnErrorRecoveryBeginProxyMethodId); |
||||
|
||||
InitCallbackMethodId( |
||||
m_on_error_recovery_completed_mid, |
||||
EnabledEventCallback::ON_ERROR_RECOVERY_COMPLETED, env, |
||||
AbstractEventListenerJni::getOnErrorRecoveryCompletedMethodId); |
||||
} |
||||
|
||||
EventListenerJniCallback::~EventListenerJniCallback() {} |
||||
|
||||
void EventListenerJniCallback::OnFlushCompleted( |
||||
DB* db, const FlushJobInfo& flush_job_info) { |
||||
if (m_on_flush_completed_proxy_mid == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
JNIEnv* env; |
||||
jboolean attached_thread; |
||||
jobject jflush_job_info = SetupCallbackInvocation<FlushJobInfo>( |
||||
env, attached_thread, flush_job_info, |
||||
FlushJobInfoJni::fromCppFlushJobInfo); |
||||
|
||||
if (jflush_job_info != nullptr) { |
||||
env->CallVoidMethod(m_jcallback_obj, m_on_flush_completed_proxy_mid, |
||||
reinterpret_cast<jlong>(db), jflush_job_info); |
||||
} |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {&jflush_job_info}); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnFlushBegin( |
||||
DB* db, const FlushJobInfo& flush_job_info) { |
||||
if (m_on_flush_begin_proxy_mid == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
JNIEnv* env; |
||||
jboolean attached_thread; |
||||
jobject jflush_job_info = SetupCallbackInvocation<FlushJobInfo>( |
||||
env, attached_thread, flush_job_info, |
||||
FlushJobInfoJni::fromCppFlushJobInfo); |
||||
|
||||
if (jflush_job_info != nullptr) { |
||||
env->CallVoidMethod(m_jcallback_obj, m_on_flush_begin_proxy_mid, |
||||
reinterpret_cast<jlong>(db), jflush_job_info); |
||||
} |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {&jflush_job_info}); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnTableFileDeleted( |
||||
const TableFileDeletionInfo& info) { |
||||
if (m_on_table_file_deleted_mid == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
JNIEnv* env; |
||||
jboolean attached_thread; |
||||
jobject jdeletion_info = SetupCallbackInvocation<TableFileDeletionInfo>( |
||||
env, attached_thread, info, |
||||
TableFileDeletionInfoJni::fromCppTableFileDeletionInfo); |
||||
|
||||
if (jdeletion_info != nullptr) { |
||||
env->CallVoidMethod(m_jcallback_obj, m_on_table_file_deleted_mid, |
||||
jdeletion_info); |
||||
} |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {&jdeletion_info}); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnCompactionBegin(DB* db, |
||||
const CompactionJobInfo& ci) { |
||||
if (m_on_compaction_begin_proxy_mid == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
JNIEnv* env; |
||||
jboolean attached_thread; |
||||
jobject jcompaction_job_info = SetupCallbackInvocation<CompactionJobInfo>( |
||||
env, attached_thread, ci, CompactionJobInfoJni::fromCppCompactionJobInfo); |
||||
|
||||
if (jcompaction_job_info != nullptr) { |
||||
env->CallVoidMethod(m_jcallback_obj, m_on_compaction_begin_proxy_mid, |
||||
reinterpret_cast<jlong>(db), jcompaction_job_info); |
||||
} |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {&jcompaction_job_info}); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnCompactionCompleted( |
||||
DB* db, const CompactionJobInfo& ci) { |
||||
if (m_on_compaction_completed_proxy_mid == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
JNIEnv* env; |
||||
jboolean attached_thread; |
||||
jobject jcompaction_job_info = SetupCallbackInvocation<CompactionJobInfo>( |
||||
env, attached_thread, ci, CompactionJobInfoJni::fromCppCompactionJobInfo); |
||||
|
||||
if (jcompaction_job_info != nullptr) { |
||||
env->CallVoidMethod(m_jcallback_obj, m_on_compaction_completed_proxy_mid, |
||||
reinterpret_cast<jlong>(db), jcompaction_job_info); |
||||
} |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {&jcompaction_job_info}); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnTableFileCreated( |
||||
const TableFileCreationInfo& info) { |
||||
if (m_on_table_file_created_mid == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
JNIEnv* env; |
||||
jboolean attached_thread; |
||||
jobject jfile_creation_info = SetupCallbackInvocation<TableFileCreationInfo>( |
||||
env, attached_thread, info, |
||||
TableFileCreationInfoJni::fromCppTableFileCreationInfo); |
||||
|
||||
if (jfile_creation_info != nullptr) { |
||||
env->CallVoidMethod(m_jcallback_obj, m_on_table_file_created_mid, |
||||
jfile_creation_info); |
||||
} |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {&jfile_creation_info}); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnTableFileCreationStarted( |
||||
const TableFileCreationBriefInfo& info) { |
||||
if (m_on_table_file_creation_started_mid == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
JNIEnv* env; |
||||
jboolean attached_thread; |
||||
jobject jcreation_brief_info = |
||||
SetupCallbackInvocation<TableFileCreationBriefInfo>( |
||||
env, attached_thread, info, |
||||
TableFileCreationBriefInfoJni::fromCppTableFileCreationBriefInfo); |
||||
|
||||
if (jcreation_brief_info != nullptr) { |
||||
env->CallVoidMethod(m_jcallback_obj, m_on_table_file_creation_started_mid, |
||||
jcreation_brief_info); |
||||
} |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {&jcreation_brief_info}); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnMemTableSealed(const MemTableInfo& info) { |
||||
if (m_on_mem_table_sealed_mid == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
JNIEnv* env; |
||||
jboolean attached_thread; |
||||
jobject jmem_table_info = SetupCallbackInvocation<MemTableInfo>( |
||||
env, attached_thread, info, MemTableInfoJni::fromCppMemTableInfo); |
||||
|
||||
if (jmem_table_info != nullptr) { |
||||
env->CallVoidMethod(m_jcallback_obj, m_on_mem_table_sealed_mid, |
||||
jmem_table_info); |
||||
} |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {&jmem_table_info}); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnColumnFamilyHandleDeletionStarted( |
||||
ColumnFamilyHandle* handle) { |
||||
if (m_on_column_family_handle_deletion_started_mid == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
JNIEnv* env; |
||||
jboolean attached_thread; |
||||
jobject jcf_handle = SetupCallbackInvocation<ColumnFamilyHandle>( |
||||
env, attached_thread, *handle, |
||||
ColumnFamilyHandleJni::fromCppColumnFamilyHandle); |
||||
|
||||
if (jcf_handle != nullptr) { |
||||
env->CallVoidMethod(m_jcallback_obj, |
||||
m_on_column_family_handle_deletion_started_mid, |
||||
jcf_handle); |
||||
} |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {&jcf_handle}); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnExternalFileIngested( |
||||
DB* db, const ExternalFileIngestionInfo& info) { |
||||
if (m_on_external_file_ingested_proxy_mid == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
JNIEnv* env; |
||||
jboolean attached_thread; |
||||
jobject jingestion_info = SetupCallbackInvocation<ExternalFileIngestionInfo>( |
||||
env, attached_thread, info, |
||||
ExternalFileIngestionInfoJni::fromCppExternalFileIngestionInfo); |
||||
|
||||
if (jingestion_info != nullptr) { |
||||
env->CallVoidMethod(m_jcallback_obj, m_on_external_file_ingested_proxy_mid, |
||||
reinterpret_cast<jlong>(db), jingestion_info); |
||||
} |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {&jingestion_info}); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnBackgroundError(BackgroundErrorReason reason, |
||||
Status* bg_error) { |
||||
if (m_on_background_error_proxy_mid == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
JNIEnv* env; |
||||
jboolean attached_thread; |
||||
jobject jstatus = SetupCallbackInvocation<Status>( |
||||
env, attached_thread, *bg_error, StatusJni::construct); |
||||
|
||||
if (jstatus != nullptr) { |
||||
env->CallVoidMethod(m_jcallback_obj, m_on_background_error_proxy_mid, |
||||
static_cast<jbyte>(reason), jstatus); |
||||
} |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {&jstatus}); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnStallConditionsChanged( |
||||
const WriteStallInfo& info) { |
||||
if (m_on_stall_conditions_changed_mid == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
JNIEnv* env; |
||||
jboolean attached_thread; |
||||
jobject jwrite_stall_info = SetupCallbackInvocation<WriteStallInfo>( |
||||
env, attached_thread, info, WriteStallInfoJni::fromCppWriteStallInfo); |
||||
|
||||
if (jwrite_stall_info != nullptr) { |
||||
env->CallVoidMethod(m_jcallback_obj, m_on_stall_conditions_changed_mid, |
||||
jwrite_stall_info); |
||||
} |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {&jwrite_stall_info}); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnFileReadFinish(const FileOperationInfo& info) { |
||||
OnFileOperation(m_on_file_read_finish_mid, info); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnFileWriteFinish( |
||||
const FileOperationInfo& info) { |
||||
OnFileOperation(m_on_file_write_finish_mid, info); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnFileFlushFinish( |
||||
const FileOperationInfo& info) { |
||||
OnFileOperation(m_on_file_flush_finish_mid, info); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnFileSyncFinish(const FileOperationInfo& info) { |
||||
OnFileOperation(m_on_file_sync_finish_mid, info); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnFileRangeSyncFinish( |
||||
const FileOperationInfo& info) { |
||||
OnFileOperation(m_on_file_range_sync_finish_mid, info); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnFileTruncateFinish( |
||||
const FileOperationInfo& info) { |
||||
OnFileOperation(m_on_file_truncate_finish_mid, info); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnFileCloseFinish( |
||||
const FileOperationInfo& info) { |
||||
OnFileOperation(m_on_file_close_finish_mid, info); |
||||
} |
||||
|
||||
bool EventListenerJniCallback::ShouldBeNotifiedOnFileIO() { |
||||
if (m_should_be_notified_on_file_io == nullptr) { |
||||
return false; |
||||
} |
||||
|
||||
jboolean attached_thread = JNI_FALSE; |
||||
JNIEnv* env = getJniEnv(&attached_thread); |
||||
assert(env != nullptr); |
||||
|
||||
jboolean jshould_be_notified = |
||||
env->CallBooleanMethod(m_jcallback_obj, m_should_be_notified_on_file_io); |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {}); |
||||
|
||||
return static_cast<bool>(jshould_be_notified); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnErrorRecoveryBegin( |
||||
BackgroundErrorReason reason, Status bg_error, bool* auto_recovery) { |
||||
if (m_on_error_recovery_begin_proxy_mid == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
JNIEnv* env; |
||||
jboolean attached_thread; |
||||
jobject jbg_error = SetupCallbackInvocation<Status>( |
||||
env, attached_thread, bg_error, StatusJni::construct); |
||||
|
||||
if (jbg_error != nullptr) { |
||||
jboolean jauto_recovery = env->CallBooleanMethod( |
||||
m_jcallback_obj, m_on_error_recovery_begin_proxy_mid, |
||||
static_cast<jbyte>(reason), jbg_error); |
||||
*auto_recovery = jauto_recovery == JNI_TRUE; |
||||
} |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {&jbg_error}); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnErrorRecoveryCompleted(Status old_bg_error) { |
||||
if (m_on_error_recovery_completed_mid == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
JNIEnv* env; |
||||
jboolean attached_thread; |
||||
jobject jold_bg_error = SetupCallbackInvocation<Status>( |
||||
env, attached_thread, old_bg_error, StatusJni::construct); |
||||
|
||||
if (jold_bg_error != nullptr) { |
||||
env->CallVoidMethod(m_jcallback_obj, m_on_error_recovery_completed_mid, |
||||
jold_bg_error); |
||||
} |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {&jold_bg_error}); |
||||
} |
||||
|
||||
void EventListenerJniCallback::InitCallbackMethodId( |
||||
jmethodID& mid, EnabledEventCallback eec, JNIEnv* env, |
||||
jmethodID (*get_id)(JNIEnv* env)) { |
||||
if (m_enabled_event_callbacks.count(eec) == 1) { |
||||
mid = get_id(env); |
||||
} else { |
||||
mid = nullptr; |
||||
} |
||||
} |
||||
|
||||
template <class T> |
||||
jobject EventListenerJniCallback::SetupCallbackInvocation( |
||||
JNIEnv*& env, jboolean& attached_thread, const T& cpp_obj, |
||||
jobject (*convert)(JNIEnv* env, const T* cpp_obj)) { |
||||
attached_thread = JNI_FALSE; |
||||
env = getJniEnv(&attached_thread); |
||||
assert(env != nullptr); |
||||
|
||||
return convert(env, &cpp_obj); |
||||
} |
||||
|
||||
void EventListenerJniCallback::CleanupCallbackInvocation( |
||||
JNIEnv* env, jboolean attached_thread, |
||||
std::initializer_list<jobject*> refs) { |
||||
for (auto* ref : refs) { |
||||
if (*ref == nullptr) continue; |
||||
env->DeleteLocalRef(*ref); |
||||
} |
||||
|
||||
if (env->ExceptionCheck()) { |
||||
// exception thrown from CallVoidMethod
|
||||
env->ExceptionDescribe(); // print out exception to stderr
|
||||
} |
||||
|
||||
releaseJniEnv(attached_thread); |
||||
} |
||||
|
||||
void EventListenerJniCallback::OnFileOperation(const jmethodID& mid, |
||||
const FileOperationInfo& info) { |
||||
if (mid == nullptr) { |
||||
return; |
||||
} |
||||
|
||||
JNIEnv* env; |
||||
jboolean attached_thread; |
||||
jobject jop_info = SetupCallbackInvocation<FileOperationInfo>( |
||||
env, attached_thread, info, |
||||
FileOperationInfoJni::fromCppFileOperationInfo); |
||||
|
||||
if (jop_info != nullptr) { |
||||
env->CallVoidMethod(m_jcallback_obj, mid, jop_info); |
||||
} |
||||
|
||||
CleanupCallbackInvocation(env, attached_thread, {&jop_info}); |
||||
} |
||||
} // namespace rocksdb
|
@ -0,0 +1,122 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// This file implements the callback "bridge" between Java and C++ for
|
||||
// rocksdb::EventListener.
|
||||
|
||||
#ifndef JAVA_ROCKSJNI_EVENT_LISTENER_JNICALLBACK_H_ |
||||
#define JAVA_ROCKSJNI_EVENT_LISTENER_JNICALLBACK_H_ |
||||
|
||||
#include <jni.h> |
||||
|
||||
#include <memory> |
||||
#include <set> |
||||
|
||||
#include "rocksdb/listener.h" |
||||
#include "rocksjni/jnicallback.h" |
||||
|
||||
namespace rocksdb { |
||||
|
||||
enum EnabledEventCallback { |
||||
ON_FLUSH_COMPLETED = 0x0, |
||||
ON_FLUSH_BEGIN = 0x1, |
||||
ON_TABLE_FILE_DELETED = 0x2, |
||||
ON_COMPACTION_BEGIN = 0x3, |
||||
ON_COMPACTION_COMPLETED = 0x4, |
||||
ON_TABLE_FILE_CREATED = 0x5, |
||||
ON_TABLE_FILE_CREATION_STARTED = 0x6, |
||||
ON_MEMTABLE_SEALED = 0x7, |
||||
ON_COLUMN_FAMILY_HANDLE_DELETION_STARTED = 0x8, |
||||
ON_EXTERNAL_FILE_INGESTED = 0x9, |
||||
ON_BACKGROUND_ERROR = 0xA, |
||||
ON_STALL_CONDITIONS_CHANGED = 0xB, |
||||
ON_FILE_READ_FINISH = 0xC, |
||||
ON_FILE_WRITE_FINISH = 0xD, |
||||
ON_FILE_FLUSH_FINISH = 0xE, |
||||
ON_FILE_SYNC_FINISH = 0xF, |
||||
ON_FILE_RANGE_SYNC_FINISH = 0x10, |
||||
ON_FILE_TRUNCATE_FINISH = 0x11, |
||||
ON_FILE_CLOSE_FINISH = 0x12, |
||||
SHOULD_BE_NOTIFIED_ON_FILE_IO = 0x13, |
||||
ON_ERROR_RECOVERY_BEGIN = 0x14, |
||||
ON_ERROR_RECOVERY_COMPLETED = 0x15, |
||||
|
||||
NUM_ENABLED_EVENT_CALLBACK = 0x16, |
||||
}; |
||||
|
||||
class EventListenerJniCallback : public JniCallback, public EventListener { |
||||
public: |
||||
EventListenerJniCallback( |
||||
JNIEnv* env, jobject jevent_listener, |
||||
const std::set<EnabledEventCallback>& enabled_event_callbacks); |
||||
virtual ~EventListenerJniCallback(); |
||||
virtual void OnFlushCompleted(DB* db, const FlushJobInfo& flush_job_info); |
||||
virtual void OnFlushBegin(DB* db, const FlushJobInfo& flush_job_info); |
||||
virtual void OnTableFileDeleted(const TableFileDeletionInfo& info); |
||||
virtual void OnCompactionBegin(DB* db, const CompactionJobInfo& ci); |
||||
virtual void OnCompactionCompleted(DB* db, const CompactionJobInfo& ci); |
||||
virtual void OnTableFileCreated(const TableFileCreationInfo& info); |
||||
virtual void OnTableFileCreationStarted( |
||||
const TableFileCreationBriefInfo& info); |
||||
virtual void OnMemTableSealed(const MemTableInfo& info); |
||||
virtual void OnColumnFamilyHandleDeletionStarted(ColumnFamilyHandle* handle); |
||||
virtual void OnExternalFileIngested(DB* db, |
||||
const ExternalFileIngestionInfo& info); |
||||
virtual void OnBackgroundError(BackgroundErrorReason reason, |
||||
Status* bg_error); |
||||
virtual void OnStallConditionsChanged(const WriteStallInfo& info); |
||||
virtual void OnFileReadFinish(const FileOperationInfo& info); |
||||
virtual void OnFileWriteFinish(const FileOperationInfo& info); |
||||
virtual void OnFileFlushFinish(const FileOperationInfo& info); |
||||
virtual void OnFileSyncFinish(const FileOperationInfo& info); |
||||
virtual void OnFileRangeSyncFinish(const FileOperationInfo& info); |
||||
virtual void OnFileTruncateFinish(const FileOperationInfo& info); |
||||
virtual void OnFileCloseFinish(const FileOperationInfo& info); |
||||
virtual bool ShouldBeNotifiedOnFileIO(); |
||||
virtual void OnErrorRecoveryBegin(BackgroundErrorReason reason, |
||||
Status bg_error, bool* auto_recovery); |
||||
virtual void OnErrorRecoveryCompleted(Status old_bg_error); |
||||
|
||||
private: |
||||
inline void InitCallbackMethodId(jmethodID& mid, EnabledEventCallback eec, |
||||
JNIEnv* env, |
||||
jmethodID (*get_id)(JNIEnv* env)); |
||||
template <class T> |
||||
inline jobject SetupCallbackInvocation( |
||||
JNIEnv*& env, jboolean& attached_thread, const T& cpp_obj, |
||||
jobject (*convert)(JNIEnv* env, const T* cpp_obj)); |
||||
inline void CleanupCallbackInvocation(JNIEnv* env, jboolean attached_thread, |
||||
std::initializer_list<jobject*> refs); |
||||
inline void OnFileOperation(const jmethodID& mid, |
||||
const FileOperationInfo& info); |
||||
|
||||
const std::set<EnabledEventCallback> m_enabled_event_callbacks; |
||||
jmethodID m_on_flush_completed_proxy_mid; |
||||
jmethodID m_on_flush_begin_proxy_mid; |
||||
jmethodID m_on_table_file_deleted_mid; |
||||
jmethodID m_on_compaction_begin_proxy_mid; |
||||
jmethodID m_on_compaction_completed_proxy_mid; |
||||
jmethodID m_on_table_file_created_mid; |
||||
jmethodID m_on_table_file_creation_started_mid; |
||||
jmethodID m_on_mem_table_sealed_mid; |
||||
jmethodID m_on_column_family_handle_deletion_started_mid; |
||||
jmethodID m_on_external_file_ingested_proxy_mid; |
||||
jmethodID m_on_background_error_proxy_mid; |
||||
jmethodID m_on_stall_conditions_changed_mid; |
||||
jmethodID m_on_file_read_finish_mid; |
||||
jmethodID m_on_file_write_finish_mid; |
||||
jmethodID m_on_file_flush_finish_mid; |
||||
jmethodID m_on_file_sync_finish_mid; |
||||
jmethodID m_on_file_range_sync_finish_mid; |
||||
jmethodID m_on_file_truncate_finish_mid; |
||||
jmethodID m_on_file_close_finish_mid; |
||||
jmethodID m_should_be_notified_on_file_io; |
||||
jmethodID m_on_error_recovery_begin_proxy_mid; |
||||
jmethodID m_on_error_recovery_completed_mid; |
||||
}; |
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // JAVA_ROCKSJNI_EVENT_LISTENER_JNICALLBACK_H_
|
@ -0,0 +1,189 @@ |
||||
#include <climits> |
||||
#include <utility> |
||||
|
||||
#include "include/org_rocksdb_test_TestableEventListener.h" |
||||
#include "rocksdb/listener.h" |
||||
|
||||
using namespace ROCKSDB_NAMESPACE; |
||||
|
||||
static TableProperties newTablePropertiesForTest() { |
||||
TableProperties table_properties; |
||||
table_properties.data_size = LLONG_MAX; |
||||
table_properties.index_size = LLONG_MAX; |
||||
table_properties.index_partitions = LLONG_MAX; |
||||
table_properties.top_level_index_size = LLONG_MAX; |
||||
table_properties.index_key_is_user_key = LLONG_MAX; |
||||
table_properties.index_value_is_delta_encoded = LLONG_MAX; |
||||
table_properties.filter_size = LLONG_MAX; |
||||
table_properties.raw_key_size = LLONG_MAX; |
||||
table_properties.raw_value_size = LLONG_MAX; |
||||
table_properties.num_data_blocks = LLONG_MAX; |
||||
table_properties.num_entries = LLONG_MAX; |
||||
table_properties.num_deletions = LLONG_MAX; |
||||
table_properties.num_merge_operands = LLONG_MAX; |
||||
table_properties.num_range_deletions = LLONG_MAX; |
||||
table_properties.format_version = LLONG_MAX; |
||||
table_properties.fixed_key_len = LLONG_MAX; |
||||
table_properties.column_family_id = LLONG_MAX; |
||||
table_properties.creation_time = LLONG_MAX; |
||||
table_properties.oldest_key_time = LLONG_MAX; |
||||
table_properties.file_creation_time = LLONG_MAX; |
||||
table_properties.db_id = "dbId"; |
||||
table_properties.db_session_id = "sessionId"; |
||||
table_properties.column_family_name = "columnFamilyName"; |
||||
table_properties.filter_policy_name = "filterPolicyName"; |
||||
table_properties.comparator_name = "comparatorName"; |
||||
table_properties.merge_operator_name = "mergeOperatorName"; |
||||
table_properties.prefix_extractor_name = "prefixExtractorName"; |
||||
table_properties.property_collectors_names = "propertyCollectorsNames"; |
||||
table_properties.compression_name = "compressionName"; |
||||
table_properties.compression_options = "compressionOptions"; |
||||
table_properties.user_collected_properties = {{"key", "value"}}; |
||||
table_properties.readable_properties = {{"key", "value"}}; |
||||
table_properties.properties_offsets = {{"key", LLONG_MAX}}; |
||||
return table_properties; |
||||
} |
||||
|
||||
/*
|
||||
* Class: org_rocksdb_test_TestableEventListener |
||||
* Method: invokeAllCallbacks |
||||
* Signature: (J)V |
||||
*/ |
||||
void Java_org_rocksdb_test_TestableEventListener_invokeAllCallbacks( |
||||
JNIEnv *, jclass, jlong jhandle) { |
||||
const auto &el = |
||||
*reinterpret_cast<std::shared_ptr<ROCKSDB_NAMESPACE::EventListener> *>( |
||||
jhandle); |
||||
|
||||
TableProperties table_properties = newTablePropertiesForTest(); |
||||
|
||||
FlushJobInfo flush_job_info; |
||||
flush_job_info.cf_id = INT_MAX; |
||||
flush_job_info.cf_name = "testColumnFamily"; |
||||
flush_job_info.file_path = "/file/path"; |
||||
flush_job_info.file_number = LLONG_MAX; |
||||
flush_job_info.oldest_blob_file_number = LLONG_MAX; |
||||
flush_job_info.thread_id = LLONG_MAX; |
||||
flush_job_info.job_id = INT_MAX; |
||||
flush_job_info.triggered_writes_slowdown = true; |
||||
flush_job_info.triggered_writes_stop = true; |
||||
flush_job_info.smallest_seqno = LLONG_MAX; |
||||
flush_job_info.largest_seqno = LLONG_MAX; |
||||
flush_job_info.table_properties = table_properties; |
||||
flush_job_info.flush_reason = FlushReason::kManualFlush; |
||||
|
||||
el->OnFlushCompleted(nullptr, flush_job_info); |
||||
el->OnFlushBegin(nullptr, flush_job_info); |
||||
|
||||
Status status = Status::Incomplete(Status::SubCode::kNoSpace); |
||||
|
||||
TableFileDeletionInfo file_deletion_info; |
||||
file_deletion_info.db_name = "dbName"; |
||||
file_deletion_info.file_path = "/file/path"; |
||||
file_deletion_info.job_id = INT_MAX; |
||||
file_deletion_info.status = status; |
||||
|
||||
el->OnTableFileDeleted(file_deletion_info); |
||||
|
||||
CompactionJobInfo compaction_job_info; |
||||
compaction_job_info.cf_id = INT_MAX; |
||||
compaction_job_info.cf_name = "compactionColumnFamily"; |
||||
compaction_job_info.status = status; |
||||
compaction_job_info.thread_id = LLONG_MAX; |
||||
compaction_job_info.job_id = INT_MAX; |
||||
compaction_job_info.base_input_level = INT_MAX; |
||||
compaction_job_info.output_level = INT_MAX; |
||||
compaction_job_info.input_files = {"inputFile.sst"}; |
||||
compaction_job_info.input_file_infos = {}; |
||||
compaction_job_info.output_files = {"outputFile.sst"}; |
||||
compaction_job_info.output_file_infos = {}; |
||||
compaction_job_info.table_properties = { |
||||
{"tableProperties", std::shared_ptr<TableProperties>( |
||||
&table_properties, [](TableProperties *) {})}}; |
||||
compaction_job_info.compaction_reason = CompactionReason::kFlush; |
||||
compaction_job_info.compression = CompressionType::kSnappyCompression; |
||||
|
||||
compaction_job_info.stats = CompactionJobStats(); |
||||
|
||||
el->OnCompactionBegin(nullptr, compaction_job_info); |
||||
el->OnCompactionCompleted(nullptr, compaction_job_info); |
||||
|
||||
TableFileCreationInfo file_creation_info; |
||||
file_creation_info.file_size = LLONG_MAX; |
||||
file_creation_info.table_properties = table_properties; |
||||
file_creation_info.status = status; |
||||
file_creation_info.file_checksum = "fileChecksum"; |
||||
file_creation_info.file_checksum_func_name = "fileChecksumFuncName"; |
||||
file_creation_info.db_name = "dbName"; |
||||
file_creation_info.cf_name = "columnFamilyName"; |
||||
file_creation_info.file_path = "/file/path"; |
||||
file_creation_info.job_id = INT_MAX; |
||||
file_creation_info.reason = TableFileCreationReason::kMisc; |
||||
|
||||
el->OnTableFileCreated(file_creation_info); |
||||
|
||||
TableFileCreationBriefInfo file_creation_brief_info; |
||||
file_creation_brief_info.db_name = "dbName"; |
||||
file_creation_brief_info.cf_name = "columnFamilyName"; |
||||
file_creation_brief_info.file_path = "/file/path"; |
||||
file_creation_brief_info.job_id = INT_MAX; |
||||
file_creation_brief_info.reason = TableFileCreationReason::kMisc; |
||||
|
||||
el->OnTableFileCreationStarted(file_creation_brief_info); |
||||
|
||||
MemTableInfo mem_table_info; |
||||
mem_table_info.cf_name = "columnFamilyName"; |
||||
mem_table_info.first_seqno = LLONG_MAX; |
||||
mem_table_info.earliest_seqno = LLONG_MAX; |
||||
mem_table_info.num_entries = LLONG_MAX; |
||||
mem_table_info.num_deletes = LLONG_MAX; |
||||
|
||||
el->OnMemTableSealed(mem_table_info); |
||||
el->OnColumnFamilyHandleDeletionStarted(nullptr); |
||||
|
||||
ExternalFileIngestionInfo file_ingestion_info; |
||||
file_ingestion_info.cf_name = "columnFamilyName"; |
||||
file_ingestion_info.external_file_path = "/external/file/path"; |
||||
file_ingestion_info.internal_file_path = "/internal/file/path"; |
||||
file_ingestion_info.global_seqno = LLONG_MAX; |
||||
file_ingestion_info.table_properties = table_properties; |
||||
el->OnExternalFileIngested(nullptr, file_ingestion_info); |
||||
|
||||
el->OnBackgroundError(BackgroundErrorReason::kFlush, &status); |
||||
|
||||
WriteStallInfo write_stall_info; |
||||
write_stall_info.cf_name = "columnFamilyName"; |
||||
write_stall_info.condition.cur = WriteStallCondition::kDelayed; |
||||
write_stall_info.condition.prev = WriteStallCondition::kStopped; |
||||
el->OnStallConditionsChanged(write_stall_info); |
||||
|
||||
FileOperationInfo op_info = FileOperationInfo( |
||||
FileOperationType::kRead, "/file/path", |
||||
std::make_pair(std::chrono::time_point<std::chrono::system_clock, |
||||
std::chrono::nanoseconds>( |
||||
std::chrono::nanoseconds(1600699420000000000ll)), |
||||
std::chrono::time_point<std::chrono::steady_clock, |
||||
std::chrono::nanoseconds>( |
||||
std::chrono::nanoseconds(1600699420000000000ll))), |
||||
std::chrono::time_point<std::chrono::steady_clock, |
||||
std::chrono::nanoseconds>( |
||||
std::chrono::nanoseconds(1600699425000000000ll)), |
||||
status); |
||||
op_info.offset = LLONG_MAX; |
||||
op_info.length = LLONG_MAX; |
||||
op_info.status = status; |
||||
|
||||
el->OnFileReadFinish(op_info); |
||||
el->OnFileWriteFinish(op_info); |
||||
el->OnFileFlushFinish(op_info); |
||||
el->OnFileSyncFinish(op_info); |
||||
el->OnFileRangeSyncFinish(op_info); |
||||
el->OnFileTruncateFinish(op_info); |
||||
el->OnFileCloseFinish(op_info); |
||||
el->ShouldBeNotifiedOnFileIO(); |
||||
|
||||
bool auto_recovery; |
||||
el->OnErrorRecoveryBegin(BackgroundErrorReason::kFlush, status, |
||||
&auto_recovery); |
||||
el->OnErrorRecoveryCompleted(status); |
||||
} |
@ -0,0 +1,334 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
import static org.rocksdb.AbstractEventListener.EnabledEventCallback.*; |
||||
|
||||
/** |
||||
* Base class for Event Listeners. |
||||
*/ |
||||
public abstract class AbstractEventListener extends RocksCallbackObject implements EventListener { |
||||
public enum EnabledEventCallback { |
||||
ON_FLUSH_COMPLETED((byte) 0x0), |
||||
ON_FLUSH_BEGIN((byte) 0x1), |
||||
ON_TABLE_FILE_DELETED((byte) 0x2), |
||||
ON_COMPACTION_BEGIN((byte) 0x3), |
||||
ON_COMPACTION_COMPLETED((byte) 0x4), |
||||
ON_TABLE_FILE_CREATED((byte) 0x5), |
||||
ON_TABLE_FILE_CREATION_STARTED((byte) 0x6), |
||||
ON_MEMTABLE_SEALED((byte) 0x7), |
||||
ON_COLUMN_FAMILY_HANDLE_DELETION_STARTED((byte) 0x8), |
||||
ON_EXTERNAL_FILE_INGESTED((byte) 0x9), |
||||
ON_BACKGROUND_ERROR((byte) 0xA), |
||||
ON_STALL_CONDITIONS_CHANGED((byte) 0xB), |
||||
ON_FILE_READ_FINISH((byte) 0xC), |
||||
ON_FILE_WRITE_FINISH((byte) 0xD), |
||||
ON_FILE_FLUSH_FINISH((byte) 0xE), |
||||
ON_FILE_SYNC_FINISH((byte) 0xF), |
||||
ON_FILE_RANGE_SYNC_FINISH((byte) 0x10), |
||||
ON_FILE_TRUNCATE_FINISH((byte) 0x11), |
||||
ON_FILE_CLOSE_FINISH((byte) 0x12), |
||||
SHOULD_BE_NOTIFIED_ON_FILE_IO((byte) 0x13), |
||||
ON_ERROR_RECOVERY_BEGIN((byte) 0x14), |
||||
ON_ERROR_RECOVERY_COMPLETED((byte) 0x15); |
||||
|
||||
private final byte value; |
||||
|
||||
EnabledEventCallback(final byte value) { |
||||
this.value = value; |
||||
} |
||||
|
||||
/** |
||||
* Get the internal representation value. |
||||
* |
||||
* @return the internal representation value |
||||
*/ |
||||
byte getValue() { |
||||
return value; |
||||
} |
||||
|
||||
/** |
||||
* Get the EnabledEventCallbacks from the internal representation value. |
||||
* |
||||
* @return the enabled event callback. |
||||
* |
||||
* @throws IllegalArgumentException if the value is unknown. |
||||
*/ |
||||
static EnabledEventCallback fromValue(final byte value) { |
||||
for (final EnabledEventCallback enabledEventCallback : EnabledEventCallback.values()) { |
||||
if (enabledEventCallback.value == value) { |
||||
return enabledEventCallback; |
||||
} |
||||
} |
||||
|
||||
throw new IllegalArgumentException( |
||||
"Illegal value provided for EnabledEventCallback: " + value); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates an Event Listener that will |
||||
* received all callbacks from C++. |
||||
* |
||||
* If you don't need all callbacks, it is much more efficient to |
||||
* just register for the ones you need by calling |
||||
* {@link #AbstractEventListener(EnabledEventCallback...)} instead. |
||||
*/ |
||||
protected AbstractEventListener() { |
||||
this(ON_FLUSH_COMPLETED, ON_FLUSH_BEGIN, ON_TABLE_FILE_DELETED, ON_COMPACTION_BEGIN, |
||||
ON_COMPACTION_COMPLETED, ON_TABLE_FILE_CREATED, ON_TABLE_FILE_CREATION_STARTED, |
||||
ON_MEMTABLE_SEALED, ON_COLUMN_FAMILY_HANDLE_DELETION_STARTED, ON_EXTERNAL_FILE_INGESTED, |
||||
ON_BACKGROUND_ERROR, ON_STALL_CONDITIONS_CHANGED, ON_FILE_READ_FINISH, ON_FILE_WRITE_FINISH, |
||||
ON_FILE_FLUSH_FINISH, ON_FILE_SYNC_FINISH, ON_FILE_RANGE_SYNC_FINISH, |
||||
ON_FILE_TRUNCATE_FINISH, ON_FILE_CLOSE_FINISH, SHOULD_BE_NOTIFIED_ON_FILE_IO, |
||||
ON_ERROR_RECOVERY_BEGIN, ON_ERROR_RECOVERY_COMPLETED); |
||||
} |
||||
|
||||
/** |
||||
* Creates an Event Listener that will |
||||
* receive only certain callbacks from C++. |
||||
* |
||||
* @param enabledEventCallbacks callbacks to enable in Java. |
||||
*/ |
||||
protected AbstractEventListener(final EnabledEventCallback... enabledEventCallbacks) { |
||||
super(packToLong(enabledEventCallbacks)); |
||||
} |
||||
|
||||
/** |
||||
* Pack EnabledEventCallbacks to a long. |
||||
* |
||||
* @param enabledEventCallbacks the flags |
||||
* |
||||
* @return a long |
||||
*/ |
||||
private static long packToLong(final EnabledEventCallback... enabledEventCallbacks) { |
||||
long l = 0; |
||||
for (int i = 0; i < enabledEventCallbacks.length; i++) { |
||||
l |= 1 << enabledEventCallbacks[i].getValue(); |
||||
} |
||||
return l; |
||||
} |
||||
|
||||
@Override |
||||
public void onFlushCompleted(final RocksDB db, final FlushJobInfo flushJobInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
/** |
||||
* Called from JNI, proxy for |
||||
* {@link #onFlushCompleted(RocksDB, FlushJobInfo)}. |
||||
* |
||||
* @param dbHandle native handle of the database |
||||
* @param flushJobInfo the flush job info |
||||
*/ |
||||
private void onFlushCompletedProxy(final long dbHandle, final FlushJobInfo flushJobInfo) { |
||||
final RocksDB db = new RocksDB(dbHandle); |
||||
db.disOwnNativeHandle(); // we don't own this!
|
||||
onFlushCompleted(db, flushJobInfo); |
||||
} |
||||
|
||||
@Override |
||||
public void onFlushBegin(final RocksDB db, final FlushJobInfo flushJobInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
/** |
||||
* Called from JNI, proxy for |
||||
* {@link #onFlushBegin(RocksDB, FlushJobInfo)}. |
||||
* |
||||
* @param dbHandle native handle of the database |
||||
* @param flushJobInfo the flush job info |
||||
*/ |
||||
private void onFlushBeginProxy(final long dbHandle, final FlushJobInfo flushJobInfo) { |
||||
final RocksDB db = new RocksDB(dbHandle); |
||||
db.disOwnNativeHandle(); // we don't own this!
|
||||
onFlushBegin(db, flushJobInfo); |
||||
} |
||||
|
||||
@Override |
||||
public void onTableFileDeleted(final TableFileDeletionInfo tableFileDeletionInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void onCompactionBegin(final RocksDB db, final CompactionJobInfo compactionJobInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
/** |
||||
* Called from JNI, proxy for |
||||
* {@link #onCompactionBegin(RocksDB, CompactionJobInfo)}. |
||||
* |
||||
* @param dbHandle native handle of the database |
||||
* @param compactionJobInfo the flush job info |
||||
*/ |
||||
private void onCompactionBeginProxy( |
||||
final long dbHandle, final CompactionJobInfo compactionJobInfo) { |
||||
final RocksDB db = new RocksDB(dbHandle); |
||||
db.disOwnNativeHandle(); // we don't own this!
|
||||
onCompactionBegin(db, compactionJobInfo); |
||||
} |
||||
|
||||
@Override |
||||
public void onCompactionCompleted(final RocksDB db, final CompactionJobInfo compactionJobInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
/** |
||||
* Called from JNI, proxy for |
||||
* {@link #onCompactionCompleted(RocksDB, CompactionJobInfo)}. |
||||
* |
||||
* @param dbHandle native handle of the database |
||||
* @param compactionJobInfo the flush job info |
||||
*/ |
||||
private void onCompactionCompletedProxy( |
||||
final long dbHandle, final CompactionJobInfo compactionJobInfo) { |
||||
final RocksDB db = new RocksDB(dbHandle); |
||||
db.disOwnNativeHandle(); // we don't own this!
|
||||
onCompactionCompleted(db, compactionJobInfo); |
||||
} |
||||
|
||||
@Override |
||||
public void onTableFileCreated(final TableFileCreationInfo tableFileCreationInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void onTableFileCreationStarted( |
||||
final TableFileCreationBriefInfo tableFileCreationBriefInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void onMemTableSealed(final MemTableInfo memTableInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void onColumnFamilyHandleDeletionStarted(final ColumnFamilyHandle columnFamilyHandle) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void onExternalFileIngested( |
||||
final RocksDB db, final ExternalFileIngestionInfo externalFileIngestionInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
/** |
||||
* Called from JNI, proxy for |
||||
* {@link #onExternalFileIngested(RocksDB, ExternalFileIngestionInfo)}. |
||||
* |
||||
* @param dbHandle native handle of the database |
||||
* @param externalFileIngestionInfo the flush job info |
||||
*/ |
||||
private void onExternalFileIngestedProxy( |
||||
final long dbHandle, final ExternalFileIngestionInfo externalFileIngestionInfo) { |
||||
final RocksDB db = new RocksDB(dbHandle); |
||||
db.disOwnNativeHandle(); // we don't own this!
|
||||
onExternalFileIngested(db, externalFileIngestionInfo); |
||||
} |
||||
|
||||
@Override |
||||
public void onBackgroundError( |
||||
final BackgroundErrorReason backgroundErrorReason, final Status backgroundError) { |
||||
// no-op
|
||||
} |
||||
|
||||
/** |
||||
* Called from JNI, proxy for |
||||
* {@link #onBackgroundError(BackgroundErrorReason, Status)}. |
||||
* |
||||
* @param reasonByte byte value representing error reason |
||||
* @param backgroundError status with error code |
||||
*/ |
||||
private void onBackgroundErrorProxy(final byte reasonByte, final Status backgroundError) { |
||||
onBackgroundError(BackgroundErrorReason.fromValue(reasonByte), backgroundError); |
||||
} |
||||
|
||||
@Override |
||||
public void onStallConditionsChanged(final WriteStallInfo writeStallInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void onFileReadFinish(final FileOperationInfo fileOperationInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void onFileWriteFinish(final FileOperationInfo fileOperationInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void OnFileFlushFinish(final FileOperationInfo fileOperationInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void OnFileSyncFinish(final FileOperationInfo fileOperationInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void OnFileRangeSyncFinish(final FileOperationInfo fileOperationInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void OnFileTruncateFinish(final FileOperationInfo fileOperationInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void OnFileCloseFinish(final FileOperationInfo fileOperationInfo) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public boolean shouldBeNotifiedOnFileIO() { |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public boolean onErrorRecoveryBegin( |
||||
final BackgroundErrorReason backgroundErrorReason, final Status backgroundError) { |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Called from JNI, proxy for |
||||
* {@link #onErrorRecoveryBegin(BackgroundErrorReason, Status)}. |
||||
* |
||||
* @param reasonByte byte value representing error reason |
||||
* @param backgroundError status with error code |
||||
*/ |
||||
private boolean onErrorRecoveryBeginProxy(final byte reasonByte, final Status backgroundError) { |
||||
return onErrorRecoveryBegin(BackgroundErrorReason.fromValue(reasonByte), backgroundError); |
||||
} |
||||
|
||||
@Override |
||||
public void onErrorRecoveryCompleted(final Status oldBackgroundError) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
protected long initializeNative(final long... nativeParameterHandles) { |
||||
return createNewEventListener(nativeParameterHandles[0]); |
||||
} |
||||
|
||||
/** |
||||
* Deletes underlying C++ native callback object pointer |
||||
*/ |
||||
@Override |
||||
protected void disposeInternal() { |
||||
disposeInternal(nativeHandle_); |
||||
} |
||||
|
||||
private native long createNewEventListener(final long enabledEventCallbackValues); |
||||
private native void disposeInternal(final long handle); |
||||
} |
@ -0,0 +1,46 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
public enum BackgroundErrorReason { |
||||
FLUSH((byte) 0x0), |
||||
COMPACTION((byte) 0x1), |
||||
WRITE_CALLBACK((byte) 0x2), |
||||
MEMTABLE((byte) 0x3); |
||||
|
||||
private final byte value; |
||||
|
||||
BackgroundErrorReason(final byte value) { |
||||
this.value = value; |
||||
} |
||||
|
||||
/** |
||||
* Get the internal representation. |
||||
* |
||||
* @return the internal representation |
||||
*/ |
||||
byte getValue() { |
||||
return value; |
||||
} |
||||
|
||||
/** |
||||
* Get the BackgroundErrorReason from the internal representation value. |
||||
* |
||||
* @return the background error reason. |
||||
* |
||||
* @throws IllegalArgumentException if the value is unknown. |
||||
*/ |
||||
static BackgroundErrorReason fromValue(final byte value) { |
||||
for (final BackgroundErrorReason backgroundErrorReason : BackgroundErrorReason.values()) { |
||||
if (backgroundErrorReason.value == value) { |
||||
return backgroundErrorReason; |
||||
} |
||||
} |
||||
|
||||
throw new IllegalArgumentException( |
||||
"Illegal value provided for BackgroundErrorReason: " + value); |
||||
} |
||||
} |
@ -0,0 +1,332 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* EventListener class contains a set of callback functions that will |
||||
* be called when specific RocksDB event happens such as flush. It can |
||||
* be used as a building block for developing custom features such as |
||||
* stats-collector or external compaction algorithm. |
||||
* |
||||
* Note that callback functions should not run for an extended period of |
||||
* time before the function returns, otherwise RocksDB may be blocked. |
||||
* For example, it is not suggested to do |
||||
* {@link RocksDB#compactFiles(CompactionOptions, ColumnFamilyHandle, List, int, int, |
||||
* CompactionJobInfo)} (as it may run for a long while) or issue many of |
||||
* {@link RocksDB#put(ColumnFamilyHandle, WriteOptions, byte[], byte[])} |
||||
* (as Put may be blocked in certain cases) in the same thread in the |
||||
* EventListener callback. |
||||
* |
||||
* However, doing |
||||
* {@link RocksDB#compactFiles(CompactionOptions, ColumnFamilyHandle, List, int, int, |
||||
* CompactionJobInfo)} and {@link RocksDB#put(ColumnFamilyHandle, WriteOptions, byte[], byte[])} in |
||||
* another thread is considered safe. |
||||
* |
||||
* [Threading] All EventListener callback will be called using the |
||||
* actual thread that involves in that specific event. For example, it |
||||
* is the RocksDB background flush thread that does the actual flush to |
||||
* call {@link #onFlushCompleted(RocksDB, FlushJobInfo)}. |
||||
* |
||||
* [Locking] All EventListener callbacks are designed to be called without |
||||
* the current thread holding any DB mutex. This is to prevent potential |
||||
* deadlock and performance issue when using EventListener callback |
||||
* in a complex way. |
||||
*/ |
||||
public interface EventListener { |
||||
/** |
||||
* A callback function to RocksDB which will be called before a |
||||
* RocksDB starts to flush memtables. |
||||
* |
||||
* Note that the this function must be implemented in a way such that |
||||
* it should not run for an extended period of time before the function |
||||
* returns. Otherwise, RocksDB may be blocked. |
||||
* |
||||
* @param db the database |
||||
* @param flushJobInfo the flush job info, contains data copied from |
||||
* respective native structure. |
||||
*/ |
||||
void onFlushBegin(final RocksDB db, final FlushJobInfo flushJobInfo); |
||||
|
||||
/** |
||||
* callback function to RocksDB which will be called whenever a |
||||
* registered RocksDB flushes a file. |
||||
* |
||||
* Note that the this function must be implemented in a way such that |
||||
* it should not run for an extended period of time before the function |
||||
* returns. Otherwise, RocksDB may be blocked. |
||||
* |
||||
* @param db the database |
||||
* @param flushJobInfo the flush job info, contains data copied from |
||||
* respective native structure. |
||||
*/ |
||||
void onFlushCompleted(final RocksDB db, final FlushJobInfo flushJobInfo); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called whenever |
||||
* a SST file is deleted. Different from |
||||
* {@link #onCompactionCompleted(RocksDB, CompactionJobInfo)} and |
||||
* {@link #onFlushCompleted(RocksDB, FlushJobInfo)}, |
||||
* this callback is designed for external logging |
||||
* service and thus only provide string parameters instead |
||||
* of a pointer to DB. Applications that build logic basic based |
||||
* on file creations and deletions is suggested to implement |
||||
* {@link #onFlushCompleted(RocksDB, FlushJobInfo)} and |
||||
* {@link #onCompactionCompleted(RocksDB, CompactionJobInfo)}. |
||||
* |
||||
* Note that if applications would like to use the passed reference |
||||
* outside this function call, they should make copies from the |
||||
* returned value. |
||||
* |
||||
* @param tableFileDeletionInfo the table file deletion info, |
||||
* contains data copied from respective native structure. |
||||
*/ |
||||
void onTableFileDeleted(final TableFileDeletionInfo tableFileDeletionInfo); |
||||
|
||||
/** |
||||
* A callback function to RocksDB which will be called before a |
||||
* RocksDB starts to compact. The default implementation is |
||||
* no-op. |
||||
* |
||||
* Note that the this function must be implemented in a way such that |
||||
* it should not run for an extended period of time before the function |
||||
* returns. Otherwise, RocksDB may be blocked. |
||||
* |
||||
* @param db a pointer to the rocksdb instance which just compacted |
||||
* a file. |
||||
* @param compactionJobInfo a reference to a native CompactionJobInfo struct, |
||||
* which is released after this function is returned, and must be copied |
||||
* if it is needed outside of this function. |
||||
*/ |
||||
void onCompactionBegin(final RocksDB db, final CompactionJobInfo compactionJobInfo); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called whenever |
||||
* a registered RocksDB compacts a file. The default implementation |
||||
* is a no-op. |
||||
* |
||||
* Note that this function must be implemented in a way such that |
||||
* it should not run for an extended period of time before the function |
||||
* returns. Otherwise, RocksDB may be blocked. |
||||
* |
||||
* @param db a pointer to the rocksdb instance which just compacted |
||||
* a file. |
||||
* @param compactionJobInfo a reference to a native CompactionJobInfo struct, |
||||
* which is released after this function is returned, and must be copied |
||||
* if it is needed outside of this function. |
||||
*/ |
||||
void onCompactionCompleted(final RocksDB db, final CompactionJobInfo compactionJobInfo); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called whenever |
||||
* a SST file is created. Different from OnCompactionCompleted and |
||||
* OnFlushCompleted, this callback is designed for external logging |
||||
* service and thus only provide string parameters instead |
||||
* of a pointer to DB. Applications that build logic basic based |
||||
* on file creations and deletions is suggested to implement |
||||
* OnFlushCompleted and OnCompactionCompleted. |
||||
* |
||||
* Historically it will only be called if the file is successfully created. |
||||
* Now it will also be called on failure case. User can check info.status |
||||
* to see if it succeeded or not. |
||||
* |
||||
* Note that if applications would like to use the passed reference |
||||
* outside this function call, they should make copies from these |
||||
* returned value. |
||||
* |
||||
* @param tableFileCreationInfo the table file creation info, |
||||
* contains data copied from respective native structure. |
||||
*/ |
||||
void onTableFileCreated(final TableFileCreationInfo tableFileCreationInfo); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called before |
||||
* a SST file is being created. It will follow by OnTableFileCreated after |
||||
* the creation finishes. |
||||
* |
||||
* Note that if applications would like to use the passed reference |
||||
* outside this function call, they should make copies from these |
||||
* returned value. |
||||
* |
||||
* @param tableFileCreationBriefInfo the table file creation brief info, |
||||
* contains data copied from respective native structure. |
||||
*/ |
||||
void onTableFileCreationStarted(final TableFileCreationBriefInfo tableFileCreationBriefInfo); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called before |
||||
* a memtable is made immutable. |
||||
* |
||||
* Note that the this function must be implemented in a way such that |
||||
* it should not run for an extended period of time before the function |
||||
* returns. Otherwise, RocksDB may be blocked. |
||||
* |
||||
* Note that if applications would like to use the passed reference |
||||
* outside this function call, they should make copies from these |
||||
* returned value. |
||||
* |
||||
* @param memTableInfo the mem table info, contains data |
||||
* copied from respective native structure. |
||||
*/ |
||||
void onMemTableSealed(final MemTableInfo memTableInfo); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called before |
||||
* a column family handle is deleted. |
||||
* |
||||
* Note that the this function must be implemented in a way such that |
||||
* it should not run for an extended period of time before the function |
||||
* returns. Otherwise, RocksDB may be blocked. |
||||
* |
||||
* @param columnFamilyHandle is a pointer to the column family handle to be |
||||
* deleted which will become a dangling pointer after the deletion. |
||||
*/ |
||||
void onColumnFamilyHandleDeletionStarted(final ColumnFamilyHandle columnFamilyHandle); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called after an external |
||||
* file is ingested using IngestExternalFile. |
||||
* |
||||
* Note that the this function will run on the same thread as |
||||
* IngestExternalFile(), if this function is blocked, IngestExternalFile() |
||||
* will be blocked from finishing. |
||||
* |
||||
* @param db the database |
||||
* @param externalFileIngestionInfo the external file ingestion info, |
||||
* contains data copied from respective native structure. |
||||
*/ |
||||
void onExternalFileIngested( |
||||
final RocksDB db, final ExternalFileIngestionInfo externalFileIngestionInfo); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called before setting the |
||||
* background error status to a non-OK value. The new background error status |
||||
* is provided in `bg_error` and can be modified by the callback. E.g., a |
||||
* callback can suppress errors by resetting it to Status::OK(), thus |
||||
* preventing the database from entering read-only mode. We do not provide any |
||||
* guarantee when failed flushes/compactions will be rescheduled if the user |
||||
* suppresses an error. |
||||
* |
||||
* Note that this function can run on the same threads as flush, compaction, |
||||
* and user writes. So, it is extremely important not to perform heavy |
||||
* computations or blocking calls in this function. |
||||
* |
||||
* @param backgroundErrorReason background error reason code |
||||
* @param backgroundError background error codes |
||||
*/ |
||||
void onBackgroundError( |
||||
final BackgroundErrorReason backgroundErrorReason, final Status backgroundError); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called whenever a change |
||||
* of superversion triggers a change of the stall conditions. |
||||
* |
||||
* Note that the this function must be implemented in a way such that |
||||
* it should not run for an extended period of time before the function |
||||
* returns. Otherwise, RocksDB may be blocked. |
||||
* |
||||
* @param writeStallInfo write stall info, |
||||
* contains data copied from respective native structure. |
||||
*/ |
||||
void onStallConditionsChanged(final WriteStallInfo writeStallInfo); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called whenever a file read |
||||
* operation finishes. |
||||
* |
||||
* @param fileOperationInfo file operation info, |
||||
* contains data copied from respective native structure. |
||||
*/ |
||||
void onFileReadFinish(final FileOperationInfo fileOperationInfo); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called whenever a file write |
||||
* operation finishes. |
||||
* |
||||
* @param fileOperationInfo file operation info, |
||||
* contains data copied from respective native structure. |
||||
*/ |
||||
void onFileWriteFinish(final FileOperationInfo fileOperationInfo); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called whenever a file flush |
||||
* operation finishes. |
||||
* |
||||
* @param fileOperationInfo file operation info, |
||||
* contains data copied from respective native structure. |
||||
*/ |
||||
void OnFileFlushFinish(final FileOperationInfo fileOperationInfo); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called whenever a file sync |
||||
* operation finishes. |
||||
* |
||||
* @param fileOperationInfo file operation info, |
||||
* contains data copied from respective native structure. |
||||
*/ |
||||
void OnFileSyncFinish(final FileOperationInfo fileOperationInfo); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called whenever a file |
||||
* rangeSync operation finishes. |
||||
* |
||||
* @param fileOperationInfo file operation info, |
||||
* contains data copied from respective native structure. |
||||
*/ |
||||
void OnFileRangeSyncFinish(final FileOperationInfo fileOperationInfo); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called whenever a file |
||||
* truncate operation finishes. |
||||
* |
||||
* @param fileOperationInfo file operation info, |
||||
* contains data copied from respective native structure. |
||||
*/ |
||||
void OnFileTruncateFinish(final FileOperationInfo fileOperationInfo); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called whenever a file close |
||||
* operation finishes. |
||||
* |
||||
* @param fileOperationInfo file operation info, |
||||
* contains data copied from respective native structure. |
||||
*/ |
||||
void OnFileCloseFinish(final FileOperationInfo fileOperationInfo); |
||||
|
||||
/** |
||||
* If true, the {@link #onFileReadFinish(FileOperationInfo)} |
||||
* and {@link #onFileWriteFinish(FileOperationInfo)} will be called. If |
||||
* false, then they won't be called. |
||||
* |
||||
* Default: false |
||||
*/ |
||||
boolean shouldBeNotifiedOnFileIO(); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called just before |
||||
* starting the automatic recovery process for recoverable background |
||||
* errors, such as NoSpace(). The callback can suppress the automatic |
||||
* recovery by setting returning false. The database will then |
||||
* have to be transitioned out of read-only mode by calling |
||||
* RocksDB#resume(). |
||||
* |
||||
* @param backgroundErrorReason background error reason code |
||||
* @param backgroundError background error codes |
||||
*/ |
||||
boolean onErrorRecoveryBegin( |
||||
final BackgroundErrorReason backgroundErrorReason, final Status backgroundError); |
||||
|
||||
/** |
||||
* A callback function for RocksDB which will be called once the database |
||||
* is recovered from read-only mode after an error. When this is called, it |
||||
* means normal writes to the database can be issued and the user can |
||||
* initiate any further recovery actions needed |
||||
* |
||||
* @param oldBackgroundError old background error codes |
||||
*/ |
||||
void onErrorRecoveryCompleted(final Status oldBackgroundError); |
||||
} |
@ -0,0 +1,103 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
public class ExternalFileIngestionInfo { |
||||
private final String columnFamilyName; |
||||
private final String externalFilePath; |
||||
private final String internalFilePath; |
||||
private final long globalSeqno; |
||||
private final TableProperties tableProperties; |
||||
|
||||
/** |
||||
* Access is package private as this will only be constructed from |
||||
* C++ via JNI and for testing. |
||||
*/ |
||||
ExternalFileIngestionInfo(final String columnFamilyName, final String externalFilePath, |
||||
final String internalFilePath, final long globalSeqno, |
||||
final TableProperties tableProperties) { |
||||
this.columnFamilyName = columnFamilyName; |
||||
this.externalFilePath = externalFilePath; |
||||
this.internalFilePath = internalFilePath; |
||||
this.globalSeqno = globalSeqno; |
||||
this.tableProperties = tableProperties; |
||||
} |
||||
|
||||
/** |
||||
* Get the name of the column family. |
||||
* |
||||
* @return the name of the column family. |
||||
*/ |
||||
public String getColumnFamilyName() { |
||||
return columnFamilyName; |
||||
} |
||||
|
||||
/** |
||||
* Get the path of the file outside the DB. |
||||
* |
||||
* @return the path of the file outside the DB. |
||||
*/ |
||||
public String getExternalFilePath() { |
||||
return externalFilePath; |
||||
} |
||||
|
||||
/** |
||||
* Get the path of the file inside the DB. |
||||
* |
||||
* @return the path of the file inside the DB. |
||||
*/ |
||||
public String getInternalFilePath() { |
||||
return internalFilePath; |
||||
} |
||||
|
||||
/** |
||||
* Get the global sequence number assigned to keys in this file. |
||||
* |
||||
* @return the global sequence number. |
||||
*/ |
||||
public long getGlobalSeqno() { |
||||
return globalSeqno; |
||||
} |
||||
|
||||
/** |
||||
* Get the Table properties of the table being flushed. |
||||
* |
||||
* @return the table properties. |
||||
*/ |
||||
public TableProperties getTableProperties() { |
||||
return tableProperties; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
if (this == o) |
||||
return true; |
||||
if (o == null || getClass() != o.getClass()) |
||||
return false; |
||||
ExternalFileIngestionInfo that = (ExternalFileIngestionInfo) o; |
||||
return globalSeqno == that.globalSeqno |
||||
&& Objects.equals(columnFamilyName, that.columnFamilyName) |
||||
&& Objects.equals(externalFilePath, that.externalFilePath) |
||||
&& Objects.equals(internalFilePath, that.internalFilePath) |
||||
&& Objects.equals(tableProperties, that.tableProperties); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return Objects.hash( |
||||
columnFamilyName, externalFilePath, internalFilePath, globalSeqno, tableProperties); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "ExternalFileIngestionInfo{" |
||||
+ "columnFamilyName='" + columnFamilyName + '\'' + ", externalFilePath='" + externalFilePath |
||||
+ '\'' + ", internalFilePath='" + internalFilePath + '\'' + ", globalSeqno=" + globalSeqno |
||||
+ ", tableProperties=" + tableProperties + '}'; |
||||
} |
||||
} |
@ -0,0 +1,112 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
/** |
||||
* Java representation of FileOperationInfo struct from include/rocksdb/listener.h |
||||
*/ |
||||
public class FileOperationInfo { |
||||
private final String path; |
||||
private final long offset; |
||||
private final long length; |
||||
private final long startTimestamp; |
||||
private final long duration; |
||||
private final Status status; |
||||
|
||||
/** |
||||
* Access is private as this will only be constructed from |
||||
* C++ via JNI. |
||||
*/ |
||||
FileOperationInfo(final String path, final long offset, final long length, |
||||
final long startTimestamp, final long duration, final Status status) { |
||||
this.path = path; |
||||
this.offset = offset; |
||||
this.length = length; |
||||
this.startTimestamp = startTimestamp; |
||||
this.duration = duration; |
||||
this.status = status; |
||||
} |
||||
|
||||
/** |
||||
* Get the file path. |
||||
* |
||||
* @return the file path. |
||||
*/ |
||||
public String getPath() { |
||||
return path; |
||||
} |
||||
|
||||
/** |
||||
* Get the offset. |
||||
* |
||||
* @return the offset. |
||||
*/ |
||||
public long getOffset() { |
||||
return offset; |
||||
} |
||||
|
||||
/** |
||||
* Get the length. |
||||
* |
||||
* @return the length. |
||||
*/ |
||||
public long getLength() { |
||||
return length; |
||||
} |
||||
|
||||
/** |
||||
* Get the start timestamp (in nanoseconds). |
||||
* |
||||
* @return the start timestamp. |
||||
*/ |
||||
public long getStartTimestamp() { |
||||
return startTimestamp; |
||||
} |
||||
|
||||
/** |
||||
* Get the operation duration (in nanoseconds). |
||||
* |
||||
* @return the operation duration. |
||||
*/ |
||||
public long getDuration() { |
||||
return duration; |
||||
} |
||||
|
||||
/** |
||||
* Get the status. |
||||
* |
||||
* @return the status. |
||||
*/ |
||||
public Status getStatus() { |
||||
return status; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
if (this == o) |
||||
return true; |
||||
if (o == null || getClass() != o.getClass()) |
||||
return false; |
||||
FileOperationInfo that = (FileOperationInfo) o; |
||||
return offset == that.offset && length == that.length && startTimestamp == that.startTimestamp |
||||
&& duration == that.duration && Objects.equals(path, that.path) |
||||
&& Objects.equals(status, that.status); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return Objects.hash(path, offset, length, startTimestamp, duration, status); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "FileOperationInfo{" |
||||
+ "path='" + path + '\'' + ", offset=" + offset + ", length=" + length + ", startTimestamp=" |
||||
+ startTimestamp + ", duration=" + duration + ", status=" + status + '}'; |
||||
} |
||||
} |
@ -0,0 +1,186 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
public class FlushJobInfo { |
||||
private final long columnFamilyId; |
||||
private final String columnFamilyName; |
||||
private final String filePath; |
||||
private final long threadId; |
||||
private final int jobId; |
||||
private final boolean triggeredWritesSlowdown; |
||||
private final boolean triggeredWritesStop; |
||||
private final long smallestSeqno; |
||||
private final long largestSeqno; |
||||
private final TableProperties tableProperties; |
||||
private final FlushReason flushReason; |
||||
|
||||
/** |
||||
* Access is package private as this will only be constructed from |
||||
* C++ via JNI and for testing. |
||||
*/ |
||||
FlushJobInfo(final long columnFamilyId, final String columnFamilyName, final String filePath, |
||||
final long threadId, final int jobId, final boolean triggeredWritesSlowdown, |
||||
final boolean triggeredWritesStop, final long smallestSeqno, final long largestSeqno, |
||||
final TableProperties tableProperties, final byte flushReasonValue) { |
||||
this.columnFamilyId = columnFamilyId; |
||||
this.columnFamilyName = columnFamilyName; |
||||
this.filePath = filePath; |
||||
this.threadId = threadId; |
||||
this.jobId = jobId; |
||||
this.triggeredWritesSlowdown = triggeredWritesSlowdown; |
||||
this.triggeredWritesStop = triggeredWritesStop; |
||||
this.smallestSeqno = smallestSeqno; |
||||
this.largestSeqno = largestSeqno; |
||||
this.tableProperties = tableProperties; |
||||
this.flushReason = FlushReason.fromValue(flushReasonValue); |
||||
} |
||||
|
||||
/** |
||||
* Get the id of the column family. |
||||
* |
||||
* @return the id of the column family |
||||
*/ |
||||
public long getColumnFamilyId() { |
||||
return columnFamilyId; |
||||
} |
||||
|
||||
/** |
||||
* Get the name of the column family. |
||||
* |
||||
* @return the name of the column family |
||||
*/ |
||||
public String getColumnFamilyName() { |
||||
return columnFamilyName; |
||||
} |
||||
|
||||
/** |
||||
* Get the path to the newly created file. |
||||
* |
||||
* @return the path to the newly created file |
||||
*/ |
||||
public String getFilePath() { |
||||
return filePath; |
||||
} |
||||
|
||||
/** |
||||
* Get the id of the thread that completed this flush job. |
||||
* |
||||
* @return the id of the thread that completed this flush job |
||||
*/ |
||||
public long getThreadId() { |
||||
return threadId; |
||||
} |
||||
|
||||
/** |
||||
* Get the job id, which is unique in the same thread. |
||||
* |
||||
* @return the job id |
||||
*/ |
||||
public int getJobId() { |
||||
return jobId; |
||||
} |
||||
|
||||
/** |
||||
* Determine if rocksdb is currently slowing-down all writes to prevent |
||||
* creating too many Level 0 files as compaction seems not able to |
||||
* catch up the write request speed. |
||||
* |
||||
* This indicates that there are too many files in Level 0. |
||||
* |
||||
* @return true if rocksdb is currently slowing-down all writes, |
||||
* false otherwise |
||||
*/ |
||||
public boolean isTriggeredWritesSlowdown() { |
||||
return triggeredWritesSlowdown; |
||||
} |
||||
|
||||
/** |
||||
* Determine if rocksdb is currently blocking any writes to prevent |
||||
* creating more L0 files. |
||||
* |
||||
* This indicates that there are too many files in level 0. |
||||
* Compactions should try to compact L0 files down to lower levels as soon |
||||
* as possible. |
||||
* |
||||
* @return true if rocksdb is currently blocking any writes, false otherwise |
||||
*/ |
||||
public boolean isTriggeredWritesStop() { |
||||
return triggeredWritesStop; |
||||
} |
||||
|
||||
/** |
||||
* Get the smallest sequence number in the newly created file. |
||||
* |
||||
* @return the smallest sequence number |
||||
*/ |
||||
public long getSmallestSeqno() { |
||||
return smallestSeqno; |
||||
} |
||||
|
||||
/** |
||||
* Get the largest sequence number in the newly created file. |
||||
* |
||||
* @return the largest sequence number |
||||
*/ |
||||
public long getLargestSeqno() { |
||||
return largestSeqno; |
||||
} |
||||
|
||||
/** |
||||
* Get the Table properties of the table being flushed. |
||||
* |
||||
* @return the Table properties of the table being flushed |
||||
*/ |
||||
public TableProperties getTableProperties() { |
||||
return tableProperties; |
||||
} |
||||
|
||||
/** |
||||
* Get the reason for initiating the flush. |
||||
* |
||||
* @return the reason for initiating the flush. |
||||
*/ |
||||
public FlushReason getFlushReason() { |
||||
return flushReason; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
if (this == o) |
||||
return true; |
||||
if (o == null || getClass() != o.getClass()) |
||||
return false; |
||||
FlushJobInfo that = (FlushJobInfo) o; |
||||
return columnFamilyId == that.columnFamilyId && threadId == that.threadId && jobId == that.jobId |
||||
&& triggeredWritesSlowdown == that.triggeredWritesSlowdown |
||||
&& triggeredWritesStop == that.triggeredWritesStop && smallestSeqno == that.smallestSeqno |
||||
&& largestSeqno == that.largestSeqno |
||||
&& Objects.equals(columnFamilyName, that.columnFamilyName) |
||||
&& Objects.equals(filePath, that.filePath) |
||||
&& Objects.equals(tableProperties, that.tableProperties) && flushReason == that.flushReason; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return Objects.hash(columnFamilyId, columnFamilyName, filePath, threadId, jobId, |
||||
triggeredWritesSlowdown, triggeredWritesStop, smallestSeqno, largestSeqno, tableProperties, |
||||
flushReason); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "FlushJobInfo{" |
||||
+ "columnFamilyId=" + columnFamilyId + ", columnFamilyName='" + columnFamilyName + '\'' |
||||
+ ", filePath='" + filePath + '\'' + ", threadId=" + threadId + ", jobId=" + jobId |
||||
+ ", triggeredWritesSlowdown=" + triggeredWritesSlowdown |
||||
+ ", triggeredWritesStop=" + triggeredWritesStop + ", smallestSeqno=" + smallestSeqno |
||||
+ ", largestSeqno=" + largestSeqno + ", tableProperties=" + tableProperties |
||||
+ ", flushReason=" + flushReason + '}'; |
||||
} |
||||
} |
@ -0,0 +1,53 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
public enum FlushReason { |
||||
OTHERS((byte) 0x00), |
||||
GET_LIVE_FILES((byte) 0x01), |
||||
SHUTDOWN((byte) 0x02), |
||||
EXTERNAL_FILE_INGESTION((byte) 0x03), |
||||
MANUAL_COMPACTION((byte) 0x04), |
||||
WRITE_BUFFER_MANAGER((byte) 0x05), |
||||
WRITE_BUFFER_FULL((byte) 0x06), |
||||
TEST((byte) 0x07), |
||||
DELETE_FILES((byte) 0x08), |
||||
AUTO_COMPACTION((byte) 0x09), |
||||
MANUAL_FLUSH((byte) 0x0a), |
||||
ERROR_RECOVERY((byte) 0xb); |
||||
|
||||
private final byte value; |
||||
|
||||
FlushReason(final byte value) { |
||||
this.value = value; |
||||
} |
||||
|
||||
/** |
||||
* Get the internal representation. |
||||
* |
||||
* @return the internal representation |
||||
*/ |
||||
byte getValue() { |
||||
return value; |
||||
} |
||||
|
||||
/** |
||||
* Get the FlushReason from the internal representation value. |
||||
* |
||||
* @return the flush reason. |
||||
* |
||||
* @throws IllegalArgumentException if the value is unknown. |
||||
*/ |
||||
static FlushReason fromValue(final byte value) { |
||||
for (final FlushReason flushReason : FlushReason.values()) { |
||||
if (flushReason.value == value) { |
||||
return flushReason; |
||||
} |
||||
} |
||||
|
||||
throw new IllegalArgumentException("Illegal value provided for FlushReason: " + value); |
||||
} |
||||
} |
@ -0,0 +1,103 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
public class MemTableInfo { |
||||
private final String columnFamilyName; |
||||
private final long firstSeqno; |
||||
private final long earliestSeqno; |
||||
private final long numEntries; |
||||
private final long numDeletes; |
||||
|
||||
/** |
||||
* Access is package private as this will only be constructed from |
||||
* C++ via JNI and for testing. |
||||
*/ |
||||
MemTableInfo(final String columnFamilyName, final long firstSeqno, final long earliestSeqno, |
||||
final long numEntries, final long numDeletes) { |
||||
this.columnFamilyName = columnFamilyName; |
||||
this.firstSeqno = firstSeqno; |
||||
this.earliestSeqno = earliestSeqno; |
||||
this.numEntries = numEntries; |
||||
this.numDeletes = numDeletes; |
||||
} |
||||
|
||||
/** |
||||
* Get the name of the column family to which memtable belongs. |
||||
* |
||||
* @return the name of the column family. |
||||
*/ |
||||
public String getColumnFamilyName() { |
||||
return columnFamilyName; |
||||
} |
||||
|
||||
/** |
||||
* Get the Sequence number of the first element that was inserted into the |
||||
* memtable. |
||||
* |
||||
* @return the sequence number of the first inserted element. |
||||
*/ |
||||
public long getFirstSeqno() { |
||||
return firstSeqno; |
||||
} |
||||
|
||||
/** |
||||
* Get the Sequence number that is guaranteed to be smaller than or equal |
||||
* to the sequence number of any key that could be inserted into this |
||||
* memtable. It can then be assumed that any write with a larger(or equal) |
||||
* sequence number will be present in this memtable or a later memtable. |
||||
* |
||||
* @return the earliest sequence number. |
||||
*/ |
||||
public long getEarliestSeqno() { |
||||
return earliestSeqno; |
||||
} |
||||
|
||||
/** |
||||
* Get the total number of entries in memtable. |
||||
* |
||||
* @return the total number of entries. |
||||
*/ |
||||
public long getNumEntries() { |
||||
return numEntries; |
||||
} |
||||
|
||||
/** |
||||
* Get the total number of deletes in memtable. |
||||
* |
||||
* @return the total number of deletes. |
||||
*/ |
||||
public long getNumDeletes() { |
||||
return numDeletes; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
if (this == o) |
||||
return true; |
||||
if (o == null || getClass() != o.getClass()) |
||||
return false; |
||||
MemTableInfo that = (MemTableInfo) o; |
||||
return firstSeqno == that.firstSeqno && earliestSeqno == that.earliestSeqno |
||||
&& numEntries == that.numEntries && numDeletes == that.numDeletes |
||||
&& Objects.equals(columnFamilyName, that.columnFamilyName); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return Objects.hash(columnFamilyName, firstSeqno, earliestSeqno, numEntries, numDeletes); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "MemTableInfo{" |
||||
+ "columnFamilyName='" + columnFamilyName + '\'' + ", firstSeqno=" + firstSeqno |
||||
+ ", earliestSeqno=" + earliestSeqno + ", numEntries=" + numEntries |
||||
+ ", numDeletes=" + numDeletes + '}'; |
||||
} |
||||
} |
@ -0,0 +1,107 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
public class TableFileCreationBriefInfo { |
||||
private final String dbName; |
||||
private final String columnFamilyName; |
||||
private final String filePath; |
||||
private final int jobId; |
||||
private final TableFileCreationReason reason; |
||||
|
||||
/** |
||||
* Access is private as this will only be constructed from |
||||
* C++ via JNI, either directly of via |
||||
* {@link TableFileCreationInfo#TableFileCreationInfo(long, TableProperties, Status, String, |
||||
* String, String, int, byte)}. |
||||
* |
||||
* @param dbName the database name |
||||
* @param columnFamilyName the column family name |
||||
* @param filePath the path to the table file |
||||
* @param jobId the job identifier |
||||
* @param tableFileCreationReasonValue the reason for creation of the table file |
||||
*/ |
||||
protected TableFileCreationBriefInfo(final String dbName, final String columnFamilyName, |
||||
final String filePath, final int jobId, final byte tableFileCreationReasonValue) { |
||||
this.dbName = dbName; |
||||
this.columnFamilyName = columnFamilyName; |
||||
this.filePath = filePath; |
||||
this.jobId = jobId; |
||||
this.reason = TableFileCreationReason.fromValue(tableFileCreationReasonValue); |
||||
} |
||||
|
||||
/** |
||||
* Get the name of the database where the file was created. |
||||
* |
||||
* @return the name of the database. |
||||
*/ |
||||
public String getDbName() { |
||||
return dbName; |
||||
} |
||||
|
||||
/** |
||||
* Get the name of the column family where the file was created. |
||||
* |
||||
* @return the name of the column family. |
||||
*/ |
||||
public String getColumnFamilyName() { |
||||
return columnFamilyName; |
||||
} |
||||
|
||||
/** |
||||
* Get the path to the created file. |
||||
* |
||||
* @return the path. |
||||
*/ |
||||
public String getFilePath() { |
||||
return filePath; |
||||
} |
||||
|
||||
/** |
||||
* Get the id of the job (which could be flush or compaction) that |
||||
* created the file. |
||||
* |
||||
* @return the id of the job. |
||||
*/ |
||||
public int getJobId() { |
||||
return jobId; |
||||
} |
||||
|
||||
/** |
||||
* Get the reason for creating the table. |
||||
* |
||||
* @return the reason for creating the table. |
||||
*/ |
||||
public TableFileCreationReason getReason() { |
||||
return reason; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
if (this == o) |
||||
return true; |
||||
if (o == null || getClass() != o.getClass()) |
||||
return false; |
||||
TableFileCreationBriefInfo that = (TableFileCreationBriefInfo) o; |
||||
return jobId == that.jobId && Objects.equals(dbName, that.dbName) |
||||
&& Objects.equals(columnFamilyName, that.columnFamilyName) |
||||
&& Objects.equals(filePath, that.filePath) && reason == that.reason; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return Objects.hash(dbName, columnFamilyName, filePath, jobId, reason); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "TableFileCreationBriefInfo{" |
||||
+ "dbName='" + dbName + '\'' + ", columnFamilyName='" + columnFamilyName + '\'' |
||||
+ ", filePath='" + filePath + '\'' + ", jobId=" + jobId + ", reason=" + reason + '}'; |
||||
} |
||||
} |
@ -0,0 +1,86 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
public class TableFileCreationInfo extends TableFileCreationBriefInfo { |
||||
private final long fileSize; |
||||
private final TableProperties tableProperties; |
||||
private final Status status; |
||||
|
||||
/** |
||||
* Access is protected as this will only be constructed from |
||||
* C++ via JNI. |
||||
* |
||||
* @param fileSize the size of the table file |
||||
* @param tableProperties the properties of the table file |
||||
* @param status the status of the creation operation |
||||
* @param dbName the database name |
||||
* @param columnFamilyName the column family name |
||||
* @param filePath the path to the table file |
||||
* @param jobId the job identifier |
||||
* @param tableFileCreationReasonValue the reason for creation of the table file |
||||
*/ |
||||
protected TableFileCreationInfo(final long fileSize, final TableProperties tableProperties, |
||||
final Status status, final String dbName, final String columnFamilyName, |
||||
final String filePath, final int jobId, final byte tableFileCreationReasonValue) { |
||||
super(dbName, columnFamilyName, filePath, jobId, tableFileCreationReasonValue); |
||||
this.fileSize = fileSize; |
||||
this.tableProperties = tableProperties; |
||||
this.status = status; |
||||
} |
||||
|
||||
/** |
||||
* Get the size of the file. |
||||
* |
||||
* @return the size. |
||||
*/ |
||||
public long getFileSize() { |
||||
return fileSize; |
||||
} |
||||
|
||||
/** |
||||
* Get the detailed properties of the created file. |
||||
* |
||||
* @return the properties. |
||||
*/ |
||||
public TableProperties getTableProperties() { |
||||
return tableProperties; |
||||
} |
||||
|
||||
/** |
||||
* Get the status indicating whether the creation was successful or not. |
||||
* |
||||
* @return the status. |
||||
*/ |
||||
public Status getStatus() { |
||||
return status; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
if (this == o) |
||||
return true; |
||||
if (o == null || getClass() != o.getClass()) |
||||
return false; |
||||
TableFileCreationInfo that = (TableFileCreationInfo) o; |
||||
return fileSize == that.fileSize && Objects.equals(tableProperties, that.tableProperties) |
||||
&& Objects.equals(status, that.status); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return Objects.hash(fileSize, tableProperties, status); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "TableFileCreationInfo{" |
||||
+ "fileSize=" + fileSize + ", tableProperties=" + tableProperties + ", status=" + status |
||||
+ '}'; |
||||
} |
||||
} |
@ -0,0 +1,46 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
public enum TableFileCreationReason { |
||||
FLUSH((byte) 0x00), |
||||
COMPACTION((byte) 0x01), |
||||
RECOVERY((byte) 0x02), |
||||
MISC((byte) 0x03); |
||||
|
||||
private final byte value; |
||||
|
||||
TableFileCreationReason(final byte value) { |
||||
this.value = value; |
||||
} |
||||
|
||||
/** |
||||
* Get the internal representation. |
||||
* |
||||
* @return the internal representation |
||||
*/ |
||||
byte getValue() { |
||||
return value; |
||||
} |
||||
|
||||
/** |
||||
* Get the TableFileCreationReason from the internal representation value. |
||||
* |
||||
* @return the table file creation reason. |
||||
* |
||||
* @throws IllegalArgumentException if the value is unknown. |
||||
*/ |
||||
static TableFileCreationReason fromValue(final byte value) { |
||||
for (final TableFileCreationReason tableFileCreationReason : TableFileCreationReason.values()) { |
||||
if (tableFileCreationReason.value == value) { |
||||
return tableFileCreationReason; |
||||
} |
||||
} |
||||
|
||||
throw new IllegalArgumentException( |
||||
"Illegal value provided for TableFileCreationReason: " + value); |
||||
} |
||||
} |
@ -0,0 +1,86 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
public class TableFileDeletionInfo { |
||||
private final String dbName; |
||||
private final String filePath; |
||||
private final int jobId; |
||||
private final Status status; |
||||
|
||||
/** |
||||
* Access is package private as this will only be constructed from |
||||
* C++ via JNI and for testing. |
||||
*/ |
||||
TableFileDeletionInfo( |
||||
final String dbName, final String filePath, final int jobId, final Status status) { |
||||
this.dbName = dbName; |
||||
this.filePath = filePath; |
||||
this.jobId = jobId; |
||||
this.status = status; |
||||
} |
||||
|
||||
/** |
||||
* Get the name of the database where the file was deleted. |
||||
* |
||||
* @return the name of the database. |
||||
*/ |
||||
public String getDbName() { |
||||
return dbName; |
||||
} |
||||
|
||||
/** |
||||
* Get the path to the deleted file. |
||||
* |
||||
* @return the path. |
||||
*/ |
||||
public String getFilePath() { |
||||
return filePath; |
||||
} |
||||
|
||||
/** |
||||
* Get the id of the job which deleted the file. |
||||
* |
||||
* @return the id of the job. |
||||
*/ |
||||
public int getJobId() { |
||||
return jobId; |
||||
} |
||||
|
||||
/** |
||||
* Get the status indicating whether the deletion was successful or not. |
||||
* |
||||
* @return the status |
||||
*/ |
||||
public Status getStatus() { |
||||
return status; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
if (this == o) |
||||
return true; |
||||
if (o == null || getClass() != o.getClass()) |
||||
return false; |
||||
TableFileDeletionInfo that = (TableFileDeletionInfo) o; |
||||
return jobId == that.jobId && Objects.equals(dbName, that.dbName) |
||||
&& Objects.equals(filePath, that.filePath) && Objects.equals(status, that.status); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return Objects.hash(dbName, filePath, jobId, status); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "TableFileDeletionInfo{" |
||||
+ "dbName='" + dbName + '\'' + ", filePath='" + filePath + '\'' + ", jobId=" + jobId |
||||
+ ", status=" + status + '}'; |
||||
} |
||||
} |
@ -0,0 +1,44 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
public enum WriteStallCondition { |
||||
NORMAL((byte) 0x0), |
||||
DELAYED((byte) 0x1), |
||||
STOPPED((byte) 0x2); |
||||
|
||||
private final byte value; |
||||
|
||||
WriteStallCondition(final byte value) { |
||||
this.value = value; |
||||
} |
||||
|
||||
/** |
||||
* Get the internal representation. |
||||
* |
||||
* @return the internal representation |
||||
*/ |
||||
byte getValue() { |
||||
return value; |
||||
} |
||||
|
||||
/** |
||||
* Get the WriteStallCondition from the internal representation value. |
||||
* |
||||
* @return the flush reason. |
||||
* |
||||
* @throws IllegalArgumentException if the value is unknown. |
||||
*/ |
||||
static WriteStallCondition fromValue(final byte value) { |
||||
for (final WriteStallCondition writeStallCondition : WriteStallCondition.values()) { |
||||
if (writeStallCondition.value == value) { |
||||
return writeStallCondition; |
||||
} |
||||
} |
||||
|
||||
throw new IllegalArgumentException("Illegal value provided for WriteStallCondition: " + value); |
||||
} |
||||
} |
@ -0,0 +1,75 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
public class WriteStallInfo { |
||||
private final String columnFamilyName; |
||||
private final WriteStallCondition currentCondition; |
||||
private final WriteStallCondition previousCondition; |
||||
|
||||
/** |
||||
* Access is package private as this will only be constructed from |
||||
* C++ via JNI and for testing. |
||||
*/ |
||||
WriteStallInfo(final String columnFamilyName, final byte currentConditionValue, |
||||
final byte previousConditionValue) { |
||||
this.columnFamilyName = columnFamilyName; |
||||
this.currentCondition = WriteStallCondition.fromValue(currentConditionValue); |
||||
this.previousCondition = WriteStallCondition.fromValue(previousConditionValue); |
||||
} |
||||
|
||||
/** |
||||
* Get the name of the column family. |
||||
* |
||||
* @return the name of the column family. |
||||
*/ |
||||
public String getColumnFamilyName() { |
||||
return columnFamilyName; |
||||
} |
||||
|
||||
/** |
||||
* Get the current state of the write controller. |
||||
* |
||||
* @return the current state. |
||||
*/ |
||||
public WriteStallCondition getCurrentCondition() { |
||||
return currentCondition; |
||||
} |
||||
|
||||
/** |
||||
* Get the previous state of the write controller. |
||||
* |
||||
* @return the previous state. |
||||
*/ |
||||
public WriteStallCondition getPreviousCondition() { |
||||
return previousCondition; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
if (this == o) |
||||
return true; |
||||
if (o == null || getClass() != o.getClass()) |
||||
return false; |
||||
WriteStallInfo that = (WriteStallInfo) o; |
||||
return Objects.equals(columnFamilyName, that.columnFamilyName) |
||||
&& currentCondition == that.currentCondition && previousCondition == that.previousCondition; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return Objects.hash(columnFamilyName, currentCondition, previousCondition); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "WriteStallInfo{" |
||||
+ "columnFamilyName='" + columnFamilyName + '\'' + ", currentCondition=" + currentCondition |
||||
+ ", previousCondition=" + previousCondition + '}'; |
||||
} |
||||
} |
@ -0,0 +1,589 @@ |
||||
package org.rocksdb; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.junit.Assert.*; |
||||
|
||||
import java.nio.file.Path; |
||||
import java.nio.file.Paths; |
||||
import java.util.Collections; |
||||
import java.util.Map; |
||||
import java.util.Random; |
||||
import java.util.UUID; |
||||
import java.util.concurrent.atomic.AtomicBoolean; |
||||
import org.junit.ClassRule; |
||||
import org.junit.Rule; |
||||
import org.junit.Test; |
||||
import org.junit.rules.TemporaryFolder; |
||||
import org.rocksdb.test.TestableEventListener; |
||||
|
||||
public class EventListenerTest { |
||||
@ClassRule |
||||
public static final RocksNativeLibraryResource ROCKS_NATIVE_LIBRARY_RESOURCE = |
||||
new RocksNativeLibraryResource(); |
||||
|
||||
@Rule public TemporaryFolder dbFolder = new TemporaryFolder(); |
||||
|
||||
public static final Random rand = PlatformRandomHelper.getPlatformSpecificRandomFactory(); |
||||
|
||||
void flushDb(final AbstractEventListener el, final AtomicBoolean wasCbCalled) |
||||
throws RocksDBException { |
||||
try (final Options opt = |
||||
new Options().setCreateIfMissing(true).setListeners(Collections.singletonList(el)); |
||||
final RocksDB db = RocksDB.open(opt, dbFolder.getRoot().getAbsolutePath())) { |
||||
assertThat(db).isNotNull(); |
||||
final byte[] value = new byte[24]; |
||||
rand.nextBytes(value); |
||||
db.put("testKey".getBytes(), value); |
||||
db.flush(new FlushOptions()); |
||||
assertTrue(wasCbCalled.get()); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void onFlushCompleted() throws RocksDBException { |
||||
// Callback is synchronous, but we need mutable container to update boolean value in other
|
||||
// method
|
||||
final AtomicBoolean wasCbCalled = new AtomicBoolean(); |
||||
AbstractEventListener onFlushCompletedListener = new AbstractEventListener() { |
||||
@Override |
||||
public void onFlushCompleted(final RocksDB rocksDb, final FlushJobInfo flushJobInfo) { |
||||
assertNotNull(flushJobInfo.getColumnFamilyName()); |
||||
assertEquals(FlushReason.MANUAL_FLUSH, flushJobInfo.getFlushReason()); |
||||
wasCbCalled.set(true); |
||||
} |
||||
}; |
||||
flushDb(onFlushCompletedListener, wasCbCalled); |
||||
} |
||||
|
||||
@Test |
||||
public void onFlushBegin() throws RocksDBException { |
||||
// Callback is synchronous, but we need mutable container to update boolean value in other
|
||||
// method
|
||||
final AtomicBoolean wasCbCalled = new AtomicBoolean(); |
||||
AbstractEventListener onFlushBeginListener = new AbstractEventListener() { |
||||
@Override |
||||
public void onFlushBegin(final RocksDB rocksDb, final FlushJobInfo flushJobInfo) { |
||||
assertNotNull(flushJobInfo.getColumnFamilyName()); |
||||
assertEquals(FlushReason.MANUAL_FLUSH, flushJobInfo.getFlushReason()); |
||||
wasCbCalled.set(true); |
||||
} |
||||
}; |
||||
flushDb(onFlushBeginListener, wasCbCalled); |
||||
} |
||||
|
||||
void deleteTableFile(final AbstractEventListener el, final AtomicBoolean wasCbCalled) |
||||
throws RocksDBException, InterruptedException { |
||||
try (final Options opt = |
||||
new Options().setCreateIfMissing(true).setListeners(Collections.singletonList(el)); |
||||
final RocksDB db = RocksDB.open(opt, dbFolder.getRoot().getAbsolutePath())) { |
||||
assertThat(db).isNotNull(); |
||||
final byte[] value = new byte[24]; |
||||
rand.nextBytes(value); |
||||
db.put("testKey".getBytes(), value); |
||||
RocksDB.LiveFiles liveFiles = db.getLiveFiles(); |
||||
assertNotNull(liveFiles); |
||||
assertNotNull(liveFiles.files); |
||||
assertFalse(liveFiles.files.isEmpty()); |
||||
db.deleteFile(liveFiles.files.get(0)); |
||||
assertTrue(wasCbCalled.get()); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void onTableFileDeleted() throws RocksDBException, InterruptedException { |
||||
// Callback is synchronous, but we need mutable container to update boolean value in other
|
||||
// method
|
||||
final AtomicBoolean wasCbCalled = new AtomicBoolean(); |
||||
AbstractEventListener onTableFileDeletedListener = new AbstractEventListener() { |
||||
@Override |
||||
public void onTableFileDeleted(final TableFileDeletionInfo tableFileDeletionInfo) { |
||||
assertNotNull(tableFileDeletionInfo.getDbName()); |
||||
wasCbCalled.set(true); |
||||
} |
||||
}; |
||||
deleteTableFile(onTableFileDeletedListener, wasCbCalled); |
||||
} |
||||
|
||||
void compactRange(final AbstractEventListener el, final AtomicBoolean wasCbCalled) |
||||
throws RocksDBException { |
||||
try (final Options opt = |
||||
new Options().setCreateIfMissing(true).setListeners(Collections.singletonList(el)); |
||||
final RocksDB db = RocksDB.open(opt, dbFolder.getRoot().getAbsolutePath())) { |
||||
assertThat(db).isNotNull(); |
||||
final byte[] value = new byte[24]; |
||||
rand.nextBytes(value); |
||||
db.put("testKey".getBytes(), value); |
||||
db.compactRange(); |
||||
assertTrue(wasCbCalled.get()); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void onCompactionBegin() throws RocksDBException { |
||||
// Callback is synchronous, but we need mutable container to update boolean value in other
|
||||
// method
|
||||
final AtomicBoolean wasCbCalled = new AtomicBoolean(); |
||||
AbstractEventListener onCompactionBeginListener = new AbstractEventListener() { |
||||
@Override |
||||
public void onCompactionBegin(final RocksDB db, final CompactionJobInfo compactionJobInfo) { |
||||
assertEquals(CompactionReason.kManualCompaction, compactionJobInfo.compactionReason()); |
||||
wasCbCalled.set(true); |
||||
} |
||||
}; |
||||
compactRange(onCompactionBeginListener, wasCbCalled); |
||||
} |
||||
|
||||
@Test |
||||
public void onCompactionCompleted() throws RocksDBException { |
||||
// Callback is synchronous, but we need mutable container to update boolean value in other
|
||||
// method
|
||||
final AtomicBoolean wasCbCalled = new AtomicBoolean(); |
||||
AbstractEventListener onCompactionCompletedListener = new AbstractEventListener() { |
||||
@Override |
||||
public void onCompactionCompleted( |
||||
final RocksDB db, final CompactionJobInfo compactionJobInfo) { |
||||
assertEquals(CompactionReason.kManualCompaction, compactionJobInfo.compactionReason()); |
||||
wasCbCalled.set(true); |
||||
} |
||||
}; |
||||
compactRange(onCompactionCompletedListener, wasCbCalled); |
||||
} |
||||
|
||||
@Test |
||||
public void onTableFileCreated() throws RocksDBException { |
||||
// Callback is synchronous, but we need mutable container to update boolean value in other
|
||||
// method
|
||||
final AtomicBoolean wasCbCalled = new AtomicBoolean(); |
||||
AbstractEventListener onTableFileCreatedListener = new AbstractEventListener() { |
||||
@Override |
||||
public void onTableFileCreated(final TableFileCreationInfo tableFileCreationInfo) { |
||||
assertEquals(TableFileCreationReason.FLUSH, tableFileCreationInfo.getReason()); |
||||
wasCbCalled.set(true); |
||||
} |
||||
}; |
||||
flushDb(onTableFileCreatedListener, wasCbCalled); |
||||
} |
||||
|
||||
@Test |
||||
public void onTableFileCreationStarted() throws RocksDBException { |
||||
// Callback is synchronous, but we need mutable container to update boolean value in other
|
||||
// method
|
||||
final AtomicBoolean wasCbCalled = new AtomicBoolean(); |
||||
AbstractEventListener onTableFileCreationStartedListener = new AbstractEventListener() { |
||||
@Override |
||||
public void onTableFileCreationStarted( |
||||
final TableFileCreationBriefInfo tableFileCreationBriefInfo) { |
||||
assertEquals(TableFileCreationReason.FLUSH, tableFileCreationBriefInfo.getReason()); |
||||
wasCbCalled.set(true); |
||||
} |
||||
}; |
||||
flushDb(onTableFileCreationStartedListener, wasCbCalled); |
||||
} |
||||
|
||||
void deleteColumnFamilyHandle(final AbstractEventListener el, final AtomicBoolean wasCbCalled) |
||||
throws RocksDBException { |
||||
try (final Options opt = |
||||
new Options().setCreateIfMissing(true).setListeners(Collections.singletonList(el)); |
||||
final RocksDB db = RocksDB.open(opt, dbFolder.getRoot().getAbsolutePath())) { |
||||
assertThat(db).isNotNull(); |
||||
final byte[] value = new byte[24]; |
||||
rand.nextBytes(value); |
||||
db.put("testKey".getBytes(), value); |
||||
ColumnFamilyHandle columnFamilyHandle = db.getDefaultColumnFamily(); |
||||
columnFamilyHandle.close(); |
||||
assertTrue(wasCbCalled.get()); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void onColumnFamilyHandleDeletionStarted() throws RocksDBException { |
||||
// Callback is synchronous, but we need mutable container to update boolean value in other
|
||||
// method
|
||||
final AtomicBoolean wasCbCalled = new AtomicBoolean(); |
||||
AbstractEventListener onColumnFamilyHandleDeletionStartedListener = |
||||
new AbstractEventListener() { |
||||
@Override |
||||
public void onColumnFamilyHandleDeletionStarted( |
||||
final ColumnFamilyHandle columnFamilyHandle) { |
||||
assertNotNull(columnFamilyHandle); |
||||
wasCbCalled.set(true); |
||||
} |
||||
}; |
||||
deleteColumnFamilyHandle(onColumnFamilyHandleDeletionStartedListener, wasCbCalled); |
||||
} |
||||
|
||||
void ingestExternalFile(final AbstractEventListener el, final AtomicBoolean wasCbCalled) |
||||
throws RocksDBException { |
||||
try (final Options opt = |
||||
new Options().setCreateIfMissing(true).setListeners(Collections.singletonList(el)); |
||||
final RocksDB db = RocksDB.open(opt, dbFolder.getRoot().getAbsolutePath())) { |
||||
assertThat(db).isNotNull(); |
||||
String uuid = UUID.randomUUID().toString(); |
||||
SstFileWriter sstFileWriter = new SstFileWriter(new EnvOptions(), opt); |
||||
Path externalFilePath = Paths.get(db.getName(), uuid); |
||||
sstFileWriter.open(externalFilePath.toString()); |
||||
sstFileWriter.put("testKey".getBytes(), uuid.getBytes()); |
||||
sstFileWriter.finish(); |
||||
db.ingestExternalFile( |
||||
Collections.singletonList(externalFilePath.toString()), new IngestExternalFileOptions()); |
||||
assertTrue(wasCbCalled.get()); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void onExternalFileIngested() throws RocksDBException { |
||||
// Callback is synchronous, but we need mutable container to update boolean value in other
|
||||
// method
|
||||
final AtomicBoolean wasCbCalled = new AtomicBoolean(); |
||||
AbstractEventListener onExternalFileIngestedListener = new AbstractEventListener() { |
||||
@Override |
||||
public void onExternalFileIngested( |
||||
final RocksDB db, final ExternalFileIngestionInfo externalFileIngestionInfo) { |
||||
assertNotNull(db); |
||||
wasCbCalled.set(true); |
||||
} |
||||
}; |
||||
ingestExternalFile(onExternalFileIngestedListener, wasCbCalled); |
||||
} |
||||
|
||||
@Test |
||||
public void testAllCallbacksInvocation() { |
||||
final int TEST_INT_VAL = Integer.MAX_VALUE; |
||||
final long TEST_LONG_VAL = Long.MAX_VALUE; |
||||
// Expected test data objects
|
||||
final Map<String, String> userCollectedPropertiesTestData = |
||||
Collections.singletonMap("key", "value"); |
||||
final Map<String, String> readablePropertiesTestData = Collections.singletonMap("key", "value"); |
||||
final Map<String, Long> propertiesOffsetsTestData = |
||||
Collections.singletonMap("key", TEST_LONG_VAL); |
||||
final TableProperties tablePropertiesTestData = new TableProperties(TEST_LONG_VAL, |
||||
TEST_LONG_VAL, TEST_LONG_VAL, TEST_LONG_VAL, TEST_LONG_VAL, TEST_LONG_VAL, TEST_LONG_VAL, |
||||
TEST_LONG_VAL, TEST_LONG_VAL, TEST_LONG_VAL, TEST_LONG_VAL, TEST_LONG_VAL, TEST_LONG_VAL, |
||||
TEST_LONG_VAL, TEST_LONG_VAL, TEST_LONG_VAL, TEST_LONG_VAL, TEST_LONG_VAL, TEST_LONG_VAL, |
||||
"columnFamilyName".getBytes(), "filterPolicyName", "comparatorName", "mergeOperatorName", |
||||
"prefixExtractorName", "propertyCollectorsNames", "compressionName", |
||||
userCollectedPropertiesTestData, readablePropertiesTestData, propertiesOffsetsTestData); |
||||
final FlushJobInfo flushJobInfoTestData = new FlushJobInfo(TEST_INT_VAL, "testColumnFamily", |
||||
"/file/path", TEST_LONG_VAL, TEST_INT_VAL, true, true, TEST_LONG_VAL, TEST_LONG_VAL, |
||||
tablePropertiesTestData, (byte) 0x0a); |
||||
final Status statusTestData = new Status(Status.Code.Incomplete, Status.SubCode.NoSpace, null); |
||||
final TableFileDeletionInfo tableFileDeletionInfoTestData = |
||||
new TableFileDeletionInfo("dbName", "/file/path", TEST_INT_VAL, statusTestData); |
||||
final TableFileCreationInfo tableFileCreationInfoTestData = |
||||
new TableFileCreationInfo(TEST_LONG_VAL, tablePropertiesTestData, statusTestData, "dbName", |
||||
"columnFamilyName", "/file/path", TEST_INT_VAL, (byte) 0x03); |
||||
final TableFileCreationBriefInfo tableFileCreationBriefInfoTestData = |
||||
new TableFileCreationBriefInfo( |
||||
"dbName", "columnFamilyName", "/file/path", TEST_INT_VAL, (byte) 0x03); |
||||
final MemTableInfo memTableInfoTestData = new MemTableInfo( |
||||
"columnFamilyName", TEST_LONG_VAL, TEST_LONG_VAL, TEST_LONG_VAL, TEST_LONG_VAL); |
||||
final FileOperationInfo fileOperationInfoTestData = new FileOperationInfo("/file/path", |
||||
TEST_LONG_VAL, TEST_LONG_VAL, 1_600_699_420_000_000_000L, 5_000_000_000L, statusTestData); |
||||
final WriteStallInfo writeStallInfoTestData = |
||||
new WriteStallInfo("columnFamilyName", (byte) 0x1, (byte) 0x2); |
||||
final ExternalFileIngestionInfo externalFileIngestionInfoTestData = |
||||
new ExternalFileIngestionInfo("columnFamilyName", "/external/file/path", |
||||
"/internal/file/path", TEST_LONG_VAL, tablePropertiesTestData); |
||||
|
||||
final int CALLBACKS_COUNT = 22; |
||||
final AtomicBoolean[] wasCalled = new AtomicBoolean[CALLBACKS_COUNT]; |
||||
for (int i = 0; i < CALLBACKS_COUNT; ++i) { |
||||
wasCalled[i] = new AtomicBoolean(); |
||||
} |
||||
TestableEventListener listener = new TestableEventListener() { |
||||
@Override |
||||
public void onFlushCompleted(final RocksDB db, final FlushJobInfo flushJobInfo) { |
||||
assertEquals(flushJobInfoTestData, flushJobInfo); |
||||
wasCalled[0].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onFlushBegin(final RocksDB db, final FlushJobInfo flushJobInfo) { |
||||
assertEquals(flushJobInfoTestData, flushJobInfo); |
||||
wasCalled[1].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onTableFileDeleted(final TableFileDeletionInfo tableFileDeletionInfo) { |
||||
assertEquals(tableFileDeletionInfoTestData, tableFileDeletionInfo); |
||||
wasCalled[2].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onCompactionBegin(final RocksDB db, final CompactionJobInfo compactionJobInfo) { |
||||
assertArrayEquals( |
||||
"compactionColumnFamily".getBytes(), compactionJobInfo.columnFamilyName()); |
||||
assertEquals(statusTestData, compactionJobInfo.status()); |
||||
assertEquals(TEST_LONG_VAL, compactionJobInfo.threadId()); |
||||
assertEquals(TEST_INT_VAL, compactionJobInfo.jobId()); |
||||
assertEquals(TEST_INT_VAL, compactionJobInfo.baseInputLevel()); |
||||
assertEquals(TEST_INT_VAL, compactionJobInfo.outputLevel()); |
||||
assertEquals(Collections.singletonList("inputFile.sst"), compactionJobInfo.inputFiles()); |
||||
assertEquals(Collections.singletonList("outputFile.sst"), compactionJobInfo.outputFiles()); |
||||
assertEquals(Collections.singletonMap("tableProperties", tablePropertiesTestData), |
||||
compactionJobInfo.tableProperties()); |
||||
assertEquals(CompactionReason.kFlush, compactionJobInfo.compactionReason()); |
||||
assertEquals(CompressionType.SNAPPY_COMPRESSION, compactionJobInfo.compression()); |
||||
wasCalled[3].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onCompactionCompleted( |
||||
final RocksDB db, final CompactionJobInfo compactionJobInfo) { |
||||
assertArrayEquals( |
||||
"compactionColumnFamily".getBytes(), compactionJobInfo.columnFamilyName()); |
||||
assertEquals(statusTestData, compactionJobInfo.status()); |
||||
assertEquals(TEST_LONG_VAL, compactionJobInfo.threadId()); |
||||
assertEquals(TEST_INT_VAL, compactionJobInfo.jobId()); |
||||
assertEquals(TEST_INT_VAL, compactionJobInfo.baseInputLevel()); |
||||
assertEquals(TEST_INT_VAL, compactionJobInfo.outputLevel()); |
||||
assertEquals(Collections.singletonList("inputFile.sst"), compactionJobInfo.inputFiles()); |
||||
assertEquals(Collections.singletonList("outputFile.sst"), compactionJobInfo.outputFiles()); |
||||
assertEquals(Collections.singletonMap("tableProperties", tablePropertiesTestData), |
||||
compactionJobInfo.tableProperties()); |
||||
assertEquals(CompactionReason.kFlush, compactionJobInfo.compactionReason()); |
||||
assertEquals(CompressionType.SNAPPY_COMPRESSION, compactionJobInfo.compression()); |
||||
wasCalled[4].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onTableFileCreated(final TableFileCreationInfo tableFileCreationInfo) { |
||||
assertEquals(tableFileCreationInfoTestData, tableFileCreationInfo); |
||||
wasCalled[5].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onTableFileCreationStarted( |
||||
final TableFileCreationBriefInfo tableFileCreationBriefInfo) { |
||||
assertEquals(tableFileCreationBriefInfoTestData, tableFileCreationBriefInfo); |
||||
wasCalled[6].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onMemTableSealed(final MemTableInfo memTableInfo) { |
||||
assertEquals(memTableInfoTestData, memTableInfo); |
||||
wasCalled[7].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onColumnFamilyHandleDeletionStarted(final ColumnFamilyHandle columnFamilyHandle) { |
||||
wasCalled[8].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onExternalFileIngested( |
||||
final RocksDB db, final ExternalFileIngestionInfo externalFileIngestionInfo) { |
||||
assertEquals(externalFileIngestionInfoTestData, externalFileIngestionInfo); |
||||
wasCalled[9].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onBackgroundError( |
||||
final BackgroundErrorReason backgroundErrorReason, final Status backgroundError) { |
||||
wasCalled[10].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onStallConditionsChanged(final WriteStallInfo writeStallInfo) { |
||||
assertEquals(writeStallInfoTestData, writeStallInfo); |
||||
wasCalled[11].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onFileReadFinish(final FileOperationInfo fileOperationInfo) { |
||||
assertEquals(fileOperationInfoTestData, fileOperationInfo); |
||||
wasCalled[12].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onFileWriteFinish(final FileOperationInfo fileOperationInfo) { |
||||
assertEquals(fileOperationInfoTestData, fileOperationInfo); |
||||
wasCalled[13].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void OnFileFlushFinish(final FileOperationInfo fileOperationInfo) { |
||||
assertEquals(fileOperationInfoTestData, fileOperationInfo); |
||||
wasCalled[14].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void OnFileSyncFinish(final FileOperationInfo fileOperationInfo) { |
||||
assertEquals(fileOperationInfoTestData, fileOperationInfo); |
||||
wasCalled[15].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void OnFileRangeSyncFinish(final FileOperationInfo fileOperationInfo) { |
||||
assertEquals(fileOperationInfoTestData, fileOperationInfo); |
||||
wasCalled[16].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void OnFileTruncateFinish(final FileOperationInfo fileOperationInfo) { |
||||
assertEquals(fileOperationInfoTestData, fileOperationInfo); |
||||
wasCalled[17].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void OnFileCloseFinish(final FileOperationInfo fileOperationInfo) { |
||||
assertEquals(fileOperationInfoTestData, fileOperationInfo); |
||||
wasCalled[18].set(true); |
||||
} |
||||
|
||||
@Override |
||||
public boolean shouldBeNotifiedOnFileIO() { |
||||
wasCalled[19].set(true); |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public boolean onErrorRecoveryBegin( |
||||
final BackgroundErrorReason backgroundErrorReason, final Status backgroundError) { |
||||
assertEquals(BackgroundErrorReason.FLUSH, backgroundErrorReason); |
||||
assertEquals(statusTestData, backgroundError); |
||||
wasCalled[20].set(true); |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public void onErrorRecoveryCompleted(final Status oldBackgroundError) { |
||||
assertEquals(statusTestData, oldBackgroundError); |
||||
wasCalled[21].set(true); |
||||
} |
||||
}; |
||||
listener.invokeAllCallbacks(); |
||||
for (int i = 0; i < CALLBACKS_COUNT; ++i) { |
||||
assertTrue("Callback method " + i + " was not called", wasCalled[i].get()); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void testEnabledCallbacks() { |
||||
final AtomicBoolean wasOnMemTableSealedCalled = new AtomicBoolean(); |
||||
final AtomicBoolean wasOnErrorRecoveryCompletedCalled = new AtomicBoolean(); |
||||
final TestableEventListener listener = new TestableEventListener( |
||||
AbstractEventListener.EnabledEventCallback.ON_MEMTABLE_SEALED, |
||||
AbstractEventListener.EnabledEventCallback.ON_ERROR_RECOVERY_COMPLETED) { |
||||
@Override |
||||
public void onFlushCompleted(final RocksDB db, final FlushJobInfo flushJobInfo) { |
||||
fail("onFlushCompleted was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void onFlushBegin(final RocksDB db, final FlushJobInfo flushJobInfo) { |
||||
fail("onFlushBegin was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void onTableFileDeleted(final TableFileDeletionInfo tableFileDeletionInfo) { |
||||
fail("onTableFileDeleted was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void onCompactionBegin(final RocksDB db, final CompactionJobInfo compactionJobInfo) { |
||||
fail("onCompactionBegin was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void onCompactionCompleted( |
||||
final RocksDB db, final CompactionJobInfo compactionJobInfo) { |
||||
fail("onCompactionCompleted was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void onTableFileCreated(final TableFileCreationInfo tableFileCreationInfo) { |
||||
fail("onTableFileCreated was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void onTableFileCreationStarted( |
||||
final TableFileCreationBriefInfo tableFileCreationBriefInfo) { |
||||
fail("onTableFileCreationStarted was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void onMemTableSealed(final MemTableInfo memTableInfo) { |
||||
wasOnMemTableSealedCalled.set(true); |
||||
} |
||||
|
||||
@Override |
||||
public void onColumnFamilyHandleDeletionStarted(final ColumnFamilyHandle columnFamilyHandle) { |
||||
fail("onColumnFamilyHandleDeletionStarted was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void onExternalFileIngested( |
||||
final RocksDB db, final ExternalFileIngestionInfo externalFileIngestionInfo) { |
||||
fail("onExternalFileIngested was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void onBackgroundError( |
||||
final BackgroundErrorReason backgroundErrorReason, final Status backgroundError) { |
||||
fail("onBackgroundError was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void onStallConditionsChanged(final WriteStallInfo writeStallInfo) { |
||||
fail("onStallConditionsChanged was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void onFileReadFinish(final FileOperationInfo fileOperationInfo) { |
||||
fail("onFileReadFinish was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void onFileWriteFinish(final FileOperationInfo fileOperationInfo) { |
||||
fail("onFileWriteFinish was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void OnFileFlushFinish(final FileOperationInfo fileOperationInfo) { |
||||
fail("OnFileFlushFinish was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void OnFileSyncFinish(final FileOperationInfo fileOperationInfo) { |
||||
fail("OnFileSyncFinish was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void OnFileRangeSyncFinish(final FileOperationInfo fileOperationInfo) { |
||||
fail("OnFileRangeSyncFinish was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void OnFileTruncateFinish(final FileOperationInfo fileOperationInfo) { |
||||
fail("OnFileTruncateFinish was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public void OnFileCloseFinish(final FileOperationInfo fileOperationInfo) { |
||||
fail("OnFileCloseFinish was not enabled"); |
||||
} |
||||
|
||||
@Override |
||||
public boolean shouldBeNotifiedOnFileIO() { |
||||
fail("shouldBeNotifiedOnFileIO was not enabled"); |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public boolean onErrorRecoveryBegin( |
||||
final BackgroundErrorReason backgroundErrorReason, final Status backgroundError) { |
||||
fail("onErrorRecoveryBegin was not enabled"); |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public void onErrorRecoveryCompleted(final Status oldBackgroundError) { |
||||
wasOnErrorRecoveryCompletedCalled.set(true); |
||||
} |
||||
}; |
||||
listener.invokeAllCallbacks(); |
||||
assertTrue(wasOnMemTableSealedCalled.get()); |
||||
assertTrue(wasOnErrorRecoveryCompletedCalled.get()); |
||||
} |
||||
} |
@ -0,0 +1,19 @@ |
||||
package org.rocksdb.test; |
||||
|
||||
import org.rocksdb.AbstractEventListener; |
||||
|
||||
public class TestableEventListener extends AbstractEventListener { |
||||
public TestableEventListener() { |
||||
super(); |
||||
} |
||||
|
||||
public TestableEventListener(final EnabledEventCallback... enabledEventCallbacks) { |
||||
super(enabledEventCallbacks); |
||||
} |
||||
|
||||
public void invokeAllCallbacks() { |
||||
invokeAllCallbacks(nativeHandle_); |
||||
} |
||||
|
||||
private static native void invokeAllCallbacks(final long handle); |
||||
} |
Loading…
Reference in new issue