This commit is contained in:
anumjs 2017-10-22 19:34:57 -07:00
Коммит 0bb37df54d
321 изменённых файлов: 82926 добавлений и 0 удалений

33
.gitattributes поставляемый Normal file
Просмотреть файл

@ -0,0 +1,33 @@
# Auto detect text files and perform LF normalization
* text=auto
#
# The above will handle all files NOT found below
#
# These files are text and should be normalized (Convert crlf => lf)
*.css text
*.md text
*.htm text
*.html text
*.java text
*.js text
*.json text
*.properties text
*.sh text
*.svg text
*.txt text
*.xml text
# These files are binary and should be left untouched
# (binary is a macro for -text -diff)
*.class binary
*.dll binary
*.ear binary
*.gif binary
*.ico binary
*.jar binary
*.jpg binary
*.jpeg binary
*.png binary
*.so binary
*.war binary

263
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,263 @@
# Compiled class file
*.class
# Log file
*.csv
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar
*.7z
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
#################
## Eclipse
#################
.project
.metadata
bin/
tmp/
target/
/target/
**/target/
*/target/*
build/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.vscode/
.settings
.settings/
.gradle/
.loadpath
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
#################
## Visual Studio
#################
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
x64/
build/
[Bb]in/
[Oo]bj/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
*.ncrunch*
.*crunch*.local.xml
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.Publish.xml
*.pubxml
*.publishproj
# NuGet Packages Directory
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
#packages/
# Windows Azure Build Output
csx
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.[Pp]ublish.xml
*.pfx
*.publishsettings
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
App_Data/*.mdf
App_Data/*.ldf
#############
## Windows detritus
#############
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Mac crap
.DS_Store
#############
## Python
#############
*.py[cod]
# Packages
*.egg
*.egg-info
dist/
build/
eggs/
parts/
var/
sdist/
develop-eggs/
.installed.cfg
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
#Translations
*.mo
#Mr Developer
.mr.developer.cfg
#############
## Intellij
#############
## Directory-based project format
.idea/
## File-based project format
*.ipr
*.iws
*.iml
## Additional for IntelliJ
out/
gen/

313
Code_Formatter.xml Normal file
Просмотреть файл

@ -0,0 +1,313 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="12">
<profile kind="CodeFormatterProfile" name="mssql-jdbc" version="12">
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="81"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="150"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="81"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="2147483647"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.compiler.source" value="1.8"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.8"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="81"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.8"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="150"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
</profile>
</profiles>

270
Coding_Guidelines.md Normal file
Просмотреть файл

@ -0,0 +1,270 @@
# Java Coding Standards
The purpose of the Java Coding Standards is to create a collaboration baseline. It will be helpful for scenarios where many people are creating, modifying, and contributing to the same project.
## 0. Use Wisely
**Do NOT** blindly obey these guidelines, use them (after understanding) where they make sense.
## 1. General
All **changed code**, must obey these *coding standards*. As we have huge legacy code we will have code which defers Coding Standards. Eclipse code formatter [mssql-jdbc_formatter.xml](mssql-jdbc_formatter.xml) must be used to format the **changed code**, except for [enum types](#EnumSpec).
## 2. Comments
We should give appropriate java comments in code. Please find ideal example of java comments. We do not expect to have full javadocs for common methods like getters / setters.
**Correct:**
```java
/**
* Get property-only names that do not work with connection String
*
* @param name
* to normalize
* @param logger
* @return the normalized property name
*/
static String getPropertyOnlyName(String name, Logger logger) {
if (null == name) {
return name;
}
... some complex logic
}
```
**Incorrect:**
```java
// Get property-only names
static String getPropertyOnlyName(String name, Logger logger) {
if(null == name) {
return name;
}
...
}
```
## 3. Copyrights
All Java files should contain the appropriate copyright notice at the beginning of the file.
```java
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
```
## 4. Good Practices
Pay special attention to the most common style errors...
* classes too long
* methods too long
* little or no javadoc comments
* swallow exceptions
* multiple *return* statements
* Overuse of arrays in place of collections
* too much or no whitespace
### 4.1. Avoid multiple *return* statements
Multiple *return* statements are hard and time consuming to debug.
**Correct:**
```java
public class StringUtils {
public static boolean isEmpty(String string) {
return string == null || "".equals(string.trim());
}
}
or
public class StringUtils {
public static boolean isEmpty(String string) {
boolean empty = false;
if (string == null) {
empty = true;
} else if ("".equals(string.trim())) {
empty = true;
}
return empty;
}
}
```
**Incorrect:**
```java
public class StringUtils {
public static boolean isEmpty(String string) {
if (string == null) {
return true;
} else if ("".equals(string.trim())) {
return true;
}
return false;
}
}
```
### 4.2. Boolean comparisons
Mirroring the natural language "if the current state is not active" rather than "if active is not the current state"
**Correct:**
```java
!active
```
**Incorrect:**
```java
active == false
```
### 4.3. *for* loops Vs *for-each* loops
When iterating over iterable elements where the current index in the iteration is not important for-each loops are preferred. **Not compatible for JDK 1.4 **
**Correct:**
```java
for (String name: names) {
doStuff(name);
}
```
**Incorrect:**
```java
for (int i = 0; i < names.length; i++) {
doStuff(names[i]);
}
```
### 4.4. *String* concatenation
Avoid the use of + or += to concatenate strings. Use java standards designed for that purposes such as String.format, StringBuilder, etc. If you are doing thread safe operation then use StringBuilder instead of StringBuffer.
**Correct:**
```java
log.debug(String.format("found %s items", amount));
```
**Incorrect:**
```java
log.debug("found " + amount + " items");
```
### 4.5. Collections
Use the right collections for the right task.
**Duplicates**
* Allows duplicates: [List](http://docs.oracle.com/javase/8/docs/api/java/util/List.html)
* Does Not Allow Duplicates: [Set](http://docs.oracle.com/javase/8/docs/api/java/util/Set.html), [Map](http://docs.oracle.com/javase/8/docs/api/java/util/Map.html)
**Implementations Iteration Order**
* [HashSet](http://docs.oracle.com/javase/8/docs/api/java/util/HashSet.html) - undefined
* [ConcurrentHashMap](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html) - undefined
* [HashMap](http://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html) - undefined
* [LinkedHashSet](http://docs.oracle.com/javase/8/docs/api/java/util/LinkedHashSet.html) - insertion order
* [LinkedHashMap](http://docs.oracle.com/javase/8/docs/api/java/util/LinkedHashMap.html) - insertion order of keys
* [ArrayList](http://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html) - insertion order
* [LinkedList](http://docs.oracle.com/javase/8/docs/api/java/util/LinkedList.html) - insertion order
* [TreeSet](http://docs.oracle.com/javase/8/docs/api/java/util/TreeSet.html) - ascending order (Comparable / Comparator)
### 4.6. Raw types
Avoid using raw types when using classes that support generics.
**Correct:**
```java
List<String> people = Arrays.asList("you", "me");
```
**Incorrect:**
```java
List people = Arrays.asList("you", "me");
```
## 5. Design Patterns
Consider the use of common design patterns.
### 5.1. Enums <a name="EnumSpec"></a>
Constrain arguments by using type safe enumerations.
**Correct:**
```java
public enum Options {
YES, NO
}
```
**Incorrect:**
```java
String yes = "YES";
String no = "NO";
```
<br />
enum value with multiple constants must be alligned such that the constant of same type from all the values must be alligned in same column.
**Correct:**
```java
public enum SqlType{
VARCHAR ("varchar", JDBCType.VARCHAR),
NVARCHAR ("nvarchar", JDBCType.NVARCHAR),
CHAR ("char", JDBCType.CHAR);
}
```
**Incorrect**
```java
public enum SqlType {
VARCHAR("varchar", JDBCType.VARCHAR),
NVARCHAR("nvarchar", JDBCType.NVARCHAR),
CHAR("char", JDBCType.CHAR);
}
```
### 5.2. Private Helpers
Consider private helper methods to break down complex flows and long methods into more readable code.
## 6. Active enforcement
Java developers are expected to use tools that enforce some of these good practices and quality code in general.
- The de-facto Editor is the Eclipse IDE
- New developed code should pass SonarQube rules
- Strongly recommend [Sonar Lint](http://www.sonarlint.org/) tool
- **New developed code / Only changed code** should be formatted by Eclipse formatter.

21
LICENSE Normal file
Просмотреть файл

@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE

45
README.md Normal file
Просмотреть файл

@ -0,0 +1,45 @@
# Elastic database tools for JAVA (Azure SQL Database)
Elastic database tools client library allows JAVA developers to create applications that use database sharding in Azure SQL Database. This repository contains the library/tools along with a sample project. For C# version of the database tools client library, see https://github.com/Azure/elastic-db-tools.
# Prerequisites
* A Java Developer Kit (JDK), v 1.8 or later
* [Maven](http://maven.apache.org/download.cgi)
* A logical server in Azure or local SQL Server
# Running the sample code in [sample](https://github.com/Microsoft/elastic-db-tools-for-java/tree/develop/samples)
Follow the steps below to build JAR files and get started with the sample project:
* Clone the repository
* From the _./tools_ directory, build the jars using `mvn install -D maven.test.skip=true`. This creates the jars in the _./target_ directory.
- If you want to run the tests, use `mvn clean install` instead. You will also have to configure a test connection by adding your username, password, and logical server name for either a logical server in Azure or local SQL Server in the _./tools/src/test/resources/resource.properties_ file.
* Edit the _./sample/src/main/resources/resource.properties_ file to add your username, password, and logical server name.
* From the _./sample_ directory, run `mvn install` to build the sample project.
* From the _./sample_ directory, run `mvn -q exec:java "-Dexec.mainClass=com.microsoft.azure.elasticdb.samples.elasticscalestarterkit.Program"` to start the sample project.
# Download
For using the released JAR, simply add the following dependancy to your POM file:
```xml
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>elastic-db-tools</artifactId>
<version>1.0.0</version>
</dependency>
```
# Contribute code
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.
If you would like to become an active contributor to this project please follow the instructions provided in [Microsoft Azure Projects Contribution Guidelines](http://azure.github.io/guidelines.html).
1. Fork the repository
2. Create your branch (`git checkout -b my-new-branch`)
3. Commit your changes (`git commit -am 'Add some change/feature'`)
4. Push to the branch (`git push origin my-new-branch`)
5. Create new Pull Request
# License
The Elastic database tools client library for Java is licensed under the MIT license. See the [LICENSE](https://github.com/Microsoft/mssql-jdbc/blob/master/LICENSE) file for more details.
# Code of conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

123
elastic-db-tools/pom.xml Normal file
Просмотреть файл

@ -0,0 +1,123 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.microsoft.azure</groupId>
<artifactId>elastic-db-tools</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<name>Azure Elastic Database Tools</name>
<developers>
<developer>
<id>microsoft</id>
<name>Microsoft</name>
</developer>
</developers>
<description>Microsoft Azure Elastic database tools client library allows Java JDBC developers to create applications that implement and use the pattern known as database sharding in Azure SQL Database.</description>
<url>https://github.com/Microsoft/elastic-db-tools-for-java</url>
<properties>
<azurejavasdk.version>${project.version}</azurejavasdk.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jre.version>1.8</jre.version>
<junit.version>4.12</junit.version>
<maven.compiler.version>3.7.0</maven.compiler.version>
<maven.surefire.version>2.20.1</maven.surefire.version>
<mssql.jdbc.version>6.1.0.jre8</mssql.jdbc.version>
<guava.version>19.0</guava.version>
<commons.collections.version>3.2.2</commons.collections.version>
<commons.logging.version>1.1.3</commons.logging.version>
<commons.lang3.version>3.5</commons.lang3.version>
<jaxp.api.version>1.4.5</jaxp.api.version>
<slf4j.api.version>1.7.25</slf4j.api.version>
<log4j.slf4j.impl.version>2.8.2</log4j.slf4j.impl.version>
<log4j.core.version>2.8.2</log4j.core.version>
</properties>
<licenses>
<license>
<name>MIT License</name>
<url>http://www.opensource.org/licenses/mit-license.php</url>
</license>
</licenses>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<configuration>
<source>${jre.version}</source>
<target>${jre.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven.surefire.version}</version>
<configuration>
<includes>
<include>*Tests.java</include>
</includes>
<reuseForks>false</reuseForks>
<forkCount>1</forkCount>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons.lang3.version}</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons.collections.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>${mssql.jdbc.version}</version>
</dependency>
<dependency>
<groupId>javax.xml.parsers</groupId>
<artifactId>jaxp-api</artifactId>
<version>${jaxp.api.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.api.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.slf4j.impl.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.core.version}</version>
</dependency>
</dependencies>
</project>

Просмотреть файл

@ -0,0 +1,17 @@
package com.microsoft.azure.elasticdb.core.commons.helpers;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
@FunctionalInterface
public interface ActionGeneric3Param<T1, T2, T3, T> {
T invoke(T1 t1,
T2 t2,
T3 t3);
}

Просмотреть файл

@ -0,0 +1,49 @@
package com.microsoft.azure.elasticdb.core.commons.helpers;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import org.apache.commons.lang3.StringUtils;
public final class ApplicationNameHelper {
private ApplicationNameHelper() {
}
public static final int MAX_APPLICATION_NAME_LENGTH = 128;
/**
* Adds suffix to the application name, but not exceeding certain length().
*
* @param originalApplicationName
* Application provided application name.
* @param suffixToAppend
* Suffix to append to the application name.
* @return Application name with suffix appended.
*/
public static String addApplicationNameSuffix(String originalApplicationName,
String suffixToAppend) {
if (originalApplicationName == null || StringUtils.isEmpty(originalApplicationName)) {
return suffixToAppend;
}
if (suffixToAppend == null || StringUtils.isEmpty(suffixToAppend)) {
return originalApplicationName;
}
int maxAppNameSubStringAllowed = MAX_APPLICATION_NAME_LENGTH - suffixToAppend.length();
if (originalApplicationName.length() <= maxAppNameSubStringAllowed) {
return originalApplicationName + suffixToAppend;
}
else {
// Take the substring of application name that will be fit within the 'program_name' column in dm_exec_sessions.
return originalApplicationName.substring(0, maxAppNameSubStringAllowed) + suffixToAppend;
}
}
}

Просмотреть файл

@ -0,0 +1,80 @@
package com.microsoft.azure.elasticdb.core.commons.helpers;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class Event<T> {
private Map<String, T> namedListeners = new HashMap<>();
private List<T> anonymousListeners = new ArrayList<>();
/**
* Add an Event Listener.
*
* @param methodName
* Name of the Method
* @param namedEventHandlerMethod
* Named handler method
*/
public void addListener(String methodName,
T namedEventHandlerMethod) {
if (!namedListeners.containsKey(methodName)) {
namedListeners.put(methodName, namedEventHandlerMethod);
}
}
/**
* Add an Event Listener.
*
* @param unnamedEventHandlerMethod
* Unnamed handler method
*/
public void addListener(T unnamedEventHandlerMethod) {
anonymousListeners.add(unnamedEventHandlerMethod);
}
/**
* Remove the Event Listener.
*
* @param methodName
* Name of the method
*/
public void removeListener(String methodName) {
if (namedListeners.containsKey(methodName)) {
namedListeners.remove(methodName);
}
}
/**
* Remove the Event Listener.
*
* @param unnamedEventHandlerMethod
* Unnamed handler method
*/
public void removeListener(T unnamedEventHandlerMethod) {
anonymousListeners.remove(unnamedEventHandlerMethod);
}
/**
* List of named and unnamed Listeners.
*
* @return ArrayList of named and unnamed Listeners
*/
public List<T> listeners() {
List<T> allListeners = new ArrayList<>();
allListeners.addAll(namedListeners.values());
allListeners.addAll(anonymousListeners);
return allListeners;
}
}

Просмотреть файл

@ -0,0 +1,15 @@
package com.microsoft.azure.elasticdb.core.commons.helpers;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
public class EventArgs {
public EventArgs() {
}
}

Просмотреть файл

@ -0,0 +1,16 @@
package com.microsoft.azure.elasticdb.core.commons.helpers;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
@FunctionalInterface
public interface EventHandler<T extends EventArgs> {
void invoke(Object sender,
T e);
}

Просмотреть файл

@ -0,0 +1,19 @@
package com.microsoft.azure.elasticdb.core.commons.helpers;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
public class ReferenceObjectHelper<T> {
public T outValue;
public T argValue;
public ReferenceObjectHelper(T referenceValue) {
outValue = referenceValue;
}
}

Просмотреть файл

@ -0,0 +1,26 @@
package com.microsoft.azure.elasticdb.core.commons.helpers;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
public class Resources {
private Resources() {
}
public static String ArgumentCannotBeGreaterThanBaseline = "The specified argument %1$s cannot be greater than its ceiling value of %2$s.";
public static String ArgumentCannotBeNegative = "The specified argument %1$s cannot be initialized with a negative value.";
public static String DefaultRetryStrategyMappingNotFound = "Default retry strategy for technology %1$s, named '%2$s', is not defined.";
public static String DefaultRetryStrategyNotFound = "Default retry strategy for technology %1$s was not not defined, and there is no overall default strategy.";
public static String ExceptionRetryManagerAlreadySet = "The RetryManager is already set.";
public static String ExceptionRetryManagerNotSet = "The default RetryManager has not been set. Set it by invoking the RetryManager.SetDefault static method, or if you are using declarative configuration, you can invoke the RetryPolicyFactory.CreateDefault() method to automatically create the retry manager from the configuration file.";
public static String RetryLimitExceeded = "The action has exceeded its defined retry limit.";
public static String RetryStrategyNotFound = "The retry strategy with name '%1$s' cannot be found.";
public static String StringCannotBeEmpty = "The specified string argument %1$s must not be empty.";
public static String TaskCannotBeNull = "The specified argument '%1$s' cannot return a null task when invoked.";
public static String TaskMustBeScheduled = "The specified argument '%1$s' must return a scheduled task (also known as 'hot' task) when invoked.";
}

Просмотреть файл

@ -0,0 +1,36 @@
package com.microsoft.azure.elasticdb.core.commons.logging;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import java.util.UUID;
/**
* Utility class to set and restore the CorrelationManager ActivityId via the using pattern.
*/
public final class ActivityIdScope implements AutoCloseable {
/**
* The previous activity id that was in scope.
*/
private UUID previousActivityId;
/**
* Creates an instance of the <see cref="ActivityIdScope"/> class.
*/
public ActivityIdScope(UUID activityId) {
// TODO
}
/**
* Restores the previous activity id when this instance is disposed.
*/
public void close() {
// TODO
}
}

Просмотреть файл

@ -0,0 +1,58 @@
package com.microsoft.azure.elasticdb.core.commons.patterns;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* A disposable object which opts-out of disposing the inner disposable only when instructed by the caller. <typeparam name="T"></typeparam>
*/
public final class ConditionalDisposable<T extends AutoCloseable> implements AutoCloseable {
/**
* Inner disposable object.
*/
private T innerDisposable;
private boolean doNotDispose;
/**
* Constructor which takes an inner disposable object.
*/
public ConditionalDisposable(T innerDisposable) {
this.innerDisposable = innerDisposable;
}
/**
* Used for notifying about disposable decision on inner object.
*/
public boolean getDoNotDispose() {
return doNotDispose;
}
/**
* Used for notifying about disposable decision on inner object.
*/
public void setDoNotDispose(boolean value) {
doNotDispose = value;
}
/**
* Disposes the inner object if doNotDispose is set to false.
*/
public void close() throws Exception {
if (!this.getDoNotDispose()) {
innerDisposable.close();
}
}
/**
* Gets the inner disposable object.
*/
public T getValue() {
return this.innerDisposable;
}
}

Просмотреть файл

@ -0,0 +1,133 @@
package com.microsoft.azure.elasticdb.core.commons.transientfaulthandling;
import java.time.Duration;
import java.util.concurrent.ThreadLocalRandom;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
/**
* A retry strategy with backoff parameters for calculating the exponential delay between retries.
*/
public class ExponentialBackoff extends RetryStrategy {
private int retryCount;
private Duration minBackoff = Duration.ZERO;
private Duration maxBackoff = Duration.ZERO;
private Duration deltaBackoff = Duration.ZERO;
/**
* Initializes a new instance of the <see cref="ExponentialBackoff"/> class.
*/
public ExponentialBackoff() {
this(DEFAULT_CLIENT_RETRY_COUNT, DEFAULT_MIN_BACKOFF, DEFAULT_MAX_BACKOFF, DEFAULT_CLIENT_BACKOFF);
}
/**
* Initializes a new instance of the <see cref="ExponentialBackoff"/> class with the specified retry settings.
*
* @param retryCount
* The maximum number of retry attempts.
* @param minBackoff
* The minimum backoff time
* @param maxBackoff
* The maximum backoff time.
* @param deltaBackoff
* The value that will be used to calculate a random delta in the exponential delay between retries.
*/
public ExponentialBackoff(int retryCount,
Duration minBackoff,
Duration maxBackoff,
Duration deltaBackoff) {
this(null, retryCount, minBackoff, maxBackoff, deltaBackoff, DEFAULT_FIRST_FAST_RETRY);
}
/**
* Initializes a new instance of the <see cref="ExponentialBackoff"/> class with the specified name and retry settings.
*
* @param name
* The name of the retry strategy.
* @param retryCount
* The maximum number of retry attempts.
* @param minBackoff
* The minimum backoff time
* @param maxBackoff
* The maximum backoff time.
* @param deltaBackoff
* The value that will be used to calculate a random delta in the exponential delay between retries.
*/
public ExponentialBackoff(String name,
int retryCount,
Duration minBackoff,
Duration maxBackoff,
Duration deltaBackoff) {
this(name, retryCount, minBackoff, maxBackoff, deltaBackoff, DEFAULT_FIRST_FAST_RETRY);
}
/**
* Initializes a new instance of the <see cref="ExponentialBackoff"/> class with the specified name, retry settings, and fast retry option.
*
* @param name
* The name of the retry strategy.
* @param retryCount
* The maximum number of retry attempts.
* @param minBackoff
* The minimum backoff time
* @param maxBackoff
* The maximum backoff time.
* @param deltaBackoff
* The value that will be used to calculate a random delta in the exponential delay between retries.
* @param firstFastRetry
* true to immediately retry in the first attempt; otherwise, false. The subsequent retries will remain subject to the configured retry
* interval.
*/
public ExponentialBackoff(String name,
int retryCount,
Duration minBackoff,
Duration maxBackoff,
Duration deltaBackoff,
boolean firstFastRetry) {
super(name, firstFastRetry);
Guard.argumentNotNegativeValue(retryCount, "retryCount");
Guard.argumentNotNegativeValue(minBackoff.getSeconds(), "minBackoff");
Guard.argumentNotNegativeValue(maxBackoff.getSeconds(), "maxBackoff");
Guard.argumentNotNegativeValue(deltaBackoff.getSeconds(), "deltaBackoff");
Guard.argumentNotGreaterThan(minBackoff.getSeconds(), maxBackoff.getSeconds(), "minBackoff");
this.retryCount = retryCount;
this.minBackoff = minBackoff;
this.maxBackoff = maxBackoff;
this.deltaBackoff = deltaBackoff;
}
/**
* Returns the corresponding ShouldRetry delegate.
*
* @return The ShouldRetry delegate.
*/
@Override
public ShouldRetry getShouldRetry() {
return (int currentRetryCount,
RuntimeException lastException,
ReferenceObjectHelper<Duration> refRetryInterval) -> {
if (currentRetryCount < this.retryCount) {
Double delta = this.deltaBackoff == Duration.ZERO ? 0.0
: (Math.pow(2.0, currentRetryCount) - 1) * ThreadLocalRandom.current().nextDouble((this.deltaBackoff.getSeconds() * 0.8),
(this.deltaBackoff.getSeconds() * 1.2));
Long interval = Math.min((this.minBackoff.getSeconds() + delta.intValue()), this.maxBackoff.getSeconds());
refRetryInterval.argValue = Duration.ofMillis(interval);
return true;
}
refRetryInterval.argValue = Duration.ZERO;
return false;
};
}
}

Просмотреть файл

@ -0,0 +1,124 @@
package com.microsoft.azure.elasticdb.core.commons.transientfaulthandling;
import java.time.Duration;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
/**
* Represents a retry strategy with a specified number of retry attempts and a default, fixed time interval between retries.
*/
public class FixedInterval extends RetryStrategy {
private int retryCount;
private Duration retryInterval = Duration.ZERO;
/**
* Initializes a new instance of the <see cref="FixedInterval"/> class.
*/
public FixedInterval() {
this(DEFAULT_CLIENT_RETRY_COUNT);
}
/**
* Initializes a new instance of the <see cref="FixedInterval"/> class with the specified number of retry attempts.
*
* @param retryCount
* The number of retry attempts.
*/
public FixedInterval(int retryCount) {
this(retryCount, DEFAULT_RETRY_INTERVAL);
}
/**
* Initializes a new instance of the <see cref="FixedInterval"/> class with the specified number of retry attempts and time interval.
*
* @param retryCount
* The number of retry attempts.
* @param retryInterval
* The time interval between retries.
*/
public FixedInterval(int retryCount,
Duration retryInterval) {
this(null, retryCount, retryInterval, DEFAULT_FIRST_FAST_RETRY);
}
/**
* Initializes a new instance of the <see cref="FixedInterval"/> class with the specified number of retry attempts, time interval, and retry
* strategy.
*
* @param name
* The retry strategy name.
* @param retryCount
* The number of retry attempts.
* @param retryInterval
* The time interval between retries.
*/
public FixedInterval(String name,
int retryCount,
Duration retryInterval) {
this(name, retryCount, retryInterval, DEFAULT_FIRST_FAST_RETRY);
}
/**
* Initializes a new instance of the <see cref="FixedInterval"/> class with the specified number of retry attempts, time interval, retry strategy,
* and fast start option.
*
* @param name
* The retry strategy name.
* @param retryCount
* The number of retry attempts.
* @param retryInterval
* The time interval between retries.
* @param firstFastRetry
* true to immediately retry in the first attempt; otherwise, false. The subsequent retries will remain subject to the configured retry
* interval.
*/
public FixedInterval(String name,
int retryCount,
Duration retryInterval,
boolean firstFastRetry) {
super(name, firstFastRetry);
Guard.argumentNotNegativeValue(retryCount, "retryCount");
Guard.argumentNotNegativeValue(retryInterval.getSeconds(), "retryInterval");
this.retryCount = retryCount;
this.retryInterval = retryInterval;
}
/**
* Returns the corresponding ShouldRetry delegate.
*
* @return The ShouldRetry delegate.
*/
@Override
public ShouldRetry getShouldRetry() {
if (retryCount == 0) {
return (int currentRetryCount,
RuntimeException lastException,
ReferenceObjectHelper<Duration> interval) -> {
interval.argValue = Duration.ZERO;
return false;
};
}
return (int currentRetryCount,
RuntimeException lastException,
ReferenceObjectHelper<Duration> interval) -> {
if (currentRetryCount < retryCount) {
interval.argValue = retryInterval;
return true;
}
interval.argValue = Duration.ZERO;
return false;
};
}
}

Просмотреть файл

@ -0,0 +1,108 @@
package com.microsoft.azure.elasticdb.core.commons.transientfaulthandling;
import java.util.Locale;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.helpers.Resources;
/**
* Implements the common guard methods.
*/
public class Guard {
/**
* Checks a string argument to ensure that it isn't null or empty.
*
* @param argumentValue
* The argument value to check.
* @param argumentName
* The name of the argument.
* @return The return value should be ignored. It is intended to be used only when validating arguments during instance creation (for example,
* when calling the base constructor).
*/
public static boolean argumentNotNullOrEmptyString(String argumentValue,
String argumentName) {
argumentNotNull(argumentValue, argumentName);
if (argumentValue.length() == 0) {
throw new IllegalArgumentException(String.format(Locale.getDefault(), Resources.StringCannotBeEmpty, argumentName));
}
return true;
}
/**
* Checks an argument to ensure that it isn't null.
*
* @param argumentValue
* The argument value to check.
* @param argumentName
* The name of the argument.
* @return The return value should be ignored. It is intended to be used only when validating arguments during instance creation (for example,
* when calling the base constructor).
*/
public static boolean argumentNotNull(Object argumentValue,
String argumentName) {
if (argumentValue == null) {
throw new IllegalArgumentException(argumentName);
}
return true;
}
/**
* Checks an argument to ensure that its 32-bit signed value isn't negative.
*
* @param argumentValue
* The Integer value of the argument.
* @param argumentName
* The name of the argument for diagnostic purposes.
*/
public static void argumentNotNegativeValue(int argumentValue,
String argumentName) {
if (argumentValue < 0) {
throw new IllegalArgumentException(String.format(Locale.getDefault(), Resources.ArgumentCannotBeNegative, argumentName));
}
}
/**
* Checks an argument to ensure that its 64-bit signed value isn't negative.
*
* @param argumentValue
* The Long value of the argument.
* @param argumentName
* The name of the argument for diagnostic purposes.
*/
public static void argumentNotNegativeValue(long argumentValue,
String argumentName) {
if (argumentValue < 0) {
throw new IllegalArgumentException(String.format(Locale.getDefault(), Resources.ArgumentCannotBeNegative, argumentName));
}
}
/**
* Checks an argument to ensure that its value doesn't exceed the specified ceiling baseline.
*
* @param argumentValue
* The Double value of the argument.
* @param ceilingValue
* The Double ceiling value of the argument.
* @param argumentName
* The name of the argument for diagnostic purposes.
*/
public static void argumentNotGreaterThan(double argumentValue,
double ceilingValue,
String argumentName) {
if (argumentValue > ceilingValue) {
throw new IllegalArgumentException(
String.format(Locale.getDefault(), Resources.ArgumentCannotBeGreaterThanBaseline, argumentName, ceilingValue));
}
}
}

Просмотреть файл

@ -0,0 +1,24 @@
package com.microsoft.azure.elasticdb.core.commons.transientfaulthandling;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Defines an interface that must be implemented by custom components responsible for detecting specific transient conditions.
*/
public interface ITransientErrorDetectionStrategy {
/**
* Determines whether the specified exception represents a transient failure that can be compensated by a retry.
*
* @param ex
* The exception object to be verified.
* @return true if the specified exception is considered as transient; otherwise, false.
*/
boolean isTransient(Exception ex);
}

Просмотреть файл

@ -0,0 +1,116 @@
package com.microsoft.azure.elasticdb.core.commons.transientfaulthandling;
import java.time.Duration;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
/**
* A retry strategy with a specified number of retry attempts and an incremental time interval between retries.
*/
public class Incremental extends RetryStrategy {
private int retryCount;
private Duration initialInterval = Duration.ZERO;
private Duration increment = Duration.ZERO;
/**
* Initializes a new instance of the <see cref="Incremental"/> class.
*/
public Incremental() {
this(DEFAULT_CLIENT_RETRY_COUNT, DEFAULT_RETRY_INTERVAL, DEFAULT_RETRY_INCREMENT);
}
/**
* Initializes a new instance of the <see cref="Incremental"/> class with the specified retry settings.
*
* @param retryCount
* The number of retry attempts.
* @param initialInterval
* The initial interval that will apply for the first retry.
* @param increment
* The incremental time value that will be used to calculate the progressive delay between retries.
*/
public Incremental(int retryCount,
Duration initialInterval,
Duration increment) {
this(null, retryCount, initialInterval, increment);
}
/**
* Initializes a new instance of the <see cref="Incremental"/> class with the specified name and retry settings.
*
* @param name
* The retry strategy name.
* @param retryCount
* The number of retry attempts.
* @param initialInterval
* The initial interval that will apply for the first retry.
* @param increment
* The incremental time value that will be used to calculate the progressive delay between retries.
*/
public Incremental(String name,
int retryCount,
Duration initialInterval,
Duration increment) {
this(name, retryCount, initialInterval, increment, DEFAULT_FIRST_FAST_RETRY);
}
/**
* Initializes a new instance of the <see cref="Incremental"/> class with the specified number of retry attempts, time interval, retry strategy,
* and fast start option.
*
* @param name
* The retry strategy name.
* @param retryCount
* The number of retry attempts.
* @param initialInterval
* The initial interval that will apply for the first retry.
* @param increment
* The incremental time value that will be used to calculate the progressive delay between retries.
* @param firstFastRetry
* true to immediately retry in the first attempt; otherwise, false. The subsequent retries will remain subject to the configured retry
* interval.
*/
public Incremental(String name,
int retryCount,
Duration initialInterval,
Duration increment,
boolean firstFastRetry) {
super(name, firstFastRetry);
Guard.argumentNotNegativeValue(retryCount, "retryCount");
Guard.argumentNotNegativeValue(initialInterval.getSeconds(), "initialInterval");
Guard.argumentNotNegativeValue(increment.getSeconds(), "increment");
this.retryCount = retryCount;
this.initialInterval = initialInterval;
this.increment = increment;
}
/**
* Returns the corresponding ShouldRetry delegate.
*
* @return The ShouldRetry delegate.
*/
@Override
public ShouldRetry getShouldRetry() {
return (int currentRetryCount,
RuntimeException lastException,
ReferenceObjectHelper<Duration> retryInterval) -> {
if (currentRetryCount < retryCount) {
retryInterval.argValue = Duration.ofSeconds(initialInterval.getSeconds() + (increment.getSeconds() * currentRetryCount));
return true;
}
retryInterval.argValue = Duration.ZERO;
return false;
};
}
}

Просмотреть файл

@ -0,0 +1,51 @@
package com.microsoft.azure.elasticdb.core.commons.transientfaulthandling;
import java.util.function.Function;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.google.common.base.Preconditions;
/**
* Defines the retry behavior to use for detecting transient errors.
*/
public final class RetryBehavior {
/**
* Retry policy that tries up to 5 times with a 1 second backoff before giving up.
*/
private static final RetryBehavior DEFAULT_RETRY_BEHAVIOR = new RetryBehavior((e) -> false);
/**
* Transient error detector predicate which decides whether a given exception is transient or not.
*/
private Function<Exception, Boolean> transientErrorDetector;
/**
* Initializes an instance of the <see cref="RetryBehavior"/> class.
*
* @param transientErrorDetector
* Function that detects transient errors given an exception. The function needs to return true for an exception that should be treated
* as transient.
*/
public RetryBehavior(Function<Exception, Boolean> transientErrorDetector) {
this.transientErrorDetector = Preconditions.checkNotNull(transientErrorDetector);
}
/**
* Gets the default retry behavior. The default retry behavior has a built-in set of exceptions that are considered transient. You may create and
* use a custom <see cref="RetryBehavior"/> object in order to treat additional exceptions as transient.
*/
public static RetryBehavior getDefaultRetryBehavior() {
return DEFAULT_RETRY_BEHAVIOR;
}
public Function<Exception, Boolean> getTransientErrorDetector() {
return transientErrorDetector;
}
}

Просмотреть файл

@ -0,0 +1,396 @@
package com.microsoft.azure.elasticdb.core.commons.transientfaulthandling;
import java.time.Duration;
import java.util.concurrent.Callable;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.helpers.Event;
import com.microsoft.azure.elasticdb.core.commons.helpers.EventHandler;
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
/**
* Provides the base implementation of the retry mechanism for unreliable actions and transient conditions.
*/
public class RetryPolicy {
/**
* Retry policy that tries up to 5 times with exponential backoff before giving up.
*/
private static final RetryPolicy DEFAULT_RETRY_POLICY = new RetryPolicy(5, RetryStrategy.DEFAULT_MIN_BACKOFF, RetryStrategy.DEFAULT_MAX_BACKOFF,
RetryStrategy.DEFAULT_CLIENT_BACKOFF);
private static RetryPolicy noRetry = new RetryPolicy(new TransientErrorIgnoreStrategy(), RetryStrategy.getNoRetry());
private static RetryPolicy defaultFixed = new RetryPolicy(new TransientErrorCatchAllStrategy(), RetryStrategy.getDefaultFixed());
private static RetryPolicy defaultProgressive = new RetryPolicy(new TransientErrorCatchAllStrategy(), RetryStrategy.getDefaultProgressive());
private static RetryPolicy defaultExponential = new RetryPolicy(new TransientErrorCatchAllStrategy(), RetryStrategy.getDefaultExponential());
/**
* An instance of a callback delegate that will be invoked whenever a retry condition is encountered.
*/
public Event<EventHandler<RetryingEventArgs>> retrying;
/**
* Gets the number of retries.
*/
private int retryCount;
/**
* Gets minimum backoff time.
*/
private Duration minBackOff = Duration.ZERO;
/**
* Gets maximum backoff time.
*/
private Duration maxBackOff = Duration.ZERO;
/**
* Gets value used to calculate random delta in the exponential delay between retries. Time delta for next retry attempt is 2^(currentRetryCount -
* 1) * random value between 80% and 120% of DeltaBackOff.
*/
private Duration deltaBackOff = Duration.ZERO;
/**
* Gets the retry strategy.
*/
private RetryStrategy retryStrategy;
/**
* Gets the instance of the error detection strategy.
*/
private ITransientErrorDetectionStrategy errorDetectionStrategy;
/**
* Initializes an instance of the <see cref="RetryPolicy"/> class.
*
* @param retryCount
* The number of retry attempts.
* @param minBackOff
* Minimum backoff time for exponential backoff policy.
* @param maxBackOff
* Maximum backoff time for exponential backoff policy.
* @param deltaBackOff
* Delta backoff time for exponential backoff policy.
*/
public RetryPolicy(int retryCount,
Duration minBackOff,
Duration maxBackOff,
Duration deltaBackOff) {
this.setRetryCount((retryCount < 0) ? 0 : retryCount);
this.setMinBackOff((minBackOff.getSeconds() < Duration.ZERO.getSeconds()) ? Duration.ZERO : minBackOff);
this.setMaxBackOff((maxBackOff.getSeconds() < Duration.ZERO.getSeconds()) ? Duration.ZERO : maxBackOff);
this.setDeltaBackOff((deltaBackOff.getSeconds() < Duration.ZERO.getSeconds()) ? Duration.ZERO : deltaBackOff);
this.setRetryStrategy(getExponentialRetryStrategy());
}
/**
* Initializes a new instance of the <see cref="RetryPolicy"/> class with the specified number of retry attempts and parameters defining the
* progressive delay between retries.
*
* @param errorDetectionStrategy
* The <see cref="ITransientErrorDetectionStrategy"/> that is responsible for detecting transient conditions.
* @param retryStrategy
* The strategy to use for this retry policy.
*/
public RetryPolicy(ITransientErrorDetectionStrategy errorDetectionStrategy,
RetryStrategy retryStrategy) {
Guard.argumentNotNull(errorDetectionStrategy, "errorDetectionStrategy");
Guard.argumentNotNull(retryStrategy, "retryStrategy");
this.setErrorDetectionStrategy(errorDetectionStrategy);
this.setRetryStrategy(retryStrategy);
this.retrying = new Event<>();
}
/**
* Initializes a new instance of the <see cref="RetryPolicy"/> class with the specified number of retry attempts and default fixed time interval
* between retries.
*
* @param errorDetectionStrategy
* The <see cref="ITransientErrorDetectionStrategy"/> that is responsible for detecting transient conditions.
* @param retryCount
* The number of retry attempts.
*/
public RetryPolicy(ITransientErrorDetectionStrategy errorDetectionStrategy,
int retryCount) {
this(errorDetectionStrategy, new FixedInterval(retryCount));
}
/**
* Initializes a new instance of the <see cref="RetryPolicy"/> class with the specified number of retry attempts and fixed time interval between
* retries.
*
* @param errorDetectionStrategy
* The <see cref="ITransientErrorDetectionStrategy"/> that is responsible for detecting transient conditions.
* @param retryCount
* The number of retry attempts.
* @param retryInterval
* The interval between retries.
*/
public RetryPolicy(ITransientErrorDetectionStrategy errorDetectionStrategy,
int retryCount,
Duration retryInterval) {
this(errorDetectionStrategy, new FixedInterval(retryCount, retryInterval));
}
/**
* Initializes a new instance of the <see cref="RetryPolicy"/> class with the specified number of retry attempts and backoff parameters for
* calculating the exponential delay between retries.
*
* @param errorDetectionStrategy
* The <see cref="ITransientErrorDetectionStrategy"/> that is responsible for detecting transient conditions.
* @param retryCount
* The number of retry attempts.
* @param minBackoff
* The minimum backoff time.
* @param maxBackoff
* The maximum backoff time.
* @param deltaBackoff
* The time value that will be used to calculate a random delta in the exponential delay between retries.
*/
public RetryPolicy(ITransientErrorDetectionStrategy errorDetectionStrategy,
int retryCount,
Duration minBackoff,
Duration maxBackoff,
Duration deltaBackoff) {
this(errorDetectionStrategy, new ExponentialBackoff(retryCount, minBackoff, maxBackoff, deltaBackoff));
}
/**
* Initializes a new instance of the <see cref="RetryPolicy"/> class with the specified number of retry attempts and parameters defining the
* progressive delay between retries.
*
* @param errorDetectionStrategy
* The <see cref="ITransientErrorDetectionStrategy"/> that is responsible for detecting transient conditions.
* @param retryCount
* The number of retry attempts.
* @param initialInterval
* The initial interval that will apply for the first retry.
* @param increment
* The incremental time value that will be used to calculate the progressive delay between retries.
*/
public RetryPolicy(ITransientErrorDetectionStrategy errorDetectionStrategy,
int retryCount,
Duration initialInterval,
Duration increment) {
this(errorDetectionStrategy, new Incremental(retryCount, initialInterval, increment));
}
/**
* Gets the default retry policy. 5 retries at 1 second intervals.
*/
public static RetryPolicy getDefaultRetryPolicy() {
return DEFAULT_RETRY_POLICY;
}
/**
* Returns a default policy that performs no retries, but invokes the action only once.
*/
public static RetryPolicy getNoRetry() {
return noRetry;
}
/**
* Returns a default policy that implements a fixed retry interval configured with the default <see cref="FixedInterval"/> retry strategy. The
* default retry policy treats all caught exceptions as transient errors.
*/
public static RetryPolicy getDefaultFixed() {
return defaultFixed;
}
/**
* Returns a default policy that implements a progressive retry interval configured with the default <see cref="Incremental"/> retry strategy. The
* default retry policy treats all caught exceptions as transient errors.
*/
public static RetryPolicy getDefaultProgressive() {
return defaultProgressive;
}
/**
* Returns a default policy that implements a random exponential retry interval configured with the default <see cref="FixedInterval"/> retry
* strategy. The default retry policy treats all caught exceptions as transient errors.
*/
public static RetryPolicy getDefaultExponential() {
return defaultExponential;
}
public int getRetryCount() {
return retryCount;
}
private void setRetryCount(int value) {
retryCount = value;
}
public Duration getMinBackOff() {
return minBackOff;
}
private void setMinBackOff(Duration value) {
minBackOff = value;
}
public Duration getMaxBackOff() {
return maxBackOff;
}
private void setMaxBackOff(Duration value) {
maxBackOff = value;
}
public Duration getDeltaBackOff() {
return deltaBackOff;
}
private void setDeltaBackOff(Duration value) {
deltaBackOff = value;
}
/**
* Marshals this instance into the TFH library RetryStrategy type.
*
* @return The RetryStrategy
*/
public RetryStrategy getExponentialRetryStrategy() {
return new ExponentialBackoff(this.getRetryCount(), this.getMinBackOff(), this.getMaxBackOff(), this.getDeltaBackOff());
}
/**
* String representation of <see cref="RetryPolicy"/>.
*/
@Override
public String toString() {
return String.format("RetryCount: %1$s, , MinBackoff: %2$s, MaxBackoff: %3$s, DeltaBackoff: %4$s", this.getRetryCount(), this.getMinBackOff(),
this.getMaxBackOff(), this.getDeltaBackOff());
}
public final RetryStrategy getRetryStrategy() {
return retryStrategy;
}
private void setRetryStrategy(RetryStrategy value) {
retryStrategy = value;
}
public final ITransientErrorDetectionStrategy getErrorDetectionStrategy() {
return errorDetectionStrategy;
}
private void setErrorDetectionStrategy(ITransientErrorDetectionStrategy value) {
errorDetectionStrategy = value;
}
/**
* Repetitively executes the specified action while it satisfies the current retry policy.
*
* @param action
* A delegate that represents the executable action that doesn't return any results.
*/
public void executeAction(Runnable action) throws Exception {
Guard.argumentNotNull(action, "action");
this.executeAction(() -> {
action.run();
return null;
});
}
/**
* Repetitively executes the specified action while it satisfies the current retry policy. <typeparam name="ResultT">The type of result expected
* from the executable action.</typeparam>
*
* @param callable
* A delegate that represents the executable action that returns the result of type <typeparamref name="ResultT"/>.
* @return The result from the action.
*/
public <ResultT> ResultT executeAction(Callable<ResultT> callable) throws Exception {
Guard.argumentNotNull(callable, "callable");
int retryCount = 0;
Duration delay = Duration.ZERO;
RuntimeException lastError;
ShouldRetry shouldRetry = this.getRetryStrategy().getShouldRetry();
for (;;) {
lastError = null;
try {
return callable.call();
/*
* } catch (RetryLimitExceededException limitExceededEx) { // The user code can throw a RetryLimitExceededException to force the exit
* from the retry // loop. The RetryLimitExceeded exception can have an inner exception attached to it. This // is the exception which
* we will have to throw up the stack so that callers can handle it. if (limitExceededEx.getCause() != null) { throw
* limitExceededEx.getCause(); } else { return null; }
*/
}
catch (RuntimeException ex) {
lastError = ex;
ReferenceObjectHelper<Duration> tempRefDelay = new ReferenceObjectHelper<>(delay);
if (this.getErrorDetectionStrategy().isTransient(lastError) && shouldRetry.invoke(retryCount++, lastError, tempRefDelay)) {
delay = tempRefDelay.argValue;
}
else {
throw ex;
}
}
// Perform an extra check in the delay interval. Should prevent from accidentally ending up
// with the value of -1 that will block a thread indefinitely. In addition, any other negative
// numbers will cause an OutOfRangeException fault that will be thrown by Thread.Sleep.
if (delay.getSeconds() < 0) {
delay = Duration.ZERO;
}
this.onRetrying(retryCount, lastError, delay);
if (retryCount > 1 || !this.getRetryStrategy().getFastFirstRetry()) {
Thread.sleep(delay.getSeconds());
}
}
}
private void onRetrying(int retryCount,
RuntimeException lastError,
Duration delay) {
if (this.retrying != null) {
this.retrying.listeners().forEach(e -> e.invoke(this, new RetryingEventArgs(retryCount, delay, lastError)));
}
}
/**
* Implements a strategy that treats all exceptions as transient errors.
*/
private static final class TransientErrorCatchAllStrategy implements ITransientErrorDetectionStrategy {
/**
* Always returns true.
*
* @param ex
* The exception.
* @return Always true.
*/
@Override
public boolean isTransient(Exception ex) {
return true;
}
}
/**
* Implements a strategy that ignores any transient errors.
*/
private static final class TransientErrorIgnoreStrategy implements ITransientErrorDetectionStrategy {
/**
* Always returns false.
*
* @param ex
* The exception.
* @return Always false.
*/
@Override
public boolean isTransient(Exception ex) {
return false;
}
}
}

Просмотреть файл

@ -0,0 +1,143 @@
package com.microsoft.azure.elasticdb.core.commons.transientfaulthandling;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import java.time.Duration;
/**
* Represents a retry strategy that determines the number of retry attempts and the interval between retries.
*/
public abstract class RetryStrategy {
/// #region Public members
/**
* Represents the default number of retry attempts.
*/
public static final int DEFAULT_CLIENT_RETRY_COUNT = 10;
/**
* Represents the default amount of time used when calculating a random delta in the exponential delay between retries.
*/
public static final Duration DEFAULT_CLIENT_BACKOFF = Duration.ofSeconds(10);
/**
* Represents the default maximum amount of time used when calculating the exponential delay between retries.
*/
public static final Duration DEFAULT_MAX_BACKOFF = Duration.ofSeconds(30);
/**
* Represents the default minimum amount of time used when calculating the exponential delay between retries.
*/
public static final Duration DEFAULT_MIN_BACKOFF = Duration.ofSeconds(1);
/**
* Represents the default interval between retries.
*/
public static final Duration DEFAULT_RETRY_INTERVAL = Duration.ofSeconds(1);
/**
* Represents the default time increment between retry attempts in the progressive delay policy.
*/
public static final Duration DEFAULT_RETRY_INCREMENT = Duration.ofSeconds(1);
/**
* Represents the default flag indicating whether the first retry attempt will be made immediately, whereas subsequent retries will remain subject
* to the retry interval.
*/
public static final boolean DEFAULT_FIRST_FAST_RETRY = true;
/// #endregion
private static RetryStrategy noRetry = new FixedInterval(0, DEFAULT_RETRY_INTERVAL);
private static RetryStrategy defaultFixed = new FixedInterval(DEFAULT_CLIENT_RETRY_COUNT, DEFAULT_RETRY_INTERVAL);
private static RetryStrategy defaultProgressive = new Incremental(DEFAULT_CLIENT_RETRY_COUNT, DEFAULT_RETRY_INTERVAL, DEFAULT_RETRY_INCREMENT);
private static RetryStrategy defaultExponential = new ExponentialBackoff(DEFAULT_CLIENT_RETRY_COUNT, DEFAULT_MIN_BACKOFF, DEFAULT_MAX_BACKOFF,
DEFAULT_CLIENT_BACKOFF);
/**
* Gets or sets a value indicating whether the first retry attempt will be made immediately, whereas subsequent retries will remain subject to the
* retry interval.
*/
private boolean fastFirstRetry;
/**
* Gets the name of the retry strategy.
*/
private String name;
/**
* Initializes a new instance of the <see cref="RetryStrategy"/> class.
*
* @param name
* The name of the retry strategy.
* @param firstFastRetry
* true to immediately retry in the first attempt; otherwise, false. The subsequent retries will remain subject to the configured retry
* interval.
*/
protected RetryStrategy(String name,
boolean firstFastRetry) {
this.setName(name);
this.setFastFirstRetry(firstFastRetry);
}
/**
* Returns a default policy that performs no retries, but invokes the action only once.
*/
public static RetryStrategy getNoRetry() {
return noRetry;
}
/**
* Returns a default policy that implements a fixed retry interval configured with the <see cref="RetryStrategy.DEFAULT_CLIENT_RETRY_COUNT"/> and
* <see cref="RetryStrategy.DEFAULT_RETRY_INTERVAL"/> parameters. The default retry policy treats all caught exceptions as transient errors.
*/
public static RetryStrategy getDefaultFixed() {
return defaultFixed;
}
/**
* Returns a default policy that implements a progressive retry interval configured with the
* <see cref="RetryStrategy.DEFAULT_CLIENT_RETRY_COUNT"/>, <see cref="RetryStrategy.DEFAULT_RETRY_INTERVAL"/>, and
* <see cref="RetryStrategy.DEFAULT_RETRY_INCREMENT"/> parameters. The default retry policy treats all caught exceptions as transient errors.
*/
public static RetryStrategy getDefaultProgressive() {
return defaultProgressive;
}
/**
* Returns a default policy that implements a random exponential retry interval configured with the
* <see cref="RetryStrategy.DEFAULT_CLIENT_RETRY_COUNT"/>, <see cref="RetryStrategy.DEFAULT_MIN_BACKOFF"/>,
* <see cref="RetryStrategy.DEFAULT_MAX_BACKOFF"/>, and <see cref="RetryStrategy.DEFAULT_CLIENT_BACKOFF"/> parameters. The default retry policy
* treats all caught exceptions as transient errors.
*/
public static RetryStrategy getDefaultExponential() {
return defaultExponential;
}
public final boolean getFastFirstRetry() {
return fastFirstRetry;
}
public final void setFastFirstRetry(boolean value) {
fastFirstRetry = value;
}
public final String getName() {
return name;
}
private void setName(String value) {
name = value;
}
/**
* Returns the corresponding ShouldRetry delegate.
*
* @return The ShouldRetry delegate.
*/
public abstract ShouldRetry getShouldRetry();
}

Просмотреть файл

@ -0,0 +1,86 @@
package com.microsoft.azure.elasticdb.core.commons.transientfaulthandling;
import java.time.Duration;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.helpers.EventArgs;
/**
* Shard management retrying event arguments.
*/
public final class RetryingEventArgs extends EventArgs {
/**
* Gets the current retry count.
*/
private int currentRetryCount;
/**
* Gets the delay that indicates how long the current thread will be suspended before the next iteration is invoked.
*/
private Duration delay = Duration.ZERO;
/**
* Gets the exception that caused the retry conditions to occur.
*/
private RuntimeException lastException;
/**
* Initializes new instance of <see cref="RetryingEventArgs"/> class.
*
* @param arg
* RetryingEventArgs from RetryPolicy.retrying event.
*/
public RetryingEventArgs(RetryingEventArgs arg) {
this.setCurrentRetryCount(arg.currentRetryCount);
this.setDelay(arg.delay);
this.setLastException(arg.lastException);
}
/**
* Initializes a new instance of the <see cref="RetryingEventArgs"/> class.
*
* @param retryCount
* The current retry attempt count.
* @param delay
* The delay that indicates how long the current thread will be suspended before the next iteration is invoked.
* @param ex
* The exception that caused the retry conditions to occur.
*/
public RetryingEventArgs(int retryCount,
Duration delay,
RuntimeException ex) {
this.setCurrentRetryCount(retryCount);
this.setDelay(delay);
this.setLastException(ex);
}
public int getCurrentRetryCount() {
return currentRetryCount;
}
private void setCurrentRetryCount(int value) {
currentRetryCount = value;
}
public Duration getDelay() {
return delay;
}
private void setDelay(Duration value) {
delay = value;
}
public RuntimeException getLastException() {
return lastException;
}
private void setLastException(RuntimeException value) {
lastException = value;
}
}

Просмотреть файл

@ -0,0 +1,35 @@
package com.microsoft.azure.elasticdb.core.commons.transientfaulthandling;
import java.time.Duration;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
/**
* Defines a callback delegate that will be invoked whenever a retry condition is encountered.
*/
@FunctionalInterface
public interface ShouldRetry {
/**
* Defines a callback delegate that will be invoked whenever a retry condition is encountered.
*
* @param retryCount
* The current retry attempt count.
* @param lastException
* The exception that caused the retry conditions to occur.
* @param delay
* The delay that indicates how long the current thread will be suspended before the next iteration is invoked.
* @return <see langword="true"/> if a retry is allowed; otherwise, <see langword="false"/>.
*/
boolean invoke(int retryCount,
RuntimeException lastException,
ReferenceObjectHelper<Duration> delay);
}

Просмотреть файл

@ -0,0 +1,221 @@
package com.microsoft.azure.elasticdb.core.commons.transientfaulthandling;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*
* Notes: This class was forked from the Windows Azure Transient Fault Handling Library ("Topaz") available here: http://topaz.codeplex.com/ and will
* now be maintained by Microsoft. In the future, we should consider moving this to a config file to make updates easier in-case WA Sql Database
* decides to change their error codes.
*/
import java.sql.SQLException;
import java.util.concurrent.TimeoutException;
/**
* Provides the transient error detection logic for transient faults that are specific to SQL Database.
*/
public final class SqlDatabaseTransientErrorDetectionStrategy implements ITransientErrorDetectionStrategy {
/**
* Determines whether the specified exception represents a transient failure that can be compensated by a retry.
*
* @param ex
* The exception object to be verified.
* @return true if the specified exception is considered as transient; otherwise, false.
*/
@Override
public boolean isTransient(Exception ex) {
// TODO: Complete this method.
if (ex != null) {
SQLException sqlException;
Exception e = ex.getCause() != null ? (Exception) ex.getCause() : ex;
if ((sqlException = (SQLException) ((e instanceof SQLException) ? e : null)) != null) {
// Enumerate through all errors found in the exception.
// for (Iterator err : sqlException.iterator()) {
switch (sqlException.getErrorCode()) {
// SQL Error Code: 40501
// The service is currently busy. Retry the request after 10 seconds. Code:
// (reason code to be decoded).
/*
* case ThrottlingCondition.ThrottlingErrorNumber: // Decode the reason code from the error message to determine the grounds for
* throttling. var condition = ThrottlingCondition.FromError(err);
*
* // Attach the decoded values as additional attributes to the original SQL exception.
* sqlException.Data[condition.ThrottlingMode.getClass().getSimpleName()] = condition.ThrottlingMode.toString();
* sqlException.Data[condition.getClass().getSimpleName()] = condition;
*
* return true;
*/
// SQL Error Code: 10928
// Resource ID: %d. The %s limit for the database is %d and has been reached.
case 10928:
// SQL Error Code: 10929
// Resource ID: %d. The %s minimum guarantee is %d, maximum limit is %d and the current
// usage for the database is %d. However, the server is currently too busy to support
// requests greater than %d for this database.
case 10929:
// SQL Error Code: 10053
// A transport-level error has occurred when receiving results from the server.
// An established connection was aborted by the software in your host machine.
case 10053:
// SQL Error Code: 10054
// A transport-level error has occurred when sending the request to the server.
// (provider: TCP Provider, error: 0 - An existing connection was forcibly closed
// by the remote host.)
case 10054:
// SQL Error Code: 10060
// A network-related or instance-specific error occurred while establishing a connection
// to SQL Server The server was not found or was not accessible. Verify that the
// instance name is correct and that SQL Server is configured to allow remote
// connections. (provider: TCP Provider, error: 0 - A connection attempt failed because
// the connected party did not properly respond after a period of time, or established
// connection failed because connected host has failed to respond.)"}
case 10060:
// SQL Error Code: 18401
// Login failed for user '%s'. Reason: Server is in script upgrade mode. Only
// administrator can connect at this time.
// Devnote: this can happen when SQL is going through recovery (e.g. after failover)
case 18401:
// SQL Error Code: 40197
// The service has encountered an error processing your request. Please try again.
case 40197:
// SQL Error Code: 40540
// The service has encountered an error processing your request. Please try again.
case 40540:
// SQL Error Code: 40613
// Database XXXX on server YYYY is not currently available.
// Please retry the connection later. If the problem persists, contact customer support,
// and provide them the session tracing ID of ZZZZZ.
case 40613:
// SQL Error Code: 40143
// The service has encountered an error processing your request. Please try again.
case 40143:
// SQL Error Code: 233
// The client was unable to establish a connection because of an error during connection
// initialization process before login. Possible causes include the following:
// the client tried to connect to an unsupported version of SQL Server;
// the server was too busy to accept new connections; or
// there was a resource limitation (insufficient memory or maximum allowed connections)
// on the server.
// (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by
// the remote host.)
case 233:
// SQL Error Code: 64
// A connection was successfully established with the server, but then an error occurred
// during the login process.
// (provider: TCP Provider, error: 0 - The specified network name is no longer
// available.)
case 64:
// DBNETLIB Error Code: 20
// The instance of SQL Server you attempted to connect to does not support encryption.
// case ProcessNetLibErrorCode.EncryptionNotSupported.getValue():
default:
return true;
}
// }
// Prelogin failure can happen due to waits expiring on windows handles. Or
// due to a bug in the gateway code, a dropped database with a pooled connection
// when reset results in a timeout error instead of immediate failure.
/*
* Win32Exception wex = (Win32Exception) ((sqlException.getCause() instanceof Win32Exception) ? sqlException.getCause() : null); if
* (wex != null) { switch (wex.NativeErrorCode) { // Timeout expired case 0x102: return true;
*
* // Semaphore timeout expired case 0x121: return true; } }
*/
}
else if (ex instanceof TimeoutException) {
return true;
}
}
return false;
}
/**
* Error codes reported by the DBNETLIB module.
*/
private enum ProcessNetLibErrorCode {
ZeroBytes(-3),
Timeout(-2),
/*
* Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
*/
Unknown(-1),
InsufficientMemory(1),
AccessDenied(2),
ConnectionBusy(3),
ConnectionBroken(4),
ConnectionLimit(5),
ServerNotFound(6),
NetworkNotFound(7),
InsufficientResources(8),
NetworkBusy(9),
NetworkAccessDenied(10),
GeneralError(11),
IncorrectMode(12),
NameNotFound(13),
InvalidConnection(14),
ReadWriteError(15),
TooManyHandles(16),
ServerError(17),
SSLError(18),
EncryptionError(19),
EncryptionNotSupported(20);
public static final int SIZE = Integer.SIZE;
private static java.util.HashMap<Integer, ProcessNetLibErrorCode> mappings;
private int intValue;
ProcessNetLibErrorCode(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, ProcessNetLibErrorCode> getMappings() {
if (mappings == null) {
synchronized (ProcessNetLibErrorCode.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static ProcessNetLibErrorCode forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}
}

Просмотреть файл

@ -0,0 +1,129 @@
package com.microsoft.azure.elasticdb.query.exception;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
/**
* Represents one or more <see cref="Exception"/> errors that occurred when executing a query across a shard set. The InnerExceptions field collects
* these exceptions and one can iterate through the InnerExceptions for further inspection or processing. Purpose: Public type that communicates
* errors that occurred across multiple shards
*/
public class MultiShardAggregateException extends Exception implements Serializable {
private List<Exception> innerExceptions;
/**
* Initializes a new instance of the <see cref="MultiShardAggregateException"/> class.
*/
public MultiShardAggregateException() {
this("One or more errors occurred across shards");
}
/**
* Initializes a new instance of the <see cref="MultiShardAggregateException"/> class.
*
* @param message
* The error message that explains the reason for the exception
*/
public MultiShardAggregateException(String message) {
super(message);
innerExceptions = new ArrayList<>();
innerExceptions.add(new Exception(message));
}
/**
* Initializes a new instance of the <see cref="MultiShardAggregateException"/> class.
*
* @param innerException
* The <see cref="Exception"/> that caused the current exception
*/
public MultiShardAggregateException(Exception innerException) {
this(Collections.singletonList(innerException));
}
/**
* Initializes a new instance of the <see cref="MultiShardAggregateException"/> class.
*
* @param message
* The error message that explains the reason for the exception
* @param innerException
* A list of <see cref="Exception"/> that caused the current exception
*/
public MultiShardAggregateException(String message,
Exception innerException) {
super(message);
if (innerExceptions == null) {
innerExceptions = new ArrayList<>();
}
innerExceptions.add(innerException);
}
/**
* Initializes a new instance of the <see cref="MultiShardAggregateException"/> class.
*
* @param innerExceptions
* A list of <see cref="Exception"/> that caused the current exception
*/
public MultiShardAggregateException(List<Exception> innerExceptions) {
this("One or more errors occurred across shards", innerExceptions);
}
/**
* Initializes a new instance of the <see cref="MultiShardAggregateException"/> class.
*
* @param message
* The error message that explains the reason for the exception
* @param innerExceptions
* A list of <see cref="Exception"/> that caused the current exception
* @throws IllegalArgumentException
* The <paramref name="innerExceptions"/> is null
*/
public MultiShardAggregateException(String message,
List<Exception> innerExceptions) {
super(message, innerExceptions != null ? innerExceptions.get(0) : null);
if (null == innerExceptions) {
throw new IllegalArgumentException("innerExceptions");
}
if (this.innerExceptions == null) {
this.innerExceptions = new ArrayList<>();
}
this.innerExceptions.addAll(innerExceptions);
}
/**
* Gets a read-only collection of the <see cref="Exception"/> instances that caused the current exception.
*/
public final List<Exception> getInnerExceptions() {
// Put them in a readonly collection
return Collections.unmodifiableList(innerExceptions);
}
/**
* Provides a string representation of this exception including its inner exceptions.
*/
@Override
public String toString() {
String newLine = System.lineSeparator();
String text = super.toString();
for (int i = 0; i < innerExceptions.size(); i++) {
text = String.format(Locale.getDefault(), "%1$s%2$s---> (Inner Exception #%3$s) %4$s%5$s%6$s", text, newLine, i,
innerExceptions.get(i).toString(), "<---", newLine);
}
return text;
}
}

Просмотреть файл

@ -0,0 +1,146 @@
package com.microsoft.azure.elasticdb.query.exception;
import java.io.Serializable;
import java.util.Locale;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
//
// Purpose:
// Public type to communicate failures when performing operations against a shard
// Suppression rationale: "Multi" is the spelling we want here.
//
import com.microsoft.azure.elasticdb.shard.base.ShardLocation;
/**
* DEVNOTE: Encapsulate SMM ShardLocation type for now since Shard isn't Serializable Support for serialization of ShardLocation is in the works. A
* MultiShardException represents an exception that occurred when performing operations against a shard. It provides information about both the
* identity of the shard and the exception that occurred. Depending on the nature of the exception, one can try re-running the multi-shard query,
* execute a separate query targeted directly at the shard(s) on that yielded the exception, or lastly execute the query manually against the shard
* using a common tool such as SSMS.
*/
public class MultiShardException extends Exception implements Serializable {
private ShardLocation shardLocation;
/**
* Initializes a new instance of the MultiShardException class.
*/
public MultiShardException() {
this(dummyShardLocation());
}
/**
* Initializes a new instance of the MultiShardException class with the specified error message.
*
* @param message
* specifies the exception encountered at the shard.
*/
public MultiShardException(String message) {
this(dummyShardLocation(), message);
}
/**
* Initializes a new instance of the MultiShardException class with the specified error message and the reference to the inner exception that is
* the cause of this exception.
*
* @param message
* specifies the message that explains the reason for the exception.
* @param innerException
* specifies the exception encountered at the shard.
*/
public MultiShardException(String message,
Exception innerException) {
this(dummyShardLocation(), message, innerException);
}
/**
* Initializes a new instance of the <see cref="MultiShardException"/> class with the specified shard location.
*
* @param shardLocation
* specifies the location of the shard where the exception occurred.
*/
public MultiShardException(ShardLocation shardLocation) {
this(shardLocation, String.format("Exception encountered on shard: %1$s", shardLocation));
}
/**
* Initializes a new instance of the <see cref="MultiShardException"/> class with the specified shard location and error message.
*
* @param shardLocation
* specifies the location of the shard where the exception occurred.
* @param message
* specifies the message that explains the reason for the exception.
*/
public MultiShardException(ShardLocation shardLocation,
String message) {
this(shardLocation, message, null);
}
/**
* Initializes a new instance of the <see cref="MultiShardException"/> class with the specified shard location and exception.
*
* @param shardLocation
* specifies the location of the shard where the exception occurred.
* @param inner
* specifies the exception encountered at the shard.
*/
public MultiShardException(ShardLocation shardLocation,
Exception inner) {
this(shardLocation, String.format("Exception encountered on shard: %1$s", shardLocation), inner);
}
/**
* Initializes a new instance of the <see cref="MultiShardException"/> class with the specified shard location, error message and exception
* encountered.
*
* @param shardLocation
* specifies the location of the shard where the exception occurred.
* @param message
* specifies the message that explains the reason for the exception.
* @param inner
* specifies the exception encountered at the shard.
* @throws IllegalArgumentException
* The <paramref name="shardLocation"/> is null
*/
public MultiShardException(ShardLocation shardLocation,
String message,
Exception inner) {
super(message, inner);
if (null == shardLocation) {
throw new IllegalArgumentException("shardLocation");
}
this.shardLocation = shardLocation;
}
private static ShardLocation dummyShardLocation() {
return new ShardLocation("unknown", "unknown");
}
/**
* The shard associated with this exception.
*/
public final ShardLocation getShardLocation() {
return shardLocation;
}
/**
* Creates and returns a string representation of the current <see cref="MultiShardException"/>.
*
* @return String representation of the current exception.
*/
@Override
public String toString() {
String text = super.toString();
return String.format(Locale.getDefault(), "MultiShardException encountered on shard: %1$s %2$s %3$s", getShardLocation(),
System.lineSeparator(), text);
}
}

Просмотреть файл

@ -0,0 +1,61 @@
package com.microsoft.azure.elasticdb.query.exception;
import java.io.Serializable;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.base.ShardLocation;
/**
* The <see cref="MultiShardResultSet"/> throws this exception when an exception has been hit reading data from one of the underlying shards. This
* indicates that not all rows have been successfully retrieved from the targeted shard(s). Users can then take the steps necessary to decide whether
* to re-run the query, or whether to continue working with the rows that have already been retrieved. This exception is only thrown with the partial
* results policy. Purpose: Custom exception to throw when the MultiShardResultSet hits an exception during a next() call to one of the underlying
* ResultSets. When that happens all we know is that we were not able to read all the results from that shard, so we need to notify the user somehow.
*/
public class MultiShardPartialReadException extends MultiShardException implements Serializable {
public MultiShardPartialReadException(ShardLocation shardLocation,
String message,
RuntimeException inner) {
super(shardLocation, message, inner);
}
/**
* Initializes a new instance of the MultiShardPartialReadException class with the specified error message and reference to the inner exception
* causing the MultiShardPartialReadException.
*
* @param message
* specifies the message that explains the reason for the exception.
* @param innerException
* specifies the exception encountered at the shard.
*/
public MultiShardPartialReadException(String message,
RuntimeException innerException) {
super(message, innerException);
}
/**
* Initializes a new instance of the MultiShardPartialReadException class with the specified error message.
*
* @param message
* specifies the message that explains the reason for the exception.
*/
public MultiShardPartialReadException(String message) {
super(message);
}
/**
* Initializes a new instance of the MultiShardPartialReadException class.
*/
public MultiShardPartialReadException() {
super();
}
}

Просмотреть файл

@ -0,0 +1,49 @@
package com.microsoft.azure.elasticdb.query.exception;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import java.io.Serializable;
/**
* Custom exception to throw when the <see cref="MultiShardResultSet"/> is closed and the user attempts to perform an operation on the closed reader.
* Purpose: Custom exception to throw when the MultiShardResultSet is closed and the user attempts to perform some operation.
*/
public class MultiShardResultSetClosedException extends RuntimeException implements Serializable {
/**
* Initializes a new instance of the MultiShardReaderClosedException class with a specified error message and a reference to the inner exception
* that is the cause of this exception.
*
* @param message
* The error message that explains the reason for the exception.
* @param innerException
* The exception that is the cause of the current exception, or a null reference if no inner exception is specified.
*/
public MultiShardResultSetClosedException(String message,
RuntimeException innerException) {
super(message, innerException);
}
/**
* Initializes a new instance of the MultiShardResultSetClosedException class with a specified error message.
*
* @param message
* The message that describes the error.
*/
public MultiShardResultSetClosedException(String message) {
super(message);
}
/**
* Initializes a new instance of the MultiShardResultSetClosedException class.
*/
public MultiShardResultSetClosedException() {
super();
}
}

Просмотреть файл

@ -0,0 +1,49 @@
package com.microsoft.azure.elasticdb.query.exception;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import java.io.Serializable;
/**
* Custom exception that is thrown when the <see cref="MultiShardResultSet"/> is in an invalid state. If you experience this exception repeatedly,
* please contact Microsoft Customer Support. Purpose: Custom exception to throw when the MultiShardResultSet is in an invalid state. This error
* should not make it out to the user.
*/
public class MultiShardResultSetInternalException extends RuntimeException implements Serializable {
/**
* Initializes a new instance of the MultiShardResultSetInternalException class.
*/
public MultiShardResultSetInternalException() {
super();
}
/**
* Initializes a new instance of the MultiShardResultSetInternalException class with a specified error message.
*
* @param message
* The message that describes the error.
*/
public MultiShardResultSetInternalException(String message) {
super(message);
}
/**
* Initializes a new instance of the MultiShardResultSetInternalException class with a message and an inner exception.
*
* @param message
* The message to encapsulate in the exception.
* @param innerException
* The underlying exception that causes this exception.
*/
public MultiShardResultSetInternalException(String message,
RuntimeException innerException) {
super(message, innerException);
}
}

Просмотреть файл

@ -0,0 +1,57 @@
package com.microsoft.azure.elasticdb.query.exception;
import java.io.Serializable;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.base.ShardLocation;
/**
* Custom exception thrown when the schema on at least one of the shards participating in the overall query does not conform to the expected schema
* for the multi-shard query as a whole. Purpose: Custom exception to throw when the schema from a ResultSet from a given shard does not conform to
* the expected schema for the fanout query as a whole.
*/
public class MultiShardSchemaMismatchException extends MultiShardException implements Serializable {
public MultiShardSchemaMismatchException(ShardLocation shardLocation,
String message) {
super(shardLocation, message);
}
/**
* Initializes a new instance of the MultiShardSchemaMismatchException class with the specified error message and the reference to the inner
* exception that is the cause of this exception.
*
* @param message
* specifies the message that explains the reason for the exception.
* @param innerException
* specifies the exception encountered at the shard.
*/
public MultiShardSchemaMismatchException(String message,
RuntimeException innerException) {
super(message, innerException);
}
/**
* Initializes a new instance of the MultiShardSchemaMismatchException class with the specified error message.
*
* @param message
* specifies the message that explains the reason for the exception.
*/
public MultiShardSchemaMismatchException(String message) {
super(message);
}
/**
* Initializes a new instance of the MultiShardSchemaMismatchException class.
*/
public MultiShardSchemaMismatchException() {
super();
}
}

Просмотреть файл

@ -0,0 +1,78 @@
package com.microsoft.azure.elasticdb.query.logging;
/**
* Summary: Provides a description of the results of the query and its effect on the database.
*/
public enum CommandBehavior {
/**
* The query may return multiple result sets. Execution of the query may affect the database state. Default sets no CommandBehavior flags, so
* calling executeQuery(CommandBehavior.Default) is functionally equivalent to calling executeQuery().
*/
Default(0),
/**
* The query returns a single result set.
*/
SingleResult(1),
/**
* The query returns column information only. When using CommandBehavior.SchemaOnly, the .NET Framework Data Provider for SQL Server precedes the
* statement being executed with SET FMTONLY ON.
*/
SchemaOnly(2),
/**
* The query returns column and primary key information.
*/
KeyInfo(4),
/**
* The query is expected to return a single row of the first result set. Execution of the query may affect the database state. Some .NET Framework
* data providers may, but are not required to, use this information to optimize the performance of the command. It is possible to specify
* SingleRow when executing queries that are expected to return multiple result sets. In that case, where both a multi-result set SQL query and
* single row are specified, the result returned will contain only the first row of the first result set. The other result sets of the query will
* not be returned.
*/
SingleRow(8),
/**
* Provides a way for the ResultSet to handle rows that contain columns with large binary values. Rather than loading the entire row,
* SequentialAccess enables the ResultSet to load data as a stream. You can then use the GetBytes or GetChars method to specify a byte location to
* start the read operation, and a limited buffer size for the data being returned.
*/
SequentialAccess(16),
/**
* When the command is executed, the associated Connection object is closed when the associated ResultSet object is closed.
*/
CloseConnection(32);
public static final int SIZE = java.lang.Integer.SIZE;
private static java.util.HashMap<Integer, CommandBehavior> mappings;
private int intValue;
CommandBehavior(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, CommandBehavior> getMappings() {
if (mappings == null) {
synchronized (CommandBehavior.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static CommandBehavior forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,52 @@
package com.microsoft.azure.elasticdb.query.logging;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Defines the available options when executing commands against multiple shards. This enumeration has a flags attribute.
*/
public enum MultiShardExecutionOptions {
/**
* , Execute without any options enabled.
*/
None(0),
/**
* Whether the $ShardName pseudo column should be included in the result-sets.
*/
IncludeShardNameColumn(1);
public static final int SIZE = java.lang.Integer.SIZE;
private static java.util.HashMap<Integer, MultiShardExecutionOptions> mappings;
private int intValue;
MultiShardExecutionOptions(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, MultiShardExecutionOptions> getMappings() {
if (mappings == null) {
synchronized (MultiShardExecutionOptions.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static MultiShardExecutionOptions forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,56 @@
package com.microsoft.azure.elasticdb.query.logging;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Defines the possible query execution policies. Purpose: Defines the possible query execution policies Suppression rationale: "Multi" is the
* spelling we want here.
*/
public enum MultiShardExecutionPolicy {
/**
* With the complete results execution policy an unsuccessful execution against any shard leads to all results being discarded and an exception
* being thrown either by the ExecuteReader method on the command or the Read method on the reader.
*/
CompleteResults(0),
/**
* A best-effort execution policy that, unlike CompleteResults, tolerates unsuccessful command execution on some (but not all) shards and returns
* the results of the successful commands. Any errors encountered are returned to the user along with the partial results. The caller can inspect
* exceptions encountered during execution through the <see cref="MultiShardAggregateException"/> property of <see cref="MultiShardResultSet"/>.
*/
PartialResults(1);
public static final int SIZE = java.lang.Integer.SIZE;
private static java.util.HashMap<Integer, MultiShardExecutionPolicy> mappings;
private int intValue;
MultiShardExecutionPolicy(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, MultiShardExecutionPolicy> getMappings() {
if (mappings == null) {
synchronized (MultiShardExecutionPolicy.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static MultiShardExecutionPolicy forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,17 @@
package com.microsoft.azure.elasticdb.query.multishard;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
public class GlobalConstants {
/**
* Version information for Microsoft.Azure.SqlDatabase.ElasticScale.Query code.
*/
public static String MultiShardQueryVersionInfo = "1.0.0.0";
}

Просмотреть файл

@ -0,0 +1,164 @@
package com.microsoft.azure.elasticdb.query.multishard;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.query.exception.MultiShardException;
import com.microsoft.azure.elasticdb.shard.base.ShardLocation;
/**
* Simple, immutable class for affiliating a ResultSet with additional information related to the reader (e.g. Statement, shard, exceptions
* encountered etc) Useful when grabbing ResultSets asynchronously. Purpose: Convenience class that holds a ResultSet along with a string label for
* the shard that the data underlying the ResultSet came from. Notes: This is useful for keeping the ResultSet and the label together when executing
* asynchronously.
*/
public class LabeledResultSet implements AutoCloseable {
/**
* Whether ResultSet has been disposed or not.
*/
private boolean disposed;
/**
* The location of the shard.
*/
private ShardLocation shardLocation;
/**
* The Shard location information.
*/
private String shardLabel = "";
/**
* The exception encountered when trying to execute against this reader Could be null if the ResultSet was instantiated successfully for this
* Shard.
*/
private MultiShardException exception;
/**
* The ResultSet to keep track of. Could be null if we encountered an exception whilst executing the statement against this shard.
*/
private ResultSet resultSet;
/**
* The statement object that produces this reader.
*/
private Statement statement;
/**
* Simple constructor to set up an immutable LabeledResultSet object.
*
* @param shardLocation
* The Shard this reader belongs to
* @param statement
* The statement object that produced the reader.
* @throws IllegalArgumentException
* If either of the arguments is null.
*/
public LabeledResultSet(ResultSet resultSet,
ShardLocation shardLocation,
Statement statement) {
this(shardLocation, statement);
if (null == resultSet) {
throw new IllegalArgumentException("resultSet");
}
this.resultSet = resultSet;
}
/**
* Simple constructor to set up an immutable LabeledResultSet object.
*
* @param shardLocation
* The Shard this reader belongs to
* @param statement
* The statement object that produced the reader.
* @throws IllegalArgumentException
* If either of the arguments is null.
*/
public LabeledResultSet(MultiShardException exception,
ShardLocation shardLocation,
Statement statement) {
this(shardLocation, statement);
if (null == exception) {
throw new IllegalArgumentException("exception");
}
this.exception = exception;
}
/**
* Simple constructor to set up an immutable LabeledResultSet object.
*
* @param shardLocation
* The Shard this reader belongs to
* @param statement
* The statement object that produced the reader.
* @throws IllegalArgumentException
* If either of the arguments is null.
*/
public LabeledResultSet(ShardLocation shardLocation,
Statement statement) {
if (null == shardLocation) {
throw new IllegalArgumentException("shardLocation");
}
if (null == statement) {
throw new IllegalArgumentException("statement");
}
this.shardLocation = shardLocation;
this.statement = statement;
}
public final ShardLocation getShardLocation() {
return shardLocation;
}
public final String getShardLabel() {
return shardLabel;
}
public final void setShardLabel(String value) {
this.shardLabel = value;
}
public final MultiShardException getException() {
return exception;
}
public final ResultSet getResultSet() {
return resultSet;
}
/**
* The Connection associated with this reader.
*/
public final Connection getConnection() throws SQLException {
return this.statement.getConnection();
}
public final Statement getStatement() {
return statement;
}
/**
* AutoClosable Implementation.
*/
public final void close() throws SQLException {
if (!this.disposed && this.resultSet != null) {
this.resultSet.close();
this.disposed = true;
}
}
}

Просмотреть файл

@ -0,0 +1,270 @@
package com.microsoft.azure.elasticdb.query.multishard;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.base.Shard;
import com.microsoft.azure.elasticdb.shard.base.ShardLocation;
import com.microsoft.azure.elasticdb.shard.sqlstore.SqlConnectionStringBuilder;
import com.microsoft.azure.elasticdb.shard.utils.StringUtilsLocal;
/**
* Represents a connection to a set of shards and provides the ability to process queries across the shard set. Purpose: Creates connections to the
* given set of shards and governs their lifetime Notes: This class is NOT thread-safe.
*/
public final class MultiShardConnection implements AutoCloseable {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* The suffix to append to each shard's ApplicationName. Will help with server-side telemetry.
*/
public static String ApplicationNameSuffix = "ESC_MSQv" + GlobalConstants.MultiShardQueryVersionInfo;
/**
* Whether this instance has already been disposed.
*/
private boolean isDisposed = false;
/**
* Gets the collection of <see cref="Shard"/>s associated with this connection.
*/
private List<Shard> shards;
private List<Pair<ShardLocation, Connection>> shardConnections;
private String connectionString;
/**
* Initializes a new instance of the <see cref="MultiShardConnection"/> class.
*
* @param connectionString
* These credentials will be used to connect to the <see cref="Shard"/>s. The same credentials are used on all shards. Therefore, all
* shards need to provide the appropriate permissions for these credentials to execute the command.
* @param shards
* The collection of <see cref="Shard"/>s used for this connection instances.
*
*
* Multiple Active Result Sets (MARS) are not supported and are disabled for any processing at the shards.
*/
public MultiShardConnection(String connectionString,
Shard... shards) {
if (StringUtilsLocal.isNullOrEmpty(connectionString)) {
throw new IllegalArgumentException("connectionString");
}
// Enhance the ApplicationName with this library's name as a suffix
// Devnote: If connection string specifies Active Directory authentication and runtime is not
// .NET 4.6 or higher, then below call will throw.
SqlConnectionStringBuilder connectionStringBuilder = (new SqlConnectionStringBuilder(connectionString))
.withApplicationNameSuffix(ApplicationNameSuffix);
List<Shard> shardList = Arrays.asList(shards);
validateConnectionArguments(shardList, "shards", connectionStringBuilder);
this.connectionString = connectionString;
this.shards = shardList;
this.shardConnections = shardList.stream().map(s -> {
try {
return (createDbConnectionForLocation(s.getLocation(), connectionStringBuilder));
}
catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}).collect(Collectors.toList());
}
/**
* Initializes a new instance of the <see cref="MultiShardConnection"/> class.
*
* @param shardLocations
* The collection of <see cref="ShardLocation"/>s used for this connection instances.
* @param connectionString
* These credentials will be used to connect to the <see cref="Shard"/>s. The same credentials are used on all shards. Therefore, all
* shards need to provide the appropriate permissions for these credentials to execute the command.
*
*
* Multiple Active Result Sets (MARS) are not supported and are disabled for any processing at the shards.
*/
public MultiShardConnection(String connectionString,
ShardLocation... shardLocations) {
if (StringUtilsLocal.isNullOrEmpty(connectionString)) {
throw new IllegalArgumentException("connectionString");
}
// Enhance the ApplicationName with this library's name as a suffix
// Devnote: If connection string specifies Active Directory authentication and runtime is not
// .NET 4.6 or higher, then below call will throw.
SqlConnectionStringBuilder connectionStringBuilder = (new SqlConnectionStringBuilder(connectionString))
.withApplicationNameSuffix(ApplicationNameSuffix);
List<ShardLocation> shardLocationList = Arrays.asList(shardLocations);
validateConnectionArguments(shardLocationList, "shardLocations", connectionStringBuilder);
List<Pair<ShardLocation, Connection>> connForLocation = shardLocationList.stream().map(s -> {
try {
return (createDbConnectionForLocation(s, connectionStringBuilder));
}
catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}).collect(Collectors.toList());
this.connectionString = connectionString;
this.shardConnections = connForLocation;
this.shards = null;
}
/**
* Creates an instance of this class. TEST ONLY
*
* @param shardConnections
* Connections to the shards
*/
public MultiShardConnection(ArrayList<Pair<ShardLocation, Connection>> shardConnections) {
if (shardConnections == null || shardConnections.size() == 0) {
throw new IllegalArgumentException("connections");
}
try {
this.connectionString = shardConnections.get(0).getRight().getMetaData().getURL();
}
catch (SQLException e) {
throw new IllegalArgumentException("connectionString");
}
this.shards = null;
this.shardConnections = shardConnections;
}
private static <T> void validateConnectionArguments(List<T> namedCollection,
String collectionName,
SqlConnectionStringBuilder connectionStringBuilder) {
if (namedCollection == null) {
throw new IllegalArgumentException(collectionName);
}
if (0 == namedCollection.size()) {
throw new IllegalArgumentException(String.format("No %1$s provided.", collectionName));
}
// Datasource must not be set
if (!StringUtilsLocal.isNullOrEmpty(connectionStringBuilder.getDataSource())) {
throw new IllegalArgumentException("DataSource must not be set in the connectionStringBuilder");
}
// Initial catalog must not be set
if (!StringUtilsLocal.isNullOrEmpty(connectionStringBuilder.getDataSource())) {
throw new IllegalArgumentException("InitialCatalog must not be set in the connectionStringBuilder");
}
}
private static Pair<ShardLocation, Connection> createDbConnectionForLocation(ShardLocation shardLocation,
SqlConnectionStringBuilder connStr) throws SQLException {
connStr.setDatabaseName(shardLocation.getDatabase());
connStr.setDataSource(shardLocation.getDataSource());
return new ImmutablePair<>(shardLocation, DriverManager.getConnection(connStr.getConnectionString()));
}
/**
* Gets the collection of <see cref="Shard"/>s associated with this connection.
*/
public List<Shard> getShards() {
return shards;
}
/**
* Gets the collection of <see cref="ShardLocation"/>s associated with this connection.
*/
public List<ShardLocation> getShardLocations() {
return this.getShardConnections().stream().map(Pair::getLeft).collect(Collectors.toList());
}
/**
* Gets the collection of ShardLocations and Connections associated with this connection.
*/
public List<Pair<ShardLocation, Connection>> getShardConnections() {
return shardConnections;
}
/**
* Gets the connection string associated with this connection.
*/
public String getConnectionString() {
return this.connectionString;
}
/**
* Creates and returns a <see cref="MultiShardStatement"/> object. The <see cref="MultiShardStatement"/> object can then be used to execute a
* command against all shards specified in the connection.
*
* @return the <see cref="MultiShardStatement"/> with CommandText set to null.
*/
public MultiShardStatement createCommand() {
return MultiShardStatement.create(this, null);
}
/**
* Releases all resources used by this object.
*/
public void close() throws IOException {
if (!isDisposed) {
// Dispose off the shard connections
this.getShardConnections().forEach((c) -> {
if (c.getRight() != null) {
try {
c.getRight().close();
}
catch (SQLException e) {
e.printStackTrace();
}
}
});
isDisposed = true;
log.warn("MultiShardConnection.close - Connection was disposed");
}
}
/**
* Closes any open connections to shards. Does a best-effort close and doesn't throw.
*/
public void closeOpenConnections() {
for (Pair<ShardLocation, Connection> conn : this.getShardConnections()) {
try {
if (conn.getRight() != null && !conn.getRight().isClosed()) {
conn.getRight().close();
}
}
catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* Check if connection is closed or not.
*
* @return true if open, false if closed.
*/
public boolean isClosed() {
return isDisposed;
}
}

Просмотреть файл

@ -0,0 +1,54 @@
package com.microsoft.azure.elasticdb.query.multishard;
import java.util.function.Function;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.transientfaulthandling.ITransientErrorDetectionStrategy;
import com.microsoft.azure.elasticdb.core.commons.transientfaulthandling.RetryBehavior;
import com.microsoft.azure.elasticdb.core.commons.transientfaulthandling.SqlDatabaseTransientErrorDetectionStrategy;
/**
* Provides the transient error detection logic for transient faults that are specific to cross shard query.
*/
public final class MultiShardQueryTransientErrorDetectionStrategy implements ITransientErrorDetectionStrategy {
/**
* Delegate used for detecting transient faults.
*/
private Function<Exception, Boolean> transientFaultDetector;
/**
* Standard transient error detection strategy.
*/
private SqlDatabaseTransientErrorDetectionStrategy standardDetectionStrategy;
/**
* Creates a new instance of transient error detection strategy for Shard map manager.
*
* @param retryBehavior
* Behavior for detecting transient errors.
*/
public MultiShardQueryTransientErrorDetectionStrategy(RetryBehavior retryBehavior) {
standardDetectionStrategy = new SqlDatabaseTransientErrorDetectionStrategy();
transientFaultDetector = (Exception arg) -> retryBehavior.getTransientErrorDetector().apply(arg);
}
/**
* Determines whether the specified exception represents a transient failure that can be compensated by a retry.
*
* @param ex
* The exception object to be verified.
* @return true if the specified exception is considered as transient; otherwise, false.
*/
@Override
public boolean isTransient(Exception ex) {
return standardDetectionStrategy.isTransient(ex) || transientFaultDetector.apply(ex);
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,47 @@
package com.microsoft.azure.elasticdb.query.multishard;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.transientfaulthandling.RetryBehavior;
import com.microsoft.azure.elasticdb.core.commons.transientfaulthandling.RetryPolicy;
/**
* Purpose: Various utilities used by other classes in this project.
*/
public final class MultiShardUtils {
/**
* The retry policy to use when connecting to sql databases
*
* @param retryPolicyPerShard
* An instance of the <see cref="RetryPolicy"/> class
* @param retryBehavior
* Behavior to use for detecting transient faults.
* @return An instance of the <see cref="RetryPolicy"/> class Separate method from the one below because we might allow for custom retry
* strategies in the near future
*/
public static RetryPolicy getSqlConnectionRetryPolicy(RetryPolicy retryPolicyPerShard,
RetryBehavior retryBehavior) {
return new RetryPolicy(new MultiShardQueryTransientErrorDetectionStrategy(retryBehavior), retryPolicyPerShard.getExponentialRetryStrategy());
}
/**
* The retry policy to use when executing commands against sql databases
*
* @param retryPolicyPerShard
* An instance of the <see cref="RetryPolicy"/> class
* @param retryBehavior
* Behavior to use for detecting transient faults.
* @return An instance of the <see cref="RetryPolicy"/> class
*/
public static RetryPolicy getSqlCommandRetryPolicy(RetryPolicy retryPolicyPerShard,
RetryBehavior retryBehavior) {
return new RetryPolicy(new MultiShardQueryTransientErrorDetectionStrategy(retryBehavior), retryPolicyPerShard.getExponentialRetryStrategy());
}
}

Просмотреть файл

@ -0,0 +1,55 @@
package com.microsoft.azure.elasticdb.query.multishard;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.helpers.EventArgs;
import com.microsoft.azure.elasticdb.shard.base.ShardLocation;
/**
* Input to be passed to per-shard event handlers.
*/
public class ShardExecutionEventArgs extends EventArgs {
/**
* The exception to process, if applicable. Null if no exception was thrown.
*/
private Exception ex;
/**
* The location of the shard on which the MultiShardStatement is currently executing.
*/
private ShardLocation shardLocation;
/**
* FOR INTERNAL USE ONLY: The returned input reader.
*/
private LabeledResultSet reader;
public final Exception getException() {
return ex;
}
public final void setException(Exception value) {
ex = value;
}
public final ShardLocation getShardLocation() {
return shardLocation;
}
public final void setShardLocation(ShardLocation value) {
shardLocation = value;
}
public final LabeledResultSet getReader() {
return reader;
}
public final void setReader(LabeledResultSet value) {
reader = value;
}
}

Просмотреть файл

@ -0,0 +1,100 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.utils.ExceptionUtils;
/**
* Base class for updates to mappings from shardlets to shards. <typeparam name="StatusT">Type of status field.</typeparam>
*/
public abstract class BaseMappingUpdate<StatusT> implements IMappingUpdate<StatusT> {
/**
* Records the modified properties for update.
*/
private MappingUpdatedProperties updatedProperties;
/**
* Holder for update to status property.
*/
private StatusT status;
/**
* Holder for update to shard property.
*/
private Shard shard;
/**
* Gets the Status property.
*/
public final StatusT getStatus() {
return status;
}
/**
* Sets the Status property.
*/
public final void setStatus(StatusT value) {
status = value;
updatedProperties = updatedProperties == null ? MappingUpdatedProperties.Status
: MappingUpdatedProperties.forValue(updatedProperties.getValue() | MappingUpdatedProperties.Status.getValue());
}
/**
* Gets the Shard property.
*/
public final Shard getShard() {
return shard.clone();
}
/**
* Sets the Shard property.
*/
public final void setShard(Shard value) {
ExceptionUtils.disallowNullArgument(value, "value");
shard = value.clone();
updatedProperties = updatedProperties == null ? MappingUpdatedProperties.Shard
: MappingUpdatedProperties.forValue(updatedProperties.getValue() | MappingUpdatedProperties.Shard.getValue());
}
/**
* Checks if any property is set in the given bitmap.
*
* @param properties
* Properties bitmap.
* @return True if any of the properties is set, false otherwise.
*/
public final boolean isAnyPropertySet(MappingUpdatedProperties properties) {
return (updatedProperties.getValue() & properties.getValue()) != 0;
}
/**
* Checks if the mapping is being taken offline.
*
* @param originalStatus
* Original status.
* @return True of the update will take the mapping offline.
*/
public final boolean isMappingBeingTakenOffline(StatusT originalStatus) {
return (updatedProperties.getValue() & MappingUpdatedProperties.Status.getValue()) == MappingUpdatedProperties.Status.getValue()
&& this.isBeingTakenOffline(originalStatus, this.getStatus());
}
/**
* Detects if the current mapping is being taken offline.
*
* @param originalStatus
* Original status.
* @param updatedStatus
* Updated status.
* @return Detects in the derived types if the mapping is being taken offline.
*/
protected abstract boolean isBeingTakenOffline(StatusT originalStatus,
StatusT updatedStatus);
}

Просмотреть файл

@ -0,0 +1,45 @@
package com.microsoft.azure.elasticdb.shard.base;
import java.util.UUID;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardMapManager;
import com.microsoft.azure.elasticdb.shard.store.StoreMapping;
/**
* Interface that represents capability to provide information relevant to Add/Remove/Update operations for a mapping object.
*/
public interface IMappingInfoProvider {
/**
* ShardMapManager for the object.
*/
ShardMapManager getShardMapManager();
/**
* Shard map associated with the mapping.
*/
UUID getShardMapId();
/**
* Storage representation of the mapping.
*/
StoreMapping getStoreMapping();
/**
* Type of the mapping.
*/
MappingKind getKind();
/**
* Mapping type, useful for diagnostics.
*/
String getTypeName();
}

Просмотреть файл

@ -0,0 +1,43 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Common interface for point/range mapping updates. <typeparam name="StatusT">Status type.</typeparam>
*/
public interface IMappingUpdate<StatusT> {
/**
* Status property.
*/
StatusT getStatus();
/**
* Shard property.
*/
Shard getShard();
/**
* Checks if any property is set in the given bitmap.
*
* @param properties
* Properties bitmap.
* @return True if any of the properties is set, false otherwise.
*/
boolean isAnyPropertySet(MappingUpdatedProperties properties);
/**
* Checks if the mapping is being taken offline.
*
* @param originalStatus
* Original status.
* @return True of the update will take the mapping offline.
*/
boolean isMappingBeingTakenOffline(StatusT originalStatus);
}

Просмотреть файл

@ -0,0 +1,53 @@
package com.microsoft.azure.elasticdb.shard.base;
import java.sql.Connection;
import java.util.concurrent.Callable;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.store.StoreShardMap;
/**
* Represents capabilities to provide a Shard along with an associated value.
*/
public interface IShardProvider<ValueT> {
/**
* Shard for the ShardProvider object.
*/
Shard getShardInfo();
/**
* Value corresponding to the Shard. Represents traits of the Shard object provided by the ShardInfo property.
*/
ValueT getValue();
/**
* Performs validation that the local representation is as up-to-date as the representation on the backing data store.
*
* @param shardMap
* Shard map to which the shard provider belongs.
* @param conn
* Connection used for validation.
*/
void validate(StoreShardMap shardMap,
Connection conn);
/**
* Asynchronously performs validation that the local representation is as up-to-date as the representation on the backing data store.
*
* @param shardMap
* Shard map to which the shard provider belongs.
* @param conn
* Connection used for validation.
* @return A task to await validation completion
*/
Callable validateAsync(StoreShardMap shardMap,
Connection conn);
}

Просмотреть файл

@ -0,0 +1,62 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Set of operations that can be performed on mappings with lockOwnerId.
*/
public enum LockOwnerIdOpType {
/**
* Lock the range mapping with the given lockOwnerId.
*/
Lock(0),
/**
* Unlock the range mapping that has the given lockOwnerId.
*/
UnlockMappingForId(1),
/**
* Unlock all the range mappings that have the given lockOwnerId.
*/
UnlockAllMappingsForId(2),
/**
* Unlock all locked range mappings.
*/
UnlockAllMappings(3);
public static final int SIZE = java.lang.Integer.SIZE;
private static java.util.HashMap<Integer, LockOwnerIdOpType> mappings;
private int intValue;
LockOwnerIdOpType(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, LockOwnerIdOpType> getMappings() {
if (mappings == null) {
synchronized (LockOwnerIdOpType.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static LockOwnerIdOpType forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,78 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import java.util.HashMap;
/**
* Specifies where mapping lookup operations will search for mappings.
*/
public class LookupOptions {
/**
* Default invalid kind of lookup options.
*/
public static final LookupOptions NONE = new LookupOptions(0);
/**
* Attempt to lookup in the local cache. If LookupInCache and LookupInStore are both specified, the cache will be searched first, then the store.
*/
public static final LookupOptions LOOKUP_IN_CACHE = new LookupOptions(1);
/**
* Attempt to lookup in the global shard map store. If LookupInCache and LookupInStore are both specified, the cache will be searched first, then
* the store.
*/
public static final LookupOptions LOOKUP_IN_STORE = new LookupOptions(1 << 2);
public static final int SIZE = java.lang.Integer.SIZE;
private static HashMap<Integer, LookupOptions> mappings;
private int intValue;
private LookupOptions(int value) {
intValue = value;
synchronized (LookupOptions.class) {
getMappings().put(value, this);
}
}
private static HashMap<Integer, LookupOptions> getMappings() {
if (mappings == null) {
synchronized (LookupOptions.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
/**
* Lookup Options for an Integer Value.
*
* @param value
* Input integer value
* @return Lookup Option
*/
public static LookupOptions forValue(int value) {
synchronized (LookupOptions.class) {
LookupOptions enumObj = getMappings().get(value);
if (enumObj == null) {
return new LookupOptions(value);
}
else {
return enumObj;
}
}
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,45 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Types of supported mappings.
*/
public enum MappingKind {
PointMapping(0),
RangeMapping(1);
public static final int SIZE = java.lang.Integer.SIZE;
private static java.util.HashMap<Integer, MappingKind> mappings;
private int intValue;
MappingKind(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, MappingKind> getMappings() {
if (mappings == null) {
synchronized (MappingKind.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static MappingKind forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,115 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import java.util.UUID;
/**
* Public type that represents the owner of a lock held on a mapping. This class is immutable.
*/
public final class MappingLockToken {
/**
* Token representing the default state where the mapping isn't locked.
*/
public static final MappingLockToken NoLock = new MappingLockToken(new UUID(0L, 0L));
/**
* Token that can be used to force an unlock on any locked mapping.
*/
public static final MappingLockToken ForceUnlock = new MappingLockToken(UUID.fromString("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"));
private UUID lockOwnerId;
/**
* Instantiates an instance of <see cref="MappingLockToken"/> with the given lock owner id.
*
* @param lockOwnerId
* The lock owner id
*/
public MappingLockToken(UUID lockOwnerId) {
this.setLockOwnerId(lockOwnerId);
}
/**
* Creates an instance of <see cref="MappingLockToken"/>.
*
* @return An instance of <see cref="MappingLockToken"/>
*/
public static MappingLockToken create() {
return new MappingLockToken(UUID.randomUUID());
}
/**
* Equality operator.
*
* @param leftMappingLockToken
* An instance of <see cref="MappingLockToken"/>
* @param rightMappingLockToken
* An instance of <see cref="MappingLockToken"/>
* @return True if both belong to the same lock owner
*/
public static boolean opEquality(MappingLockToken leftMappingLockToken,
MappingLockToken rightMappingLockToken) {
return leftMappingLockToken.equals(rightMappingLockToken);
}
/**
* Inequality operator.
*
* @param leftMappingLockToken
* An instance of <see cref="MappingLockToken"/>
* @param rightMappingLockToken
* An instance of <see cref="MappingLockToken"/>
* @return True if both belong to the same lock owner
*/
public static boolean opInequality(MappingLockToken leftMappingLockToken,
MappingLockToken rightMappingLockToken) {
return leftMappingLockToken.equals(rightMappingLockToken);
}
public UUID getLockOwnerId() {
return lockOwnerId;
}
public void setLockOwnerId(UUID value) {
lockOwnerId = value;
}
/**
* Determines whether the specified object is equal to the current object.
*
* @param obj
* The object to compare with the current object.
* @return True if the specified object is equal to the current object; otherwise, false.
*/
@Override
public boolean equals(Object obj) {
return this.equals((MappingLockToken) ((obj instanceof MappingLockToken) ? obj : null));
}
/**
* Compares two instances of <see cref="MappingLockToken"/> to see if they have the same owner.
*
* @return True if they both belong to the same lock owner
*/
public boolean equals(MappingLockToken other) {
return other != null && this.getLockOwnerId().equals(other.getLockOwnerId());
}
/**
* Calculates the hash code for this instance.
*
* @return Hash code for the object.
*/
@Override
public int hashCode() {
return this.getLockOwnerId().hashCode();
}
}

Просмотреть файл

@ -0,0 +1,52 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Status of a mapping.
*/
public enum MappingStatus {
/**
* Mapping is Offline.
*/
Offline(0),
/**
* Mapping is Online.
*/
Online(1);
public static final int SIZE = java.lang.Integer.SIZE;
private static java.util.HashMap<Integer, MappingStatus> mappings;
private int intValue;
MappingStatus(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, MappingStatus> getMappings() {
if (mappings == null) {
synchronized (MappingStatus.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static MappingStatus forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,47 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Records the updated properties for a mapping update object.
*/
public enum MappingUpdatedProperties {
Status(1),
// Only applicable for point and range update.
Shard(2),
All(1 | 2);
public static final int SIZE = java.lang.Integer.SIZE;
private static java.util.HashMap<Integer, MappingUpdatedProperties> mappings;
private int intValue;
MappingUpdatedProperties(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, MappingUpdatedProperties> getMappings() {
if (mappings == null) {
synchronized (MappingUpdatedProperties.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static MappingUpdatedProperties forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,301 @@
package com.microsoft.azure.elasticdb.shard.base;
import java.lang.invoke.MethodHandles;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.google.common.base.Stopwatch;
import com.microsoft.azure.elasticdb.shard.map.ShardMap;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementException;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardMapManager;
import com.microsoft.azure.elasticdb.shard.store.StoreMapping;
import com.microsoft.azure.elasticdb.shard.store.StoreShardMap;
import com.microsoft.azure.elasticdb.shard.utils.StringUtilsLocal;
/**
* Represents a mapping between the singleton key value of a shardlet (a point) and a <see cref="Shard"/>. <typeparam name="KeyT">Type of the key
* (point).</typeparam>
*/
public final class PointMapping implements IShardProvider<Object>, Cloneable, IMappingInfoProvider {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* Shard object associated with the mapping.
*/
private Shard shard;
/**
* Gets key value.
*/
private Object value;
/**
* Holder of the key value's binary representation.
*/
private ShardKey key;
/**
* Reference to the ShardMapManager.
*/
private ShardMapManager shardMapManager;
/**
* Storage representation of the mapping.
*/
private StoreMapping storeMapping;
/**
* Constructs a point mapping given mapping creation arguments.
*
* @param shardMapManager
* Owning ShardMapManager.
* @param creationInfo
* Mapping creation information.
*/
public PointMapping(ShardMapManager shardMapManager,
PointMappingCreationInfo creationInfo) {
assert shardMapManager != null;
assert creationInfo != null;
assert creationInfo.getShard() != null;
this.setManager(shardMapManager);
shard = creationInfo.getShard();
this.setStoreMapping(new StoreMapping(UUID.randomUUID(), creationInfo.getShard(), creationInfo.getKey().getRawValue(), null,
creationInfo.getStatus().getValue()));
this.setKey(creationInfo.getKey());
this.setValue(creationInfo.getValue());
}
/**
* Internal constructor used for deserialization from store representation of the mapping object.
*
* @param shardMapManager
* Owning ShardMapManager.
* @param shardMap
* Owning shard map.
* @param mapping
* Storage representation of the mapping.
*/
public PointMapping(ShardMapManager shardMapManager,
ShardMap shardMap,
StoreMapping mapping) {
assert shardMapManager != null;
assert mapping != null;
assert mapping.getShardMapId() != null;
assert mapping.getStoreShard().getShardMapId() != null;
this.setManager(shardMapManager);
this.setStoreMapping(mapping);
shard = new Shard(this.getShardMapManager(), shardMap, mapping.getStoreShard());
this.setKey(ShardKey.fromRawValue(shardMap.getKeyType(), mapping.getMinValue()));
this.setValue(this.getKey().getValue());
}
/**
* Gets Status of the mapping.
*/
public MappingStatus getStatus() {
if (this.getStoreMapping().getStatus() == MappingStatus.Online.getValue()) {
return MappingStatus.Online;
}
return MappingStatus.Offline;
}
/**
* Gets Shard that contains the key value.
*/
public Shard getShard() {
return shard;
}
public Object getValue() {
return value;
}
private void setValue(Object value) {
this.value = value;
}
public ShardKey getKey() {
return key;
}
public void setKey(ShardKey value) {
key = value;
}
/**
* Identity of the mapping.
*/
public UUID getId() {
return this.getStoreMapping().getId();
}
/**
* Identify of the ShardMap this shard belongs to.
*/
public UUID getShardMapId() {
return this.getStoreMapping().getShardMapId();
}
public ShardMapManager getShardMapManager() {
return shardMapManager;
}
public void setManager(ShardMapManager value) {
shardMapManager = value;
}
public StoreMapping getStoreMapping() {
return storeMapping;
}
private void setStoreMapping(StoreMapping value) {
storeMapping = value;
}
/**
* Converts the object to its string representation.
*
* @return String representation of the object.
*/
@Override
public String toString() {
return StringUtilsLocal.formatInvariant("P[%s:%s]", this.getId().toString(), this.getKey().toString());
}
/**
* Determines whether the specified object is equal to the current object.
*
* @param obj
* The object to compare with the current object.
* @return True if the specified object is equal to the current object; otherwise, false.
*/
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof PointMapping)) {
return false;
}
PointMapping other = (PointMapping) obj;
return this.getId().equals(other.getId()) && this.getKey().equals(other.getKey());
}
/**
* Calculates the hash code for this instance.
*
* @return Hash code for the object.
*/
@Override
public int hashCode() {
return this.getId().hashCode();
}
/**
* Shard that contains the key value.
*/
public Shard getShardInfo() {
return this.getShard();
}
/**
* Performs validation that the local representation is as up-to-date as the representation on the backing data store.
*
* @param shardMap
* Shard map to which the shard provider belongs.
* @param conn
* Connection used for validation.
*/
@Override
public void validate(StoreShardMap shardMap,
Connection conn) {
try {
log.info("PointMapping Validate Start; Connection: {}", conn.getMetaData().getURL());
Stopwatch stopwatch = Stopwatch.createStarted();
ValidationUtils.validateMapping(conn, this.getShardMapManager(), shardMap, this.getStoreMapping());
stopwatch.stop();
log.info("PointMapping Validate Complete; Connection: {}; Duration:{}", conn.getMetaData().getURL(),
stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
catch (SQLException e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
}
/**
* Asynchronously performs validation that the local representation is as up-to-date as the representation on the backing data store.
*
* @param shardMap
* Shard map to which the shard provider belongs.
* @param conn
* Connection used for validation.
* @return A task to await validation completion
*/
@Override
public Callable validateAsync(StoreShardMap shardMap,
Connection conn) {
Callable returnVal;
try {
log.info("PointMapping ValidateAsync Start; Connection: {}", conn.getMetaData().getURL());
Stopwatch stopwatch = Stopwatch.createStarted();
returnVal = ValidationUtils.validateMappingAsync(conn, this.getShardMapManager(), shardMap, this.getStoreMapping());
stopwatch.stop();
log.info("PointMapping ValidateAsync Complete; Connection: {} Duration:{}", conn.getMetaData().getURL(),
stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
catch (SQLException e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
return returnVal;
}
/**
* Clones the instance.
*
* @return clone of the instance.
*/
public PointMapping clone() {
return new PointMapping(this.getShardMapManager(), this.getShard().getShardMap(), this.getStoreMapping());
}
/**
* Type of the mapping.
*/
public MappingKind getKind() {
return MappingKind.PointMapping;
}
/**
* Mapping type, useful for diagnostics.
*/
public String getTypeName() {
return "PointMapping";
}
}

Просмотреть файл

@ -0,0 +1,90 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.utils.ExceptionUtils;
/**
* Arguments used to create a <see cref="PointMapping"/>.
*/
public final class PointMappingCreationInfo {
/**
* Gets the point value being mapped.
*/
private Object value;
/**
* Gets the Shard of the mapping.
*/
private Shard shard;
/**
* Gets the Status of the mapping.
*/
private MappingStatus status = MappingStatus.values()[0];
/**
* Gets the key value associated with the <see cref="PointMapping{KeyT}"/>.
*/
private ShardKey key;
/**
* Arguments used to create a point mapping.
*
* @param point
* Point value being mapped.
* @param shard
* Shard used as the mapping target.
* @param status
* Status of the mapping.
*/
public PointMappingCreationInfo(Object point,
Shard shard,
MappingStatus status) {
ExceptionUtils.disallowNullArgument(shard, "shard");
this.setValue(point);
this.setShard(shard);
this.setStatus(status);
this.setKey(new ShardKey(ShardKey.shardKeyTypeFromType(point.getClass()), point));
}
public Object getValue() {
return value;
}
private void setValue(Object value) {
this.value = value;
}
public Shard getShard() {
return shard;
}
private void setShard(Shard value) {
shard = value;
}
public MappingStatus getStatus() {
return status;
}
private void setStatus(MappingStatus value) {
status = value;
}
public ShardKey getKey() {
return key;
}
public void setKey(ShardKey value) {
key = value;
}
}

Просмотреть файл

@ -0,0 +1,38 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Represents updates to a mapping between the singleton key value of a shardlet (a point) and the shard that holds its data. Also see
* <see cref="PointMapping{KeyT}"/>.
*/
public final class PointMappingUpdate extends BaseMappingUpdate<MappingStatus> {
/**
* Instantiates a new point mapping update object.
*/
public PointMappingUpdate() {
super();
}
/**
* Detects if the current mapping is being taken offline.
*
* @param originalStatus
* Original status.
* @param updatedStatus
* Updated status.
* @return Detects in the derived types if the mapping is being taken offline.
*/
@Override
protected boolean isBeingTakenOffline(MappingStatus originalStatus,
MappingStatus updatedStatus) {
return originalStatus == MappingStatus.Online && updatedStatus == MappingStatus.Offline;
}
}

Просмотреть файл

@ -0,0 +1,125 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Represents a left-inclusive, right-exclusive range of values.
*/
public final class Range {
/**
* The shard range value corresponding to this value.
*/
private ShardRange shardRange;
/**
* Gets the low boundary value (inclusive).
*/
private Object low;
/**
* Gets the high boundary value (exclusive).
*/
private Object high;
/**
* True if the high boundary value equals +infinity; otherwise, false.
*/
private boolean highIsMax;
/**
* Constructs range based on its low and high boundary values.
*
* @param low
* Low boundary value (inclusive).
* @param high
* High boundary value (exclusive).
*/
public Range(Object low,
Object high) {
ShardKeyType k = ShardKey.shardKeyTypeFromType(low.getClass());
shardRange = new ShardRange(new ShardKey(k, low), new ShardKey(k, high));
this.low = low;
this.high = high;
}
/**
* Constructs range based on its low boundary value. The low boundary value is set to the one specified in <paramref name="low"/> while the high
* boundary value is set to maximum possible value i.e. +infinity.
*
* @param low
* Low boundary value (inclusive).
*/
public Range(Object low) {
ShardKeyType k = ShardKey.shardKeyTypeFromType(low.getClass());
shardRange = new ShardRange(new ShardKey(k, low), new ShardKey(k, null));
this.low = low;
this.setHighIsMax(true);
}
public ShardRange getShardRange() {
return shardRange;
}
public Object getLow() {
return low;
}
public Object getHigh() {
return high;
}
public boolean isHighMax() {
return highIsMax;
}
private void setHighIsMax(boolean value) {
highIsMax = value;
}
/**
* Converts the object to its string representation.
*
* @return String representation of the object.
*/
@Override
public String toString() {
return shardRange.toString();
}
/**
* Calculates the hash code for this instance.
*
* @return Hash code for the object.
*/
@Override
public int hashCode() {
return shardRange.hashCode();
}
/**
* Determines whether the specified object is equal to the current object.
*
* @param obj
* The object to compare with the current object.
* @return True if the specified object is equal to the current object; otherwise, false.
*/
@Override
public boolean equals(Object obj) {
return this.equals((Range) ((obj instanceof Range) ? obj : null));
}
/**
* Performs equality comparison with another Range.
*
* @param other
* Range to compare with.
* @return True if same Range, false otherwise.
*/
public boolean equals(Range other) {
return other != null && shardRange.equals(other.shardRange);
}
}

Просмотреть файл

@ -0,0 +1,311 @@
package com.microsoft.azure.elasticdb.shard.base;
import java.lang.invoke.MethodHandles;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.google.common.base.Stopwatch;
import com.microsoft.azure.elasticdb.shard.map.ShardMap;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementException;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardMapManager;
import com.microsoft.azure.elasticdb.shard.store.StoreMapping;
import com.microsoft.azure.elasticdb.shard.store.StoreShardMap;
import com.microsoft.azure.elasticdb.shard.utils.StringUtilsLocal;
/**
* Represents a mapping between a range of key values and a <see cref="Shard"/>.
*/
public final class RangeMapping implements IShardProvider<Range>, Cloneable, IMappingInfoProvider {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* Shard object associated with the mapping.
*/
private Shard shard;
/**
* Gets the Range of the mapping.
*/
private Range value;
/**
* Holder of the range value's binary representation.
*/
private ShardRange range;
/**
* Reference to the ShardMapManager.
*/
private ShardMapManager shardMapManager;
/**
* Storage representation of the mapping.
*/
private StoreMapping storeMapping;
/**
* Constructs a range mapping given mapping creation arguments.
*
* @param shardMapManager
* Owning ShardMapManager.
* @param creationInfo
* Mapping creation information.
*/
public RangeMapping(ShardMapManager shardMapManager,
RangeMappingCreationInfo creationInfo) {
assert shardMapManager != null;
assert creationInfo != null;
assert creationInfo.getShard() != null;
this.setShardMapManager(shardMapManager);
shard = creationInfo.getShard();
this.setStoreMapping(new StoreMapping(UUID.randomUUID(), creationInfo.getShard(), creationInfo.getRange().getLow().getRawValue(),
creationInfo.getRange().getHigh().getRawValue(), creationInfo.getStatus().getValue()));
this.setRange(creationInfo.getRange());
this.setValue(creationInfo.getValue());
}
/**
* Internal constructor used for deserialization from store representation of the mapping object.
*
* @param shardMapManager
* Owning ShardMapManager.
* @param shardMap
* Owning shard map.
* @param mapping
* Storage representation of the mapping.
*/
public RangeMapping(ShardMapManager shardMapManager,
ShardMap shardMap,
StoreMapping mapping) {
assert shardMapManager != null;
this.setShardMapManager(shardMapManager);
assert mapping != null;
assert mapping.getShardMapId() != null;
assert mapping.getStoreShard().getShardMapId() != null;
this.setStoreMapping(mapping);
shard = new Shard(this.getShardMapManager(), shardMap, mapping.getStoreShard());
this.setRange(new ShardRange(ShardKey.fromRawValue(shardMap.getKeyType(), mapping.getMinValue()),
ShardKey.fromRawValue(shardMap.getKeyType(), mapping.getMaxValue())));
ShardKey high = this.getRange().getHigh();
ShardKey low = this.getRange().getLow();
Class lowDataType = low.getDataType();
this.setValue(high.getIsMax() ? new Range(low.getValueWithCheck(lowDataType))
: new Range(low.getValueWithCheck(lowDataType), high.getValueWithCheck(high.getDataType())));
}
/**
* Gets the <see cref="MappingStatus"/> of the mapping.
*/
public MappingStatus getStatus() {
if (this.getStoreMapping().getStatus() == MappingStatus.Online.getValue()) {
return MappingStatus.Online;
}
return MappingStatus.Offline;
}
/**
* Gets Shard that contains the range of values.
*/
public Shard getShard() {
return shard;
}
public Range getValue() {
return value;
}
private void setValue(Range value) {
this.value = value;
}
public ShardRange getRange() {
return range;
}
public void setRange(ShardRange value) {
range = value;
}
/**
* Identity of the mapping.
*/
public UUID getId() {
return this.getStoreMapping().getId();
}
/**
* Identify of the ShardMap this shard belongs to.
*/
public UUID getShardMapId() {
return this.getStoreMapping().getShardMapId();
}
public ShardMapManager getShardMapManager() {
return shardMapManager;
}
public void setShardMapManager(ShardMapManager value) {
shardMapManager = value;
}
public StoreMapping getStoreMapping() {
return storeMapping;
}
private void setStoreMapping(StoreMapping value) {
storeMapping = value;
}
/**
* Converts the object to its string representation.
*
* @return String representation of the object.
*/
@Override
public String toString() {
return StringUtilsLocal.formatInvariant("R[%s:%s]", this.getId().toString(), this.getRange().toString());
}
/**
* Determines whether the specified object is equal to the current object.
*
* @param obj
* The object to compare with the current object.
* @return True if the specified object is equal to the current object; otherwise, false.
*/
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof RangeMapping)) {
return false;
}
RangeMapping other = (RangeMapping) obj;
if (this.getId().equals(other.getId())) {
assert this.getRange().equals(other.getRange());
return true;
}
return false;
}
/**
* Calculates the hash code for this instance.
*
* @return Hash code for the object.
*/
@Override
public int hashCode() {
return this.getId().hashCode();
}
/**
* Shard that contains the range of values.
*/
public Shard getShardInfo() {
return this.getShard();
}
/**
* Performs validation that the local representation is as up-to-date as the representation on the backing data store.
*
* @param shardMap
* Shard map to which the shard provider belongs.
* @param conn
* Connection used for validation.
*/
@Override
public void validate(StoreShardMap shardMap,
Connection conn) {
try {
log.info("RangeMapping Validate Start; Connection: {}", conn.getMetaData().getURL());
Stopwatch stopwatch = Stopwatch.createStarted();
ValidationUtils.validateMapping(conn, this.getShardMapManager(), shardMap, this.getStoreMapping());
stopwatch.stop();
log.info("RangeMapping Validate Complete; Connection: {} Duration:{}", conn.getMetaData().getURL(),
stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
catch (SQLException e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
}
/**
* Asynchronously performs validation that the local representation is as up-to-date as the representation on the backing data store.
*
* @param shardMap
* Shard map to which the shard provider belongs.
* @param conn
* Connection used for validation.
* @return A task to await validation completion
*/
@Override
public Callable validateAsync(StoreShardMap shardMap,
Connection conn) {
Callable returnVal;
try {
log.info("RangeMapping ValidateAsync Start; Connection: {}", conn.getMetaData().getURL());
Stopwatch stopwatch = Stopwatch.createStarted();
returnVal = ValidationUtils.validateMappingAsync(conn, this.getShardMapManager(), shardMap, this.getStoreMapping());
stopwatch.stop();
log.info("RangeMapping ValidateAsync Complete; Connection: {} Duration:{}", conn.getMetaData().getURL(),
stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
catch (SQLException e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
return returnVal;
}
/**
* Clones the instance which implements the interface.
*
* @return clone of the instance.
*/
public RangeMapping clone() {
return new RangeMapping(this.getShardMapManager(), this.getShard().getShardMap(), this.getStoreMapping());
}
/**
* Type of the mapping.
*/
public MappingKind getKind() {
return MappingKind.RangeMapping;
}
/**
* Mapping type, useful for diagnostics.
*/
public String getTypeName() {
return "RangeMapping";
}
}

Просмотреть файл

@ -0,0 +1,92 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.utils.ExceptionUtils;
/**
* Arguments used to create a <see cref="RangeMapping"/>.
*/
public final class RangeMappingCreationInfo {
/**
* Gets Range being mapped.
*/
private Range value;
/**
* Gets Shard for the mapping.
*/
private Shard shard;
/**
* Gets Status of the mapping.
*/
private MappingStatus status = MappingStatus.values()[0];
/**
* Gets Range associated with the <see cref="RangeMapping"/>.
*/
private ShardRange range;
/**
* Arguments used for creation of a range mapping.
*
* @param value
* Range being mapped.
* @param shard
* Shard used as the mapping target.
* @param status
* Status of the mapping.
*/
public RangeMappingCreationInfo(Range value,
Shard shard,
MappingStatus status) {
ExceptionUtils.disallowNullArgument(value, "value");
ExceptionUtils.disallowNullArgument(shard, "shard");
this.setValue(value);
this.setShard(shard);
this.setStatus(status);
ShardKey low = new ShardKey(value.getLow());
ShardKey high = value.isHighMax() ? new ShardKey(low.getKeyType(), null) : new ShardKey(value.getHigh());
this.setRange(new ShardRange(low, high));
}
public Range getValue() {
return value;
}
private void setValue(Range value) {
this.value = value;
}
public Shard getShard() {
return shard;
}
private void setShard(Shard value) {
shard = value;
}
public MappingStatus getStatus() {
return status;
}
private void setStatus(MappingStatus value) {
status = value;
}
public ShardRange getRange() {
return range;
}
public void setRange(ShardRange value) {
range = value;
}
}

Просмотреть файл

@ -0,0 +1,38 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Represents updates to a mapping between a <see cref="Range"/> of values and the <see cref="Shard"/> that stores its data. Also see
* <see cref="RangeMapping"/>.
*/
public final class RangeMappingUpdate extends BaseMappingUpdate<MappingStatus> {
/**
* Instantiates a new range mapping update object.
*/
public RangeMappingUpdate() {
super();
}
/**
* Detects if the current mapping is being taken offline.
*
* @param originalStatus
* Original status.
* @param updatedStatus
* Updated status.
* @return Detects in the derived types if the mapping is being taken offline.
*/
@Override
protected boolean isBeingTakenOffline(MappingStatus originalStatus,
MappingStatus updatedStatus) {
return originalStatus == MappingStatus.Online && updatedStatus == MappingStatus.Offline;
}
}

Просмотреть файл

@ -0,0 +1,390 @@
package com.microsoft.azure.elasticdb.shard.base;
import java.lang.invoke.MethodHandles;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.google.common.base.Stopwatch;
import com.microsoft.azure.elasticdb.core.commons.logging.ActivityIdScope;
import com.microsoft.azure.elasticdb.shard.map.ShardMap;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementException;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardMapManager;
import com.microsoft.azure.elasticdb.shard.mapper.ConnectionOptions;
import com.microsoft.azure.elasticdb.shard.store.StoreShard;
import com.microsoft.azure.elasticdb.shard.store.StoreShardMap;
import com.microsoft.azure.elasticdb.shard.utils.StringUtilsLocal;
/**
* Representation of a single shard. Shards are basically locators for data sources i.e. <see cref="ShardLocation"/>s that have been registered with a
* shard map. Shards are used in mapping as targets of mappings (see <see cref="PointMapping"/> and <see cref="RangeMapping"/>).
*/
public final class Shard implements IShardProvider<ShardLocation>, Cloneable {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* Hashcode for the shard.
*/
private int hashCode;
/**
* Shard map object to which shard belongs.
*/
private ShardMap shardMap;
/**
* Reference to the ShardMapManager.
*/
private ShardMapManager shardMapManager;
/**
* Storage representation of the shard.
*/
private StoreShard storeShard;
/**
* Constructs a Shard given shard creation arguments.
*
* @param shardMapManager
* Owning ShardMapManager.
* @param shardMap
* Owning shard map.
* @param creationInfo
* Shard creation information.
*/
public Shard(ShardMapManager shardMapManager,
ShardMap shardMap,
ShardCreationInfo creationInfo) {
assert shardMapManager != null;
assert shardMap != null;
assert creationInfo != null;
this.setShardMapManager(shardMapManager);
this.setShardMap(shardMap);
this.setStoreShard(new StoreShard(UUID.randomUUID(), UUID.randomUUID(), shardMap.getId(), creationInfo.getLocation(),
creationInfo.getStatus().getValue()));
hashCode = this.calculateHashCode();
}
/**
* Internal constructor that uses storage representation.
*
* @param shardMapManager
* Owning ShardMapManager.
* @param shardMap
* Owning shard map.
* @param storeShard
* Storage representation of the shard.
*/
public Shard(ShardMapManager shardMapManager,
ShardMap shardMap,
StoreShard storeShard) {
assert shardMapManager != null;
this.setShardMapManager(shardMapManager);
assert shardMap != null;
this.setShardMap(shardMap);
assert storeShard.getShardMapId() != null;
this.setStoreShard(storeShard);
hashCode = this.calculateHashCode();
}
/**
* Gets Location of the shard.
*/
public ShardLocation getLocation() {
return this.getStoreShard().getLocation();
}
/**
* Gets the status of the shard which can be either online or offline. Connections can only be opened using
* <see cref="Shard.OpenConnection(string, ConnectionOptions)"/> on the shard map when the shard is online. Setting the shard status to offline
* prevents connections when the shard is undergoing maintenance operations.
*/
public ShardStatus getStatus() {
return ShardStatus.forValue(this.getStoreShard().getStatus());
}
/**
* Identity of the shard. Each shard should have a unique one.
*/
public UUID getId() {
return this.getStoreShard().getId();
}
/**
* Shard version.
*/
public UUID getVersion() {
return this.getStoreShard().getVersion();
}
/**
* Shard for the ShardProvider object.
*/
public Shard getShardInfo() {
return this;
}
/**
* Value corresponding to the Shard. Represents traits of the Shard object provided by the ShardInfo property.
*/
public ShardLocation getValue() {
return this.getLocation();
}
public ShardMap getShardMap() {
return shardMap;
}
public void setShardMap(ShardMap value) {
shardMap = value;
}
/**
* Identify of the ShardMap this shard belongs to.
*/
public UUID getShardMapId() {
return this.getStoreShard().getShardMapId();
}
public ShardMapManager getShardMapManager() {
return shardMapManager;
}
public void setShardMapManager(ShardMapManager value) {
shardMapManager = value;
}
public StoreShard getStoreShard() {
return storeShard;
}
public void setStoreShard(StoreShard value) {
storeShard = value;
}
/**
* Opens a regular <see cref="SqlConnection"/> to the specified shard, with <see cref="ConnectionOptions.Validate"/>.
*
* @param connectionString
* Connection string with credential information such as SQL Server credentials or Integrated Security settings. The hostname of the
* server and the database name for the shard are obtained from the lookup operation for key. Note that the <see cref="SqlConnection"/>
* object returned by this call is not protected against transient faults. Callers should follow best practices to protect the
* connection against transient faults in their application code, e.g., by using the transient fault handling functionality in the
* Enterprise Library from Microsoft Patterns and Practices team.
*/
public Connection openConnection(String connectionString) {
return this.openConnection(connectionString, ConnectionOptions.Validate);
}
/**
* Opens a regular <see cref="SqlConnection"/> to the specified shard.
*
* @param connectionString
* Connection string with credential information such as SQL Server credentials or Integrated Security settings. The hostname of the
* server and the database name for the shard are obtained from the lookup operation for key.
* @param options
* Options for validation operations to perform on opened connection. Note that the <see cref="SqlConnection"/> object returned by this
* call is not protected against transient faults. Callers should follow best practices to protect the connection against transient
* faults in their application code, e.g., by using the transient fault handling functionality in the Enterprise Library from Microsoft
* Patterns and Practices team.
*/
public Connection openConnection(String connectionString,
ConnectionOptions options) {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
return this.getShardMap().openConnection(this, connectionString, options);
}
}
/**
* Asynchronously opens a regular <see cref="SqlConnection"/> to the specified shard, with <see cref="ConnectionOptions.Validate"/>.
*
* @param connectionString
* Connection string with credential information such as SQL Server credentials or Integrated Security settings. The hostname of the
* server and the database name for the shard are obtained from the lookup operation for key.
* @return A Task encapsulating an opened SqlConnection Note that the <see cref="SqlConnection"/> object returned by this call is not protected
* against transient faults. Callers should follow best practices to protect the connection against transient faults in their application
* code, e.g., by using the transient fault handling functionality in the Enterprise Library from Microsoft Patterns and Practices team.
* All non-usage errors will be propagated via the returned Task.
*/
public Callable<Connection> openConnectionAsync(String connectionString) {
return this.openConnectionAsync(connectionString, ConnectionOptions.Validate);
}
/**
* Asynchronously a regular <see cref="SqlConnection"/> to the specified shard.
*
* @param connectionString
* Connection string with credential information such as SQL Server credentials or Integrated Security settings. The hostname of the
* server and the database name for the shard are obtained from the lookup operation for key.
* @param options
* Options for validation operations to perform on opened connection.
* @return A Task encapsulating an opened SqlConnection Note that the <see cref="SqlConnection"/> object returned by this call is not protected
* against transient faults. Callers should follow best practices to protect the connection against transient faults in their application
* code, e.g., by using the transient fault handling functionality in the Enterprise Library from Microsoft Patterns and Practices team.
* All non-usage errors will be propagated via the returned Task.
*/
public Callable<Connection> openConnectionAsync(String connectionString,
ConnectionOptions options) {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
return this.getShardMap().openConnectionAsync(this, connectionString, options);
}
}
/**
* Performs validation that the local representation is as up-to-date as the representation on the backing data store.
*
* @param shardMap
* Shard map to which the shard provider belongs.
* @param conn
* Connection used for validation.
*/
@Override
public void validate(StoreShardMap shardMap,
Connection conn) {
try {
Stopwatch stopwatch = Stopwatch.createStarted();
log.info("Shard Validate Start; Connection: {}", conn.getMetaData().getURL());
ValidationUtils.validateShard(conn, this.getShardMapManager(), shardMap, this.getStoreShard());
stopwatch.stop();
log.info("Shard Validate Complete; Connection: {} Duration:{}", conn.getMetaData().getURL(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
catch (SQLException e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
}
/**
* Asynchronously performs validation that the local representation is as up-to-date as the representation on the backing data store.
*
* @param shardMap
* Shard map to which the shard provider belongs.
* @param conn
* Connection used for validation.
* @return A task to await validation completion
*/
@Override
public Callable validateAsync(StoreShardMap shardMap,
Connection conn) {
Callable returnVal;
try {
Stopwatch stopwatch = Stopwatch.createStarted();
log.info("Shard ValidateAsync Start; Connection: {}", conn.getMetaData().getURL());
returnVal = ValidationUtils.validateShardAsync(conn, this.getShardMapManager(), shardMap, this.getStoreShard());
stopwatch.stop();
log.info("Shard ValidateAsync Complete; Connection: {} Duration:{}", conn.getMetaData().getURL(),
stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
catch (SQLException e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
return returnVal;
}
/**
* Clones the instance.
*
* @return clone of the instance.
*/
public Shard clone() {
return new Shard(this.getShardMapManager(), this.getShardMap(), this.getStoreShard());
}
/**
* Converts the object to its string representation.
*
* @return String representation of the object.
*/
@Override
public String toString() {
return StringUtilsLocal.formatInvariant("S[%s:%s:%s]", this.getId().toString(), this.getVersion().toString(), this.getLocation().toString());
}
/**
* Determines whether the specified object is equal to the current object.
*
* @param obj
* The object to compare with the current object.
* @return True if the specified object is equal to the current object; otherwise, false.
*/
@Override
public boolean equals(Object obj) {
return this.equals((Shard) ((obj instanceof Shard) ? obj : null));
}
/**
* Performs equality comparison with given Shard.
*
* @param other
* Shard to compare with.
* @return True if this object is equal to other object, false otherwise.
*/
public boolean equals(Shard other) {
if (null == other) {
return false;
}
else {
if (this.hashCode() != other.hashCode()) {
return false;
}
else {
// DEVNOTE(wbasheer): We are assuming identify comparison, without caring about version.
boolean result = this.getId().equals(other.getId()) && this.getVersion().equals(other.getVersion());
assert !result || this.getShardMapId().equals(other.getShardMapId());
assert !result || (this.getLocation().hashCode() == other.getLocation().hashCode());
assert !result || (this.getStatus() == other.getStatus());
return result;
}
}
}
/**
* Calculates the hash code for this instance.
*
* @return Hash code for the object.
*/
@Override
public int hashCode() {
return hashCode;
}
/**
* Calculates the hash code for the object.
*
* @return Hash code for the object.
*/
private int calculateHashCode() {
// DEVNOTE(wbasheer): We are assuming identify comparison, without caring about version.
return this.getId().hashCode();
// return ShardKey.qpHash(this.Id.GetHashCode(), this.Version.GetHashCode());
}
}

Просмотреть файл

@ -0,0 +1,69 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.utils.ExceptionUtils;
/**
* Arguments used to create a <see cref="Shard"/>.
*/
public final class ShardCreationInfo {
/**
* Gets Location of the shard.
*/
private ShardLocation location;
/**
* Gets Status of the shard. Users can assign application-specific values to the status field, which are kept together with the shard for
* convenience.
*/
private ShardStatus status = ShardStatus.values()[0];
/**
* Arguments used to create a <see cref="Shard"/>.
*
* @param location
* Location of the shard.
*/
public ShardCreationInfo(ShardLocation location) {
this(location, ShardStatus.Online);
}
/**
* Arguments used to create a <see cref="Shard"/>.
*
* @param location
* Location of the shard.
* @param status
* Status of the shard.
*/
public ShardCreationInfo(ShardLocation location,
ShardStatus status) {
ExceptionUtils.disallowNullArgument(location, "location");
this.setLocation(location);
this.setStatus(status);
}
public ShardLocation getLocation() {
return location;
}
private void setLocation(ShardLocation value) {
location = value;
}
public ShardStatus getStatus() {
return status;
}
public void setStatus(ShardStatus value) {
status = value;
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,100 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;
/**
* Type of shard key. Currently, only Int32, Int64, Guid and byte[] are the data types supported as shard keys.
*/
@XmlEnum
public enum ShardKeyType {
/**
* No type specified.
*/
@XmlEnumValue("0")
None(0, 0),
/**
* 32-bit integral value.
*/
@XmlEnumValue("1")
Int32(1, Integer.BYTES),
/**
* 64-bit integral value.
*/
@XmlEnumValue("2")
Int64(2, Long.BYTES),
/**
* UniqueIdentifier value.
*/
@XmlEnumValue("3")
Guid(3, ShardKey.SIZE_OF_GUID),
/**
* Array of bytes value.
*/
@XmlEnumValue("4")
Binary(4, ShardKey.MAXIMUM_VAR_BYTES_KEY_SIZE),
/**
* Date and time value.
*/
@XmlEnumValue("5")
DateTime(5, Long.BYTES),
/**
* Time value.
*/
@XmlEnumValue("6")
TimeSpan(6, Long.BYTES),
/**
* Date and time value with offset.
*/
@XmlEnumValue("7")
DateTimeOffset(7, ShardKey.SIZE_OF_DATE_TIME_OFFSET);
private static java.util.HashMap<Integer, ShardKeyType> mappings;
private int intValue;
private int expectedByteArrayLength;
ShardKeyType(int value,
int expectedByteArrayLength) {
intValue = value;
this.expectedByteArrayLength = expectedByteArrayLength;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, ShardKeyType> getMappings() {
if (mappings == null) {
synchronized (ShardKeyType.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static ShardKeyType forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
public int getByteArraySize() {
return expectedByteArrayLength;
}
}

Просмотреть файл

@ -0,0 +1,264 @@
package com.microsoft.azure.elasticdb.shard.base;
import java.io.Serializable;
import java.util.Locale;
import javax.xml.bind.annotation.XmlElement;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.utils.Errors;
import com.microsoft.azure.elasticdb.shard.utils.ExceptionUtils;
import com.microsoft.azure.elasticdb.shard.utils.GlobalConstants;
import com.microsoft.azure.elasticdb.shard.utils.StringUtilsLocal;
/**
* Represents the location of a shard in terms of its server name and database name. This is used to manage connections to the shard and to support
* other operations on shards. As opposed to a <see cref="Shard"/>, a shard location is not registered with the shard map.
*/
public final class ShardLocation implements Serializable {
/**
* Hashcode for the shard location.
*/
private int hashCode;
/**
* Protocol name prefix.
*/
@XmlElement(name = "Protocol")
private SqlProtocol protocol = SqlProtocol.values()[0];
/**
* Gets the fully qualified hostname of the server for the shard database.
*/
@XmlElement(name = "ServerName")
private String server;
/**
* Communication port for TCP/IP protocol. If no port is specified, the property returns 0.
*/
@XmlElement(name = "Port")
private int port;
/**
* Gets the database name of the shard.
*/
@XmlElement(name = "DatabaseName")
private String database;
public ShardLocation() {
}
/**
* Constructor that allows specification of protocol, address, port and database to identify a shard.
*
* @param server
* Fully qualified hostname of the server for the shard database.
* @param database
* Name of the shard database.
* @param protocol
* Transport protcol used for the connection.
* @param port
* Port number for TCP/IP connections. Specify 0 to use the default port for the specified <paramref name="protocol"/>.
*/
public ShardLocation(String server,
String database,
SqlProtocol protocol,
int port) {
if (protocol.getValue() < SqlProtocol.Default.getValue() || protocol.getValue() > SqlProtocol.SharedMemory.getValue()) {
throw new IllegalArgumentException(StringUtilsLocal.formatInvariant(Errors._ShardLocation_UnsupportedProtocol, protocol),
new Throwable("protocol"));
}
if (port < 0 || 65535 < port) {
throw new IllegalArgumentException("port", new Throwable(StringUtilsLocal.formatInvariant(Errors._ShardLocation_InvalidPort, port)));
}
ExceptionUtils.disallowNullOrEmptyStringArgument(server, "server");
ExceptionUtils.disallowNullOrEmptyStringArgument(database, "database");
if (server.length() > GlobalConstants.MaximumServerLength) {
throw new IllegalArgumentException(
StringUtilsLocal.formatInvariant(Errors._ShardLocation_InvalidServerOrDatabase, "Server", GlobalConstants.MaximumServerLength),
new Throwable("server"));
}
if (database.length() > GlobalConstants.MaximumDatabaseLength) {
throw new IllegalArgumentException(StringUtilsLocal.formatInvariant(Errors._ShardLocation_InvalidServerOrDatabase, "Database",
GlobalConstants.MaximumDatabaseLength), new Throwable("database"));
}
this.setProtocol(protocol);
this.setServer(server);
this.setPort(port);
this.setDatabase(database);
hashCode = this.calculateHashCode();
}
/**
* Constructor that allows specification of address and database to identify a shard.
*
* @param server
* Fully qualified hostname of the server for the shard database.
* @param database
* Name of the shard database.
*/
public ShardLocation(String server,
String database) {
this(server, database, SqlProtocol.Default, 0);
}
/**
* Constructor that allows specification of address and database to identify a shard.
*
* @param server
* Fully qualified hostname of the server for the shard database.
* @param database
* Name of the shard database.
* @param protocol
* Transport protcol used for the connection.
*/
public ShardLocation(String server,
String database,
SqlProtocol protocol) {
this(server, database, protocol, 0);
}
public SqlProtocol getProtocol() {
return protocol;
}
private void setProtocol(SqlProtocol value) {
protocol = value;
}
public String getServer() {
return server;
}
private void setServer(String value) {
server = value;
}
public int getPort() {
return port;
}
private void setPort(int value) {
port = value;
}
/**
* DataSource name which can be used to construct connection string Data Source property.
*/
public String getDataSource() {
return StringUtilsLocal.formatInvariant("%s%s%s", this.getProtocolPrefix(), this.getServer(), this.getPortSuffix());
}
public String getDatabase() {
return database;
}
private void setDatabase(String value) {
database = value;
}
/**
* Converts the shard location to its string representation.
*
* @return String representation of shard location.
*/
@Override
public String toString() {
return StringUtilsLocal.formatInvariant("[DataSource=%s Database=%s]", this.getDataSource(), this.getDatabase());
}
/**
* Calculates the hash code for this instance.
*
* @return Hash code for the object.
*/
@Override
public int hashCode() {
return hashCode;
}
/**
* Determines whether the specified object is equal to the current object.
*
* @param obj
* The object to compare with the current object.
* @return True if the specified object is equal to the current object; otherwise, false.
*/
@Override
public boolean equals(Object obj) {
return this.equals((ShardLocation) ((obj instanceof ShardLocation) ? obj : null));
}
/**
* Performs equality comparison with another given ShardLocation.
*
* @param other
* ShardLocation to compare with.
* @return True if same locations, false otherwise.
*/
public boolean equals(ShardLocation other) {
return other != null && this.hashCode() == other.hashCode() && (this.getProtocol() == other.getProtocol() && this.getPort() == other.getPort()
&& this.getDataSource().equalsIgnoreCase(other.getDataSource()) && this.getDatabase().equalsIgnoreCase(other.getDatabase()));
}
/**
* Calculates the hash code for the object.
*
* @return Hash code for the object.
*/
private int calculateHashCode() {
int h;
h = ShardKey.qpHash(this.getProtocol().hashCode(), this.getDataSource().toUpperCase(Locale.ROOT).hashCode());
h = ShardKey.qpHash(h, (new Integer(this.getPort())).hashCode());
h = ShardKey.qpHash(h, this.getDatabase().toUpperCase(Locale.ROOT).hashCode());
return h;
}
/**
* Gets the connection string data source prefix for the supported protocol.
*
* @return Connection string prefix containing string representation of protocol.
*/
private String getProtocolPrefix() {
switch (this.getProtocol()) {
case Tcp:
return "tcp:";
case NamedPipes:
return "np:";
case SharedMemory:
return "lpc:";
default:
assert this.getProtocol() == SqlProtocol.Default;
return "";
}
}
/**
* Gets the connection string data source suffix for supplied port number.
*
* @return Connection string suffix containing string representation of port.
*/
private String getPortSuffix() {
if (this.getPort() != 0) {
return StringUtilsLocal.formatInvariant(",%s", this.getPort());
}
else {
return "";
}
}
}

Просмотреть файл

@ -0,0 +1,488 @@
package com.microsoft.azure.elasticdb.shard.base;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.utils.Errors;
import com.microsoft.azure.elasticdb.shard.utils.ExceptionUtils;
import com.microsoft.azure.elasticdb.shard.utils.StringUtilsLocal;
/**
* A range of shard keys between a low key and a high key. The low key is inclusive (part of the range) while the high key is exclusive (not part of
* the range). The ShardRange class is immutable.
*/
@XmlAccessorType(XmlAccessType.NONE)
public final class ShardRange implements Comparable<ShardRange> {
public static final ShardRange NULL = new ShardRange();
/**
* Full range that starts from the min value for a key to the max value.
*/
private static ShardRange shardRangeInt32 = new ShardRange(ShardKey.getMinInt(), ShardKey.getMaxInt());
/**
* Full range that starts from the min value for a key to the max value.
*/
private static ShardRange shardRangeInt64 = new ShardRange(ShardKey.getMinLong(), ShardKey.getMaxLong());
/**
* Full range that starts from the min value for a key to the max value.
*/
private static ShardRange shardRangeGuid = new ShardRange(ShardKey.getMinGuid(), ShardKey.getMaxGuid());
/**
* Full range that starts from the min value for a key to the max value.
*/
private static ShardRange shardRangeBinary = new ShardRange(ShardKey.getMinBinary(), ShardKey.getMaxBinary());
/**
* Full range that starts from the min value for a key to the max value.
*/
private static ShardRange shardRangeDateTime = new ShardRange(ShardKey.getMinDateTime(), ShardKey.getMaxDateTime());
/**
* Full range that starts from the min value for a key to the max value.
*/
private static ShardRange shardRangeTimeSpan = new ShardRange(ShardKey.getMinTimeSpan(), ShardKey.getMaxTimeSpan());
/**
* Full range that starts from the min value for a key to the max value.
*/
private static ShardRange shardRangeDateTimeOffset = new ShardRange(ShardKey.getMinDateTimeOffset(), ShardKey.getMaxDateTimeOffset());
/**
* Hashcode for the shard range.
*/
private int hashCode;
/**
* Accessor for low boundary (inclusive).
*/
private ShardKey low;
/**
* Accessor for high boundary (exclusive).
*/
private ShardKey high;
/**
* Gets the key type of shard range.
*/
private ShardKeyType keyType;
@XmlAttribute(name = "Null")
private int isNull;
public ShardRange() {
isNull = 1;
}
/**
* Constructs a shard range from low boundary (inclusive) to high high boundary (exclusive).
*
* @param low
* low boundary (inclusive)
* @param high
* high boundary (exclusive)
*/
public ShardRange(ShardKey low,
ShardKey high) {
ExceptionUtils.disallowNullArgument(low, "low");
ExceptionUtils.disallowNullArgument(high, "high");
if (low.compareTo(high) > 0) {
throw new IllegalArgumentException(String.format(Errors._ShardRange_LowGreaterThanOrEqualToHigh, low, high));
}
this.setLow(low);
this.setHigh(high);
this.setKeyType(getLow().getKeyType());
hashCode = this.calculateHashCode();
isNull = 0;
}
/**
* Full range that starts from the min value for a key to the max value.
*/
public static ShardRange getFullRangeInt32() {
return shardRangeInt32;
}
/**
* Full range that starts from the min value for a key to the max value.
*/
public static ShardRange getFullRangeInt64() {
return shardRangeInt64;
}
/**
* Full range that starts from the min value for a key to the max value.
*/
public static ShardRange getFullRangeGuid() {
return shardRangeGuid;
}
/**
* Full range that starts from the min value for a key to the max value.
*/
public static ShardRange getFullRangeBinary() {
return shardRangeBinary;
}
/**
* Full range that starts from the min value for a key to the max value.
*/
public static ShardRange getFullRangeDateTime() {
return shardRangeDateTime;
}
/**
* Full range that starts from the min value for a key to the max value.
*/
public static ShardRange getFullRangeTimeSpan() {
return shardRangeTimeSpan;
}
/**
* Full range that starts from the min value for a key to the max value.
*/
public static ShardRange getFullRangeDateTimeOffset() {
return shardRangeDateTimeOffset;
}
/**
* Compares two <see cref="ShardRange"/> using lexicographic order (less than).
*
* @param left
* Left hand side <see cref="ShardRange"/> of the operator.
* @param right
* Right hand side <see cref="ShardRange"/> of the operator.
* @return True if lhs &lt; rhs
*/
public static boolean opLessThan(ShardRange left,
ShardRange right) {
if (left == null) {
return right != null;
}
else {
return (left.compareTo(right) < 0);
}
}
/**
* Compares two <see cref="ShardRange"/> using lexicographic order (greater than).
*
* @param left
* Left hand side <see cref="ShardRange"/> of the operator.
* @param right
* Right hand side <see cref="ShardRange"/> of the operator.
* @return True if lhs &gt; rhs
*/
public static boolean opGreaterThan(ShardRange left,
ShardRange right) {
return opLessThan(right, left);
}
/**
* Compares two <see cref="ShardRange"/> using lexicographic order (less or equal).
*
* @param left
* Left hand side <see cref="ShardRange"/> of the operator.
* @param right
* Right hand side <see cref="ShardRange"/> of the operator.
* @return True if lhs &lt;= rhs
*/
public static boolean opLessThanOrEqual(ShardRange left,
ShardRange right) {
return !opGreaterThan(left, right);
}
/**
* Compares two <see cref="ShardRange"/> using lexicographic order (greater or equal).
*
* @param left
* Left hand side <see cref="ShardRange"/> of the operator.
* @param right
* Right hand side <see cref="ShardRange"/> of the operator.
* @return True if lhs &gt;= rhs
*/
public static boolean opGreaterThanOrEqual(ShardRange left,
ShardRange right) {
return !opLessThan(left, right);
}
/**
* Equality operator.
*
* @param left
* Left hand side
* @param right
* Right hand side
* @return True if the two objects are equal, false in all other cases
*/
public static boolean opEquality(ShardRange left,
ShardRange right) {
return left.equals(right);
}
/**
* Inequality operator.
*
* @param left
* Left hand side
* @param right
* Right hand side
* @return True if the two objects are not equal, false in all other cases
*/
public static boolean opInequality(ShardRange left,
ShardRange right) {
return !opEquality(left, right);
}
/**
* Gets a shard range corresponding to a specified key type.
*
* @param keyType
* Type of key.
* @return Full range for given key type.
*/
public static ShardRange getFullRange(ShardKeyType keyType) {
assert keyType != ShardKeyType.None;
switch (keyType) {
case Int32:
return ShardRange.getFullRangeInt32();
case Int64:
return ShardRange.getFullRangeInt64();
case Guid:
return ShardRange.getFullRangeGuid();
case Binary:
return ShardRange.getFullRangeBinary();
case DateTime:
return ShardRange.getFullRangeDateTime();
case TimeSpan:
return ShardRange.getFullRangeTimeSpan();
case DateTimeOffset:
return ShardRange.getFullRangeDateTimeOffset();
default:
// Debug.Fail("Unexpected ShardKeyType.");
return null;
}
}
public ShardKey getLow() {
return low;
}
private void setLow(ShardKey value) {
low = value;
}
public ShardKey getHigh() {
return high;
}
private void setHigh(ShardKey value) {
high = value;
}
public ShardKeyType getKeyType() {
return keyType;
}
private void setKeyType(ShardKeyType value) {
keyType = value;
}
/**
* Converts the object to its string representation.
*
* @return String representation of the object.
*/
@Override
public String toString() {
return String.format("[%s:%s]", this.getLow().toString(), this.getHigh().toString());
}
/**
* Calculates the hash code for this instance.
*
* @return Hash code for the object.
*/
@Override
public int hashCode() {
return hashCode;
}
/**
* Determines whether the specified object is equal to the current object.
*
* @param obj
* The object to compare with the current object.
* @return True if the specified object is equal to the current object; otherwise, false.
*/
@Override
public boolean equals(Object obj) {
return this.equals((ShardRange) ((obj instanceof ShardRange) ? obj : null));
}
/**
* Performs equality comparison with another given ShardRange.
*
* @param other
* ShardRange to compare with.
* @return True if same shard range, false otherwise.
*/
public boolean equals(ShardRange other) {
return other != null && this.hashCode() == other.hashCode() && this.compareTo(other) == 0;
}
/**
* Checks whether the specified key is inside the range.
*
* @param key
* The key to check
* @return True if inside, false otherwise
*/
public boolean contains(ShardKey key) {
ExceptionUtils.disallowNullArgument(key, "key");
return (key.compareTo(getLow()) >= 0 && key.compareTo(getHigh()) < 0);
}
/**
* Checks whether the range is inside the range.
*
* @param range
* The range to check.
* @return True if inside, false otherwise.
*/
public boolean contains(ShardRange range) {
ExceptionUtils.disallowNullArgument(range, "range");
return ShardKey.opGreaterThanOrEqual(range.getLow(), getLow()) && ShardKey.opLessThanOrEqual(range.getHigh(), getHigh());
}
/**
* Performs comparison between two shard range values.
*
* @param other
* The shard range compared with this object.
* @return -1 : if this range's low boundary is less than the <paramref name="other"/>'s low boundary; -1 : if the low boundary values match and
* the high boundary value of this range is less than the <paramref name="other"/>'s. 1 : if this range's high boundary is greater than
* the <paramref name="other"/>'s high boundary; 1 : if the low boundary value of this range is higher than <paramref name="other"/>'s low
* boundary and high boundary value of this range is less than or equal to <paramref name="other"/>'s high boundary . 0 : if this range
* has the same boundaries as <paramref name="other"/>.
*/
public int compareTo(ShardRange other) {
ExceptionUtils.disallowNullArgument(other, "other");
if (ShardKey.opLessThan(this.getLow(), other.getLow())) {
return -1;
}
if (ShardKey.opGreaterThan(this.getHigh(), other.getHigh())) {
return 1;
}
if (ShardKey.opEquality(this.getLow(), other.getLow())) {
if (ShardKey.opEquality(this.getHigh(), other.getHigh())) {
return 0;
}
else {
return -1;
}
}
else {
assert ShardKey.opGreaterThan(this.getLow(), other.getLow());
assert ShardKey.opLessThanOrEqual(this.getHigh(), other.getHigh());
return 1;
}
}
/**
* Checks whether the range intersects with the current range.
*
* @param range
* The range to check.
* @return True if it intersects, False otherwise.
*/
public boolean intersects(ShardRange range) {
ExceptionUtils.disallowNullArgument(range, "range");
return ShardKey.opGreaterThan(range.getHigh(), getLow()) && ShardKey.opLessThan(range.getLow(), getHigh());
}
/**
* Returns the intersection of two ranges.
*
* @param range
* Range to intersect with.
* @return The intersection of the current range and the specified range, null if ranges dont intersect.
*/
public ShardRange intersect(ShardRange range) {
ExceptionUtils.disallowNullArgument(range, "range");
ShardKey intersectLow = ShardKey.max(getLow(), range.getLow());
ShardKey intersectHigh = ShardKey.min(getHigh(), range.getHigh());
if (ShardKey.opGreaterThanOrEqual(intersectLow, intersectHigh)) {
return null;
}
return new ShardRange(intersectLow, intersectHigh);
}
/**
* Calculates the hash code for the object.
*
* @return Hash code for the object.
*/
private int calculateHashCode() {
return ShardKey.qpHash(this.getLow().hashCode(), this.getHigh().hashCode());
}
@XmlElement(name = "MinValue")
private String getMinValue() {
return low == null ? "" : low.getStoreValue();
}
@XmlElement(name = "MaxValue")
private MaxShardKey getMaxValue() {
String key = high == null ? "" : high.getStoreValue();
return new MaxShardKey(key);
}
@XmlAccessorType(XmlAccessType.NONE)
@XmlType(name = "MaxValue")
public static class MaxShardKey {
@XmlAttribute(name = "Null")
private int isNull;
@XmlValue
private String key;
public MaxShardKey() {
isNull = 1;
}
MaxShardKey(String key) {
this.key = key;
this.isNull = StringUtilsLocal.isNullOrEmpty(key) ? 1 : 0;
}
}
}

Просмотреть файл

@ -0,0 +1,52 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Status of a shard.
*/
public enum ShardStatus {
/**
* Shard is Offline.
*/
Offline(0),
/**
* Shard is Online.
*/
Online(1);
public static final int SIZE = Integer.SIZE;
private static java.util.HashMap<Integer, ShardStatus> mappings;
private int intValue;
ShardStatus(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, ShardStatus> getMappings() {
if (mappings == null) {
synchronized (ShardStatus.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static ShardStatus forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,59 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Represents updates to a Shard.
*/
public final class ShardUpdate {
/**
* Records the modified properties for update.
*/
private ShardUpdatedProperties updatedProperties = ShardUpdatedProperties.forValue(0);
/**
* Holder for update to status property.
*/
private ShardStatus status = ShardStatus.values()[0];
/**
* Instantiates the shard update object with no property set.
*/
public ShardUpdate() {
}
/**
* Get Status property.
*/
public ShardStatus getStatus() {
return status;
}
/**
* Set Status property.
*/
public void setStatus(ShardStatus value) {
status = value;
int shardUpdatePropertyValue = updatedProperties == null ? ShardUpdatedProperties.Status.getValue()
: updatedProperties.getValue() | ShardUpdatedProperties.Status.getValue();
updatedProperties = ShardUpdatedProperties.forValue(shardUpdatePropertyValue);
}
/**
* Checks if any of the properties specified in the given bitmap have been set by the user.
*
* @param p
* Bitmap of properties.
* @return True if any property is set, false otherwise.
*/
public boolean isAnyPropertySet(ShardUpdatedProperties p) {
return (updatedProperties.getValue() & p.getValue()) != 0;
}
}

Просмотреть файл

@ -0,0 +1,45 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Records the updated properties on the shard.
*/
public enum ShardUpdatedProperties {
Status(1),
All(1);
public static final int SIZE = java.lang.Integer.SIZE;
private static java.util.HashMap<Integer, ShardUpdatedProperties> mappings;
private int intValue;
ShardUpdatedProperties(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, ShardUpdatedProperties> getMappings() {
if (mappings == null) {
synchronized (ShardUpdatedProperties.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static ShardUpdatedProperties forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,68 @@
package com.microsoft.azure.elasticdb.shard.base;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import javax.xml.bind.annotation.XmlEnumValue;
/**
* Types of transport protocols supported in SQL Server connections.
*/
public enum SqlProtocol {
/**
* Default protocol.
*/
@XmlEnumValue("0")
Default(0),
/**
* TCP/IP protocol.
*/
@XmlEnumValue("1")
Tcp(1),
/**
* Named pipes protocol.
*/
@XmlEnumValue("2")
NamedPipes(2),
/**
* Shared memory protocol.
*/
@XmlEnumValue("3")
SharedMemory(3);
public static final int SIZE = java.lang.Integer.SIZE;
private static java.util.HashMap<Integer, SqlProtocol> mappings;
private int intValue;
SqlProtocol(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, SqlProtocol> getMappings() {
if (mappings == null) {
synchronized (SqlProtocol.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static SqlProtocol forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,238 @@
package com.microsoft.azure.elasticdb.shard.base;
import java.lang.invoke.MethodHandles;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.sql.Types;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.transform.sax.SAXResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.google.common.base.Stopwatch;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardMapManager;
import com.microsoft.azure.elasticdb.shard.sqlstore.SqlResults;
import com.microsoft.azure.elasticdb.shard.store.StoreMapping;
import com.microsoft.azure.elasticdb.shard.store.StoreResult;
import com.microsoft.azure.elasticdb.shard.store.StoreResults;
import com.microsoft.azure.elasticdb.shard.store.StoreShard;
import com.microsoft.azure.elasticdb.shard.store.StoreShardMap;
import com.microsoft.azure.elasticdb.shard.storeops.base.StoreOperationErrorHandler;
import com.microsoft.azure.elasticdb.shard.storeops.base.StoreOperationInput;
import com.microsoft.azure.elasticdb.shard.storeops.base.StoreOperationRequestBuilder;
public final class ValidationUtils {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* Performs validation that the local representation is as up-to-date as the representation on the backing data store.
*
* @param conn
* Connection used for validation.
* @param shardMapManager
* ShardMapManager reference.
* @param shardMap
* Shard map for the mapping.
* @param storeMapping
* Mapping to validate.
*/
public static void validateMapping(Connection conn,
ShardMapManager shardMapManager,
StoreShardMap shardMap,
StoreMapping storeMapping) {
Stopwatch stopwatch = Stopwatch.createStarted();
StoreResults lsmResult = new StoreResults();
JAXBElement jaxbElement = StoreOperationRequestBuilder.validateShardMappingLocal(shardMap.getId(), storeMapping.getId());
try (CallableStatement cstmt = conn
.prepareCall(String.format("{call %s(?,?)}", StoreOperationRequestBuilder.SP_VALIDATE_SHARD_MAPPING_LOCAL))) {
SQLXML sqlxml = conn.createSQLXML();
JAXBContext context = JAXBContext.newInstance(StoreOperationInput.class, StoreShard.class, StoreShardMap.class);
// Set the result value from SAX events.
SAXResult sxResult = sqlxml.setResult(SAXResult.class);
context.createMarshaller().marshal(jaxbElement, sxResult);
/*
* log.info("Xml:{}\n{}", "ValidateShardMappingLocal", SqlStoreTransactionScope.asString(context, jaxbElement));//
*/
cstmt.setSQLXML("input", sqlxml);
cstmt.registerOutParameter("result", Types.INTEGER);
Boolean hasResults = cstmt.execute();
StoreResults storeResults = SqlResults.newInstance(cstmt);
// After iterating resultSet's, get result integer.
int result = cstmt.getInt("result");
lsmResult.setResult(StoreResult.forValue(result));
}
catch (SQLException | JAXBException e) {
e.printStackTrace();
}
stopwatch.stop();
try {
log.info("Shard ValidateMapping Complete; Shard: {}; Connection: {}; Result:{}; Duration: {}", storeMapping.getStoreShard().getLocation(),
conn.getMetaData().getURL(), lsmResult.getResult(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
catch (SQLException e) {
e.printStackTrace();
}
if (lsmResult.getResult() != StoreResult.Success) {
if (lsmResult.getResult() == StoreResult.ShardMapDoesNotExist) {
shardMapManager.getCache().deleteShardMap(shardMap);
}
else {
if (lsmResult.getResult() == StoreResult.MappingDoesNotExist) {
// Only evict from cache is mapping is no longer present,
// for Offline mappings, we don't even retry, so same request
// will continue to go to the LSM.
shardMapManager.getCache().deleteMapping(storeMapping);
}
}
// Possible errors are:
// StoreResult.ShardMapDoesNotExist
// StoreResult.MappingDoesNotExist
// StoreResult.MappingIsOffline
// StoreResult.ShardVersionMismatch
// StoreResult.StoreVersionMismatch
// StoreResult.MissingParametersForStoredProcedure
throw StoreOperationErrorHandler.onValidationErrorLocal(lsmResult, shardMap, storeMapping.getStoreShard().getLocation(),
"ValidateMapping", StoreOperationRequestBuilder.SP_VALIDATE_SHARD_LOCAL);
}
assert lsmResult.getResult() == StoreResult.Success;
}
/**
* Asynchronously performs validation that the local representation is as up-to-date as the representation on the backing data store.
*
* @param conn
* Connection used for validation.
* @param shardMapManager
* ShardMapManager reference.
* @param shardMap
* Shard map for the mapping.
* @param storeMapping
* Mapping to validate.
* @return A task to await validation completion
*/
public static Callable validateMappingAsync(Connection conn,
ShardMapManager shardMapManager,
StoreShardMap shardMap,
StoreMapping storeMapping) {
return () -> {
validateMapping(conn, shardMapManager, shardMap, storeMapping);
return null;
};
}
/**
* Performs validation that the local representation is as up-to-date as the representation on the backing data store.
*
* @param conn
* Connection used for validation.
* @param shardMapManager
* ShardMapManager reference.
* @param shardMap
* Shard map for the shard.
* @param shard
* Shard to validate.
*/
public static void validateShard(Connection conn,
ShardMapManager shardMapManager,
StoreShardMap shardMap,
StoreShard shard) {
Stopwatch stopwatch = Stopwatch.createStarted();
StoreResults lsmResult = new StoreResults();
JAXBElement jaxbElement = StoreOperationRequestBuilder.validateShardLocal(shardMap.getId(), shard.getId(), shard.getVersion());
try (CallableStatement cstmt = conn.prepareCall(String.format("{call %s(?,?)}", StoreOperationRequestBuilder.SP_VALIDATE_SHARD_LOCAL))) {
SQLXML sqlxml = conn.createSQLXML();
JAXBContext context = JAXBContext.newInstance(StoreOperationInput.class, StoreShard.class, StoreShardMap.class);
// Set the result value from SAX events.
SAXResult sxResult = sqlxml.setResult(SAXResult.class);
context.createMarshaller().marshal(jaxbElement, sxResult);
/*
* log.info("Xml:{}\n{}", "ValidateShardLocal", SqlStoreTransactionScope.asString(context, jaxbElement));//
*/
cstmt.setSQLXML("input", sqlxml);
cstmt.registerOutParameter("result", Types.INTEGER);
Boolean hasResults = cstmt.execute();
StoreResults storeResults = SqlResults.newInstance(cstmt);
// After iterating resultSet's, get result integer.
int result = cstmt.getInt("result");
lsmResult.setResult(StoreResult.forValue(result));
stopwatch.stop();
log.info("Shard ValidateShard", "Complete; Shard: {}; Connection: {}; Result: {}; Duration: {}", shard.getLocation(),
conn.getMetaData().getURL(), lsmResult.getResult(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
catch (SQLException | JAXBException e) {
e.printStackTrace();
}
if (lsmResult.getResult() != StoreResult.Success) {
if (lsmResult.getResult() == StoreResult.ShardMapDoesNotExist) {
shardMapManager.getCache().deleteShardMap(shardMap);
}
// Possible errors are:
// StoreResult.ShardMapDoesNotExist
// StoreResult.ShardDoesNotExist
// StoreResult.ShardVersionMismatch
// StoreResult.StoreVersionMismatch
// StoreResult.MissingParametersForStoredProcedure
throw StoreOperationErrorHandler.onValidationErrorLocal(lsmResult, shardMap, shard.getLocation(), "ValidateShard",
StoreOperationRequestBuilder.SP_VALIDATE_SHARD_LOCAL);
}
}
/**
* Asynchronously performs validation that the local representation is as up-to-date as the representation on the backing data store.
*
* @param conn
* Connection used for validation.
* @param shardMapManager
* ShardMapManager reference.
* @param shardMap
* Shard map for the shard.
* @param shard
* Shard to validate.
* @return A task to await validation completion
*/
public static Callable validateShardAsync(Connection conn,
ShardMapManager shardMapManager,
StoreShardMap shardMap,
StoreShard shard) {
return () -> {
validateShard(conn, shardMapManager, shardMap, shard);
return null;
};
}
}

Просмотреть файл

@ -0,0 +1,154 @@
package com.microsoft.azure.elasticdb.shard.cache;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
import com.microsoft.azure.elasticdb.shard.base.ShardKey;
import com.microsoft.azure.elasticdb.shard.base.ShardKeyType;
import com.microsoft.azure.elasticdb.shard.base.ShardRange;
import com.microsoft.azure.elasticdb.shard.store.StoreMapping;
/**
* Cached representation of collection of mappings within shard map. The items consist of a single point values.
*/
public class CacheListMapper extends CacheMapper {
/**
* Mappings organized by Key.
*/
private Map<ShardKey, CacheMapping> mappingsByKey;
/**
* Constructs the mapper, notes the key type for lookups.
*
* @param keyType
* Key type.
*/
public CacheListMapper(ShardKeyType keyType) {
super(keyType);
// Use concurrentHashMap as it locks at key level instead of entire map for better performance.
mappingsByKey = new ConcurrentHashMap<>();
}
/**
* Add or update a mapping in cache.
*
* @param sm
* Storage mapping object.
* @param policy
* Policy to use for preexisting cache entries during update.
*/
@Override
public void addOrUpdate(StoreMapping sm,
CacheStoreMappingUpdatePolicy policy) {
// Make key out of mapping key.
ShardKey key = ShardKey.fromRawValue(this.getKeyType(), sm.getMinValue());
CacheMapping cm = null;
if (mappingsByKey.containsKey(key)) {
cm = mappingsByKey.get(key);
}
// We need to update TTL and update entry if:
// a) We are in update TTL mode
// b) Mapping exists and same as the one we already have
// c) Entry is beyond the TTL limit
if (policy == CacheStoreMappingUpdatePolicy.UpdateTimeToLive && cm != null && cm.getMapping().getId().equals(sm.getId())) {
cm = new CacheMapping(sm, CacheMapper.calculateNewTimeToLiveMilliseconds(cm));
}
else {
cm = new CacheMapping(sm);
}
// Remove existing entry.
this.remove(sm);
// Add the entry to lookup table by Key.
mappingsByKey.put(key, cm);
}
/**
* Remove a mapping object from cache.
*
* @param sm
* Storage mapping object.
*/
@Override
public void remove(StoreMapping sm) {
// Make key value out of mapping key.
ShardKey key = ShardKey.fromRawValue(this.getKeyType(), sm.getMinValue());
// Remove existing entry.
if (mappingsByKey.containsKey(key)) {
mappingsByKey.remove(key);
}
}
/**
* Looks up a mapping by key.
*
* @param key
* Key value.
* @return Mapping object which has the key value.
*/
@Override
public ICacheStoreMapping lookupByKey(ShardKey key) {
return mappingsByKey.get(key);
}
/**
* Looks up a mapping by Range.
*
* @param range
* Optional range value, if null, we cover everything.
* @param sm
* Storage mapping object.
* @return Mapping object which has the key value.
*/
@Override
public List<ICacheStoreMapping> lookupByRange(ShardRange range,
ReferenceObjectHelper<List<StoreMapping>> sm) {
List<StoreMapping> mappings = new ArrayList<>();
List<ICacheStoreMapping> filteredList = range == null ? new ArrayList<>(mappingsByKey.values())
: mappingsByKey.entrySet().stream()
.filter(m -> ShardKey.opGreaterThanOrEqual(m.getKey(), range.getLow()) && ShardKey.opLessThan(m.getKey(), range.getHigh()))
.map(Entry::getValue).collect(Collectors.toList());
filteredList.forEach(item -> mappings.add(item.getMapping()));
sm.argValue = mappings;
return filteredList;
}
/**
* Get number of point mappings cached in this mapper.
*
* @return Number of cached point mappings.
*/
@Override
public long getMappingsCount() {
return mappingsByKey.size();
}
/**
* Clears all the mappings in the lookup by Id table as well as lookup by key table.
*/
@Override
protected void clear() {
mappingsByKey.clear();
}
}

Просмотреть файл

@ -0,0 +1,115 @@
package com.microsoft.azure.elasticdb.shard.cache;
import java.util.List;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
import com.microsoft.azure.elasticdb.shard.base.ShardKey;
import com.microsoft.azure.elasticdb.shard.base.ShardKeyType;
import com.microsoft.azure.elasticdb.shard.base.ShardRange;
import com.microsoft.azure.elasticdb.shard.store.StoreMapping;
/**
* Cached representation of collection of mappings within shard map. Derived classes implement either list or range functionality.
*/
public abstract class CacheMapper {
/**
* Key type, usable by lookups by key in derived classes.
*/
private ShardKeyType keyType;
/**
* Constructs the mapper, notes the key type for lookups.
*
* @param keyType
* Key type.
*/
public CacheMapper(ShardKeyType keyType) {
this.setKeyType(keyType);
}
/**
* Given current value of TTL, calculates the next TTL value in milliseconds.
*
* @param csm
* Current cached mapping object.
* @return New TTL value.
*/
protected static long calculateNewTimeToLiveMilliseconds(ICacheStoreMapping csm) {
if (csm.getTimeToLiveMilliseconds() <= 0) {
return 5000;
}
// Exponentially increase the time up to a limit of 30 seconds, after which we keep
// returning 30 seconds as the TTL for the mapping entry.
return Math.min(30000, csm.getTimeToLiveMilliseconds() * 2);
}
protected final ShardKeyType getKeyType() {
return keyType;
}
private void setKeyType(ShardKeyType value) {
keyType = value;
}
/**
* Add or update a mapping in cache.
*
* @param sm
* Storage mapping object.
* @param policy
* Policy to use for preexisting cache entries during update.
*/
public abstract void addOrUpdate(StoreMapping sm,
CacheStoreMappingUpdatePolicy policy);
/**
* Remove a mapping object from cache.
*
* @param sm
* Storage mapping object.
*/
public abstract void remove(StoreMapping sm);
/**
* Looks up a mapping by key.
*
* @param key
* Key value.
* @return Mapping object which has the key value.
*/
public abstract ICacheStoreMapping lookupByKey(ShardKey key);
/**
* Looks up a mapping by Range.
*
* @param range
* Optional range value, if null, we cover everything.
* @param sm
* Storage mapping object.
* @return Mapping object which has the key value.
*/
public abstract List<ICacheStoreMapping> lookupByRange(ShardRange range,
ReferenceObjectHelper<List<StoreMapping>> sm);
/**
* Gets mappings dictionary size.
*
* @return Number of mappings cached in the dictionary.
*/
public abstract long getMappingsCount();
/**
* Clears all the mappings in the lookup by Id table.
*/
protected abstract void clear();
}

Просмотреть файл

@ -0,0 +1,102 @@
package com.microsoft.azure.elasticdb.shard.cache;
import java.time.LocalDateTime;
import java.time.temporal.ChronoField;
import java.util.concurrent.atomic.AtomicLong;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.store.StoreMapping;
/**
* Cached representation of a single mapping.
*/
public class CacheMapping implements ICacheStoreMapping {
/**
* Time to live for the cache entry.
*/
private long timeToLiveMilliseconds;
/**
* Storage representation of the mapping.
*/
private StoreMapping mapping;
/**
* Mapping entry creation time.
*/
private long creationTime;
/**
* Constructs cached representation of a mapping object.
*
* @param storeMapping
* Storage representation of mapping.
*/
public CacheMapping(StoreMapping storeMapping) {
this(storeMapping, 0);
}
/**
* Constructs cached representation of a mapping object.
*
* @param storeMapping
* Storage representation of mapping.
* @param timeToLiveMilliseconds
* Mapping expiration time.
*/
public CacheMapping(StoreMapping storeMapping,
long timeToLiveMilliseconds) {
this.setMapping(storeMapping);
this.setCreationTime(LocalDateTime.now().getLong(ChronoField.MILLI_OF_SECOND));
this.setTimeToLiveMilliseconds(timeToLiveMilliseconds);
}
public final StoreMapping getMapping() {
return mapping;
}
private void setMapping(StoreMapping value) {
mapping = value;
}
public final long getCreationTime() {
return creationTime;
}
private void setCreationTime(long value) {
creationTime = value;
}
/**
* Mapping entry expiration time.
*/
public final long getTimeToLiveMilliseconds() {
return timeToLiveMilliseconds;
}
private void setTimeToLiveMilliseconds(long value) {
timeToLiveMilliseconds = value;
}
/**
* Resets the mapping entry expiration time to 0.
*/
public final void resetTimeToLive() {
timeToLiveMilliseconds = new AtomicLong(0L).get();
}
/**
* Whether TimeToLiveMilliseconds have elapsed since the CreationTime.
*
* @return True if they have
*/
public final boolean hasTimeToLiveExpired() {
return (System.nanoTime() - creationTime) >= timeToLiveMilliseconds;
}
}

Просмотреть файл

@ -0,0 +1,315 @@
package com.microsoft.azure.elasticdb.shard.cache;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListMap;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
import com.microsoft.azure.elasticdb.shard.base.ShardKey;
import com.microsoft.azure.elasticdb.shard.base.ShardKeyType;
import com.microsoft.azure.elasticdb.shard.base.ShardRange;
import com.microsoft.azure.elasticdb.shard.store.StoreMapping;
/**
* Cached representation of collection of mappings within shard map. The items consist of a ranges of key values.
*/
public class CacheRangeMapper extends CacheMapper {
/**
* Mappings organized by Key Ranges.
*/
private NavigableMap<ShardRange, CacheMapping> mappingsByRange;
/**
* Constructs the mapper, notes the key type for lookups.
*
* @param keyType
* Key type.
*/
public CacheRangeMapper(ShardKeyType keyType) {
super(keyType);
// Use concurrent sorted map to automatically take care of locking at key-level.
mappingsByRange = new ConcurrentSkipListMap<>();
}
/**
* Add or update a mapping in cache.
*
* @param sm
* Storage mapping object.
* @param policy
* Policy to use for preexisting cache entries during update.
*/
@Override
public void addOrUpdate(StoreMapping sm,
CacheStoreMappingUpdatePolicy policy) {
ShardKey min = ShardKey.fromRawValue(this.getKeyType(), sm.getMinValue());
// Make range out of mapping key ranges.
ShardRange range = new ShardRange(min, ShardKey.fromRawValue(this.getKeyType(), sm.getMaxValue()));
CacheMapping cm;
ICacheStoreMapping csm;
// We need to update TTL and update entry if:
// a) We are in update TTL mode
// b) Mapping exists and same as the one we already have
// c) Entry is beyond the TTL limit
if (policy == CacheStoreMappingUpdatePolicy.UpdateTimeToLive && (csm = this.lookupByKey(min)) != null
&& csm.getMapping().getId() == sm.getId()) {
cm = new CacheMapping(sm, CacheMapper.calculateNewTimeToLiveMilliseconds(csm));
}
else {
cm = new CacheMapping(sm);
}
this.remove(sm);
// Add the entry to lookup table by Range.
mappingsByRange.put(range, cm);
}
/**
* Remove a mapping object from cache. Q: Do we ever need to remove multiple entries from the cache which cover the same range? A: Yes. Imagine
* that you have some stale mapping in the cache, user performs an AddRangeMapping operation on a subset of stale mapping range, now you should
* remove the stale mapping.
*
* @param sm
* Storage mapping object.
*/
@Override
public void remove(StoreMapping sm) {
ShardKey minKey = ShardKey.fromRawValue(this.getKeyType(), sm.getMinValue());
ShardKey maxKey = ShardKey.fromRawValue(this.getKeyType(), sm.getMaxValue());
// Make range out of mapping key.
ShardRange range = new ShardRange(minKey, maxKey);
if (mappingsByRange.size() > 0) {
// Fast code path, where cache does contain the exact range.
if (mappingsByRange.containsKey(range)) {
mappingsByRange.remove(range);
}
else {
int indexMin = this.getIndexOfMappingWithClosestMinLessThanOrEqualToMinKey(minKey);
int indexMax = this.getIndexOfMappingWithClosestMaxGreaterThanOrEqualToMaxKey(maxKey);
if (indexMin < 0) {
indexMin = 0;
}
if (indexMax >= mappingsByRange.size()) {
indexMax = mappingsByRange.size() - 1;
}
// Do we need this? If yes, why?
// Find first range with max greater than min key.
ShardRange rangeMaxGreatMinKey = mappingsByRange.keySet().stream().filter(r -> ShardKey.opGreaterThan(r.getHigh(), minKey))
.findFirst().orElse(null);
// Find first range with min less than or equal to max key.
ShardRange rangeMinLessEqualMaxKey = mappingsByRange.keySet().stream().filter(r -> ShardKey.opLessThanOrEqual(r.getLow(), maxKey))
.findFirst().orElse(null);
ArrayList<ShardRange> rangesToRemove = new ArrayList<>();
List<ShardRange> keys = new ArrayList<>(mappingsByRange.keySet());
for (; indexMin <= indexMax; indexMin++) {
rangesToRemove.add(keys.get(indexMin));
}
for (ShardRange rangeToRemove : rangesToRemove) {
mappingsByRange.remove(rangeToRemove);
}
}
}
}
/**
* Looks up a mapping by key.
*
* @param key
* Key value.
* @return Mapping object which has the key value.
*/
@Override
public ICacheStoreMapping lookupByKey(ShardKey key) {
CacheMapping cm = null;
// Performs a binary search in the ranges for key value and
// then return the result.
ShardRange range = this.getIndexOfMappingContainingShardKey(key);
if (range != null) {
cm = mappingsByRange.get(range);
}
return cm;
}
/**
* Looks up a mapping by Range.
*
* @param range
* Optional range value, if null, we cover everything.
* @param sm
* Storage mapping object.
* @return Mapping object which has the key value.
*/
@Override
public List<ICacheStoreMapping> lookupByRange(ShardRange range,
ReferenceObjectHelper<List<StoreMapping>> sm) {
List<ICacheStoreMapping> cm = new ArrayList<>();
sm.argValue = new ArrayList<>();
if (range == null) {
// Filter
for (Entry<ShardRange, CacheMapping> e : mappingsByRange.entrySet()) {
sm.argValue.add(e.getValue().getMapping());
cm.add(e.getValue());
}
return cm;
}
// Performs a binary search in the ranges for key value and then return the result.
ShardRange lowerIndex = this.getIndexOfMappingContainingShardKey(range.getLow());
ShardRange higherIndex = this.getIndexOfMappingContainingShardKey(range.getHigh());
if (lowerIndex != null && higherIndex != null) {
Map<ShardRange, CacheMapping> m = mappingsByRange.subMap(lowerIndex, true, higherIndex, true);
// Filter
for (Entry<ShardRange, CacheMapping> e : m.entrySet()) {
sm.argValue.add(e.getValue().getMapping());
cm.add(e.getValue());
}
return cm;
}
else {
sm.argValue = null;
}
return null;
}
/**
* Get number of range mappings cached in this mapper.
*
* @return Number of cached range mappings.
*/
@Override
public long getMappingsCount() {
return mappingsByRange.size();
}
/**
* Clears all the mappings in the lookup by Id table as well as lookup by range table.
*/
@Override
protected void clear() {
mappingsByRange.clear();
}
/**
* Performs binary search on the cached mappings and returns the index of mapping object which contains the given key.
*
* @param key
* Input key.
* @return Index of range in the cache which contains the given key.
*/
private ShardRange getIndexOfMappingContainingShardKey(ShardKey key) {
// Keep low and high as same key when you lookup in sorted map.
ShardRange rangeKey = new ShardRange(key, key);
// Could potentially match with lower bound.
ShardRange floorKey = mappingsByRange.floorKey(rangeKey);
if (floorKey != null && floorKey.contains(key)) {
return floorKey;
}
// Could potentially match with upper bound.
ShardRange ceilingKey = mappingsByRange.ceilingKey(rangeKey);
if (ceilingKey != null && ceilingKey.contains(key)) {
return ceilingKey;
}
return null;
}
/**
* Performs binary search on the cached mappings and returns the index of mapping object whose min-value is less than and closest to given key
* value.
*
* @param key
* Input key.
* @return Index of range in the cache which contains the given key.
*/
private int getIndexOfMappingWithClosestMinLessThanOrEqualToMinKey(ShardKey key) {
Set<ShardRange> rangeKeys = mappingsByRange.keySet();
int lb = 0;
int ub = rangeKeys.size() - 1;
while (lb <= ub) {
int mid = lb + (ub - lb) / 2;
ShardRange current = (ShardRange) rangeKeys.toArray()[mid];
if (ShardKey.opLessThanOrEqual(current.getLow(), key)) {
if (ShardKey.opGreaterThan(current.getHigh(), key)) {
return mid;
}
else {
lb = mid + 1;
}
}
else {
ub = mid - 1;
}
}
return -1;
}
/**
* Performs binary search on the cached mappings and returns the index of mapping object whose min-value is less than and closest to given key
* value.
*
* @param key
* Input key.
* @return Index of range in the cache which contains the given key.
*/
private int getIndexOfMappingWithClosestMaxGreaterThanOrEqualToMaxKey(ShardKey key) {
Set<ShardRange> rangeKeys = mappingsByRange.keySet();
int lb = 0;
int ub = rangeKeys.size() - 1;
while (lb <= ub) {
int mid = lb + (ub - lb) / 2;
ShardRange current = (ShardRange) rangeKeys.toArray()[mid];
if (ShardKey.opGreaterThan(current.getHigh(), key)) {
if (ShardKey.opLessThanOrEqual(current.getLow(), key)) {
return mid;
}
else {
ub = mid - 1;
}
}
else {
lb = mid + 1;
}
}
return -1;
}
}

Просмотреть файл

@ -0,0 +1,66 @@
package com.microsoft.azure.elasticdb.shard.cache;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.store.StoreShardMap;
/**
* Cached representation of shard map.
*/
public class CacheShardMap {
/**
* Storage representation of shard map.
*/
private StoreShardMap storeShardMap;
/**
* Mapper object. Exists only for List/Range/Hash shard maps.
*/
private CacheMapper mapper;
/**
* Constructs the cached shard map.
*
* @param ssm
* Storage representation of shard map.
*/
public CacheShardMap(StoreShardMap ssm) {
storeShardMap = ssm;
switch (ssm.getMapType()) {
case List:
mapper = new CacheListMapper(ssm.getKeyType());
break;
case Range:
mapper = new CacheRangeMapper(ssm.getKeyType());
break;
default:
throw new RuntimeException("Unknown shardMapType:" + ssm.getMapType());
}
}
public final StoreShardMap getStoreShardMap() {
return storeShardMap;
}
public final CacheMapper getMapper() {
return mapper;
}
/**
* Transfers the child cache objects to current instance from the source instance. Useful for maintaining the cache even in case of refreshes to
* shard map objects.
*
* @param source
* Source cached shard map to copy child objects from.
*/
public final void transferStateFrom(CacheShardMap source) {
mapper = source.getMapper();
}
}

Просмотреть файл

@ -0,0 +1,189 @@
package com.microsoft.azure.elasticdb.shard.cache;
import java.lang.invoke.MethodHandles;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
import com.microsoft.azure.elasticdb.shard.base.ShardKey;
import com.microsoft.azure.elasticdb.shard.base.ShardRange;
import com.microsoft.azure.elasticdb.shard.store.StoreMapping;
import com.microsoft.azure.elasticdb.shard.store.StoreShardMap;
/**
* Client side cache store.
*/
public class CacheStore implements ICacheStore {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* Contained shard maps. Look up to be done by name.
*/
private Map<String, CacheShardMap> shardMapsByName;
/**
* Contained shard maps. Lookup to be done by Id.
*/
private Map<UUID, CacheShardMap> shardMapsById;
/**
* Constructs an instance of client side cache object.
*/
public CacheStore() {
shardMapsByName = new ConcurrentHashMap<>();
shardMapsById = new ConcurrentHashMap<>();
}
/**
* Invoked for refreshing shard map in cache from store.
*
* @param ssm
* Storage representation of shard map.
*/
public void addOrUpdateShardMap(StoreShardMap ssm) {
CacheShardMap csm = new CacheShardMap(ssm);
CacheShardMap csmOldByName = shardMapsByName.get(ssm.getName());
CacheShardMap csmOldById = shardMapsById.get(ssm.getId());
if (csmOldByName != null) {
shardMapsByName.remove(ssm.getName());
}
if (csmOldById != null) {
shardMapsById.remove(ssm.getId());
}
// Both should be found or none should be found.
assert (csmOldByName == null && csmOldById == null) || (csmOldByName != null && csmOldById != null);
// Both should point to same cached copy.
assert csmOldByName == csmOldById;
if (csmOldByName != null) {
csm.transferStateFrom(csmOldByName);
}
shardMapsByName.put(ssm.getName(), csm);
shardMapsById.put(ssm.getId(), csm);
}
/**
* Invoked for deleting shard map in cache because it no longer exists in store.
*
* @param shardMap
* Storage representation of shard map.
*/
public void deleteShardMap(StoreShardMap shardMap) {
shardMapsByName.remove(shardMap.getName());
shardMapsById.remove(shardMap.getId());
}
/**
* Looks up a given shard map in cache based on it's name.
*
* @param shardMapName
* Name of shard map.
* @return The shard being searched.
*/
public StoreShardMap lookupShardMapByName(String shardMapName) {
CacheShardMap csm = shardMapsByName.get(shardMapName);
log.info("Cache {}; ShardMap: {}", csm == null ? "miss" : "hit", shardMapName);
return (csm != null) ? csm.getStoreShardMap() : null;
}
/**
* Invoked for refreshing mapping in cache from store.
*
* @param mapping
* Storage representation of mapping.
* @param policy
* Policy to use for preexisting cache entries during update.
*/
public void addOrUpdateMapping(StoreMapping mapping,
CacheStoreMappingUpdatePolicy policy) {
CacheShardMap csm = shardMapsById.get(mapping.getShardMapId());
if (csm == null) {
return;
}
// Mapper by itself is thread-safe using ConcurrentHashMap and ConcurrentSkipListMap.
csm.getMapper().addOrUpdate(mapping, policy);
log.info("Cache Add/Update mapping complete. Mapping Id: {}", mapping.getId());
}
/**
* Invoked for deleting mapping in cache because it no longer exists in store.
*
* @param mapping
* Storage representation of mapping.
*/
public void deleteMapping(StoreMapping mapping) {
CacheShardMap csm = shardMapsById.get(mapping.getShardMapId());
if (csm == null) {
return;
}
csm.getMapper().remove(mapping);
log.info("Cache delete mapping complete. Mapping Id: {}", mapping.getId());
}
/**
* Looks up a given key in given shard map.
*
* @param shardMap
* Storage representation of shard map.
* @param key
* Key value.
* @return Mapping corresponding to <paramref name="key"/> or null.
*/
public ICacheStoreMapping lookupMappingByKey(StoreShardMap shardMap,
ShardKey key) {
CacheShardMap csm = shardMapsById.get(shardMap.getId());
if (csm == null) {
return null;
}
return csm.getMapper().lookupByKey(key);
}
/**
* Looks up a given range in given shard map.
*
* @param shardMap
* Storage representation of shard map.
* @param range
* Optional range value, if null, we cover everything.
* @return Mapping corresponding to <paramref name="key"/> or null.
*/
public List<ICacheStoreMapping> lookupMappingsForRange(StoreShardMap shardMap,
ShardRange range) {
CacheShardMap csm = shardMapsById.get(shardMap.getId());
if (csm == null) {
return null;
}
ReferenceObjectHelper<List<StoreMapping>> tempRefSmDummy = new ReferenceObjectHelper<>(null);
return csm.getMapper().lookupByRange(range, tempRefSmDummy);
}
/**
* Clears the cache.
*/
public void clear() {
shardMapsByName.clear();
shardMapsById.clear();
}
}

Просмотреть файл

@ -0,0 +1,52 @@
package com.microsoft.azure.elasticdb.shard.cache;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Policy for AddOrUpdateMapping operation.
*/
public enum CacheStoreMappingUpdatePolicy {
/**
* Overwrite the mapping blindly.
*/
OverwriteExisting(0),
/**
* Keep the original mapping but change TTL.
*/
UpdateTimeToLive(1);
public static final int SIZE = java.lang.Integer.SIZE;
private static java.util.HashMap<Integer, CacheStoreMappingUpdatePolicy> mappings;
private int intValue;
CacheStoreMappingUpdatePolicy(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, CacheStoreMappingUpdatePolicy> getMappings() {
if (mappings == null) {
synchronized (CacheStoreMappingUpdatePolicy.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static CacheStoreMappingUpdatePolicy forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,95 @@
package com.microsoft.azure.elasticdb.shard.cache;
import java.util.List;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.base.ShardKey;
import com.microsoft.azure.elasticdb.shard.base.ShardRange;
import com.microsoft.azure.elasticdb.shard.store.StoreMapping;
import com.microsoft.azure.elasticdb.shard.store.StoreShardMap;
/**
* Representation of client side cache.
*/
public interface ICacheStore {
/**
* Invoked for refreshing shard map in cache from store.
*
* @param shardMap
* Storage representation of shard map.
*/
void addOrUpdateShardMap(StoreShardMap shardMap);
/**
* Invoked for deleting shard map in cache because it no longer exists in store.
*
* @param shardMap
* Storage representation of shard map.
*/
void deleteShardMap(StoreShardMap shardMap);
/**
* Looks up a given shard map in cache based on it's name.
*
* @param shardMapName
* Name of shard map.
* @return The shard being searched.
*/
StoreShardMap lookupShardMapByName(String shardMapName);
/**
* Invoked for refreshing mapping in cache from store.
*
* @param mapping
* Storage representation of mapping.
* @param policy
* Policy to use for preexisting cache entries during update.
*/
void addOrUpdateMapping(StoreMapping mapping,
CacheStoreMappingUpdatePolicy policy);
/**
* Invoked for deleting mapping in cache because it no longer exists in store.
*
* @param mapping
* Storage representation of mapping.
*/
void deleteMapping(StoreMapping mapping);
/**
* Looks up a given key in given shard map.
*
* @param shardMap
* Storage representation of shard map.
* @param key
* Key value.
* @return Mapping corresponding to <paramref name="key"/> or null.
*/
ICacheStoreMapping lookupMappingByKey(StoreShardMap shardMap,
ShardKey key);
/**
* Looks up a given range in given shard map.
*
* @param shardMap
* Storage representation of shard map.
* @param range
* Optional range value, if null, we cover everything.
* @return Mapping corresponding to <paramref name="key"/> or null.
*/
List<ICacheStoreMapping> lookupMappingsForRange(StoreShardMap shardMap,
ShardRange range);
/**
* Clears the cache.
*/
void clear();
}

Просмотреть файл

@ -0,0 +1,42 @@
package com.microsoft.azure.elasticdb.shard.cache;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.store.StoreMapping;
/**
* Represents a cache entry for a mapping.
*/
public interface ICacheStoreMapping {
/**
* Store representation of mapping.
*/
StoreMapping getMapping();
/**
* Mapping entry creation time.
*/
long getCreationTime();
/**
* Mapping entry expiration time.
*/
long getTimeToLiveMilliseconds();
/**
* Resets the mapping entry expiration time to 0.
*/
void resetTimeToLive();
/**
* Whether TimeToLiveMilliseconds have elapsed since CreationTime.
*/
boolean hasTimeToLiveExpired();
}

Просмотреть файл

@ -0,0 +1,679 @@
package com.microsoft.azure.elasticdb.shard.map;
import java.lang.invoke.MethodHandles;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.google.common.base.Stopwatch;
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
import com.microsoft.azure.elasticdb.core.commons.logging.ActivityIdScope;
import com.microsoft.azure.elasticdb.shard.base.LockOwnerIdOpType;
import com.microsoft.azure.elasticdb.shard.base.LookupOptions;
import com.microsoft.azure.elasticdb.shard.base.MappingLockToken;
import com.microsoft.azure.elasticdb.shard.base.MappingStatus;
import com.microsoft.azure.elasticdb.shard.base.PointMapping;
import com.microsoft.azure.elasticdb.shard.base.PointMappingCreationInfo;
import com.microsoft.azure.elasticdb.shard.base.PointMappingUpdate;
import com.microsoft.azure.elasticdb.shard.base.Range;
import com.microsoft.azure.elasticdb.shard.base.Shard;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardMapManager;
import com.microsoft.azure.elasticdb.shard.mapper.IShardMapper;
import com.microsoft.azure.elasticdb.shard.mapper.ListShardMapper;
import com.microsoft.azure.elasticdb.shard.store.StoreShardMap;
import com.microsoft.azure.elasticdb.shard.utils.ExceptionUtils;
/**
* Represents a shard map of points where points are of the specified key. <typeparam name="KeyT">Key type.</typeparam>
*/
public final class ListShardMap<KeyT> extends ShardMap implements Cloneable {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* Mapper b/w points and shards.
*/
private ListShardMapper lsm;
/**
* Constructs a new instance.
*
* @param shardMapManager
* Reference to ShardMapManager.
* @param ssm
* Storage representation.
*/
public ListShardMap(ShardMapManager shardMapManager,
StoreShardMap ssm) {
super(shardMapManager, ssm);
lsm = new ListShardMapper(shardMapManager, this);
}
/**
* Creates and adds a point mapping to ShardMap.
*
* @param creationInfo
* Information about mapping to be added.
* @return Newly created mapping.
*/
public PointMapping createPointMapping(PointMappingCreationInfo creationInfo) {
ExceptionUtils.disallowNullArgument(creationInfo, "args");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
Stopwatch stopwatch = Stopwatch.createStarted();
String mappingKey = creationInfo.getKey().getRawValue().toString();
log.info("CreatePointMapping Start; ShardMap name: {}; Point Mapping: {} ", this.getName(), mappingKey);
PointMapping mapping = lsm.add(new PointMapping(this.getShardMapManager(), creationInfo));
stopwatch.stop();
log.info("CreatePointMapping Complete; ShardMap name: {}; Point Mapping: {}; Duration: {}", this.getName(), mappingKey,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return mapping;
}
}
/**
* Creates and adds a point mapping to ShardMap.
*
* @param point
* Point for which to create the mapping.
* @param shard
* Shard associated with the point mapping.
* @return Newly created mapping.
*/
public PointMapping createPointMapping(KeyT point,
Shard shard) {
ExceptionUtils.disallowNullArgument(shard, "shard");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
PointMappingCreationInfo args = new PointMappingCreationInfo(point, shard, MappingStatus.Online);
String mappingKey = args.getKey().toString();
log.info("CreatePointMapping Start; ShardMap name: {}; Point Mapping: {}", this.getName(), mappingKey);
Stopwatch stopwatch = Stopwatch.createStarted();
PointMapping pointMapping = lsm.add(new PointMapping(this.getShardMapManager(), args));
stopwatch.stop();
log.info("CreatePointMapping Complete; ShardMap name: {}; Point Mapping: {}; Duration: {}", this.getName(), mappingKey,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return pointMapping;
}
}
/**
* Removes a point mapping.
*
* @param mapping
* Mapping being removed.
*/
public void deleteMapping(PointMapping mapping) {
ExceptionUtils.disallowNullArgument(mapping, "mapping");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
String mappingKey = mapping.getKey().getRawValue().toString();
log.info("DeletePointMapping Start; ShardMap name: {}; Point Mapping: {}", this.getName(), mappingKey);
Stopwatch stopwatch = Stopwatch.createStarted();
lsm.remove(mapping);
stopwatch.stop();
log.info("DeletePointMapping Completed; ShardMap name: {}; Point Mapping: {}; Duration: {}", this.getName(), mappingKey,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
}
/**
* Looks up the key value and returns the corresponding mapping. Only the global shard map store is searched, not the local cache. This is
* equivalent to <code>getMappingForKey(key,
* LookupOptions.LookupInStore)</code>.
*
* @param key
* Input key value.
* @return Mapping that contains the key value.
*/
public PointMapping getMappingForKey(KeyT key) {
return getMappingForKey(key, LookupOptions.LOOKUP_IN_STORE);
}
/**
* Looks up the key value and returns the corresponding mapping.
*
* @param key
* Input key value.
* @param lookupOptions
* Whether to search in the cache and/or store.
* @return Mapping that contains the key value.
*/
public PointMapping getMappingForKey(KeyT key,
LookupOptions lookupOptions) {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("LookupPointMapping", "Start; ShardMap name: {}; Point Mapping Key Type: {}; Lookup " + "Options: {}", this.getName(),
key.getClass(), lookupOptions);
Stopwatch stopwatch = Stopwatch.createStarted();
PointMapping pointMapping = lsm.lookup(key, lookupOptions);
stopwatch.stop();
log.info("LookupPointMapping", "Complete; ShardMap name: {}; Point Mapping Key Type: {}; " + "Lookup Options: {}; Duration: {}",
this.getName(), key.getClass(), lookupOptions, stopwatch.elapsed(TimeUnit.MILLISECONDS));
return pointMapping;
}
}
/**
* Tries to looks up the key value and place the corresponding mapping in <paramref name="pointMapping"/>. Only the global shard map store is
* searched, not local cache. This is equivalent to <c>TryGetMappingForKey(key, LookupOptions.LookupInStore, out pointMapping)</c>.
*
* @param key
* Input key value.
* @param pointMapping
* Mapping that contains the key value.
* @return <c>true</c> if mapping is found, <c>false</c> otherwise.
*/
public boolean tryGetMappingForKey(KeyT key,
ReferenceObjectHelper<PointMapping> pointMapping) {
return tryGetMappingForKey(key, LookupOptions.LOOKUP_IN_STORE, pointMapping);
}
/**
* Tries to looks up the key value and place the corresponding mapping in <paramref name="pointMapping"/>.
*
* @param key
* Input key value.
* @param lookupOptions
* Whether to search in the cache and/or store.
* @param pointMapping
* Mapping that contains the key value.
* @return <c>true</c> if mapping is found, <c>false</c> otherwise.
*/
public boolean tryGetMappingForKey(KeyT key,
LookupOptions lookupOptions,
ReferenceObjectHelper<PointMapping> pointMapping) {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("TryLookupPointMapping", "Start; ShardMap name: {}; Point Mapping Key Type:{}; " + "Lookup Options: {}", this.getName(),
key.getClass(), lookupOptions);
Stopwatch stopwatch = Stopwatch.createStarted();
boolean result = lsm.tryLookup(key, lookupOptions, pointMapping);
stopwatch.stop();
log.info("TryLookupPointMapping", "Complete; ShardMap name: {}; Point Mapping Key Type: {}; " + "Lookup Options: {}; Duration: {}",
this.getName(), key.getClass(), lookupOptions, stopwatch.elapsed(TimeUnit.MILLISECONDS));
return result;
}
}
/**
* Gets all the point mappings for the shard map.
*
* @return Read-only collection of all point mappings on the shard map.
*/
public List<PointMapping> getMappings() {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetPointMappings", "Start;");
Stopwatch stopwatch = Stopwatch.createStarted();
List<PointMapping> pointMappings = lsm.getMappingsForRange(null, null, LookupOptions.LOOKUP_IN_STORE);
stopwatch.stop();
log.info("GetPointMappings", "Complete; Duration:{}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
return pointMappings;
}
}
/**
* Gets all the mappings that exist within given range.
*
* @param range
* Point value, any mapping overlapping with the range will be returned.
* @return Read-only collection of mappings that satisfy the given range constraint.
*/
public List<PointMapping> getMappings(Range range) {
ExceptionUtils.disallowNullArgument(range, "range");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetPointMappings", "Start; Range:{}", range);
Stopwatch stopwatch = Stopwatch.createStarted();
List<PointMapping> pointMappings = lsm.getMappingsForRange(range, null, LookupOptions.LOOKUP_IN_STORE);
stopwatch.stop();
log.info("GetPointMappings", "Complete; Range: {}; Duration:{}", range, stopwatch.elapsed(TimeUnit.MILLISECONDS));
return pointMappings;
}
}
/**
* Gets all the mappings that exist for the given shard.
*
* @param shard
* Shard for which the mappings will be returned.
* @return Read-only collection of mappings that satisfy the given shard constraint.
*/
public List<PointMapping> getMappings(Shard shard) {
ExceptionUtils.disallowNullArgument(shard, "shard");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetPointMappings", "Start; Shard:{}", shard.getLocation());
Stopwatch stopwatch = Stopwatch.createStarted();
List<PointMapping> pointMappings = lsm.getMappingsForRange(null, shard, LookupOptions.LOOKUP_IN_STORE);
stopwatch.stop();
log.info("GetPointMappings", "Complete; Shard: {}; Duration:{}", shard.getLocation(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
return pointMappings;
}
}
/**
* Gets all the mappings that exist within given range and given shard.
*
* @param range
* Point value, any mapping overlapping with the range will be returned.
* @param shard
* Shard for which the mappings will be returned.
* @return Read-only collection of mappings that satisfy the given range and shard constraints.
*/
public List<PointMapping> getMappings(Range range,
Shard shard) {
ExceptionUtils.disallowNullArgument(range, "range");
ExceptionUtils.disallowNullArgument(shard, "shard");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetPointMappings", "Start; Shard: {}; Range:{}", shard.getLocation(), range);
Stopwatch stopwatch = Stopwatch.createStarted();
List<PointMapping> pointMappings = lsm.getMappingsForRange(range, shard, LookupOptions.LOOKUP_IN_STORE);
stopwatch.stop();
log.info("GetPointMappings", "Complete; Shard: {}; Range: {}; Duration:{}", shard.getLocation(),
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return pointMappings;
}
}
/**
* Gets all the point mappings for the shard map.
*
* @param lookupOptions
* Whether to search in the cache and/or store.
* @return Read-only collection of all point mappings on the shard map.
*/
public List<PointMapping> getMappings(LookupOptions lookupOptions) {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetPointMappings", "Start; Lookup Options: {}", lookupOptions);
Stopwatch stopwatch = Stopwatch.createStarted();
List<PointMapping> pointMappings = lsm.getMappingsForRange(null, null, lookupOptions);
stopwatch.stop();
log.info("GetPointMappings", "Complete; Lookup Options: {}; Duration:{}", lookupOptions, stopwatch.elapsed(TimeUnit.MILLISECONDS));
return pointMappings;
}
}
/**
* Gets all the mappings that exist within given range.
*
* @param range
* Point value, any mapping overlapping with the range will be returned.
* @param lookupOptions
* Whether to search in the cache and/or store.
* @return Read-only collection of mappings that satisfy the given range constraint.
*/
public List<PointMapping> getMappings(Range range,
LookupOptions lookupOptions) {
ExceptionUtils.disallowNullArgument(range, "range");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetPointMappings", "Start; Range:{}; Lookup Options: {}", range, lookupOptions);
Stopwatch stopwatch = Stopwatch.createStarted();
List<PointMapping> pointMappings = lsm.getMappingsForRange(range, null, lookupOptions);
stopwatch.stop();
log.info("GetPointMappings", "Complete; Range: {}; Lookup Options: {}; Duration:{}", range, lookupOptions,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return pointMappings;
}
}
/**
* Gets all the mappings that exist for the given shard.
*
* @param shard
* Shard for which the mappings will be returned.
* @param lookupOptions
* Whether to search in the cache and/or store.
* @return Read-only collection of mappings that satisfy the given shard constraint.
*/
public List<PointMapping> getMappings(Shard shard,
LookupOptions lookupOptions) {
ExceptionUtils.disallowNullArgument(shard, "shard");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetPointMappings", "Start; Shard:{}; Lookup Options: {}", shard.getLocation(), lookupOptions);
Stopwatch stopwatch = Stopwatch.createStarted();
List<PointMapping> pointMappings = lsm.getMappingsForRange(null, shard, lookupOptions);
stopwatch.stop();
log.info("GetPointMappings", "Complete; Shard: {}; Lookup Options: {}; Duration:{}", shard.getLocation(), lookupOptions,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return pointMappings;
}
}
/**
* Gets all the mappings that exist within given range and given shard.
*
* @param range
* Point value, any mapping overlapping with the range will be returned.
* @param shard
* Shard for which the mappings will be returned.
* @param lookupOptions
* Whether to search in the cache and/or store.
* @return Read-only collection of mappings that satisfy the given range and shard constraints.
*/
public List<PointMapping> getMappings(Range range,
Shard shard,
LookupOptions lookupOptions) {
ExceptionUtils.disallowNullArgument(range, "range");
ExceptionUtils.disallowNullArgument(shard, "shard");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetPointMappings", "Start; Shard: {}; Range:{}; Lookup Options: {}", shard.getLocation(), range, lookupOptions);
Stopwatch stopwatch = Stopwatch.createStarted();
List<PointMapping> pointMappings = lsm.getMappingsForRange(range, shard, lookupOptions);
stopwatch.stop();
log.info("GetPointMappings", "Complete; Shard: {}; Range: {}; Lookup Options: {};" + "Duration: {}", shard.getLocation(), lookupOptions,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return pointMappings;
}
}
/**
* Marks the specified mapping offline.
*
* @param mapping
* Input point mapping.
* @return An offline mapping.
*/
public PointMapping markMappingOffline(PointMapping mapping) {
ExceptionUtils.disallowNullArgument(mapping, "mapping");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("MarkMappingOffline", "Start; ");
Stopwatch stopwatch = Stopwatch.createStarted();
PointMapping result = lsm.markMappingOffline(mapping);
stopwatch.stop();
log.info("MarkMappingOffline", "Complete; Duration:{}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
return result;
}
}
/**
* Marks the specified mapping online.
*
* @param mapping
* Input point mapping.
* @return An online mapping.
*/
public PointMapping markMappingOnline(PointMapping mapping) {
ExceptionUtils.disallowNullArgument(mapping, "mapping");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("MarkMappingOnline", "Start; ");
Stopwatch stopwatch = Stopwatch.createStarted();
PointMapping result = lsm.markMappingOnline(mapping);
stopwatch.stop();
log.info("MarkMappingOnline", "Complete; Duration:{}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
return result;
}
}
/**
* Updates a <see cref="PointMapping{KeyT}"/> with the updates provided in the <paramref name="update"/> parameter.
*
* @param currentMapping
* Mapping being updated.
* @param update
* Updated properties of the mapping.
* @return New instance of mapping with updated information.
*/
public PointMapping updateMapping(PointMapping currentMapping,
PointMappingUpdate update) {
return this.updateMapping(currentMapping, update, MappingLockToken.NoLock);
}
/**
* Updates a point mapping with the changes provided in the <paramref name="update"/> parameter.
*
* @param currentMapping
* Mapping being updated.
* @param update
* Updated properties of the Shard.
* @param mappingLockToken
* An instance of <see cref="MappingLockToken"/>
* @return New instance of mapping with updated information.
*/
public PointMapping updateMapping(PointMapping currentMapping,
PointMappingUpdate update,
MappingLockToken mappingLockToken) {
ExceptionUtils.disallowNullArgument(currentMapping, "currentMapping");
ExceptionUtils.disallowNullArgument(update, "update");
ExceptionUtils.disallowNullArgument(mappingLockToken, "mappingLockToken");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
String mappingKey = currentMapping.getKey().getRawValue().toString();
log.info("UpdatePointMapping", "Start; ShardMap name: {}; Current Point Mapping:{}", this.getName(), mappingKey);
Stopwatch stopwatch = Stopwatch.createStarted();
PointMapping mapping = lsm.update(currentMapping, update, mappingLockToken.getLockOwnerId());
stopwatch.stop();
log.info("UpdatePointMapping", "Complete; ShardMap name: {}; Current Point Mapping: {}; Duration: {}", this.getName(), mappingKey,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return mapping;
}
}
/**
* Gets the lock owner id of the specified mapping.
*
* @param mapping
* Input range mapping.
* @return An instance of <see cref="MappingLockToken"/>
*/
public MappingLockToken getMappingLockOwner(PointMapping mapping) {
ExceptionUtils.disallowNullArgument(mapping, "mapping");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("LookupLockOwner", "Start");
Stopwatch stopwatch = Stopwatch.createStarted();
UUID storeLockOwnerId = lsm.getLockOwnerForMapping(mapping);
stopwatch.stop();
log.info("LookupLockOwner", "Complete; Duration: {}; StoreLockOwnerId:{}", stopwatch.elapsed(TimeUnit.MILLISECONDS), storeLockOwnerId);
return new MappingLockToken(storeLockOwnerId);
}
}
/**
* Locks the mapping for the specified owner The state of a locked mapping can only be modified by the lock owner.
*
* @param mapping
* Input range mapping.
* @param mappingLockToken
* An instance of <see cref="MappingLockToken"/>
*/
public void lockMapping(PointMapping mapping,
MappingLockToken mappingLockToken) {
ExceptionUtils.disallowNullArgument(mapping, "mapping");
ExceptionUtils.disallowNullArgument(mappingLockToken, "mappingLockToken");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
// Generate a lock owner id
UUID lockOwnerId = mappingLockToken.getLockOwnerId();
log.info("Lock", "Start; LockOwnerId:{}", lockOwnerId);
Stopwatch stopwatch = Stopwatch.createStarted();
lsm.lockOrUnlockMappings(mapping, lockOwnerId, LockOwnerIdOpType.Lock);
stopwatch.stop();
log.info("Lock", "Complete; Duration: {}; StoreLockOwnerId:{}", stopwatch.elapsed(TimeUnit.MILLISECONDS), lockOwnerId);
}
}
/**
* Unlocks the specified mapping
*
* @param mapping
* Input range mapping.
* @param mappingLockToken
* An instance of <see cref="MappingLockToken"/>
*/
public void unlockMapping(PointMapping mapping,
MappingLockToken mappingLockToken) {
ExceptionUtils.disallowNullArgument(mapping, "mapping");
ExceptionUtils.disallowNullArgument(mappingLockToken, "mappingLockToken");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
UUID lockOwnerId = mappingLockToken.getLockOwnerId();
log.info("Unlock", "Start; LockOwnerId:{}", lockOwnerId);
Stopwatch stopwatch = Stopwatch.createStarted();
lsm.lockOrUnlockMappings(mapping, lockOwnerId, LockOwnerIdOpType.UnlockMappingForId);
stopwatch.stop();
log.info("UnLock", "Complete; Duration: {}; StoreLockOwnerId:{}", stopwatch.elapsed(TimeUnit.MILLISECONDS), lockOwnerId);
}
}
/**
* Unlocks all mappings in this map that belong to the given <see cref="MappingLockToken"/>.
*
* @param mappingLockToken
* An instance of <see cref="MappingLockToken"/>
*/
public void unlockMapping(MappingLockToken mappingLockToken) {
ExceptionUtils.disallowNullArgument(mappingLockToken, "mappingLockToken");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
UUID lockOwnerId = mappingLockToken.getLockOwnerId();
log.info("UnlockAllMappingsWithLockOwnerId", "Start; LockOwnerId:{}", lockOwnerId);
Stopwatch stopwatch = Stopwatch.createStarted();
lsm.lockOrUnlockMappings(null, lockOwnerId, LockOwnerIdOpType.UnlockAllMappingsForId);
stopwatch.stop();
log.info("UnlockAllMappingsWithLockOwnerId", "Complete; Duration:{}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
}
/**
* Gets the mapper. This method is used by OpenConnection/Lookup of V. <typeparam name="V">Shard provider type.</typeparam>
*
* @return ListShardMapper for given key type.
*/
@Override
public <V> IShardMapper getMapper() {
return lsm;
}
/**
* Clones the specified shard map.
*
* @return A cloned instance of the shard map.
*/
public ShardMap clone() {
return this.cloneCore();
}
/**
* Clones the current shard map instance.
*
* @return Cloned shard map instance.
*/
@Override
protected ShardMap cloneCore() {
return new ListShardMap(shardMapManager, storeShardMap);
}
}

Просмотреть файл

@ -0,0 +1,814 @@
package com.microsoft.azure.elasticdb.shard.map;
import java.lang.invoke.MethodHandles;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.google.common.base.Stopwatch;
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
import com.microsoft.azure.elasticdb.core.commons.logging.ActivityIdScope;
import com.microsoft.azure.elasticdb.shard.base.LockOwnerIdOpType;
import com.microsoft.azure.elasticdb.shard.base.LookupOptions;
import com.microsoft.azure.elasticdb.shard.base.MappingLockToken;
import com.microsoft.azure.elasticdb.shard.base.MappingStatus;
import com.microsoft.azure.elasticdb.shard.base.Range;
import com.microsoft.azure.elasticdb.shard.base.RangeMapping;
import com.microsoft.azure.elasticdb.shard.base.RangeMappingCreationInfo;
import com.microsoft.azure.elasticdb.shard.base.RangeMappingUpdate;
import com.microsoft.azure.elasticdb.shard.base.Shard;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardMapManager;
import com.microsoft.azure.elasticdb.shard.mapper.IShardMapper;
import com.microsoft.azure.elasticdb.shard.mapper.RangeShardMapper;
import com.microsoft.azure.elasticdb.shard.store.StoreShardMap;
import com.microsoft.azure.elasticdb.shard.utils.ExceptionUtils;
/**
* Represents a shard map of ranges. <typeparam name="KeyT">Key type.</typeparam>.
*/
public final class RangeShardMap<KeyT> extends ShardMap implements Cloneable {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* Mapping b/w key ranges and shards.
*/
private RangeShardMapper rsm;
/**
* Constructs a new instance.
*
* @param shardMapManager
* Reference to ShardMapManager.
* @param ssm
* Storage representation.
*/
public RangeShardMap(ShardMapManager shardMapManager,
StoreShardMap ssm) {
super(shardMapManager, ssm);
this.rsm = new RangeShardMapper(this.getShardMapManager(), this);
}
/**
* Creates and adds a range mapping to ShardMap.
*
* @param creationInfo
* Information about mapping to be added.
* @return Newly created mapping.
*/
public RangeMapping createRangeMapping(RangeMappingCreationInfo creationInfo) {
ExceptionUtils.disallowNullArgument(creationInfo, "args");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("CreateRangeMapping Start; Shard: {}", creationInfo.getShard().getLocation());
Stopwatch stopwatch = Stopwatch.createStarted();
RangeMapping map = this.rsm.add(new RangeMapping(this.getShardMapManager(), creationInfo));
stopwatch.stop();
log.info("CreateRangeMapping Complete; Shard: {}; Duration: {}", creationInfo.getShard().getLocation(),
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return map;
}
}
/**
* Creates and adds a range mapping to ShardMap.
*
* @param range
* Range for which to create the mapping.
* @param shard
* Shard associated with the range mapping.
* @return Newly created mapping.
*/
public RangeMapping createRangeMapping(Range range,
Shard shard) {
ExceptionUtils.disallowNullArgument(range, "range");
ExceptionUtils.disallowNullArgument(shard, "shard");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
RangeMappingCreationInfo args = new RangeMappingCreationInfo(range, shard, MappingStatus.Online);
log.info("CreateRangeMapping Start; Shard: {}", shard.getLocation());
Stopwatch stopwatch = Stopwatch.createStarted();
RangeMapping rangeMapping = this.rsm.add(new RangeMapping(this.getShardMapManager(), args));
stopwatch.stop();
log.info("CreateRangeMapping Complete; Shard: {}; Duration: {}", shard.getLocation(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
return rangeMapping;
}
}
/**
* Removes a range mapping.
*
* @param mapping
* Mapping being removed.
*/
public void deleteMapping(RangeMapping mapping) {
this.deleteMapping(mapping, MappingLockToken.NoLock);
}
/**
* Removes a range mapping.
*
* @param mapping
* Mapping being removed.
* @param mappingLockToken
* An instance of <see cref="MappingLockToken"/>
*/
public void deleteMapping(RangeMapping mapping,
MappingLockToken mappingLockToken) {
ExceptionUtils.disallowNullArgument(mapping, "mapping");
ExceptionUtils.disallowNullArgument(mappingLockToken, "mappingLockToken");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("DeleteMapping Start; Shard: {}", mapping.getShard().getLocation());
Stopwatch stopwatch = Stopwatch.createStarted();
this.rsm.remove(mapping, mappingLockToken.getLockOwnerId());
stopwatch.stop();
log.info("DeleteMapping Complete; Shard: {}; Duration: {}", mapping.getShard().getLocation(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
}
/**
* Looks up the key value and returns the corresponding mapping.
*
* @param key
* Input key value.
* @return Mapping that contains the key value.
*/
public RangeMapping getMappingForKey(KeyT key) {
return getMappingForKey(key, LookupOptions.LOOKUP_IN_STORE);
}
/**
* Looks up the key value and returns the corresponding mapping.
*
* @param key
* Input key value.
* @param lookupOptions
* Whether to search in the cache and/or store.
* @return Mapping that contains the key value.
*/
public RangeMapping getMappingForKey(KeyT key,
LookupOptions lookupOptions) {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetMapping Start; Range Mapping Key Type: {}; Lookup Options: {}", key.getClass(), lookupOptions);
Stopwatch stopwatch = Stopwatch.createStarted();
RangeMapping rangeMapping = this.rsm.lookup(key, lookupOptions);
stopwatch.stop();
log.info("GetMapping Complete; Range Mapping Key Type: {}; Lookup Options: {} Duration: {}", key.getClass(), lookupOptions,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return rangeMapping;
}
}
/**
* Tries to looks up the key value and place the corresponding mapping in <paramref name="rangeMapping"/>.
*
* @param key
* Input key value.
* @param rangeMapping
* Mapping that contains the key value.
* @return <c>true</c> if mapping is found, <c>false</c> otherwise.
*/
public boolean tryGetMappingForKey(KeyT key,
ReferenceObjectHelper<RangeMapping> rangeMapping) {
return tryGetMappingForKey(key, LookupOptions.LOOKUP_IN_STORE, rangeMapping);
}
/**
* Tries to looks up the key value and place the corresponding mapping in <paramref name="rangeMapping"/>.
*
* @param key
* Input key value.
* @param lookupOptions
* Whether to search in the cache and/or store.
* @param rangeMapping
* Mapping that contains the key value.
* @return <c>true</c> if mapping is found, <c>false</c> otherwise.
*/
public boolean tryGetMappingForKey(KeyT key,
LookupOptions lookupOptions,
ReferenceObjectHelper<RangeMapping> rangeMapping) {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("TryLookupRangeMapping Start; ShardMap name: {}; Range Mapping Key Type: {}; Lookup " + "Options: {}", this.getName(),
key.getClass(), lookupOptions);
Stopwatch stopwatch = Stopwatch.createStarted();
boolean result = this.rsm.tryLookup(key, lookupOptions, rangeMapping);
stopwatch.stop();
log.info("TryLookupRangeMapping Complete; ShardMap name: {}; Range Mapping Key Type: {}; " + "Lookup Options: {}; Duration: {}",
this.getName(), key.getClass(), lookupOptions, stopwatch.elapsed(TimeUnit.MILLISECONDS));
return result;
}
}
/**
* Gets all the range mappings for the shard map.
*
* @return Read-only collection of all range mappings on the shard map.
*/
public List<RangeMapping> getMappings() {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetMappings Start;");
Stopwatch stopwatch = Stopwatch.createStarted();
List<RangeMapping> rangeMappings = this.rsm.getMappingsForRange(null, null, LookupOptions.LOOKUP_IN_STORE);
stopwatch.stop();
log.info("GetMappings Complete; Duration: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
return rangeMappings;
}
}
/**
* Gets all the range mappings that exist within given range.
*
* @param range
* Range value, any mapping overlapping with the range will be returned.
* @return Read-only collection of mappings that satisfy the given range constraint.
*/
public List<RangeMapping> getMappings(Range range) {
ExceptionUtils.disallowNullArgument(range, "range");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetMappings Start; Range: {}", range);
Stopwatch stopwatch = Stopwatch.createStarted();
List<RangeMapping> rangeMappings = this.rsm.getMappingsForRange(range, null, LookupOptions.LOOKUP_IN_STORE);
stopwatch.stop();
log.info("GetMappings Complete; Range: {}; Duration: {}", range, stopwatch.elapsed(TimeUnit.MILLISECONDS));
return rangeMappings;
}
}
/**
* Gets all the range mappings that exist for the given shard.
*
* @param shard
* Shard for which the mappings will be returned.
* @return Read-only collection of mappings that satisfy the given shard constraint.
*/
public List<RangeMapping> getMappings(Shard shard) {
ExceptionUtils.disallowNullArgument(shard, "shard");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetMappings Start; Shard: {}", shard.getLocation());
Stopwatch stopwatch = Stopwatch.createStarted();
List<RangeMapping> rangeMappings = this.rsm.getMappingsForRange(null, shard, LookupOptions.LOOKUP_IN_STORE);
stopwatch.stop();
log.info("GetMappings Complete; Shard: {}; Duration: {}", shard.getLocation(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
return rangeMappings;
}
}
/**
* Gets all the range mappings that exist within given range and given shard.
*
* @param range
* Range value, any mapping overlapping with the range will be returned.
* @param shard
* Shard for which the mappings will be returned.
* @return Read-only collection of mappings that satisfy the given range and shard constraints.
*/
public List<RangeMapping> getMappings(Range range,
Shard shard) {
ExceptionUtils.disallowNullArgument(range, "range");
ExceptionUtils.disallowNullArgument(shard, "shard");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetMappings Start; Shard: {}; Range: {}", shard.getLocation(), range);
Stopwatch stopwatch = Stopwatch.createStarted();
List<RangeMapping> rangeMappings = this.rsm.getMappingsForRange(range, shard, LookupOptions.LOOKUP_IN_STORE);
stopwatch.stop();
log.info("GetMappings Complete; Shard: {}; Duration: {}", shard.getLocation(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
return rangeMappings;
}
}
/**
* Gets all the range mappings for the shard map.
*
* @param lookupOptions
* Whether to search in the cache and/or store.
* @return Read-only collection of all range mappings on the shard map.
*/
public List<RangeMapping> getMappings(LookupOptions lookupOptions) {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetMappings Start; Lookup Options: {};", lookupOptions);
Stopwatch stopwatch = Stopwatch.createStarted();
List<RangeMapping> rangeMappings = this.rsm.getMappingsForRange(null, null, lookupOptions);
stopwatch.stop();
log.info("GetMappings Complete; Lookup Options: {}; Duration: {}", lookupOptions, stopwatch.elapsed(TimeUnit.MILLISECONDS));
return rangeMappings;
}
}
/**
* Gets all the range mappings that exist within given range.
*
* @param range
* Range value, any mapping overlapping with the range will be returned.
* @param lookupOptions
* Whether to search in the cache and/or store.
* @return Read-only collection of mappings that satisfy the given range constraint.
*/
public List<RangeMapping> getMappings(Range range,
LookupOptions lookupOptions) {
ExceptionUtils.disallowNullArgument(range, "range");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetMappings Start; Range: {}; Lookup Options: {}", range, lookupOptions);
Stopwatch stopwatch = Stopwatch.createStarted();
List<RangeMapping> rangeMappings = this.rsm.getMappingsForRange(range, null, lookupOptions);
stopwatch.stop();
log.info("GetMappings Complete; Range: {}; Lookup Options: {}; Duration: {}", range, lookupOptions,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return rangeMappings;
}
}
/**
* Gets all the range mappings that exist for the given shard.
*
* @param shard
* Shard for which the mappings will be returned.
* @param lookupOptions
* Whether to search in the cache and/or store.
* @return Read-only collection of mappings that satisfy the given shard constraint.
*/
public List<RangeMapping> getMappings(Shard shard,
LookupOptions lookupOptions) {
ExceptionUtils.disallowNullArgument(shard, "shard");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetMappings Start; Shard: {}; Lookup Options: {}", shard.getLocation(), lookupOptions);
Stopwatch stopwatch = Stopwatch.createStarted();
List<RangeMapping> rangeMappings = this.rsm.getMappingsForRange(null, shard, lookupOptions);
stopwatch.stop();
log.info("GetMappings Complete; Shard: {}; Lookup Options: {}; Duration: {}", shard.getLocation(), lookupOptions,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return rangeMappings;
}
}
/**
* Gets all the range mappings that exist within given range and given shard.
*
* @param range
* Range value, any mapping overlapping with the range will be returned.
* @param shard
* Shard for which the mappings will be returned.
* @param lookupOptions
* Whether to search in the cache and/or store.
* @return Read-only collection of mappings that satisfy the given range and shard constraints.
*/
public List<RangeMapping> getMappings(Range range,
Shard shard,
LookupOptions lookupOptions) {
ExceptionUtils.disallowNullArgument(range, "range");
ExceptionUtils.disallowNullArgument(shard, "shard");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetMappings Start; Shard: {}; Range: {}; Lookup Options: {}", shard.getLocation(), lookupOptions, range);
Stopwatch stopwatch = Stopwatch.createStarted();
List<RangeMapping> rangeMappings = this.rsm.getMappingsForRange(range, shard, lookupOptions);
stopwatch.stop();
log.info("GetMappings Complete; Shard: {}; Lookup Options: {}; Duration: {}", shard.getLocation(), lookupOptions,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return rangeMappings;
}
}
/**
* Marks the specified mapping offline.
*
* @param mapping
* Input range mapping.
* @return An offline mapping.
*/
public RangeMapping markMappingOffline(RangeMapping mapping) {
return this.markMappingOffline(mapping, MappingLockToken.NoLock);
}
/**
* Marks the specified mapping offline.
*
* @param mapping
* Input range mapping.
* @param mappingLockToken
* An instance of <see cref="MappingLockToken"/>
* @return An offline mapping.
*/
public RangeMapping markMappingOffline(RangeMapping mapping,
MappingLockToken mappingLockToken) {
ExceptionUtils.disallowNullArgument(mapping, "mapping");
ExceptionUtils.disallowNullArgument(mappingLockToken, "mappingLockToken");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("MarkMappingOffline Start; ");
Stopwatch stopwatch = Stopwatch.createStarted();
RangeMapping result = this.rsm.markMappingOffline(mapping, mappingLockToken.getLockOwnerId());
stopwatch.stop();
log.info("MarkMappingOffline Complete; Duration: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
return result;
}
}
/**
* Marks the specified mapping online.
*
* @param mapping
* Input range mapping.
* @return An online mapping.
*/
public RangeMapping markMappingOnline(RangeMapping mapping) {
return this.markMappingOnline(mapping, MappingLockToken.NoLock);
}
/**
* Marks the specified mapping online.
*
* @param mapping
* Input range mapping.
* @param mappingLockToken
* An instance of <see cref="MappingLockToken"/>
* @return An online mapping.
*/
public RangeMapping markMappingOnline(RangeMapping mapping,
MappingLockToken mappingLockToken) {
ExceptionUtils.disallowNullArgument(mapping, "mapping");
ExceptionUtils.disallowNullArgument(mappingLockToken, "mappingLockToken");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("MarkMappingOnline Start; ");
Stopwatch stopwatch = Stopwatch.createStarted();
RangeMapping result = this.rsm.markMappingOnline(mapping, mappingLockToken.getLockOwnerId());
stopwatch.stop();
log.info("MarkMappingOnline Complete; Duration: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
return result;
}
}
/**
* Gets the lock owner id of the specified mapping.
*
* @param mapping
* Input range mapping.
* @return An instance of <see cref="MappingLockToken"/>
*/
public MappingLockToken getMappingLockOwner(RangeMapping mapping) {
ExceptionUtils.disallowNullArgument(mapping, "mapping");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("LookupLockOwner Start");
Stopwatch stopwatch = Stopwatch.createStarted();
UUID storeLockOwnerId = this.rsm.getLockOwnerForMapping(mapping);
stopwatch.stop();
log.info("LookupLockOwner Complete; Duration: {}; StoreLockOwnerId: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS), storeLockOwnerId);
return new MappingLockToken(storeLockOwnerId);
}
}
/**
* Locks the mapping for the specified owner The state of a locked mapping can only be modified by the lock owner.
*
* @param mapping
* Input range mapping.
* @param mappingLockToken
* An instance of <see cref="MappingLockToken"/>
*/
public void lockMapping(RangeMapping mapping,
MappingLockToken mappingLockToken) {
ExceptionUtils.disallowNullArgument(mapping, "mapping");
ExceptionUtils.disallowNullArgument(mappingLockToken, "mappingLockToken");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
// Generate a lock owner id
UUID lockOwnerId = mappingLockToken.getLockOwnerId();
log.info("Lock Start; LockOwnerId: {}", lockOwnerId);
Stopwatch stopwatch = Stopwatch.createStarted();
this.rsm.lockOrUnlockMappings(mapping, lockOwnerId, LockOwnerIdOpType.Lock);
stopwatch.stop();
log.info("Lock Complete; Duration: {}; StoreLockOwnerId: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS), lockOwnerId);
}
}
/**
* Unlocks the specified mapping
*
* @param mapping
* Input range mapping.
* @param mappingLockToken
* An instance of <see cref="MappingLockToken"/>
*/
public void unlockMapping(RangeMapping mapping,
MappingLockToken mappingLockToken) {
ExceptionUtils.disallowNullArgument(mapping, "mapping");
ExceptionUtils.disallowNullArgument(mappingLockToken, "mappingLockToken");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
UUID lockOwnerId = mappingLockToken.getLockOwnerId();
log.info("Unlock Start; LockOwnerId: {}", lockOwnerId);
Stopwatch stopwatch = Stopwatch.createStarted();
this.rsm.lockOrUnlockMappings(mapping, lockOwnerId, LockOwnerIdOpType.UnlockMappingForId);
stopwatch.stop();
log.info("UnLock Complete; Duration: {}; StoreLockOwnerId: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS), lockOwnerId);
}
}
/**
* Unlocks all mappings in this map that belong to the given <see cref="MappingLockToken"/>.
*
* @param mappingLockToken
* An instance of <see cref="MappingLockToken"/>
*/
public void unlockMapping(MappingLockToken mappingLockToken) {
ExceptionUtils.disallowNullArgument(mappingLockToken, "mappingLockToken");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
UUID lockOwnerId = mappingLockToken.getLockOwnerId();
log.info("UnlockAllMappingsWithLockOwnerId Start; LockOwnerId: {}", lockOwnerId);
Stopwatch stopwatch = Stopwatch.createStarted();
this.rsm.lockOrUnlockMappings(null, lockOwnerId, LockOwnerIdOpType.UnlockAllMappingsForId);
stopwatch.stop();
log.info("UnlockAllMappingsWithLockOwnerId Complete; Duration: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
}
/**
* Updates a <see cref="RangeMapping{KeyT}"/> with the updates provided in the <paramref name="update"/> parameter.
*
* @param currentMapping
* Mapping being updated.
* @param update
* Updated properties of the mapping.
* @return New instance of mapping with updated information.
*/
public RangeMapping updateMapping(RangeMapping currentMapping,
RangeMappingUpdate update) {
return this.updateMapping(currentMapping, update, MappingLockToken.NoLock);
}
/**
* Updates a <see cref="RangeMapping{KeyT}"/> with the updates provided in the <paramref name="update"/> parameter.
*
* @param currentMapping
* Mapping being updated.
* @param update
* Updated properties of the mapping.
* @param mappingLockToken
* An instance of <see cref="MappingLockToken"/>
* @return New instance of mapping with updated information.
*/
public RangeMapping updateMapping(RangeMapping currentMapping,
RangeMappingUpdate update,
MappingLockToken mappingLockToken) {
ExceptionUtils.disallowNullArgument(currentMapping, "currentMapping");
ExceptionUtils.disallowNullArgument(update, "update");
ExceptionUtils.disallowNullArgument(mappingLockToken, "mappingLockToken");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("UpdateMapping Start; Current mapping shard: {}", currentMapping.getShard().getLocation());
Stopwatch stopwatch = Stopwatch.createStarted();
RangeMapping map = this.rsm.update(currentMapping, update, mappingLockToken.getLockOwnerId());
stopwatch.stop();
log.info("UpdateMapping Complete; Current mapping shard: {}; Duration: {}", currentMapping.getShard().getLocation(),
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return map;
}
}
/**
* Splits the specified mapping into two new mappings using the specified key as boundary. The new mappings point to the same shard as the
* existing mapping.
*
* @param existingMapping
* Existing mapping.
* @param splitAt
* Split point.
* @return Read-only collection of two new mappings that were created.
*/
public List<RangeMapping> splitMapping(RangeMapping existingMapping,
KeyT splitAt) {
return this.splitMapping(existingMapping, splitAt, MappingLockToken.NoLock);
}
/**
* Splits the specified mapping into two new mappings using the specified key as boundary. The new mappings point to the same shard as the
* existing mapping.
*
* @param existingMapping
* Existing mapping.
* @param splitAt
* Split point.
* @param mappingLockToken
* An instance of <see cref="MappingLockToken"/>
* @return Read-only collection of two new mappings that were created.
*/
public List<RangeMapping> splitMapping(RangeMapping existingMapping,
KeyT splitAt,
MappingLockToken mappingLockToken) {
ExceptionUtils.disallowNullArgument(existingMapping, "existingMapping");
ExceptionUtils.disallowNullArgument(mappingLockToken, "mappingLockToken");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("SplitMapping Start; Shard: {}", existingMapping.getShard().getLocation());
Stopwatch stopwatch = Stopwatch.createStarted();
List<RangeMapping> rangeMapping = this.rsm.split(existingMapping, splitAt, mappingLockToken.getLockOwnerId());
stopwatch.stop();
log.info("SplitMapping Complete; Shard: {}; Duration: {}", existingMapping.getShard().getLocation(),
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return rangeMapping;
}
}
/**
* Merges 2 contiguous mappings into a single mapping. Both left and right mappings should point to the same location and must be contiguous.
*
* @param left
* Left mapping.
* @param right
* Right mapping.
* @return Mapping that results from the merge operation.
*/
public RangeMapping mergeMappings(RangeMapping left,
RangeMapping right) {
return this.mergeMappings(left, right, MappingLockToken.NoLock, MappingLockToken.NoLock);
}
/**
* Merges 2 contiguous mappings into a single mapping. Both left and right mappings should point to the same location and must be contiguous.
*
* @param left
* Left mapping.
* @param right
* Right mapping.
* @param leftMappingLockToken
* An instance of <see cref="MappingLockToken"/> for the left mapping
* @param rightMappingLockToken
* An instance of <see cref="MappingLockToken"/> for the right mapping
* @return Mapping that results from the merge operation.
*/
public RangeMapping mergeMappings(RangeMapping left,
RangeMapping right,
MappingLockToken leftMappingLockToken,
MappingLockToken rightMappingLockToken) {
ExceptionUtils.disallowNullArgument(left, "left");
ExceptionUtils.disallowNullArgument(right, "right");
ExceptionUtils.disallowNullArgument(leftMappingLockToken, "leftMappingLockToken");
ExceptionUtils.disallowNullArgument(rightMappingLockToken, "rightMappingLockToken");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("SplitMapping Start; Left Shard: {}; Right Shard: {}", left.getShard().getLocation(), right.getShard().getLocation());
Stopwatch stopwatch = Stopwatch.createStarted();
RangeMapping rangeMapping = this.rsm.merge(left, right, leftMappingLockToken.getLockOwnerId(), rightMappingLockToken.getLockOwnerId());
stopwatch.stop();
log.info("SplitMapping Complete; Duration: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
return rangeMapping;
}
}
/**
* Gets the mapper. This method is used by OpenConnection/Lookup of V. <typeparam name="V">Shard provider type.</typeparam>
*
* @return RangeShardMapper for given key type.
*/
@Override
public <V> IShardMapper getMapper() {
return rsm;
}
/**
* Clones the given range shard map.
*
* @return A cloned instance of the range shard map.
*/
public RangeShardMap clone() {
ShardMap tempVar = this.cloneCore();
return (RangeShardMap) ((tempVar instanceof RangeShardMap<?>) ? tempVar : null);
}
/**
* Clones the current shard map instance.
*
* @return Cloned shard map instance.
*/
@Override
protected ShardMap cloneCore() {
return new RangeShardMap(this.getShardMapManager(), this.getStoreShardMap());
}
}

Просмотреть файл

@ -0,0 +1,631 @@
package com.microsoft.azure.elasticdb.shard.map;
import java.lang.invoke.MethodHandles;
import java.sql.Connection;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import com.google.common.base.Strings;
import com.microsoft.azure.elasticdb.core.commons.helpers.ApplicationNameHelper;
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
import com.microsoft.azure.elasticdb.core.commons.logging.ActivityIdScope;
import com.microsoft.azure.elasticdb.core.commons.patterns.ConditionalDisposable;
import com.microsoft.azure.elasticdb.shard.base.IShardProvider;
import com.microsoft.azure.elasticdb.shard.base.Shard;
import com.microsoft.azure.elasticdb.shard.base.ShardCreationInfo;
import com.microsoft.azure.elasticdb.shard.base.ShardKey;
import com.microsoft.azure.elasticdb.shard.base.ShardKeyType;
import com.microsoft.azure.elasticdb.shard.base.ShardLocation;
import com.microsoft.azure.elasticdb.shard.base.ShardUpdate;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementErrorCategory;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementErrorCode;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementException;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardMapManager;
import com.microsoft.azure.elasticdb.shard.mapper.ConnectionOptions;
import com.microsoft.azure.elasticdb.shard.mapper.DefaultShardMapper;
import com.microsoft.azure.elasticdb.shard.mapper.IShardMapper;
import com.microsoft.azure.elasticdb.shard.sqlstore.SqlConnectionStringBuilder;
import com.microsoft.azure.elasticdb.shard.sqlstore.SqlShardMapManagerCredentials;
import com.microsoft.azure.elasticdb.shard.store.IUserStoreConnection;
import com.microsoft.azure.elasticdb.shard.store.StoreShardMap;
import com.microsoft.azure.elasticdb.shard.utils.Errors;
import com.microsoft.azure.elasticdb.shard.utils.ExceptionUtils;
import com.microsoft.azure.elasticdb.shard.utils.GlobalConstants;
import com.microsoft.azure.elasticdb.shard.utils.StringUtilsLocal;
/**
* Represents a collection of shards and mappings between keys and shards in the collection.
*/
public abstract class ShardMap implements Cloneable {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* Reference to ShardMapManager.
*/
protected ShardMapManager shardMapManager;
/**
* Storage representation.
*/
protected StoreShardMap storeShardMap;
/**
* The mapper belonging to the ShardMap.
*/
private DefaultShardMapper defaultShardMapper;
/**
* Suffix added to application name in connections.
*/
private String applicationNameSuffix;
/**
* Constructs an instance of ShardMap.
*
* @param shardMapManager
* Reference to ShardMapManager.
* @param ssm
* Storage representation.
*/
public ShardMap(ShardMapManager shardMapManager,
StoreShardMap ssm) {
this.shardMapManager = Preconditions.checkNotNull(shardMapManager);
this.storeShardMap = Preconditions.checkNotNull(ssm);
applicationNameSuffix = GlobalConstants.ShardMapManagerPrefix + ssm.getId().toString();
defaultShardMapper = new DefaultShardMapper(shardMapManager, this);
}
/**
* Shard map name.
*/
public String getName() {
return storeShardMap.getName();
}
/**
* Shard map type.
*/
public final ShardMapType getMapType() {
return storeShardMap.getMapType();
}
/**
* Shard map key type.
*/
public final ShardKeyType getKeyType() {
return storeShardMap.getKeyType();
}
/**
* Identity.
*/
public final UUID getId() {
return storeShardMap.getId();
}
public final ShardMapManager getShardMapManager() {
return shardMapManager;
}
public final StoreShardMap getStoreShardMap() {
return storeShardMap;
}
public final String getApplicationNameSuffix() {
return applicationNameSuffix;
}
/**
* Converts the object to its string representation.
*
* @return String representation of the object.
*/
@Override
public String toString() {
return storeShardMap.toString();
}
/**
* Opens a regular <see cref="SqlConnection"/> to the shard to which the specified key value is mapped, with
* <see cref="ConnectionOptions.Validate"/>. <typeparam name="KeyT">Type of the key.</typeparam>
*
* @param key
* Input key value.
* @param connectionString
* Connection string with credential information such as SQL Server credentials or Integrated Security settings. The hostname of the
* server and the database name for the shard are obtained from the lookup operation for key.
* @return An opened SqlConnection. Note that the <see cref="SqlConnection"/> object returned by this call is not protected against transient
* faults. Callers should follow best practices to protect the connection against transient faults in their application code, e.g., by
* using the transient fault handling functionality in the Enterprise Library from Microsoft Patterns and Practices team. This call only
* works if there is a single default mapping.
*/
public <KeyT> Connection openConnectionForKey(KeyT key,
String connectionString) {
return this.openConnectionForKey(key, connectionString, ConnectionOptions.Validate);
}
/**
* Opens a regular <see cref="SqlConnection"/> to the shard to which the specified key value is mapped. <typeparam name="KeyT">Type of the
* key.</typeparam>
*
* @param key
* Input key value.
* @param connectionString
* Connection string with credential information such as SQL Server credentials or Integrated Security settings. The hostname of the
* server and the database name for the shard are obtained from the lookup operation for key.
* @param options
* Options for validation operations to perform on opened connection.
* @return An opened SqlConnection. Note that the <see cref="SqlConnection"/> object returned by this call is not protected against transient
* faults. Callers should follow best practices to protect the connection against transient faults in their application code, e.g., by
* using the transient fault handling functionality in the Enterprise Library from Microsoft Patterns and Practices team. This call only
* works if there is a single default mapping.
*/
public <KeyT> Connection openConnectionForKey(KeyT key,
String connectionString,
ConnectionOptions options) {
ExceptionUtils.disallowNullArgument(connectionString, "connectionString");
assert this.getStoreShardMap().getKeyType() != ShardKeyType.None;
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
IShardMapper mapper = this.<KeyT>getMapper();
if (mapper == null) {
throw new IllegalArgumentException(
StringUtilsLocal.formatInvariant(Errors._ShardMap_OpenConnectionForKey_KeyTypeNotSupported, key.getClass(),
this.getStoreShardMap().getName(), ShardKey.typeFromShardKeyType(this.getStoreShardMap().getKeyType())),
new Throwable("key"));
}
assert mapper != null;
return mapper.openConnectionForKey(key, connectionString, options);
}
}
/**
* Asynchronously opens a regular <see cref="SqlConnection"/> to the shard to which the specified key value is mapped, with
* <see cref="ConnectionOptions.Validate"/>. <typeparam name="KeyT">Type of the key.</typeparam>
*
* @param key
* Input key value.
* @param connectionString
* Connection string with credential information such as SQL Server credentials or Integrated Security settings. The hostname of the
* server and the database name for the shard are obtained from the lookup operation for key.
* @return A Task encapsulating an opened SqlConnection. Note that the <see cref="SqlConnection"/> object returned by this call is not protected
* against transient faults. Callers should follow best practices to protect the connection against transient faults in their application
* code, e.g., by using the transient fault handling functionality in the Enterprise Library from Microsoft Patterns and Practices team.
* This call only works if there is a single default mapping.
*/
public <KeyT> Callable<Connection> openConnectionForKeyAsync(KeyT key,
String connectionString) {
return this.openConnectionForKeyAsync(key, connectionString, ConnectionOptions.Validate);
}
/**
* Asynchronously opens a regular <see cref="SqlConnection"/> to the shard to which the specified key value is mapped. <typeparam name="KeyT">Type
* of the key.</typeparam>
*
* @param key
* Input key value.
* @param connectionString
* Connection string with credential information such as SQL Server credentials or Integrated Security settings. The hostname of the
* server and the database name for the shard are obtained from the lookup operation for key.
* @param options
* Options for validation operations to perform on opened connection.
* @return A Task encapsulating an opened SqlConnection. Note that the <see cref="SqlConnection"/> object returned by this call is not protected
* against transient faults. Callers should follow best practices to protect the connection against transient faults in their application
* code, e.g., by using the transient fault handling functionality in the Enterprise Library from Microsoft Patterns and Practices team.
* This call only works if there is a single default mapping.
*/
public <KeyT> Callable<Connection> openConnectionForKeyAsync(KeyT key,
String connectionString,
ConnectionOptions options) {
ExceptionUtils.disallowNullArgument(connectionString, "connectionString");
assert this.getStoreShardMap().getKeyType() != ShardKeyType.None;
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
IShardMapper mapper = this.<KeyT>getMapper();
if (mapper == null) {
throw new IllegalArgumentException(
StringUtilsLocal.formatInvariant(Errors._ShardMap_OpenConnectionForKey_KeyTypeNotSupported, key.getClass(),
this.getStoreShardMap().getName(), ShardKey.typeFromShardKeyType(this.getStoreShardMap().getKeyType())),
new Throwable("key"));
}
assert mapper != null;
return mapper.openConnectionForKeyAsync(key, connectionString, options);
}
}
/**
* Gets all shards from the shard map.
*
* @return All shards belonging to the shard map.
*/
public final List<Shard> getShards() {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetShards Start; ");
Stopwatch stopwatch = Stopwatch.createStarted();
List<Shard> shards = defaultShardMapper.getShards();
stopwatch.stop();
log.info("GetShards Complete; Duration: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
return shards;
}
}
/**
* Obtains the shard for the specified location.
*
* @param location
* Location of the shard.
* @return Shard which has the specified location.
*/
public final Shard getShard(ShardLocation location) {
ExceptionUtils.disallowNullArgument(location, "location");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("GetShard Start; Shard Location: {} ", location);
Stopwatch stopwatch = Stopwatch.createStarted();
Shard shard = defaultShardMapper.getShardByLocation(location);
stopwatch.stop();
log.info("GetShard Complete; Shard Location: {}; Duration: {}", location, stopwatch.elapsed(TimeUnit.MILLISECONDS));
if (shard == null) {
throw new ShardManagementException(ShardManagementErrorCategory.ShardMap, ShardManagementErrorCode.ShardDoesNotExist,
Errors._ShardMap_GetShard_ShardDoesNotExist, location, this.getName());
}
return shard;
}
}
/**
* Tries to obtains the shard for the specified location.
*
* @param location
* Location of the shard.
* @param shard
* Shard which has the specified location.
* @return <c>true</c> if shard with specified location is found, <c>false</c> otherwise.
*/
public final boolean tryGetShard(ShardLocation location,
ReferenceObjectHelper<Shard> shard) {
ExceptionUtils.disallowNullArgument(location, "location");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("TryGetShard Start; Shard Location: {} ", location);
Stopwatch stopwatch = Stopwatch.createStarted();
shard.argValue = defaultShardMapper.getShardByLocation(location);
stopwatch.stop();
log.info("TryGetShard Complete; Shard Location: {}; Duration: {}", location, stopwatch.elapsed(TimeUnit.MILLISECONDS));
return shard.argValue != null;
}
}
/**
* Creates a new shard and registers it with the shard map.
*
* @param shardCreationArgs
* Information about shard to be added.
* @return A new shard registered with this shard map.
*/
public final Shard createShard(ShardCreationInfo shardCreationArgs) {
ExceptionUtils.disallowNullArgument(shardCreationArgs, "shardCreationArgs");
try (ActivityIdScope activityId = new ActivityIdScope(UUID.randomUUID())) {
log.info("CreateShard Start; Shard: {} ", shardCreationArgs.getLocation());
Stopwatch stopwatch = Stopwatch.createStarted();
Shard shard = defaultShardMapper.add(new Shard(this.getShardMapManager(), this, shardCreationArgs));
stopwatch.stop();
log.info("CreateShard Complete; Shard: {}; Duration: {}", shard.getLocation(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
return shard;
}
}
/**
* Atomically adds a shard to ShardMap using the specified location.
*
* @param location
* Location of shard to be added.
* @return A shard attached to this shard map.
*/
public final Shard createShard(ShardLocation location) {
ExceptionUtils.disallowNullArgument(location, "location");
try (ActivityIdScope activityId = new ActivityIdScope(UUID.randomUUID())) {
log.info("CreateShard", "Start; Shard: {} ", location);
Stopwatch stopwatch = Stopwatch.createStarted();
Shard shard = defaultShardMapper.add(new Shard(this.getShardMapManager(), this, new ShardCreationInfo(location)));
stopwatch.stop();
log.info("CreateShard", "Complete; Shard: {}; Duration: {}", location, stopwatch.elapsed(TimeUnit.MILLISECONDS));
return shard;
}
}
/**
* Removes a shard from ShardMap.
*
* @param shard
* Shard to remove.
*/
public final void deleteShard(Shard shard) {
ExceptionUtils.disallowNullArgument(shard, "shard");
try (ActivityIdScope activityId = new ActivityIdScope(UUID.randomUUID())) {
log.info("DeleteShard Start; Shard: {} ", shard.getLocation());
Stopwatch stopwatch = Stopwatch.createStarted();
defaultShardMapper.remove(shard);
stopwatch.stop();
log.info("DeleteShard Complete; Shard: {}; Duration: {}", shard.getLocation(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
}
/**
* Updates a shard with the changes specified in the <paramref name="update"/> parameter.
*
* @param currentShard
* Shard being updated.
* @param update
* Updated properties of the shard.
* @return New Shard with updated information.
*/
public final Shard updateShard(Shard currentShard,
ShardUpdate update) {
ExceptionUtils.disallowNullArgument(currentShard, "currentShard");
ExceptionUtils.disallowNullArgument(update, "update");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("UpdateShard", "Start; Shard:{}", currentShard.getLocation());
Stopwatch stopwatch = Stopwatch.createStarted();
Shard shard = defaultShardMapper.updateShard(currentShard, update);
stopwatch.stop();
log.info("UpdateShard", "Complete; Shard: {}; Duration:{}", currentShard.getLocation(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
return shard;
}
}
/**
* Opens a connection to the given shard provider.
*
* @param shardProvider
* Shard provider containing shard to be connected to.
* @param connectionString
* Connection string for connection. Must have credentials.
*/
public final Connection openConnection(IShardProvider shardProvider,
String connectionString) {
return openConnection(shardProvider, connectionString, ConnectionOptions.Validate);
}
/**
* Opens a connection to the given shard provider.
*
* @param shardProvider
* Shard provider containing shard to be connected to.
* @param connectionString
* Connection string for connection. Must have credentials.
*/
public final Connection openConnection(IShardProvider shardProvider,
String connectionString,
ConnectionOptions options) {
assert shardProvider != null;
ExceptionUtils.disallowNullArgument(connectionString, "connectionString");
String connectionStringFinal = this.validateAndPrepareConnectionString(shardProvider, connectionString);
ExceptionUtils.ensureShardBelongsToShardMap(this.getShardMapManager(), this, shardProvider.getShardInfo(), "OpenConnection", "Shard");
IUserStoreConnection conn = this.getShardMapManager().getStoreConnectionFactory().getUserConnection(connectionStringFinal);
log.info("OpenConnection", "Start; Shard: {}; Options: {}; ConnectionString: {}", shardProvider.getShardInfo().getLocation(), options,
connectionStringFinal);
try (ConditionalDisposable<IUserStoreConnection> cd = new ConditionalDisposable<>(conn)) {
// If validation is requested.
if ((options.getValue() & ConnectionOptions.Validate.getValue()) == ConnectionOptions.Validate.getValue()) {
shardProvider.validate(this.getStoreShardMap(), conn.getConnection());
}
cd.setDoNotDispose(true);
log.info("OpenConnection", "Complete; Shard: {} Options: {}", shardProvider.getShardInfo().getLocation(), options);
}
catch (Exception e) {
ExceptionUtils.throwStronglyTypedException(e);
}
return conn.getConnection();
}
/**
* Asynchronously opens a connection to the given shard provider.
*
* @param shardProvider
* Shard provider containing shard to be connected to.
* @param connectionString
* Connection string for connection. Must have credentials.
* @return A task encapsulating the SqlConnection All exceptions are reported via the returned task.
*/
public final Callable<Connection> openConnectionAsync(IShardProvider shardProvider,
String connectionString) {
return openConnectionAsync(shardProvider, connectionString, ConnectionOptions.Validate);
}
/**
* Asynchronously opens a connection to the given shard provider. All exceptions are reported via the returned task.
*
* @param shardProvider
* Shard provider containing shard to be connected to.
* @param connectionString
* Connection string for connection. Must have credentials.
* @return A task encapsulating the SqlConnection.
*/
public final Callable<Connection> openConnectionAsync(IShardProvider shardProvider,
String connectionString,
ConnectionOptions options) {
assert shardProvider != null;
ExceptionUtils.disallowNullArgument(connectionString, "connectionString");
String connectionStringFinal = this.validateAndPrepareConnectionString(shardProvider, connectionString);
ExceptionUtils.ensureShardBelongsToShardMap(this.getShardMapManager(), this, shardProvider.getShardInfo(), "OpenConnectionAsync", "Shard");
IUserStoreConnection conn = this.getShardMapManager().getStoreConnectionFactory().getUserConnection(connectionStringFinal);
log.info("OpenConnectionAsync", "Start; Shard: {}; Options: {}; ConnectionString: {}", shardProvider.getShardInfo().getLocation(), options,
connectionStringFinal);
try (ConditionalDisposable<IUserStoreConnection> cd = new ConditionalDisposable<>(conn)) {
// If validation is requested.
if ((options.getValue() & ConnectionOptions.Validate.getValue()) == ConnectionOptions.Validate.getValue()) {
shardProvider.validateAsync(this.getStoreShardMap(), conn.getConnection()).call();
}
cd.setDoNotDispose(true);
log.info("OpenConnectionAsync", "Complete; Shard: {} Options: {}", shardProvider.getShardInfo().getLocation(), options);
}
catch (Exception e) {
ExceptionUtils.throwStronglyTypedException(e);
}
return conn::getConnection;
}
/**
* Gets the mapper. This method is used by OpenConnection and Lookup of V. <typeparam name="V">Shard provider type.</typeparam>
*
* @return Appropriate mapper for the given shard map.
*/
public abstract <V> IShardMapper getMapper();
/**
* Clones the given shard map.
*
* @return A cloned instance of the shard map.
*/
public ShardMap clone() {
return this.cloneCore();
}
/**
* Clones the current shard map instance.
*
* @return Cloned shard map instance.
*/
protected abstract ShardMap cloneCore();
/**
* Ensures that the provided connection string is valid and builds the connection string to be used for DDR connection to the given shard
* provider.
*
* @param shardProvider
* Shard provider containing shard to be connected to.
* @param connectionString
* Input connection string.
* @return Connection string for DDR connection.
*/
private String validateAndPrepareConnectionString(IShardProvider shardProvider,
String connectionString) {
assert shardProvider != null;
assert connectionString != null;
// Devnote: If connection string specifies Active Directory authentication and runtime is not
// .NET 4.6 or higher, then below call will throw.
SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder(connectionString);
// DataSource must not be set.
if (!Strings.isNullOrEmpty(connectionStringBuilder.getDataSource())) {
throw new IllegalArgumentException(
StringUtilsLocal.formatInvariant(Errors._ShardMap_OpenConnection_ConnectionStringPropertyDisallowed, "DataSource"),
new Throwable("connectionString"));
}
// DatabaseName must not be set.
if (!Strings.isNullOrEmpty(connectionStringBuilder.getDatabaseName())) {
throw new IllegalArgumentException(
StringUtilsLocal.formatInvariant(Errors._ShardMap_OpenConnection_ConnectionStringPropertyDisallowed, "Initial Catalog"),
new Throwable("connectionString"));
}
// ConnectRetryCount must not be set (default value is 1)
if (ShardMapUtils.getIsConnectionResiliencySupported() && connectionStringBuilder.getConnectRetryCount() > 1) {
throw new IllegalArgumentException(StringUtilsLocal.formatInvariant(Errors._ShardMap_OpenConnection_ConnectionStringPropertyDisallowed,
ShardMapUtils.ConnectRetryCount), new Throwable("connectionString"));
}
// Verify that either UserID/Password or provided or integrated authentication is enabled.
SqlShardMapManagerCredentials.ensureCredentials(connectionStringBuilder, "connectionString");
Shard s = shardProvider.getShardInfo();
connectionStringBuilder.setDataSource(s.getLocation().getDataSource());
connectionStringBuilder.setDatabaseName(s.getLocation().getDatabase());
// Append the proper post-fix for ApplicationName
connectionStringBuilder.setApplicationName(
ApplicationNameHelper.addApplicationNameSuffix(connectionStringBuilder.getApplicationName(), this.getApplicationNameSuffix()));
// Disable connection resiliency if necessary
if (ShardMapUtils.getIsConnectionResiliencySupported()) {
connectionStringBuilder.setItem(ShardMapUtils.ConnectRetryCount, "0");
}
return connectionStringBuilder.getConnectionString();
}
}

Просмотреть файл

@ -0,0 +1,114 @@
package com.microsoft.azure.elasticdb.shard.map;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.base.ShardKey;
import com.microsoft.azure.elasticdb.shard.base.ShardKeyType;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementErrorCategory;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementErrorCode;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementException;
import com.microsoft.azure.elasticdb.shard.store.StoreShardMap;
import com.microsoft.azure.elasticdb.shard.utils.Errors;
import com.microsoft.azure.elasticdb.shard.utils.ExceptionUtils;
/**
* Extension methods on ShardMaps that allow down-casting.
*/
public final class ShardMapExtensions {
/**
* Downcasts to ListShardMap of KeyT. <typeparam name="KeyT">Key type.</typeparam>
*
* @param shardMap
* Input shard map.
* @return ListShardMap representation of this object.
*/
public static <KeyT> ListShardMap<KeyT> asListShardMap(ShardMap shardMap) {
ExceptionUtils.disallowNullArgument(shardMap, "shardMap");
return ShardMapExtensions.asListShardMap(shardMap, true);
}
/**
* Downcasts to ListShardMap of KeyT. <typeparam name="KeyT">Key type.</typeparam>
*
* @param shardMap
* Input shard map.
* @param throwOnFailure
* Whether to throw exception or return null on failure.
* @return ListShardMap representation of this object.
*/
public static <KeyT> ListShardMap<KeyT> asListShardMap(ShardMap shardMap,
boolean throwOnFailure) {
ListShardMap<KeyT> lsm = null;
if (shardMap != null && shardMap.getMapType() == ShardMapType.List) {
lsm = (ListShardMap<KeyT>) ((shardMap instanceof ListShardMap) ? shardMap : null);
}
if (lsm == null && throwOnFailure) {
throw ShardMapExtensions.getConversionException(shardMap.getStoreShardMap(), "Unknown", "List");
}
return lsm;
}
/**
* Downcasts to RangeShardMap of KeyT. <typeparam name="KeyT">Key type.</typeparam>
*
* @param shardMap
* Input shard map.
* @return RangeShardMap representation of this object.
*/
public static <KeyT> RangeShardMap<KeyT> asRangeShardMap(ShardMap shardMap) {
ExceptionUtils.disallowNullArgument(shardMap, "shardMap");
return ShardMapExtensions.asRangeShardMap(shardMap, true);
}
/**
* Downcasts to RangeShardMap of KeyT. <typeparam name="KeyT">Key type.</typeparam>
*
* @param shardMap
* Input shard map.
* @param throwOnFailure
* Whether to throw exception or return null on failure.
* @return RangeShardMap representation of this object.
*/
public static <KeyT> RangeShardMap<KeyT> asRangeShardMap(ShardMap shardMap,
boolean throwOnFailure) {
RangeShardMap<KeyT> rsm = null;
if (shardMap != null && shardMap.getMapType() == ShardMapType.Range) {
rsm = (RangeShardMap<KeyT>) ((shardMap instanceof RangeShardMap) ? shardMap : null);
}
if (rsm == null && throwOnFailure) {
throw ShardMapExtensions.getConversionException(shardMap.getStoreShardMap(), "Unknown", "Range");
}
return rsm;
}
/**
* Raise conversion exception. <typeparam name="KeyT">Key type.</typeparam>
*
* @param ssm
* Shard map whose conversion failed.
* @param targetKind
* Requested type of shard map.
*/
public static ShardManagementException getConversionException(StoreShardMap ssm,
String sourceKind,
String targetKind) {
return new ShardManagementException(ShardManagementErrorCategory.ShardMapManager, ShardManagementErrorCode.ShardMapTypeConversionError,
Errors._ShardMapExtensions_AsTypedShardMap_ConversionFailure, ssm.getName(), targetKind, sourceKind, ssm.getMapType().toString(),
ssm.getKeyType() == ShardKeyType.None ? "" : ShardKey.typeFromShardKeyType(ssm.getKeyType()).getName());
}
}

Просмотреть файл

@ -0,0 +1,62 @@
package com.microsoft.azure.elasticdb.shard.map;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import javax.xml.bind.annotation.XmlEnumValue;
/**
* Type of shard map.
*/
public enum ShardMapType {
/**
* Invalid kind of shard map. Only used for serialization/deserialization.
*/
@XmlEnumValue("0")
None(0),
/**
* Shard map with list based mappings.
*/
@XmlEnumValue("1")
List(1),
/**
* Shard map with range based mappings.
*/
@XmlEnumValue("2")
Range(2);
public static final int SIZE = Integer.SIZE;
private static java.util.HashMap<Integer, ShardMapType> mappings;
private int intValue;
ShardMapType(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, ShardMapType> getMappings() {
if (mappings == null) {
synchronized (ShardMapType.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static ShardMapType forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,80 @@
package com.microsoft.azure.elasticdb.shard.map;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardMapManager;
import com.microsoft.azure.elasticdb.shard.sqlstore.SqlConnectionStringBuilder;
import com.microsoft.azure.elasticdb.shard.store.StoreShardMap;
/**
* Helper methods related to shard map instantiation.
*/
public final class ShardMapUtils {
/**
* SqlConnectionStringBuilder property that allows one to specify the number of reconnect attempts on connection failure.
*/
public static final String ConnectRetryCount = "ConnectRetryCount";
/**
* SqlConnectionStringBuilder property that allows specifying active directoty authentication to connect to SQL instance.
*/
public static final String Authentication = "Authentication";
/**
* String representation of SqlAuthenticationMethod.ActiveDirectoryIntegrated SqlAuthenticationMethod.ActiveDirectoryIntegrated.ToString() cannot
* be used because it may not be available in the .NET framework version that we are running in
*/
public static final String ActiveDirectoryIntegratedStr = "ActiveDirectoryIntegrated";
/**
* Whether this SqlClient instance supports Connection Resiliency.
*/
private static boolean IsConnectionResiliencySupported;
static {
// Connection resiliency is supported if this SqlClient instance
// allows setting the retry count on connection failure
SqlConnectionStringBuilder bldr = new SqlConnectionStringBuilder();
if (bldr.containsKey(ConnectRetryCount)) {
setIsConnectionResiliencySupported(true);
}
}
public static boolean getIsConnectionResiliencySupported() {
return IsConnectionResiliencySupported;
}
public static void setIsConnectionResiliencySupported(boolean value) {
IsConnectionResiliencySupported = value;
}
/**
* Converts StoreShardMap to ShardMap.
*
* @param shardMapManager
* Reference to shard map manager.
* @param ssm
* Storage representation for ShardMap.
* @return ShardMap object corresponding to storange representation.
*/
public static ShardMap createShardMapFromStoreShardMap(ShardMapManager shardMapManager,
StoreShardMap ssm) {
switch (ssm.getMapType()) {
case List:
// Create ListShardMap<KeyT>
return new ListShardMap<>(shardMapManager, ssm);
case Range:
// Create RangeShardMap<KeyT>
return new RangeShardMap<>(shardMapManager, ssm);
default:
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,87 @@
package com.microsoft.azure.elasticdb.shard.mapmanager;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Represents error categories related to Shard Management operations.
*/
public enum ShardManagementErrorCategory {
/**
* ShardMap manager factory.
*/
ShardMapManagerFactory(0),
/**
* ShardMap manager.
*/
ShardMapManager(1),
/**
* ShardMap.
*/
ShardMap(2),
/**
* List shard map.
*/
ListShardMap(3),
/**
* Range shard map.
*/
RangeShardMap(4),
/**
* Version validation.
*/
Validation(5),
/**
* Recovery oriented errors.
*/
Recovery(6),
/**
* Errors related to Schema Info Collection.
*/
SchemaInfoCollection(7),
/**
* General failure category.
*/
General(8);
public static final int SIZE = java.lang.Integer.SIZE;
private static java.util.HashMap<Integer, ShardManagementErrorCategory> mappings;
private int intValue;
ShardManagementErrorCategory(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, ShardManagementErrorCategory> getMappings() {
if (mappings == null) {
synchronized (ShardManagementErrorCategory.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static ShardManagementErrorCategory forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,229 @@
package com.microsoft.azure.elasticdb.shard.mapmanager;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Represents error codes related to <see cref="ShardMapManager"/> operations.
*/
public enum ShardManagementErrorCode {
/**
* Successful execution.
*/
Success(0),
/// #region ShardMapManagerFactory
/**
* Store already exists on target shard map manager database.
*/
ShardMapManagerStoreAlreadyExists(11),
/**
* Store does not exist on target shard map manager database.
*/
ShardMapManagerStoreDoesNotExist(12),
/// #endregion
/// #region ShardMapManager
/**
* ShardMap with specified name already exists.
*/
ShardMapAlreadyExists(21),
/**
* ShardMap with specified name not found.
*/
ShardMapLookupFailure(22),
/**
* ShardMap has shards associated with it.
*/
ShardMapHasShards(23),
/**
* GSM store version does not match with client library.
*/
GlobalStoreVersionMismatch(24),
/**
* LSM store version does not match with client library.
*/
LocalStoreVersionMismatch(25),
/**
* All necessary parameters for GSM stored procedure are not supplied.
*/
GlobalStoreOperationInsufficientParameters(26),
/**
* All necessary parameters for LSM stored procedure are not supplied.
*/
LocalStoreOperationInsufficientParameters(27),
/// #endregion
/// #region ShardMap
/**
* Conversion of shard map failed.
*/
ShardMapTypeConversionError(31),
/**
* Shard has mappings associated with it.
*/
ShardHasMappings(32),
/**
* Shard already exists.
*/
ShardAlreadyExists(33),
/**
* Shard location already exists.
*/
ShardLocationAlreadyExists(34),
/**
* Shard has been updated by concurrent user.
*/
ShardVersionMismatch(35),
/// #endregion
/// #region PointMapping
/**
* Given point is already associated with a mapping.
*/
MappingPointAlreadyMapped(41),
/// #endregion PointMapping
/// #region RangeMapping
/**
* Specified range is already associated with a mapping.
*/
MappingRangeAlreadyMapped(51),
/// #endregion RangeMapping
/// #region Common
/**
* Storage operation failed.
*/
StorageOperationFailure(61),
/**
* ShardMap does not exist any more.
*/
ShardMapDoesNotExist(62),
/**
* Shard does not exist any more.
*/
ShardDoesNotExist(63),
/**
* An application lock could not be acquired.
*/
LockNotAcquired(64),
/**
* An application lock count not be released.
*/
LockNotReleased(65),
/**
* An unexpected error has occurred.
*/
UnexpectedError(66),
/// #endregion Common
/// #region Common Mapper
/**
* Specified mapping no longer exists.
*/
MappingDoesNotExist(71),
/**
* Could not locate a mapping corresponding to given key.
*/
MappingNotFoundForKey(72),
/**
* Specified mapping is offline.
*/
MappingIsOffline(73),
/**
* Could not terminate connections associated with the Specified mapping.
*/
MappingsKillConnectionFailure(74),
/**
* Specified mapping is not offline which certain management operations warrant.
*/
MappingIsNotOffline(75),
/**
* Specified mapping is locked and the given lock owner id does not match the owner id in the store.
*/
MappingLockOwnerIdDoesNotMatch(76),
/**
* Specified mapping has already been locked.
*/
MappingIsAlreadyLocked(77),
/// #endregion Common Mapper
/// #region Recovery
/**
* Shard does not have storage structures.
*/
ShardNotValid(81);
/// #endregion
public static final int SIZE = java.lang.Integer.SIZE;
private static java.util.HashMap<Integer, ShardManagementErrorCode> mappings;
private int intValue;
ShardManagementErrorCode(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, ShardManagementErrorCode> getMappings() {
if (mappings == null) {
synchronized (ShardManagementErrorCode.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static ShardManagementErrorCode forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,127 @@
package com.microsoft.azure.elasticdb.shard.mapmanager;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import java.io.Serializable;
import java.util.Locale;
/**
* Representation of exceptions that occur during storage operations.
*/
public final class ShardManagementException extends RuntimeException implements Serializable {
/**
* Error category.
*/
private ShardManagementErrorCategory errorCategory = ShardManagementErrorCategory.values()[0];
/**
* Error code.
*/
private ShardManagementErrorCode errorCode = ShardManagementErrorCode.values()[0];
/**
* Initializes a new instance with a specified error message.
*
* @param category
* Category of error.
* @param code
* Error code.
* @param message
* Error message.
*/
public ShardManagementException(ShardManagementErrorCategory category,
ShardManagementErrorCode code,
String message) {
super(message);
this.setErrorCategory(category);
this.setErrorCode(code);
}
/**
* Initializes a new instance with a specified formatted error message.
*
* @param category
* Category of error.
* @param code
* Error code.
* @param format
* The format message that describes the error
* @param args
* The arguments to the format string
*/
public ShardManagementException(ShardManagementErrorCategory category,
ShardManagementErrorCode code,
String format,
Object... args) {
super(String.format(Locale.getDefault(), format, args));
this.setErrorCategory(category);
this.setErrorCode(code);
}
/**
* Initializes a new instance with a specified error message and a reference to the inner exception that is the cause of this exception.
*
* @param category
* Category of error.
* @param code
* Error code.
* @param message
* A message that describes the error
* @param inner
* The exception that is the cause of the current exception
*/
public ShardManagementException(ShardManagementErrorCategory category,
ShardManagementErrorCode code,
String message,
RuntimeException inner) {
super(message, inner);
this.setErrorCategory(category);
this.setErrorCode(code);
}
/**
* Initializes a new instance with a specified formatted error message and a reference to the inner exception that is the cause of this exception.
*
* @param category
* Category of error.
* @param code
* Error code.
* @param format
* The format message that describes the error
* @param inner
* The exception that is the cause of the current exception
* @param args
* The arguments to the format string
*/
public ShardManagementException(ShardManagementErrorCategory category,
ShardManagementErrorCode code,
String format,
RuntimeException inner,
Object... args) {
super(String.format(Locale.getDefault(), format, args), inner);
this.setErrorCategory(category);
this.setErrorCode(code);
}
public ShardManagementErrorCategory getErrorCategory() {
return errorCategory;
}
private void setErrorCategory(ShardManagementErrorCategory value) {
errorCategory = value;
}
public ShardManagementErrorCode getErrorCode() {
return errorCode;
}
private void setErrorCode(ShardManagementErrorCode value) {
errorCode = value;
}
}

Просмотреть файл

@ -0,0 +1,47 @@
package com.microsoft.azure.elasticdb.shard.mapmanager;
import java.util.function.Function;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.transientfaulthandling.ITransientErrorDetectionStrategy;
import com.microsoft.azure.elasticdb.core.commons.transientfaulthandling.RetryBehavior;
import com.microsoft.azure.elasticdb.shard.utils.SqlUtils;
/**
* Provides the transient error detection logic for transient faults that are specific to Shard map manager.
*/
public final class ShardManagementTransientErrorDetectionStrategy implements ITransientErrorDetectionStrategy {
/**
* Delegate used for detecting transient faults.
*/
private Function<Exception, Boolean> transientFaultDetector;
/**
* Creates a new instance of transient error detection strategy for Shard map manager.
*
* @param retryBehavior
* User specified retry behavior.
*/
public ShardManagementTransientErrorDetectionStrategy(RetryBehavior retryBehavior) {
transientFaultDetector = retryBehavior.getTransientErrorDetector();
}
/**
* Determines whether the specified exception represents a transient failure that can be compensated by a retry.
*
* @param ex
* The exception object to be verified.
* @return true if the specified exception is considered as transient; otherwise, false.
*/
public boolean isTransient(Exception ex) {
return SqlUtils.getTransientErrorDetector().apply(ex) || transientFaultDetector.apply(ex);
}
}

Просмотреть файл

@ -0,0 +1,852 @@
package com.microsoft.azure.elasticdb.shard.mapmanager;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.google.common.base.Stopwatch;
import com.microsoft.azure.elasticdb.core.commons.helpers.Event;
import com.microsoft.azure.elasticdb.core.commons.helpers.EventHandler;
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
import com.microsoft.azure.elasticdb.core.commons.logging.ActivityIdScope;
import com.microsoft.azure.elasticdb.core.commons.transientfaulthandling.RetryBehavior;
import com.microsoft.azure.elasticdb.core.commons.transientfaulthandling.RetryPolicy;
import com.microsoft.azure.elasticdb.core.commons.transientfaulthandling.RetryingEventArgs;
import com.microsoft.azure.elasticdb.shard.base.ShardKeyType;
import com.microsoft.azure.elasticdb.shard.base.ShardLocation;
import com.microsoft.azure.elasticdb.shard.cache.ICacheStore;
import com.microsoft.azure.elasticdb.shard.map.ListShardMap;
import com.microsoft.azure.elasticdb.shard.map.RangeShardMap;
import com.microsoft.azure.elasticdb.shard.map.ShardMap;
import com.microsoft.azure.elasticdb.shard.map.ShardMapExtensions;
import com.microsoft.azure.elasticdb.shard.map.ShardMapType;
import com.microsoft.azure.elasticdb.shard.map.ShardMapUtils;
import com.microsoft.azure.elasticdb.shard.recovery.RecoveryManager;
import com.microsoft.azure.elasticdb.shard.schema.SchemaInfoCollection;
import com.microsoft.azure.elasticdb.shard.sqlstore.SqlShardMapManagerCredentials;
import com.microsoft.azure.elasticdb.shard.store.IStoreConnectionFactory;
import com.microsoft.azure.elasticdb.shard.store.StoreResults;
import com.microsoft.azure.elasticdb.shard.store.StoreShardMap;
import com.microsoft.azure.elasticdb.shard.store.Version;
import com.microsoft.azure.elasticdb.shard.storeops.base.IStoreOperationFactory;
import com.microsoft.azure.elasticdb.shard.storeops.base.IStoreOperationGlobal;
import com.microsoft.azure.elasticdb.shard.storeops.base.IStoreOperationLocal;
import com.microsoft.azure.elasticdb.shard.utils.Errors;
import com.microsoft.azure.elasticdb.shard.utils.ExceptionUtils;
import com.microsoft.azure.elasticdb.shard.utils.GlobalConstants;
import com.microsoft.azure.elasticdb.shard.utils.StringUtilsLocal;
/**
* Serves as the entry point for creation, management and lookup operations over shard maps.
*/
public final class ShardMapManager {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* Event to be raised on Shard Map Manager store retries.
*/
public Event<EventHandler<RetryingEventArgs>> shardMapManagerRetrying;
/**
* Credentials for performing ShardMapManager operations.
*/
private SqlShardMapManagerCredentials credentials;
/**
* Factory for store connections.
*/
private IStoreConnectionFactory storeConnectionFactory;
/**
* Factory for store operations.
*/
private IStoreOperationFactory storeOperationFactory;
/**
* Policy for performing retries on connections to shard map manager database.
*/
private RetryPolicy retryPolicy;
/**
* Local cache.
*/
private ICacheStore cache;
/**
* Given the connection string, opens up the corresponding data source and obtains the ShardMapManager.
*
* @param credentials
* credentials for performing ShardMapManager operations.
* @param storeConnectionFactory
* Factory for store connections.
* @param storeOperationFactory
* Factory for store operations.
* @param cacheStore
* Cache store.
* @param loadPolicy
* Initialization policy.
* @param retryPolicy
* Policy for performing retries on connections to shard map manager database.
* @param retryBehavior
* Policy for detecting transient errors.
*/
public ShardMapManager(SqlShardMapManagerCredentials credentials,
IStoreConnectionFactory storeConnectionFactory,
IStoreOperationFactory storeOperationFactory,
ICacheStore cacheStore,
ShardMapManagerLoadPolicy loadPolicy,
RetryPolicy retryPolicy,
RetryBehavior retryBehavior) {
this(credentials, storeConnectionFactory, storeOperationFactory, cacheStore, loadPolicy, retryPolicy, retryBehavior, null);
}
/**
* Given the connection string, opens up the corresponding data source and obtains the ShardMapManager.
*
* @param credentials
* credentials for performing ShardMapManager operations.
* @param storeConnectionFactory
* Factory for store connections.
* @param storeOperationFactory
* Factory for store operations.
* @param cacheStore
* Cache store.
* @param loadPolicy
* Initialization policy.
* @param retryPolicy
* Policy for performing retries on connections to shard map manager database.
* @param retryBehavior
* Policy for detecting transient errors.
* @param retryEventHandler
* Event handler for store operation retry events.
*/
public ShardMapManager(SqlShardMapManagerCredentials credentials,
IStoreConnectionFactory storeConnectionFactory,
IStoreOperationFactory storeOperationFactory,
ICacheStore cacheStore,
ShardMapManagerLoadPolicy loadPolicy,
RetryPolicy retryPolicy,
RetryBehavior retryBehavior,
EventHandler<RetryingEventArgs> retryEventHandler) {
assert credentials != null;
this.setCredentials(credentials);
this.setStoreConnectionFactory(storeConnectionFactory);
this.setStoreOperationFactory(storeOperationFactory);
this.setCache(cacheStore);
this.setRetryPolicy(
new RetryPolicy(new ShardManagementTransientErrorDetectionStrategy(retryBehavior), retryPolicy.getExponentialRetryStrategy()));
// Register for TfhImpl.RetryPolicy.retrying event.
this.retryPolicy.retrying = new Event<>();
this.retryPolicy.retrying.addListener(this::shardMapManagerRetryingEventHandler);
this.shardMapManagerRetrying = new Event<>();
// Add user specified event handler.
if (retryEventHandler != null) {
this.shardMapManagerRetrying.addListener("retryEventHandler", retryEventHandler);
}
if (loadPolicy == ShardMapManagerLoadPolicy.Eager) {
// We eagerly load everything from ShardMapManager. In case of lazy
// loading policy, we will add things to local caches based on cache
// misses on lookups.
this.loadFromStore();
}
}
/**
* Ensures that the given shard map name is valid.
*
* @param shardMapName
* Input shard map name.
*/
private static void validateShardMapName(String shardMapName) {
ExceptionUtils.disallowNullOrEmptyStringArgument(shardMapName, "shardMapName");
// Disallow non-alpha-numeric characters.
if (!StringUtilsLocal.isAlphanumericPunctuated(shardMapName)) {
throw new IllegalArgumentException(String.format(Errors._ShardMapManager_UnsupportedShardMapName, shardMapName));
}
// Ensure that length is within bounds.
if (shardMapName.length() > GlobalConstants.MaximumShardMapNameLength) {
throw new IllegalArgumentException(
String.format(Errors._ShardMapManager_UnsupportedShardMapNameLength, shardMapName, GlobalConstants.MaximumShardMapNameLength));
}
}
public SqlShardMapManagerCredentials getCredentials() {
return credentials;
}
private void setCredentials(SqlShardMapManagerCredentials value) {
credentials = value;
}
public IStoreConnectionFactory getStoreConnectionFactory() {
return storeConnectionFactory;
}
private void setStoreConnectionFactory(IStoreConnectionFactory value) {
storeConnectionFactory = value;
}
public IStoreOperationFactory getStoreOperationFactory() {
return storeOperationFactory;
}
private void setStoreOperationFactory(IStoreOperationFactory value) {
storeOperationFactory = value;
}
public RetryPolicy getRetryPolicy() {
return retryPolicy;
}
private void setRetryPolicy(RetryPolicy value) {
retryPolicy = value;
}
public ICacheStore getCache() {
return cache;
}
private void setCache(ICacheStore value) {
cache = value;
}
/**
* Creates a list based <see cref="ListShardMap{KeyT}"/>. <typeparam name="KeyT">Type of keys.</typeparam>
*
* @param shardMapName
* Name of shard map.
* @return List shard map with the specified name.
*/
public <KeyT> ListShardMap<KeyT> createListShardMap(String shardMapName,
ShardKeyType keyType) {
ShardMapManager.validateShardMapName(shardMapName);
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
StoreShardMap dssm = new StoreShardMap(UUID.randomUUID(), shardMapName, ShardMapType.List, keyType);
log.info("ShardMapManager CreateListShardMap Start; ShardMap: {}", shardMapName);
Stopwatch stopwatch = Stopwatch.createStarted();
this.addShardMapToStore("CreateListShardMap", dssm);
stopwatch.stop();
log.info("ShardMapManager CreateListShardMap Added ShardMap to Store; ShardMap: {} Duration: {}", shardMapName,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
log.info("ShardMapManager CreateListShardMap Complete; ShardMap: {} Duration: {}", shardMapName,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return new ListShardMap<>(this, dssm);
}
}
/**
* Create a range based <see cref="RangeShardMap{KeyT}"/>. <typeparam name="KeyT">Type of keys.</typeparam>
*
* @param shardMapName
* Name of shard map.
* @return Range shard map with the specified name.
*/
public <KeyT> RangeShardMap<KeyT> createRangeShardMap(String shardMapName,
ShardKeyType keyType) {
ShardMapManager.validateShardMapName(shardMapName);
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
StoreShardMap dssm = new StoreShardMap(UUID.randomUUID(), shardMapName, ShardMapType.Range, keyType);
log.info("ShardMapManager CreateRangeShardMap Start; ShardMap: {}", shardMapName);
Stopwatch stopwatch = Stopwatch.createStarted();
this.addShardMapToStore("CreateRangeShardMap", dssm);
stopwatch.stop();
log.info("ShardMapManager CreateRangeShardMap Added ShardMap to Store; ShardMap: {}; Duration: {}", shardMapName,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
log.info("ShardMapManager CreateRangeShardMap Complete; ShardMap: {} Duration: {}", shardMapName,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
return new RangeShardMap<>(this, dssm);
}
}
/**
* Removes the specified shard map.
*
* @param shardMap
* ShardMap to be removed.
*/
public void deleteShardMap(ShardMap shardMap) {
this.validateShardMap(shardMap);
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManager DeleteShardMap Start; ShardMap: {}", shardMap.getName());
Stopwatch stopwatch = Stopwatch.createStarted();
this.removeShardMapFromStore(shardMap.getStoreShardMap());
stopwatch.stop();
log.info("ShardMapManager DeleteShardMap Complete; ShardMap: {}; Duration: {}", shardMap.getName(),
stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
}
/**
* Obtains all shard maps associated with the shard map manager.
*
* @return Collection of shard maps associated with the shard map manager.
*/
public List<ShardMap> getShardMaps() {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManager GetShardMaps Start; ");
Stopwatch stopwatch = Stopwatch.createStarted();
List<ShardMap> result = this.getShardMapsFromStore();
stopwatch.stop();
log.info("ShardMapManager GetShardMaps Complete; Duration: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
return result;
}
}
/**
* Obtains a <see cref="ShardMap"/> given the name.
*
* @param shardMapName
* Name of shard map.
* @return ShardMap with the specificed name.
*/
public ShardMap getShardMap(String shardMapName) {
ShardMapManager.validateShardMapName(shardMapName);
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManager GetShardMap Start; ShardMap: {}", shardMapName);
ShardMap shardMap = this.<ShardMap>lookupAndConvertShardMapHelper("GetShardMap", shardMapName, null, true);
assert shardMap != null;
log.info("ShardMapManager GetShardMap Complete; ShardMap: {}", shardMapName);
return shardMap;
}
}
/**
* Tries to obtains a <see cref="ShardMap"/> given the name.
*
* @param shardMapName
* Name of shard map.
* @param shardMap
* Shard map with the specified name.
* @return <c>true</c> if shard map with the specified name was found, <c>false</c> otherwise.
*/
public boolean tryGetShardMap(String shardMapName,
ShardKeyType keyType,
ReferenceObjectHelper<ShardMap> shardMap) {
ShardMapManager.validateShardMapName(shardMapName);
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManager TryGetShardMap Start; ShardMap: {}", shardMapName);
shardMap.argValue = this.<ShardMap>lookupAndConvertShardMapHelper("TryGetShardMap", shardMapName, keyType, false);
log.info("ShardMapManager TryGetShardMap Complete; ShardMap: {}", shardMapName);
return shardMap.argValue != null;
}
}
/**
* Obtains a <see cref="ListShardMap{KeyT}"/> given the name. <typeparam name="KeyT">Key type.</typeparam>
*
* @param shardMapName
* Name of shard map.
* @return Resulting ShardMap.
*/
public <KeyT> ListShardMap<KeyT> getListShardMap(String shardMapName,
ShardKeyType keyType) {
ShardMapManager.validateShardMapName(shardMapName);
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManager GetListShardMap Start; ShardMap: {}", shardMapName);
ListShardMap<KeyT> shardMap = ShardMapExtensions
.asListShardMap(this.<ListShardMap<KeyT>>lookupAndConvertShardMapHelper("GetListShardMap", shardMapName, keyType, true));
assert shardMap != null;
log.info("ShardMapManager GetListShardMap Complete; ShardMap: {}", shardMapName);
return shardMap;
}
}
/**
* Tries to obtains a <see cref="ListShardMap{KeyT}"/> given the name. <typeparam name="KeyT">Key type.</typeparam>
*
* @param shardMapName
* Name of shard map.
* @return ListShardMap
*/
public <KeyT> boolean tryGetListShardMap(String shardMapName,
ShardKeyType keyType,
ReferenceObjectHelper<ListShardMap<KeyT>> shardMap) {
ShardMapManager.validateShardMapName(shardMapName);
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManager TryGetListShardMap Start; ShardMap: {}", shardMapName);
shardMap.argValue = (ListShardMap<KeyT>) this.lookupAndConvertShardMapHelper("TryGetListShardMap", shardMapName, keyType, false);
log.info("Complete; ShardMap: {}", shardMapName);
return shardMap.argValue != null;
}
}
/**
* Obtains a <see cref="RangeShardMap{KeyT}"/> given the name. <typeparam name="KeyT">Key type.</typeparam>
*
* @param shardMapName
* Name of shard map.
* @return Resulting ShardMap.
*/
public <KeyT> RangeShardMap<KeyT> getRangeShardMap(String shardMapName,
ShardKeyType keyType) {
ShardMapManager.validateShardMapName(shardMapName);
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManager GetRangeShardMap Start; ShardMap: {}", shardMapName);
RangeShardMap<KeyT> shardMap = ShardMapExtensions
.asRangeShardMap(this.lookupAndConvertShardMapHelper("GetRangeShardMap", shardMapName, keyType, true));
assert shardMap != null;
log.info("ShardMapManager GetRangeShardMap Complete; ShardMap: {}", shardMapName);
return shardMap;
}
}
/**
* Tries to obtains a <see cref="RangeShardMap{KeyT}"/> given the name.
*
* @param shardMapName
* Name of shard map.
* @return RangeShardMap
*/
public <KeyT> boolean tryGetRangeShardMap(String shardMapName,
ShardKeyType keyType,
ReferenceObjectHelper<RangeShardMap<KeyT>> shardMap) {
validateShardMapName(shardMapName);
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManager TryGetRangeShardMap Start; ShardMap: {}", shardMapName);
shardMap.argValue = (RangeShardMap<KeyT>) this.lookupAndConvertShardMapHelper("TryGetRangeShardMap", shardMapName, keyType, false);
log.info("Complete; ShardMap: {}", shardMapName);
return shardMap.argValue != null;
}
}
/**
* Obtains distinct shard locations from the shard map manager.
*
* @return Collection of shard locations associated with the shard map manager.
*/
public List<ShardLocation> getDistinctShardLocations() {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManager GetDistinctShardLocations Start; ");
Stopwatch stopwatch = Stopwatch.createStarted();
List<ShardLocation> result = this.getDistinctShardLocationsFromStore();
stopwatch.stop();
log.info("ShardMapManager GetDistinctShardLocations Complete; Duration: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
return result;
}
}
/**
* Upgrades store hosting global shard map to the latest version supported by library.
*/
public void upgradeGlobalStore() {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManager UpgradeGlobalShardMapManager Latest Version Start; ");
Stopwatch stopwatch = Stopwatch.createStarted();
this.upgradeStoreGlobal(GlobalConstants.GsmVersionClient);
stopwatch.stop();
log.info("ShardMapManager UpgradeGlobalShardMapManager Complete; Duration: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
}
/**
* Upgrades store hosting global shard map to specified version. This will be used for upgrade testing.
*
* @param targetVersion
* Target store version to deploy.
*/
public void upgradeGlobalStore(Version targetVersion) {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManager UpgradeGlobalShardMapManager Start; Version : {}", targetVersion);
Stopwatch stopwatch = Stopwatch.createStarted();
this.upgradeStoreGlobal(targetVersion);
stopwatch.stop();
log.info("ShardMapManager UpgradeGlobalShardMapManager Complete; Duration: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
}
/**
* Upgrades store location to the specified version. This will be used for upgrade testing.
*
* @param location
* Shard location to upgrade.
* @param targetVersion
* Target store version to deploy.
*/
public void upgradeLocalStore(ShardLocation location,
Version targetVersion) {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManager UpgradeGlobalShardMapManager Start; ");
Stopwatch stopwatch = Stopwatch.createStarted();
this.upgradeStoreLocal(location, targetVersion);
stopwatch.stop();
log.info("ShardMapManager UpgradeGlobalShardMapManager Complete; Duration: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
}
/**
* Upgrades store location to the latest version supported by library.
*
* @param location
* Shard location to upgrade.
*/
public void upgradeLocalStore(ShardLocation location) {
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManager UpgradeGlobalShardMapManager Start; ");
Stopwatch stopwatch = Stopwatch.createStarted();
this.upgradeStoreLocal(location, GlobalConstants.LsmVersionClient);
stopwatch.stop();
log.info("ShardMapManager UpgradeGlobalShardMapManager Complete; Duration: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
}
/**
* Obtains the recovery manager for the current shard map manager instance.
*
* @return Recovery manager for the shard map manager.
*/
public RecoveryManager getRecoveryManager() {
return new RecoveryManager(this);
}
/**
* Obtains the schema info collection object for the current shard map manager instance.
*
* @return schema info collection for shard map manager.
*/
public SchemaInfoCollection getSchemaInfoCollection() {
return new SchemaInfoCollection(this);
}
/**
* Finds a shard map from cache if requested and if necessary from global shard map.
*
* @param operationName
* Operation name, useful for diagnostics.
* @param shardMapName
* Name of shard map.
* @param lookInCacheFirst
* Whether to skip first lookup in cache.
* @return Shard map object corresponding to one being searched.
*/
public ShardMap lookupShardMapByName(String operationName,
String shardMapName,
boolean lookInCacheFirst) {
StoreShardMap ssm = null;
if (lookInCacheFirst) {
// Typical scenario will result in immediate lookup succeeding.
ssm = this.getCache().lookupShardMapByName(shardMapName);
}
ShardMap shardMap;
// Cache miss. Go to store and add entry to cache.
if (ssm == null) {
Stopwatch stopwatch = Stopwatch.createStarted();
shardMap = this.lookupShardMapByNameInStore(operationName, shardMapName);
stopwatch.stop();
log.info("Lookup ShardMap: {} in store complete; Duration: {}", shardMapName, stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
else {
shardMap = ShardMapUtils.createShardMapFromStoreShardMap(this, ssm);
}
return shardMap;
}
/**
* Subscriber function to RetryPolicy.retrying event.
*
* @param sender
* Sender object (RetryPolicy)
* @param arg
* Event argument.
*/
public void shardMapManagerRetryingEventHandler(Object sender,
RetryingEventArgs arg) {
// Trace out retry event.
log.info("ShardMapManager ShardMapManagerRetryingEvent Retry Count: {}; Delay: {}", arg.getCurrentRetryCount(), arg.getDelay());
this.onShardMapManagerRetryingEvent(new RetryingEventArgs(arg));
}
/**
* Publisher for ShardMapManagerRetryingEvent event.
*
* @param arg
* Event argument.
*/
public void onShardMapManagerRetryingEvent(RetryingEventArgs arg) {
if (shardMapManagerRetrying != null) {
shardMapManagerRetrying.listeners().forEach(e -> e.invoke(this, arg));
}
}
/**
* Performs lookup and conversion operation for shard map with given name. <typeparam name="TShardMap">Type to convert shard map to.</typeparam>
*
* @param operationName
* Operation name, useful for diagnostics.
* @param shardMapName
* Shard map name.
* @param throwOnFailure
* Whether to throw exception or return null on failure.
* @return The converted shard map.
*/
private ShardMap lookupAndConvertShardMapHelper(String operationName,
String shardMapName,
ShardKeyType keyType,
boolean throwOnFailure) {
ShardMap sm = this.lookupShardMapByName(operationName, shardMapName, true);
if (throwOnFailure) {
if (sm != null && keyType != null && !sm.getKeyType().equals(keyType)) {
throw ShardMapExtensions.getConversionException(sm.getStoreShardMap(), sm.getKeyType().name(), keyType.name());
}
if (sm == null) {
throw new ShardManagementException(ShardManagementErrorCategory.ShardMapManager, ShardManagementErrorCode.ShardMapLookupFailure,
Errors._ShardMapManager_ShardMapLookupFailed, shardMapName, this.credentials.getShardMapManagerLocation());
}
}
return sm;
}
/**
* Loads the shard map manager and shards from Store.
*/
private void loadFromStore() {
this.getCache().clear();
try (IStoreOperationGlobal op = this.getStoreOperationFactory().createLoadShardMapManagerGlobalOperation(this, "GetShardMapManager")) {
op.doGlobal();
}
catch (Exception e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
}
/**
* Adds a shard to global shard map.
*
* @param operationName
* Operation name, useful for diagnostics.
* @param ssm
* Storage representation of shard map object.
*/
private void addShardMapToStore(String operationName,
StoreShardMap ssm) throws ShardManagementException {
try (IStoreOperationGlobal op = this.getStoreOperationFactory().createAddShardMapGlobalOperation(this, operationName, ssm)) {
op.doGlobal();
}
catch (Exception e) {
ExceptionUtils.throwStronglyTypedException(e);
}
}
/**
* Removes a shard map from global shard map.
*
* @param ssm
* Shard map to remove.
*/
private void removeShardMapFromStore(StoreShardMap ssm) {
try (IStoreOperationGlobal op = this.getStoreOperationFactory().createRemoveShardMapGlobalOperation(this, "DeleteShardMap", ssm)) {
op.doGlobal();
}
catch (Exception e) {
ExceptionUtils.throwStronglyTypedException(e);
}
}
/**
* Obtains all ShardMaps associated with the shard map manager.
*
* @return Collection of shard maps associated with the shard map manager.
*/
private List<ShardMap> getShardMapsFromStore() {
StoreResults result;
try (IStoreOperationGlobal op = this.getStoreOperationFactory().createGetShardMapsGlobalOperation(this, "GetShardMaps")) {
result = op.doGlobal();
}
catch (Exception e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
assert result != null;
return result.getStoreShardMaps().stream().map(ssm -> ShardMapUtils.createShardMapFromStoreShardMap(this, ssm)).collect(Collectors.toList());
}
/**
* Get distinct locations for the shard map manager from store.
*
* @return Distinct locations from shard map manager.
*/
private List<ShardLocation> getDistinctShardLocationsFromStore() {
StoreResults result;
try (IStoreOperationGlobal op = this.getStoreOperationFactory().createGetDistinctShardLocationsGlobalOperation(this,
"GetDistinctShardLocations")) {
result = op.doGlobal();
}
catch (Exception e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
assert result != null;
return result.getStoreLocations();
}
/**
* Upgrades store hosting GSM.
*
* @param targetVersion
* Target version for store to upgrade to.
*/
private void upgradeStoreGlobal(Version targetVersion) {
try (IStoreOperationGlobal op = this.getStoreOperationFactory().createUpgradeStoreGlobalOperation(this, "UpgradeStoreGlobal",
targetVersion)) {
op.doGlobal();
}
catch (Exception e) {
ExceptionUtils.throwStronglyTypedException(e);
}
}
/**
* Upgrades store at specified location.
*
* @param location
* Store location to upgrade.
* @param targetVersion
* Target version for store to upgrade to.
*/
private void upgradeStoreLocal(ShardLocation location,
Version targetVersion) {
try (IStoreOperationLocal op = this.getStoreOperationFactory().createUpgradeStoreLocalOperation(this, location, "UpgradeStoreLocal",
targetVersion)) {
op.doLocal();
}
catch (IOException e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
}
/**
* Finds shard map with given name in global shard map.
*
* @param operationName
* Operation name, useful for diagnostics.
* @param shardMapName
* Name of shard map to search.
* @return Shard map corresponding to given Id.
*/
private ShardMap lookupShardMapByNameInStore(String operationName,
String shardMapName) {
StoreResults result;
try (IStoreOperationGlobal op = this.getStoreOperationFactory().createFindShardMapByNameGlobalOperation(this, operationName, shardMapName)) {
result = op.doGlobal();
return result.getStoreShardMaps().stream().map(ssm -> ShardMapUtils.createShardMapFromStoreShardMap(this, ssm)).findFirst().orElse(null);
}
catch (Exception e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
}
/**
* Validates the input shard map. This includes: Ensuring that shard map belongs to this instance of shard map manager.
*
* @param shardMap
* Input shard map.
*/
private void validateShardMap(ShardMap shardMap) {
ExceptionUtils.disallowNullArgument(shardMap, "shardMap");
if (shardMap.getShardMapManager() != this) {
throw new IllegalStateException(String.format(Errors._ShardMapManager_DifferentShardMapManager, shardMap.getName(),
this.getCredentials().getShardMapManagerLocation()));
}
}
}

Просмотреть файл

@ -0,0 +1,52 @@
package com.microsoft.azure.elasticdb.shard.mapmanager;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Describes the creation options for shard map manager storage representation.
*/
public enum ShardMapManagerCreateMode {
/**
* If the shard map manager data structures are already present in the store, then this method will raise exception.
*/
KeepExisting(0),
/**
* If the shard map manager data structures are already present in the store, then this method will overwrite them.
*/
ReplaceExisting(1);
public static final int SIZE = java.lang.Integer.SIZE;
private static java.util.HashMap<Integer, ShardMapManagerCreateMode> mappings;
private int intValue;
ShardMapManagerCreateMode(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, ShardMapManagerCreateMode> getMappings() {
if (mappings == null) {
synchronized (ShardMapManagerCreateMode.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static ShardMapManagerCreateMode forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,436 @@
package com.microsoft.azure.elasticdb.shard.mapmanager;
import java.lang.invoke.MethodHandles;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.google.common.base.Stopwatch;
import com.microsoft.azure.elasticdb.core.commons.helpers.EventHandler;
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
import com.microsoft.azure.elasticdb.core.commons.logging.ActivityIdScope;
import com.microsoft.azure.elasticdb.core.commons.transientfaulthandling.RetryBehavior;
import com.microsoft.azure.elasticdb.core.commons.transientfaulthandling.RetryPolicy;
import com.microsoft.azure.elasticdb.core.commons.transientfaulthandling.RetryingEventArgs;
import com.microsoft.azure.elasticdb.shard.cache.CacheStore;
import com.microsoft.azure.elasticdb.shard.sqlstore.SqlShardMapManagerCredentials;
import com.microsoft.azure.elasticdb.shard.sqlstore.SqlStoreConnectionFactory;
import com.microsoft.azure.elasticdb.shard.store.StoreResult;
import com.microsoft.azure.elasticdb.shard.store.StoreResults;
import com.microsoft.azure.elasticdb.shard.store.Version;
import com.microsoft.azure.elasticdb.shard.storeops.base.IStoreOperationGlobal;
import com.microsoft.azure.elasticdb.shard.storeops.base.StoreOperationFactory;
import com.microsoft.azure.elasticdb.shard.utils.Errors;
import com.microsoft.azure.elasticdb.shard.utils.ExceptionUtils;
import com.microsoft.azure.elasticdb.shard.utils.GlobalConstants;
import com.microsoft.azure.elasticdb.shard.utils.StringUtilsLocal;
/**
* Factory for <see cref="ShardMapManager"/>s facilitates the creation and management of shard map manager persistent state. Use this class as the
* entry point to the library's object hierarchy.
*/
public final class ShardMapManagerFactory {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* Creates a <see cref="ShardMapManager"/> and its corresponding storage structures in the specified SQL Server database, with
* <see cref="ShardMapManagerCreateMode.KeepExisting"/> and <see cref="RetryBehavior.DefaultRetryBehavior"/>.
*
* @param connectionString
* Connection parameters used for creating shard map manager database.
* @return A shard map manager object used for performing management and read operations for shard maps, shards and shard mappings.
*/
public static ShardMapManager createSqlShardMapManager(String connectionString) {
return createSqlShardMapManager(connectionString, ShardMapManagerCreateMode.KeepExisting, RetryBehavior.getDefaultRetryBehavior());
}
/**
* Creates a <see cref="ShardMapManager"/> and its corresponding storage structures in the specified SQL Server database, with
* <see cref="RetryBehavior.DefaultRetryBehavior"/>.
*
* @param connectionString
* Connection parameters used for creating shard map manager database.
* @param createMode
* Describes the option selected by the user for creating shard map manager database.
* @return A shard map manager object used for performing management and read operations for shard maps, shards and shard mappings.
*/
public static ShardMapManager createSqlShardMapManager(String connectionString,
ShardMapManagerCreateMode createMode) {
return createSqlShardMapManager(connectionString, createMode, RetryBehavior.getDefaultRetryBehavior());
}
/**
* Creates a <see cref="ShardMapManager"/> and its corresponding storage structures in the specified SQL Server database, with
* <see cref="RetryPolicy.getDefaultRetryPolicy()"/>.
*
* @param connectionString
* Connection parameters used for creating shard map manager database.
* @param createMode
* Describes the option selected by the user for creating shard map manager database.
* @param targetVersion
* Target version of store to create.
*/
public static ShardMapManager createSqlShardMapManager(String connectionString,
ShardMapManagerCreateMode createMode,
Version targetVersion) {
return createSqlShardMapManagerImpl(connectionString, createMode, RetryBehavior.getDefaultRetryBehavior(), null, targetVersion);
}
/**
* Creates a <see cref="ShardMapManager"/> and its corresponding storage structures in the specified SQL Server database, with
* <see cref="ShardMapManagerCreateMode.KeepExisting"/>.
*
* @param connectionString
* Connection parameters used for creating shard map manager database.
* @param retryBehavior
* Behavior for detecting transient exceptions in the store.
* @return A shard map manager object used for performing management and read operations for shard maps, shards and shard mappings.
*/
public static ShardMapManager createSqlShardMapManager(String connectionString,
RetryBehavior retryBehavior) {
return createSqlShardMapManager(connectionString, ShardMapManagerCreateMode.KeepExisting, retryBehavior);
}
/**
* Creates a <see cref="ShardMapManager"/> and its corresponding storage structures in the specified SQL Server database.
*
* @param connectionString
* Connection parameters used for creating shard map manager database.
* @param createMode
* Describes the option selected by the user for creating shard map manager database.
* @param retryBehavior
* Behavior for detecting transient exceptions in the store.
* @return A shard map manager object used for performing management and read operations for shard maps, shards and shard mappings.
*/
public static ShardMapManager createSqlShardMapManager(String connectionString,
ShardMapManagerCreateMode createMode,
RetryBehavior retryBehavior) {
return createSqlShardMapManagerImpl(connectionString, createMode, retryBehavior, null, GlobalConstants.GsmVersionClient);
}
/**
* Creates a <see cref="ShardMapManager"/> and its corresponding storage structures in the specified SQL Server database.
*
* @param connectionString
* Connection parameters used for creating shard map manager database.
* @param createMode
* Describes the option selected by the user for creating shard map manager database.
* @param retryBehavior
* Behavior for detecting transient exceptions in the store.
* @param retryEventHandler
* Event handler for store operation retry events.
* @return A shard map manager object used for performing management and read operations for shard maps, shards and shard mappings.
*/
public static ShardMapManager createSqlShardMapManager(String connectionString,
ShardMapManagerCreateMode createMode,
RetryBehavior retryBehavior,
EventHandler<RetryingEventArgs> retryEventHandler) {
return createSqlShardMapManagerImpl(connectionString, createMode, retryBehavior, retryEventHandler, GlobalConstants.GsmVersionClient);
}
/**
* Creates a <see cref="ShardMapManager"/> and its corresponding storage structures in the specified SQL Server database.
*
* @param connectionString
* Connection parameters used for creating shard map manager database.
* @param createMode
* Describes the option selected by the user for creating shard map manager database.
* @param retryBehavior
* Behavior for performing retries on connections to shard map manager database.
* @param targetVersion
* Target version of Store to deploy, this is mainly used for upgrade testing.
* @param retryEventHandler
* Event handler for store operation retry events.
* @return A shard map manager object used for performing management and read operations for shard maps, shards and shard mappings.
*/
private static ShardMapManager createSqlShardMapManagerImpl(String connectionString,
ShardMapManagerCreateMode createMode,
RetryBehavior retryBehavior,
EventHandler<RetryingEventArgs> retryEventHandler,
Version targetVersion) {
ExceptionUtils.disallowNullArgument(connectionString, "connectionString");
ExceptionUtils.disallowNullArgument(retryBehavior, "retryBehavior");
if (createMode != ShardMapManagerCreateMode.KeepExisting && createMode != ShardMapManagerCreateMode.ReplaceExisting) {
throw new IllegalArgumentException(StringUtilsLocal.formatInvariant(Errors._General_InvalidArgumentValue, createMode, "createMode"),
new Throwable("createMode"));
}
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManagerFactory CreateSqlShardMapManager Start; ");
Stopwatch stopwatch = Stopwatch.createStarted();
SqlShardMapManagerCredentials credentials = new SqlShardMapManagerCredentials(connectionString);
RetryPolicy retryPolicy = new RetryPolicy(new ShardManagementTransientErrorDetectionStrategy(retryBehavior),
RetryPolicy.getDefaultRetryPolicy().getExponentialRetryStrategy());
EventHandler<RetryingEventArgs> handler = (sender,
args) -> {
if (retryEventHandler != null) {
retryEventHandler.invoke(sender, new RetryingEventArgs(args));
}
};
try {
retryPolicy.retrying.addListener(handler);
// specifying targetVersion as GlobalConstants.GsmVersionClient
// to deploy latest store by default.
try (IStoreOperationGlobal op = (new StoreOperationFactory()).createCreateShardMapManagerGlobalOperation(credentials, retryPolicy,
"CreateSqlShardMapManager", createMode, targetVersion)) {
op.doGlobal();
}
catch (Exception e) {
e.printStackTrace();
ExceptionUtils.throwStronglyTypedException(e);
}
stopwatch.stop();
log.info("ShardMapManagerFactory CreateSqlShardMapManager Complete; Duration:{}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
finally {
retryPolicy.retrying.removeListener(handler);
}
return new ShardMapManager(credentials, new SqlStoreConnectionFactory(), new StoreOperationFactory(), new CacheStore(),
ShardMapManagerLoadPolicy.Lazy, RetryPolicy.getDefaultRetryPolicy(), retryBehavior, retryEventHandler);
}
}
/**
* Gets <see cref="ShardMapManager"/> from persisted state in a SQL Server database.
*
* @param connectionString
* Connection parameters used for performing operations against shard map manager database(s).
* @param loadPolicy
* Initialization policy.
* @param shardMapManager
* Shard map manager object used for performing management and read operations for shard maps, shards and shard mappings or <c>null</c>
* in case shard map manager does not exist.
* @return <c>true</c> if a shard map manager object was created, <c>false</c> otherwise.
*/
public static boolean tryGetSqlShardMapManager(String connectionString,
ShardMapManagerLoadPolicy loadPolicy,
ReferenceObjectHelper<ShardMapManager> shardMapManager) {
return tryGetSqlShardMapManager(connectionString, loadPolicy, RetryBehavior.getDefaultRetryBehavior(), shardMapManager);
}
/**
* Gets <see cref="ShardMapManager"/> from persisted state in a SQL Server database.
*
* @param connectionString
* Connection parameters used for performing operations against shard map manager database(s).
* @param loadPolicy
* Initialization policy.
* @param shardMapManager
* Shard map manager object used for performing management and read operations for shard maps, shards and shard mappings or <c>null</c>
* in case shard map manager does not exist.
* @param retryBehavior
* Behavior for detecting transient exceptions in the store.
* @return <c>true</c> if a shard map manager object was created, <c>false</c> otherwise.
*/
public static boolean tryGetSqlShardMapManager(String connectionString,
ShardMapManagerLoadPolicy loadPolicy,
RetryBehavior retryBehavior,
ReferenceObjectHelper<ShardMapManager> shardMapManager) {
ExceptionUtils.disallowNullArgument(connectionString, "connectionString");
ExceptionUtils.disallowNullArgument(retryBehavior, "retryBehavior");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManagerFactory TryGetSqlShardMapManager Start; ");
Stopwatch stopwatch = Stopwatch.createStarted();
shardMapManager.argValue = ShardMapManagerFactory.getSqlShardMapManager(connectionString, loadPolicy, retryBehavior, null, false);
stopwatch.stop();
log.info("ShardMapManagerFactory TryGetSqlShardMapManager Complete; Duration:{}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
return shardMapManager.argValue != null;
}
}
/**
* Gets <see cref="ShardMapManager"/> from persisted state in a SQL Server database.
*
* @param connectionString
* Connection parameters used for performing operations against shard map manager database(s).
* @param loadPolicy
* Initialization policy.
* @param shardMapManager
* Shard map manager object used for performing management and read operations for shard maps, shards and shard mappings or <c>null</c>
* in case shard map manager does not exist.
* @param retryBehavior
* Behavior for detecting transient exceptions in the store.
* @param retryEventHandler
* Event handler for store operation retry events.
* @return <c>true</c> if a shard map manager object was created, <c>false</c> otherwise.
*/
public static boolean tryGetSqlShardMapManager(String connectionString,
ShardMapManagerLoadPolicy loadPolicy,
RetryBehavior retryBehavior,
EventHandler<RetryingEventArgs> retryEventHandler,
ReferenceObjectHelper<ShardMapManager> shardMapManager) {
ExceptionUtils.disallowNullArgument(connectionString, "connectionString");
ExceptionUtils.disallowNullArgument(retryBehavior, "retryBehavior");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManagerFactory TryGetSqlShardMapManager Start; ");
Stopwatch stopwatch = Stopwatch.createStarted();
shardMapManager.argValue = ShardMapManagerFactory.getSqlShardMapManager(connectionString, loadPolicy, retryBehavior, retryEventHandler,
false);
stopwatch.stop();
log.info("ShardMapManagerFactory TryGetSqlShardMapManager Complete; Duration:{}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
return shardMapManager.argValue != null;
}
}
/**
* Gets <see cref="ShardMapManager"/> from persisted state in a SQL Server database, with <see cref="RetryBehavior.DefaultRetryBehavior"/>.
*
* @param connectionString
* Connection parameters used for performing operations against shard map manager database(s).
* @param loadPolicy
* Initialization policy.
* @return A shard map manager object used for performing management and read operations for shard maps, shards and shard mappings.
*/
public static ShardMapManager getSqlShardMapManager(String connectionString,
ShardMapManagerLoadPolicy loadPolicy) {
return getSqlShardMapManager(connectionString, loadPolicy, RetryBehavior.getDefaultRetryBehavior());
}
/**
* Gets <see cref="ShardMapManager"/> from persisted state in a SQL Server database.
*
* @param connectionString
* Connection parameters used for performing operations against shard map manager database(s).
* @param loadPolicy
* Initialization policy.
* @param retryBehavior
* Behavior for detecting transient exceptions in the store.
* @return A shard map manager object used for performing management and read operations for shard maps, shards and shard mappings.
*/
public static ShardMapManager getSqlShardMapManager(String connectionString,
ShardMapManagerLoadPolicy loadPolicy,
RetryBehavior retryBehavior) {
return getSqlShardMapManager(connectionString, loadPolicy, retryBehavior, null);
}
/**
* Gets <see cref="ShardMapManager"/> from persisted state in a SQL Server database.
*
* @param connectionString
* Connection parameters used for performing operations against shard map manager database(s).
* @param loadPolicy
* Initialization policy.
* @param retryBehavior
* Behavior for detecting transient exceptions in the store.
* @param retryEventHandler
* Event handler for store operation retry events.
* @return A shard map manager object used for performing management and read operations for shard maps, shards and shard mappings.
*/
public static ShardMapManager getSqlShardMapManager(String connectionString,
ShardMapManagerLoadPolicy loadPolicy,
RetryBehavior retryBehavior,
EventHandler<RetryingEventArgs> retryEventHandler) {
ExceptionUtils.disallowNullArgument(connectionString, "connectionString");
ExceptionUtils.disallowNullArgument(retryBehavior, "retryBehavior");
try (ActivityIdScope activityIdScope = new ActivityIdScope(UUID.randomUUID())) {
log.info("ShardMapManagerFactory GetSqlShardMapManager Start; ");
Stopwatch stopwatch = Stopwatch.createStarted();
ShardMapManager shardMapManager = ShardMapManagerFactory.getSqlShardMapManager(connectionString, loadPolicy, retryBehavior,
retryEventHandler, true);
stopwatch.stop();
assert shardMapManager != null;
log.info("ShardMapManagerFactory GetSqlShardMapManager Complete; Duration: {}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
return shardMapManager;
}
}
/**
* Gets <see cref="ShardMapManager"/> from persisted state in a SQL Server database.
*
* @param connectionString
* Connection parameters used for performing operations against shard map manager database(s).
* @param loadPolicy
* Initialization policy.
* @param retryBehavior
* Behavior for detecting transient exceptions in the store.
* @param retryEventHandler
* Event handler for store operation retry events.
* @param throwOnFailure
* Whether to raise exception on failure.
* @return A shard map manager object used for performing management and read operations for shard maps, shards and shard mappings or <c>null</c>
* if the object could not be created.
*/
private static ShardMapManager getSqlShardMapManager(String connectionString,
ShardMapManagerLoadPolicy loadPolicy,
RetryBehavior retryBehavior,
EventHandler<RetryingEventArgs> retryEventHandler,
boolean throwOnFailure) {
assert connectionString != null;
assert retryBehavior != null;
SqlShardMapManagerCredentials credentials = new SqlShardMapManagerCredentials(connectionString);
StoreOperationFactory storeOperationFactory = new StoreOperationFactory();
StoreResults result;
RetryPolicy retryPolicy = new RetryPolicy(new ShardManagementTransientErrorDetectionStrategy(retryBehavior),
RetryPolicy.getDefaultRetryPolicy().getExponentialRetryStrategy());
EventHandler<RetryingEventArgs> handler = (sender,
args) -> {
if (retryEventHandler != null) {
retryEventHandler.invoke(sender, new RetryingEventArgs(args));
}
};
try {
retryPolicy.retrying.addListener(handler);
try (IStoreOperationGlobal op = storeOperationFactory.createGetShardMapManagerGlobalOperation(credentials, retryPolicy,
throwOnFailure ? "GetSqlShardMapManager" : "TryGetSqlShardMapManager", throwOnFailure)) {
result = op.doGlobal();
}
catch (Exception e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
}
finally {
retryPolicy.retrying.removeListener(handler);
}
return result.getResult() == StoreResult.Success ? new ShardMapManager(credentials, new SqlStoreConnectionFactory(), storeOperationFactory,
new CacheStore(), loadPolicy, RetryPolicy.getDefaultRetryPolicy(), retryBehavior, retryEventHandler) : null;
}
}

Просмотреть файл

@ -0,0 +1,52 @@
package com.microsoft.azure.elasticdb.shard.mapmanager;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
/**
* Describes the policy used for initialization of <see cref="ShardMapManager"/> from the store.
*/
public enum ShardMapManagerLoadPolicy {
/**
* Load all shard maps and their corresponding mappings into the cache for fast retrieval.
*/
Eager(0),
/**
* Load all shard maps and their corresponding mappings on as needed basis.
*/
Lazy(1);
public static final int SIZE = java.lang.Integer.SIZE;
private static java.util.HashMap<Integer, ShardMapManagerLoadPolicy> mappings;
private int intValue;
ShardMapManagerLoadPolicy(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, ShardMapManagerLoadPolicy> getMappings() {
if (mappings == null) {
synchronized (ShardMapManagerLoadPolicy.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static ShardMapManagerLoadPolicy forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,777 @@
package com.microsoft.azure.elasticdb.shard.mapper;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.sql.Connection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import com.microsoft.azure.elasticdb.core.commons.helpers.ActionGeneric3Param;
import com.microsoft.azure.elasticdb.shard.base.IMappingInfoProvider;
import com.microsoft.azure.elasticdb.shard.base.IMappingUpdate;
import com.microsoft.azure.elasticdb.shard.base.IShardProvider;
import com.microsoft.azure.elasticdb.shard.base.LockOwnerIdOpType;
import com.microsoft.azure.elasticdb.shard.base.LookupOptions;
import com.microsoft.azure.elasticdb.shard.base.MappingKind;
import com.microsoft.azure.elasticdb.shard.base.MappingLockToken;
import com.microsoft.azure.elasticdb.shard.base.MappingUpdatedProperties;
import com.microsoft.azure.elasticdb.shard.base.Range;
import com.microsoft.azure.elasticdb.shard.base.Shard;
import com.microsoft.azure.elasticdb.shard.base.ShardKey;
import com.microsoft.azure.elasticdb.shard.base.ShardRange;
import com.microsoft.azure.elasticdb.shard.cache.CacheStoreMappingUpdatePolicy;
import com.microsoft.azure.elasticdb.shard.cache.ICacheStoreMapping;
import com.microsoft.azure.elasticdb.shard.map.ShardMap;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementErrorCategory;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementErrorCode;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementException;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardMapManager;
import com.microsoft.azure.elasticdb.shard.store.StoreMapping;
import com.microsoft.azure.elasticdb.shard.store.StoreResult;
import com.microsoft.azure.elasticdb.shard.store.StoreResults;
import com.microsoft.azure.elasticdb.shard.store.StoreShard;
import com.microsoft.azure.elasticdb.shard.storeops.base.IStoreOperation;
import com.microsoft.azure.elasticdb.shard.storeops.base.IStoreOperationGlobal;
import com.microsoft.azure.elasticdb.shard.storeops.base.StoreOperationCode;
import com.microsoft.azure.elasticdb.shard.storeops.base.StoreOperationRequestBuilder;
import com.microsoft.azure.elasticdb.shard.utils.Errors;
import com.microsoft.azure.elasticdb.shard.utils.ExceptionUtils;
import com.microsoft.azure.elasticdb.shard.utils.IdLock;
import com.microsoft.azure.elasticdb.shard.utils.StringUtilsLocal;
/**
* Base class for keyed mappers.
*/
public abstract class BaseShardMapper {
protected static final UUID DEFAULT_OWNER = UUID.randomUUID();
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* Reference to ShardMapManager.
*/
protected ShardMapManager shardMapManager;
/**
* Containing shard map.
*/
protected ShardMap shardMap;
/**
* Base shard mapper, which is just a holder of some fields.
*
* @param shardMapManager
* Reference to ShardMapManager.
* @param sm
* Containing shard map.
*/
protected BaseShardMapper(ShardMapManager shardMapManager,
ShardMap sm) {
this.shardMapManager = Preconditions.checkNotNull(shardMapManager);
this.shardMap = Preconditions.checkNotNull(sm);
}
/**
* Sets the status of a shard mapping <typeparam name="MappingT">Mapping type.</typeparam> <typeparam name="UpdateT">Update type.</typeparam>
* <typeparam name="StatusT">Status type.</typeparam>
*
* @param mapping
* Mapping being added.
* @param status
* Status of <paramref name="mapping">mapping</paramref> being added.
* @param getStatus
* Delegate to construct new status from <paramref name="status">input status</paramref>.
* @param createUpdate
* Delegate to construct new update from new status returned by <paramref name="getStatus">getStatus</paramref>.
* @param runUpdate
* Delegate to perform update from the <paramref name="mapping">input mapping</paramref> and the update object returned by
* <paramref name="getStatus">createUpdate </paramref>.
*/
protected static <MappingT, UpdateT, StatusT> MappingT setStatus(MappingT mapping,
StatusT status,
Function<StatusT, StatusT> getStatus,
Function<StatusT, UpdateT> createUpdate,
ActionGeneric3Param<MappingT, UpdateT, UUID, MappingT> runUpdate) {
return setStatus(mapping, status, getStatus, createUpdate, runUpdate, DEFAULT_OWNER);
}
/**
* Sets the status of a shard mapping <typeparam name="MappingT">Mapping type.</typeparam> <typeparam name="UpdateT">Update type.</typeparam>
* <typeparam name="StatusT">Status type.</typeparam>
*
* @param mapping
* Mapping being added.
* @param status
* Status of <paramref name="mapping">mapping</paramref> being added.
* @param getStatus
* Delegate to construct new status from <paramref name="status">input status</paramref>.
* @param createUpdate
* Delegate to construct new update from new status returned by <paramref name="getStatus">getStatus</paramref>.
* @param runUpdate
* Delegate to perform update from the <paramref name="mapping">input mapping</paramref> and the update object returned by
* <paramref name="getStatus">createUpdate </paramref>.
* @param lockOwnerId
* Lock owner id of this mapping
*/
protected static <MappingT, UpdateT, StatusT> MappingT setStatus(MappingT mapping,
StatusT status,
Function<StatusT, StatusT> getStatus,
Function<StatusT, UpdateT> createUpdate,
ActionGeneric3Param<MappingT, UpdateT, UUID, MappingT> runUpdate,
UUID lockOwnerId) {
StatusT newStatus = getStatus.apply(status);
UpdateT update = createUpdate.apply(newStatus);
return runUpdate.invoke(mapping, update, lockOwnerId);
}
protected final ShardMapManager getShardMapManager() {
return shardMapManager;
}
protected final ShardMap getShardMap() {
return shardMap;
}
/**
* Given a key value, obtains a SqlConnection to the shard in the mapping that contains the key value. <typeparam name="MappingT">Mapping
* type.</typeparam> <typeparam name="KeyT">Key type.</typeparam>
*
* @param key
* Input key value.
* @param constructMapping
* Delegate to construct a mapping object.
* @param errorCategory
* Error category.
* @param connectionString
* Connection string with credential information, the DataSource and Database are obtained from the results of the lookup operation for
* key.
* @return An opened SqlConnection.
*/
protected final <MappingT extends IShardProvider, KeyT> Connection openConnectionForKey(KeyT key,
ActionGeneric3Param<ShardMapManager, ShardMap, StoreMapping, MappingT> constructMapping,
ShardManagementErrorCategory errorCategory,
String connectionString) {
return openConnectionForKey(key, constructMapping, errorCategory, connectionString, ConnectionOptions.Validate);
}
/**
* Given a key value, obtains a SqlConnection to the shard in the mapping that contains the key value. <typeparam name="MappingT">Mapping
* type.</typeparam> <typeparam name="KeyT">Key type.</typeparam>
*
* @param key
* Input key value.
* @param constructMapping
* Delegate to construct a mapping object.
* @param errorCategory
* Error category.
* @param connectionString
* Connection string with credential information, the DataSource and Database are obtained from the results of the lookup operation for
* key.
* @param options
* Options for validation operations to perform on opened connection.
* @return An opened SqlConnection.
*/
protected final <MappingT extends IShardProvider, KeyT> Connection openConnectionForKey(KeyT key,
ActionGeneric3Param<ShardMapManager, ShardMap, StoreMapping, MappingT> constructMapping,
ShardManagementErrorCategory errorCategory,
String connectionString,
ConnectionOptions options) {
ShardKey sk = new ShardKey(ShardKey.shardKeyTypeFromType(key.getClass()), key);
// Try to find the mapping within the cache.
ICacheStoreMapping csm = shardMapManager.getCache().lookupMappingByKey(shardMap.getStoreShardMap(), sk);
StoreMapping sm;
if (csm != null) {
sm = csm.getMapping();
}
else {
sm = this.lookupMappingForOpenConnectionForKey(sk, CacheStoreMappingUpdatePolicy.OverwriteExisting, errorCategory);
}
Connection result;
try {
// Initially attempt to connect based on lookup results from either cache or GSM.
result = shardMap.openConnection(constructMapping.invoke(this.getShardMapManager(), this.getShardMap(), sm), connectionString, options);
// Reset TTL on successful connection.
if (csm != null && csm.getTimeToLiveMilliseconds() > 0) {
csm.resetTimeToLive();
}
return result;
}
catch (ShardManagementException ex) {
// If we hit a validation failure due to stale version of mapping,
// we will perform one more attempt.
if (((options.getValue() & ConnectionOptions.Validate.getValue()) == ConnectionOptions.Validate.getValue())
&& ex.getErrorCategory() == ShardManagementErrorCategory.Validation
&& ex.getErrorCode() == ShardManagementErrorCode.MappingDoesNotExist) {
// Assumption here is that this time the attempt should succeed since the cache entry
// has already been either evicted, or updated based on latest data from the server.
sm = this.lookupMappingForOpenConnectionForKey(sk, CacheStoreMappingUpdatePolicy.OverwriteExisting, errorCategory);
result = shardMap.openConnection(constructMapping.invoke(this.getShardMapManager(), this.getShardMap(), sm), connectionString,
options);
return result;
}
else {
// The error was not due to validation but something else e.g.
// 1) Shard map does not exist
// 2) Mapping could not be found.
throw ex;
}
}
catch (Exception e) {
// We failed to connect.
// If we were trying to connect from an entry in cache and mapping expired in cache.
if (csm != null && (System.nanoTime() - csm.getCreationTime()) >= csm.getTimeToLiveMilliseconds()) {
try (IdLock _idLock = new IdLock(csm.getMapping().getStoreShard().getId())) {
// Similar to DCL pattern, we need to refresh the mapping again to see if we still need to
// go to the store to lookup the mapping after acquiring the shard lock. It might be the
// case that a fresh version has already been obtained by some other thread.
csm = shardMapManager.getCache().lookupMappingByKey(shardMap.getStoreShardMap(), sk);
// Only go to store if the mapping is stale even after refresh.
if (csm == null || (System.nanoTime() - csm.getCreationTime()) >= csm.getTimeToLiveMilliseconds()) {
// Refresh the mapping in cache. And try to open the connection after refresh.
sm = this.lookupMappingForOpenConnectionForKey(sk, CacheStoreMappingUpdatePolicy.UpdateTimeToLive, errorCategory);
}
else {
sm = csm.getMapping();
}
}
catch (IOException e1) {
e1.printStackTrace();
}
result = shardMap.openConnection(constructMapping.invoke(this.getShardMapManager(), this.getShardMap(), sm), connectionString,
options);
// Reset TTL on successful connection.
if (csm != null && csm.getTimeToLiveMilliseconds() > 0) {
csm.resetTimeToLive();
}
return result;
}
else {
// Either:
// 1) The mapping is still within the TTL. No refresh.
// 2) Mapping was not in cache, we originally did a lookup for mapping in GSM
// and even then could not connect.
throw e;
}
}
}
protected final <MappingT extends IShardProvider, KeyT> Callable<Connection> openConnectionForKeyAsync(KeyT key,
ActionGeneric3Param<ShardMapManager, ShardMap, StoreMapping, MappingT> constructMapping,
ShardManagementErrorCategory errorCategory,
String connectionString) {
return openConnectionForKeyAsync(key, constructMapping, errorCategory, connectionString, ConnectionOptions.Validate);
}
/**
* Given a key value, asynchronously obtains a SqlConnection to the shard in the mapping that contains the key value.
* <typeparam name="MappingT">Mapping type.</typeparam> <typeparam name="KeyT">Key type.</typeparam>
*
* @param key
* Input key value.
* @param constructMapping
* Delegate to construct a mapping object.
* @param errorCategory
* Error category.
* @param connectionString
* Connection string with credential information, the DataSource and Database are obtained from the results of the lookup operation for
* key.
* @param options
* Options for validation operations to perform on opened connection.
* @return A task encapsulating an opened SqlConnection as the result.
*/
protected final <MappingT extends IShardProvider, KeyT> Callable<Connection> openConnectionForKeyAsync(KeyT key,
ActionGeneric3Param<ShardMapManager, ShardMap, StoreMapping, MappingT> constructMapping,
ShardManagementErrorCategory errorCategory,
String connectionString,
ConnectionOptions options) {
return () -> openConnectionForKey(key, constructMapping, errorCategory, connectionString, options);
}
/**
* Adds a mapping to shard map. <typeparam name="MappingT">Mapping type.</typeparam>
*
* @param mapping
* Mapping being added.
* @param constructMapping
* Delegate to construct a mapping object.
* @return The added mapping object.
*/
protected final <MappingT extends IShardProvider & IMappingInfoProvider> MappingT add(MappingT mapping,
ActionGeneric3Param<ShardMapManager, ShardMap, StoreMapping, MappingT> constructMapping) {
ExceptionUtils.ensureShardBelongsToShardMap(this.getShardMapManager(), this.getShardMap(), mapping.getShardInfo(), "CreateMapping",
mapping.getKind() == MappingKind.PointMapping ? "PointMapping" : "RangeMapping");
this.ensureMappingBelongsToShardMap(mapping, "Add", "mapping");
StoreMapping map = mapping.getStoreMapping();
StoreShard shard = mapping.getShardInfo().getStoreShard();
MappingT newMapping = constructMapping.invoke(this.getShardMapManager(), this.getShardMap(),
new StoreMapping(map.getId(), map.getShardMapId(), map.getMinValue(), map.getMaxValue(), map.getStatus(), map.getLockOwnerId(),
new StoreShard(shard.getId(), UUID.randomUUID(), shard.getShardMapId(), shard.getLocation(), shard.getStatus())));
try (IStoreOperation op = shardMapManager.getStoreOperationFactory().createAddMappingOperation(this.getShardMapManager(),
mapping.getKind() == MappingKind.RangeMapping ? StoreOperationCode.AddRangeMapping : StoreOperationCode.AddPointMapping,
shardMap.getStoreShardMap(), newMapping.getStoreMapping())) {
op.doOperation();
}
catch (Exception e) {
ExceptionUtils.throwStronglyTypedException(e);
}
return newMapping;
}
/**
* Removes a mapping from shard map. <typeparam name="MappingT">Mapping type.</typeparam>
*
* @param mapping
* Mapping being removed.
* @param constructMapping
* Delegate to construct a mapping object.
* @param lockOwnerId
* Lock owner id of this mapping
*/
protected final <MappingT extends IShardProvider & IMappingInfoProvider> void remove(MappingT mapping,
ActionGeneric3Param<ShardMapManager, ShardMap, StoreMapping, MappingT> constructMapping,
UUID lockOwnerId) {
this.ensureMappingBelongsToShardMap(mapping, "Remove", "mapping");
StoreMapping map = mapping.getStoreMapping();
StoreShard shard = mapping.getShardInfo().getStoreShard();
MappingT newMapping = constructMapping.invoke(this.getShardMapManager(), this.getShardMap(),
new StoreMapping(map.getId(), map.getShardMapId(), map.getMinValue(), map.getMaxValue(), map.getStatus(), map.getLockOwnerId(),
new StoreShard(shard.getId(), UUID.randomUUID(), shard.getShardMapId(), shard.getLocation(), shard.getStatus())));
try (IStoreOperation op = shardMapManager.getStoreOperationFactory().createRemoveMappingOperation(this.getShardMapManager(),
mapping.getKind() == MappingKind.RangeMapping ? StoreOperationCode.RemoveRangeMapping : StoreOperationCode.RemovePointMapping,
shardMap.getStoreShardMap(), newMapping.getStoreMapping(), lockOwnerId)) {
op.doOperation();
}
catch (Exception e) {
ExceptionUtils.throwStronglyTypedException(e);
}
}
/**
* Looks up the key value and returns the corresponding mapping. <typeparam name="MappingT">Mapping type.</typeparam> <typeparam name="KeyT">Key
* type.</typeparam>
*
* @param key
* Input key value.
* @param lookupOptions
* Whether to use cache and/or storage for lookups.
* @param constructMapping
* Delegate to construct a mapping object.
* @param errorCategory
* Category under which errors must be thrown.
* @return Mapping that contains the key value.
*/
protected final <MappingT extends IShardProvider, KeyT> MappingT lookup(KeyT key,
LookupOptions lookupOptions,
ActionGeneric3Param<ShardMapManager, ShardMap, StoreMapping, MappingT> constructMapping,
ShardManagementErrorCategory errorCategory) {
ShardKey sk = new ShardKey(ShardKey.shardKeyTypeFromType(key.getClass()), key);
if (lookupOptions.getValue() == 1 || lookupOptions.getValue() == 5) {
ICacheStoreMapping cachedMapping = shardMapManager.getCache().lookupMappingByKey(shardMap.getStoreShardMap(), sk);
if (cachedMapping != null) {
return constructMapping.invoke(this.getShardMapManager(), this.getShardMap(), cachedMapping.getMapping());
}
}
if (lookupOptions.getValue() >= 4) {
Stopwatch stopwatch = Stopwatch.createStarted();
StoreResults gsmResult;
try (IStoreOperationGlobal op = shardMapManager.getStoreOperationFactory().createFindMappingByKeyGlobalOperation(
this.getShardMapManager(), "Lookup", shardMap.getStoreShardMap(), sk, CacheStoreMappingUpdatePolicy.OverwriteExisting,
errorCategory, true, false)) {
gsmResult = op.doGlobal();
}
catch (Exception e) {
ExceptionUtils.throwStronglyTypedException(e);
gsmResult = new StoreResults(); // Ideally this should not be executed.
}
stopwatch.stop();
log.info("Lookup", "Lookup key from GSM complete; Key type : {} Result: {}; Duration: {}", key.getClass(), gsmResult.getResult(),
stopwatch.elapsed(TimeUnit.MILLISECONDS));
// If we could not locate the mapping, we return null and do nothing here.
if (gsmResult.getResult() != StoreResult.MappingNotFoundForKey) {
return gsmResult.getStoreMappings().stream().map(sm -> constructMapping.invoke(this.getShardMapManager(), this.getShardMap(), sm))
.findFirst().orElse(null);
}
}
return null;
}
/**
* Finds mapping in store for OpenConnectionForKey operation.
*
* @param sk
* Key to find.
* @param policy
* Cache update policy.
* @param errorCategory
* Error category.
* @return Mapping corresponding to the given key if found.
*/
private StoreMapping lookupMappingForOpenConnectionForKey(ShardKey sk,
CacheStoreMappingUpdatePolicy policy,
ShardManagementErrorCategory errorCategory) {
StoreResults gsmResult;
Stopwatch stopwatch = Stopwatch.createStarted();
try (IStoreOperationGlobal op = shardMapManager.getStoreOperationFactory().createFindMappingByKeyGlobalOperation(this.getShardMapManager(),
"Lookup", shardMap.getStoreShardMap(), sk, policy, errorCategory, true, false)) {
gsmResult = op.doGlobal();
}
catch (Exception e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
stopwatch.stop();
log.info("LookupMappingForOpenConnectionForKey", "Lookup key from GSM complete; Key type : {} Result: {}; Duration: {}", sk.getDataType(),
gsmResult.getResult(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
// If we could not locate the mapping, we throw.
if (gsmResult.getResult() == StoreResult.MappingNotFoundForKey) {
throw new ShardManagementException(errorCategory, ShardManagementErrorCode.MappingNotFoundForKey,
Errors._Store_ShardMapper_MappingNotFoundForKeyGlobal, shardMap.getName(),
StoreOperationRequestBuilder.SP_FIND_SHARD_MAPPING_BY_KEY_GLOBAL, "LookupMappingForOpenConnectionForKey");
}
else {
return gsmResult.getStoreMappings().get(0);
}
}
/**
* Asynchronously finds the mapping in store for OpenConnectionForKey operation.
*
* @param sk
* Key to find.
* @param policy
* Cache update policy.
* @param errorCategory
* Error category.
* @return Task with the Mapping corresponding to the given key if found as the result.
*/
private Callable<StoreMapping> lookupMappingForOpenConnectionForKeyAsync(ShardKey sk,
CacheStoreMappingUpdatePolicy policy,
ShardManagementErrorCategory errorCategory) {
return () -> lookupMappingForOpenConnectionForKey(sk, policy, errorCategory);
}
/**
* Gets all the mappings that exist within given range.
*
* @param range
* Optional range value, if null, we cover everything.
* @param shard
* Optional shard parameter, if null, we cover all shards.
* @param lookupOptions
* Whether to use cache and/or storage for lookups.
* @param constructMapping
* Delegate to construct a mapping object.
* @param errorCategory
* Category under which errors will be posted.
* @param mappingType
* Name of mapping type.
* @return Read-only collection of mappings that overlap with given range.
*/
protected final <MappingT> List<MappingT> getMappingsForRange(Range range,
Shard shard,
LookupOptions lookupOptions,
ActionGeneric3Param<ShardMapManager, ShardMap, StoreMapping, MappingT> constructMapping,
ShardManagementErrorCategory errorCategory,
String mappingType) {
ShardRange sr = null;
if (shard != null) {
ExceptionUtils.ensureShardBelongsToShardMap(this.getShardMapManager(), this.getShardMap(), shard, "GetMappings", mappingType);
}
if (range != null) {
sr = range.getShardRange();
}
if (lookupOptions.getValue() == 1 || lookupOptions.getValue() == 5) {
List<ICacheStoreMapping> cachedMappings = this.getShardMapManager().getCache()
.lookupMappingsForRange(this.getShardMap().getStoreShardMap(), sr);
if (cachedMappings != null && cachedMappings.size() > 0) {
return Collections.unmodifiableList(
cachedMappings.stream().map(sm -> constructMapping.invoke(this.getShardMapManager(), this.getShardMap(), sm.getMapping()))
.collect(Collectors.toList()));
}
}
if (lookupOptions.getValue() >= 4) {
StoreResults result;
try (IStoreOperationGlobal op = shardMapManager.getStoreOperationFactory().createGetMappingsByRangeGlobalOperation(shardMapManager,
"GetMappingsForRange", shardMap.getStoreShardMap(), shard != null ? shard.getStoreShard() : null, sr, errorCategory, true,
false)) {
result = op.doGlobal();
}
catch (Exception e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
return Collections.unmodifiableList(result.getStoreMappings().stream()
.map(sm -> constructMapping.invoke(this.getShardMapManager(), this.getShardMap(), sm)).collect(Collectors.toList()));
}
return null;
}
/**
* Allows for update to a mapping with the updates provided in the <paramref name="update"/> parameter.
*
* @param currentMapping
* Mapping being updated.
* @param update
* Updated properties of the Shard.
* @param constructMapping
* Delegate to construct a mapping object.
* @param statusAsInt
* Delegate to get the mapping status as an integer value.
* @param intAsStatus
* Delegate to get the mapping status from an integer value.
* @return New instance of mapping with updated information.
*/
protected final <MappingT extends IShardProvider & IMappingInfoProvider, UpdateT extends IMappingUpdate<StatusT>, StatusT> MappingT update(
MappingT currentMapping,
UpdateT update,
ActionGeneric3Param<ShardMapManager, ShardMap, StoreMapping, MappingT> constructMapping,
Function<StatusT, Integer> statusAsInt,
Function<Integer, StatusT> intAsStatus) {
return update(currentMapping, update, constructMapping, statusAsInt, intAsStatus, new UUID(0L, 0L));
}
/**
* Allows for update to a mapping with the updates provided in the <paramref name="update"/> parameter.
*
* @param currentMapping
* Mapping being updated.
* @param update
* Updated properties of the Shard.
* @param constructMapping
* Delegate to construct a mapping object.
* @param statusAsInt
* Delegate to get the mapping status as an integer value.
* @param intAsStatus
* Delegate to get the mapping status from an integer value.
* @param lockOwnerId
* Lock owner id of this mapping
* @return New instance of mapping with updated information.
*/
protected final <MappingT extends IShardProvider & IMappingInfoProvider, UpdateT extends IMappingUpdate<StatusT>, StatusT> MappingT update(
MappingT currentMapping,
UpdateT update,
ActionGeneric3Param<ShardMapManager, ShardMap, StoreMapping, MappingT> constructMapping,
Function<StatusT, Integer> statusAsInt,
Function<Integer, StatusT> intAsStatus,
UUID lockOwnerId) {
assert currentMapping != null;
assert update != null;
this.ensureMappingBelongsToShardMap(currentMapping, "Update", "currentMapping");
IMappingUpdate<StatusT> mu = (update instanceof IMappingUpdate) ? update : null;
// CONSIDER(wbasheer): Have refresh semantics for trivial case when nothing is modified.
if (!mu.isAnyPropertySet(MappingUpdatedProperties.All)) {
return currentMapping;
}
boolean shardChanged = mu.isAnyPropertySet(MappingUpdatedProperties.Shard) && !mu.getShard().equals(currentMapping.getShardInfo());
// Ensure that shard belongs to current shard map.
if (shardChanged) {
ExceptionUtils.ensureShardBelongsToShardMap(this.getShardMapManager(), this.getShardMap(), mu.getShard(), "UpdateMapping",
currentMapping.getKind() == MappingKind.PointMapping ? "PointMapping" : "RangeMapping");
}
Shard shard = currentMapping.getShardInfo();
StoreShard ss = shard.getStoreShard();
StoreShard originalShard = new StoreShard(shard.getId(), UUID.randomUUID(), ss.getShardMapId(), ss.getLocation(), ss.getStatus());
StoreMapping mapping = currentMapping.getStoreMapping();
StoreMapping originalMapping = new StoreMapping(mapping.getId(), currentMapping.getShardMapId(), mapping.getMinValue(), mapping.getMaxValue(),
mapping.getStatus(), lockOwnerId, originalShard);
StoreShard updatedShard;
if (shardChanged) {
shard = update.getShard().getShardInfo();
StoreShard storeShard = shard.getStoreShard();
updatedShard = new StoreShard(shard.getId(), UUID.randomUUID(), storeShard.getShardMapId(), storeShard.getLocation(),
storeShard.getStatus());
}
else {
updatedShard = originalShard;
}
StoreMapping updatedMapping = new StoreMapping(UUID.randomUUID(), currentMapping.getShardMapId(), mapping.getMinValue(),
mapping.getMaxValue(),
mu.isAnyPropertySet(MappingUpdatedProperties.Status) ? statusAsInt.apply(update.getStatus()) : mapping.getStatus(), lockOwnerId,
updatedShard);
boolean fromOnlineToOffline = mu.isMappingBeingTakenOffline(intAsStatus.apply(mapping.getStatus()));
StoreOperationCode opCode;
if (fromOnlineToOffline) {
opCode = currentMapping.getKind() == MappingKind.PointMapping ? StoreOperationCode.UpdatePointMappingWithOffline
: StoreOperationCode.UpdateRangeMappingWithOffline;
}
else {
opCode = currentMapping.getKind() == MappingKind.PointMapping ? StoreOperationCode.UpdatePointMapping
: StoreOperationCode.UpdateRangeMapping;
}
try (IStoreOperation op = shardMapManager.getStoreOperationFactory().createUpdateMappingOperation(this.getShardMapManager(), opCode,
shardMap.getStoreShardMap(), originalMapping, updatedMapping, shardMap.getApplicationNameSuffix(), lockOwnerId)) {
op.doOperation();
}
catch (Exception e) {
ExceptionUtils.throwStronglyTypedException(e);
}
return constructMapping.invoke(this.getShardMapManager(), this.getShardMap(), updatedMapping);
}
/**
* Gets the lock owner of a mapping.
*
* @param mapping
* The mapping
* @param errorCategory
* Error category to use for the store operation
* @return Lock owner for the mapping.
*/
public final <MappingT extends IShardProvider & IMappingInfoProvider> UUID getLockOwnerForMapping(MappingT mapping,
ShardManagementErrorCategory errorCategory) {
this.ensureMappingBelongsToShardMap(mapping, "LookupLockOwner", "mapping");
StoreResults result;
try (IStoreOperationGlobal op = shardMapManager.getStoreOperationFactory().createFindMappingByIdGlobalOperation(this.getShardMapManager(),
"LookupLockOwner", shardMap.getStoreShardMap(), mapping.getStoreMapping(), errorCategory)) {
result = op.doGlobal();
}
catch (Exception e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
return result.getStoreMappings().get(0).getLockOwnerId();
}
/**
* Locks or unlocks a given mapping or all mappings.
*
* @param mapping
* Optional mapping
* @param lockOwnerId
* The lock owner id
* @param lockOwnerIdOpType
* Operation to perform on this mapping with the given lockOwnerId
* @param errorCategory
* Error category to use for the store operation
*/
public final <MappingT extends IShardProvider & IMappingInfoProvider> void lockOrUnlockMappings(MappingT mapping,
UUID lockOwnerId,
LockOwnerIdOpType lockOwnerIdOpType,
ShardManagementErrorCategory errorCategory) {
String operationName = lockOwnerIdOpType == LockOwnerIdOpType.Lock ? "Lock" : "UnLock";
if (lockOwnerIdOpType != LockOwnerIdOpType.UnlockAllMappingsForId && lockOwnerIdOpType != LockOwnerIdOpType.UnlockAllMappings) {
this.ensureMappingBelongsToShardMap(mapping, operationName, "mapping");
if (lockOwnerIdOpType == LockOwnerIdOpType.Lock && lockOwnerId.equals(MappingLockToken.ForceUnlock.getLockOwnerId())) {
throw new IllegalArgumentException(StringUtilsLocal.formatInvariant(Errors._ShardMapping_LockIdNotSupported,
mapping.getShardInfo().getLocation(), shardMap.getName(), lockOwnerId), new Throwable("lockOwnerId"));
}
}
else {
assert mapping == null;
}
try (IStoreOperationGlobal op = shardMapManager.getStoreOperationFactory().createLockOrUnLockMappingsGlobalOperation(
this.getShardMapManager(), operationName, shardMap.getStoreShardMap(), mapping != null ? mapping.getStoreMapping() : null,
lockOwnerId, lockOwnerIdOpType, errorCategory)) {
op.doGlobal();
}
catch (Exception e) {
ExceptionUtils.throwStronglyTypedException(e);
}
}
/**
* Validates the input parameters and ensures that the mapping parameter belong to this shard map.
*
* @param mapping
* Mapping to be validated.
* @param operationName
* Operation being performed.
* @param parameterName
* Parameter name for mapping parameter.
*/
protected final <MappingT extends IMappingInfoProvider> void ensureMappingBelongsToShardMap(MappingT mapping,
String operationName,
String parameterName) {
assert mapping.getShardMapManager() != null;
// Ensure that shard belongs to current shard map.
if (!mapping.getShardMapId().equals(shardMap.getId())) {
throw new IllegalStateException(StringUtilsLocal.formatInvariant(Errors._ShardMapping_DifferentShardMap, mapping.getTypeName(),
operationName, shardMap.getName(), parameterName));
}
// Ensure that the mapping objects belong to same shard map.
if (!Objects.equals(mapping.getShardMapManager(), shardMapManager)) {
throw new IllegalStateException(StringUtilsLocal.formatInvariant(Errors._ShardMapping_DifferentShardMapManager, mapping.getTypeName(),
operationName, shardMapManager.getCredentials().getShardMapManagerLocation(), shardMap.getName(), parameterName));
}
}
}

Просмотреть файл

@ -0,0 +1,50 @@
package com.microsoft.azure.elasticdb.shard.mapper;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
public enum ConnectionOptions {
/**
* No operation will be performed on the opened connection.
*/
None(0),
/**
* Validation will be performed on the connection to ensure that the state of the corresponding mapping has not changed since the mapping
* information was last cached at the client.
*/
Validate(1);
public static final int SIZE = java.lang.Integer.SIZE;
private static java.util.HashMap<Integer, ConnectionOptions> mappings;
private int intValue;
ConnectionOptions(int value) {
intValue = value;
getMappings().put(value, this);
}
private static java.util.HashMap<Integer, ConnectionOptions> getMappings() {
if (mappings == null) {
synchronized (ConnectionOptions.class) {
if (mappings == null) {
mappings = new java.util.HashMap<>();
}
}
}
return mappings;
}
public static ConnectionOptions forValue(int value) {
return getMappings().get(value);
}
public int getValue() {
return intValue;
}
}

Просмотреть файл

@ -0,0 +1,292 @@
package com.microsoft.azure.elasticdb.shard.mapper;
import java.sql.Connection;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.google.common.base.Preconditions;
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
import com.microsoft.azure.elasticdb.shard.base.LookupOptions;
import com.microsoft.azure.elasticdb.shard.base.Shard;
import com.microsoft.azure.elasticdb.shard.base.ShardLocation;
import com.microsoft.azure.elasticdb.shard.base.ShardUpdate;
import com.microsoft.azure.elasticdb.shard.base.ShardUpdatedProperties;
import com.microsoft.azure.elasticdb.shard.map.ShardMap;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementException;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardMapManager;
import com.microsoft.azure.elasticdb.shard.store.StoreResults;
import com.microsoft.azure.elasticdb.shard.store.StoreShard;
import com.microsoft.azure.elasticdb.shard.storeops.base.IStoreOperation;
import com.microsoft.azure.elasticdb.shard.storeops.base.IStoreOperationGlobal;
import com.microsoft.azure.elasticdb.shard.utils.ExceptionUtils;
/**
* Default shard mapper, that basically is a container of shards with no keys.
*/
public final class DefaultShardMapper extends BaseShardMapper implements IShardMapper<Shard, Shard> {
/**
* Default shard mapper, which just manages Shards.
*
* @param shardMapManager
* Reference to ShardMapManager.
* @param sm
* Containing shard map.
*/
public DefaultShardMapper(ShardMapManager shardMapManager,
ShardMap sm) {
super(shardMapManager, sm);
}
/**
* Given a shard, obtains a SqlConnection to the shard. The shard must exist in the mapper.
*
* @param key
* Input shard.
* @param connectionString
* Connection string with credential information, the DataSource and Database are obtained from the results of the lookup operation.
* @return An opened SqlConnection.
*/
public Connection openConnectionForKey(Shard key,
String connectionString) {
return openConnectionForKey(key, connectionString, ConnectionOptions.Validate);
}
/**
* Given a shard, obtains a SqlConnection to the shard. The shard must exist in the mapper.
*
* @param key
* Input shard.
* @param connectionString
* Connection string with credential information, the DataSource and Database are obtained from the results of the lookup operation.
* @param options
* Options for validation operations to perform on opened connection.
* @return An opened SqlConnection.
*/
public Connection openConnectionForKey(Shard key,
String connectionString,
ConnectionOptions options) {
Preconditions.checkNotNull(key);
Preconditions.checkNotNull(connectionString);
return shardMap.openConnection(
this.lookup(key, LookupOptions.forValue(LookupOptions.LOOKUP_IN_CACHE.getValue() | LookupOptions.LOOKUP_IN_STORE.getValue())),
connectionString, options);
}
/**
* Given a shard, asynchronously obtains a SqlConnection to the shard. The shard must exist in the mapper.
*
* @param key
* Input shard.
* @param connectionString
* Connection string with credential information, the DataSource and Database are obtained from the results of the lookup operation.
* @return An opened SqlConnection.
*/
public Callable<Connection> openConnectionForKeyAsync(Shard key,
String connectionString) {
return openConnectionForKeyAsync(key, connectionString, ConnectionOptions.Validate);
}
/**
* Given a shard, asynchronously obtains a SqlConnection to the shard. The shard must exist in the mapper.
*
* @param key
* Input shard.
* @param connectionString
* Connection string with credential information, the DataSource and Database are obtained from the results of the lookup operation.
* @param options
* Options for validation operations to perform on opened connection.
* @return An opened SqlConnection.
*/
public Callable<Connection> openConnectionForKeyAsync(Shard key,
String connectionString,
ConnectionOptions options) {
Preconditions.checkNotNull(key);
Preconditions.checkNotNull(connectionString);
return shardMap.openConnectionAsync(
this.lookup(key, LookupOptions.forValue(LookupOptions.LOOKUP_IN_CACHE.getValue() | LookupOptions.LOOKUP_IN_STORE.getValue())),
connectionString, options);
}
/**
* Adds a shard.
*
* @param shard
* Shard being added.
* @return The added shard object.
*/
public Shard add(Shard shard) {
assert shard != null;
ExceptionUtils.ensureShardBelongsToShardMap(this.shardMapManager, shardMap, shard, "CreateShard", "Shard");
try (IStoreOperation op = this.shardMapManager.getStoreOperationFactory().createAddShardOperation(this.shardMapManager,
shardMap.getStoreShardMap(), shard.getStoreShard())) {
op.doOperation();
}
catch (Exception e) {
ExceptionUtils.throwStronglyTypedException(e);
}
return shard;
}
/**
* Removes a shard.
*
* @param shard
* Shard being removed.
*/
public void remove(Shard shard) {
remove(shard, new UUID(0L, 0L));
}
/**
* Removes a shard.
*
* @param shard
* Shard being removed.
* @param lockOwnerId
* Lock owner id of this mapping
*/
public void remove(Shard shard,
UUID lockOwnerId) {
assert shard != null;
ExceptionUtils.ensureShardBelongsToShardMap(this.shardMapManager, shardMap, shard, "DeleteShard", "Shard");
try (IStoreOperation op = this.shardMapManager.getStoreOperationFactory().createRemoveShardOperation(this.shardMapManager,
shardMap.getStoreShardMap(), shard.getStoreShard())) {
op.doOperation();
}
catch (Exception e) {
ExceptionUtils.throwStronglyTypedException(e);
}
}
/**
* Looks up the given shard in the mapper.
*
* @param shard
* Input shard.
* @param lookupOptions
* Whether to use cache and/or storage for lookups.
* @return Returns the shard after verifying that it is present in mapper.
*/
public Shard lookup(Shard shard,
LookupOptions lookupOptions) {
assert shard != null;
return shard;
}
/**
* Tries to looks up the key value and returns the corresponding mapping.
*
* @param key
* Input shard.
* @param lookupOptions
* Whether to use cache and/or storage for lookups.
* @param shard
* Shard that contains the key value.
* @return <c>true</c> if shard is found, <c>false</c> otherwise.
*/
public boolean tryLookup(Shard key,
LookupOptions lookupOptions,
ReferenceObjectHelper<Shard> shard) {
assert key != null;
shard.argValue = key;
return true;
}
/**
* Gets all shards for a shard map.
*
* @return All the shards belonging to the shard map.
*/
public List<Shard> getShards() {
StoreResults result;
try (IStoreOperationGlobal op = shardMapManager.getStoreOperationFactory().createGetShardsGlobalOperation("GetShards", this.shardMapManager,
shardMap.getStoreShardMap())) {
result = op.doGlobal();
}
catch (Exception e) {
ExceptionUtils.throwStronglyTypedException(e);
result = new StoreResults(); // Ideally this should not be executed.
}
return result.getStoreShards().stream().map(ss -> new Shard(shardMapManager, shardMap, ss)).collect(Collectors.toList());
}
/**
* Gets shard object based on given location.
*
* @param location
* Input location.
* @return Shard belonging to ShardMap.
*/
public Shard getShardByLocation(ShardLocation location) {
assert location != null;
StoreResults result;
try (IStoreOperationGlobal op = this.getShardMapManager().getStoreOperationFactory().createFindShardByLocationGlobalOperation(
this.getShardMapManager(), "GetShardByLocation", this.getShardMap().getStoreShardMap(), location)) {
result = op.doGlobal();
}
catch (Exception e) {
e.printStackTrace();
throw (ShardManagementException) e.getCause();
}
StoreShard onlyElement = result.getStoreShards().stream().findFirst().orElse(null);
return onlyElement == null ? null : new Shard(shardMapManager, shardMap, onlyElement);
}
/**
* Allows for update to a shard with the updates provided in the <paramref name="update"/> parameter.
*
* @param currentShard
* Shard to be updated.
* @param update
* Updated properties of the Shard.
* @return New Shard instance with updated information.
*/
public Shard updateShard(Shard currentShard,
ShardUpdate update) {
assert currentShard != null;
assert update != null;
ExceptionUtils.ensureShardBelongsToShardMap(this.shardMapManager, shardMap, currentShard, "UpdateShard", "Shard");
// CONSIDER(wbasheer): Have refresh semantics for trivial case when nothing is modified.
if (!update.isAnyPropertySet(ShardUpdatedProperties.All)) {
return currentShard;
}
StoreShard ssNew = new StoreShard(currentShard.getId(), UUID.randomUUID(), currentShard.getShardMapId(), currentShard.getLocation(),
update.isAnyPropertySet(ShardUpdatedProperties.Status) ? update.getStatus().getValue() : currentShard.getStoreShard().getStatus());
try (IStoreOperation op = this.shardMapManager.getStoreOperationFactory().createUpdateShardOperation(this.shardMapManager,
shardMap.getStoreShardMap(), currentShard.getStoreShard(), ssNew)) {
op.doOperation();
}
catch (Exception e) {
ExceptionUtils.throwStronglyTypedException(e);
}
return new Shard(this.shardMapManager, shardMap, ssNew);
}
}

Просмотреть файл

@ -0,0 +1,107 @@
package com.microsoft.azure.elasticdb.shard.mapper;
import java.sql.Connection;
import java.util.UUID;
import java.util.concurrent.Callable;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
import com.microsoft.azure.elasticdb.shard.base.IShardProvider;
import com.microsoft.azure.elasticdb.shard.base.LookupOptions;
/**
* Container for a collection of keys to shards mappings.
*/
public interface IShardMapper<MappingT extends IShardProvider, ValueT> {
Connection openConnectionForKey(ValueT key,
String connectionString);
/**
* Given a key value, obtains a SqlConnection to the shard in the mapping that contains the key value.
*
* @param key
* Input key value.
* @param connectionString
* Connection string with credential information, the DataSource and Database are obtained from the results of the lookup operation for
* key.
* @param options
* Options for validation operations to perform on opened connection.
* @return An opened SqlConnection.
*/
Connection openConnectionForKey(ValueT key,
String connectionString,
ConnectionOptions options);
Callable<Connection> openConnectionForKeyAsync(ValueT key,
String connectionString);
/**
* Given a key value, asynchronously obtains a SqlConnection to the shard in the mapping that contains the key value.
*
* @param key
* Input key value.
* @param connectionString
* Connection string with credential information, the DataSource and Database are obtained from the results of the lookup operation for
* key.
* @param options
* Options for validation operations to perform on opened connection.
* @return An opened SqlConnection.
*/
Callable<Connection> openConnectionForKeyAsync(ValueT key,
String connectionString,
ConnectionOptions options);
/**
* Adds a mapping.
*
* @param mapping
* Mapping being added.
*/
MappingT add(MappingT mapping);
/**
* Removes a mapping.
*
* @param mapping
* Mapping being removed.
* @param lockOwnerId
* Lock owner id of the mapping
*/
void remove(MappingT mapping,
UUID lockOwnerId);
/**
* Looks up the key value and returns the corresponding mapping.
*
* @param key
* Input key value.
* @param lookupOptions
* Whether to use cache and/or storage for lookups.
* @return Mapping that contains the key value.
*/
MappingT lookup(ValueT key,
LookupOptions lookupOptions);
/**
* Tries to looks up the key value and returns the corresponding mapping.
*
* @param key
* Input key value.
* @param lookupOptions
* Whether to use cache and/or storage for lookups.
* @param mapping
* Mapping that contains the key value.
* @return <c>true</c> if mapping is found, <c>false</c> otherwise.
*/
boolean tryLookup(ValueT key,
LookupOptions lookupOptions,
ReferenceObjectHelper<MappingT> mapping);
}

Просмотреть файл

@ -0,0 +1,327 @@
package com.microsoft.azure.elasticdb.shard.mapper;
import java.sql.Connection;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;
/*
* Elastic database tools for Azure SQL Database.
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
import com.microsoft.azure.elasticdb.core.commons.helpers.ReferenceObjectHelper;
import com.microsoft.azure.elasticdb.shard.base.LockOwnerIdOpType;
import com.microsoft.azure.elasticdb.shard.base.LookupOptions;
import com.microsoft.azure.elasticdb.shard.base.MappingStatus;
import com.microsoft.azure.elasticdb.shard.base.PointMapping;
import com.microsoft.azure.elasticdb.shard.base.PointMappingUpdate;
import com.microsoft.azure.elasticdb.shard.base.Range;
import com.microsoft.azure.elasticdb.shard.base.Shard;
import com.microsoft.azure.elasticdb.shard.map.ShardMap;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementErrorCategory;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementErrorCode;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardManagementException;
import com.microsoft.azure.elasticdb.shard.mapmanager.ShardMapManager;
import com.microsoft.azure.elasticdb.shard.storeops.base.StoreOperationRequestBuilder;
import com.microsoft.azure.elasticdb.shard.utils.Errors;
/**
* Mapper from single keys (points) to their corresponding shards.
*
* <typeparam name="KeyT">Key type.</typeparam>
*/
public final class ListShardMapper extends BaseShardMapper implements IShardMapper<PointMapping, Object> {
/**
* List shard mapper, which managers point mappings.
*
* @param shardMapManager
* Reference to ShardMapManager.
* @param sm
* Containing shard map.
*/
public ListShardMapper(ShardMapManager shardMapManager,
ShardMap sm) {
super(shardMapManager, sm);
}
/**
* Given a key value, obtains a SqlConnection to the shard in the mapping that contains the key value.
*
* @param key
* Input key value.
* @param connectionString
* Connection string with credential information, the DataSource and Database are obtained from the results of the lookup operation for
* key.
* @return An opened SqlConnection.
*/
public Connection openConnectionForKey(Object key,
String connectionString) {
return openConnectionForKey(key, connectionString, ConnectionOptions.Validate);
}
/**
* Given a key value, obtains a SqlConnection to the shard in the mapping that contains the key value.
*
* @param key
* Input key value.
* @param connectionString
* Connection string with credential information, the DataSource and Database are obtained from the results of the lookup operation for
* key.
* @param options
* Options for validation operations to perform on opened connection.
* @return An opened SqlConnection.
*/
public Connection openConnectionForKey(Object key,
String connectionString,
ConnectionOptions options) {
return this.openConnectionForKey(key, PointMapping::new, ShardManagementErrorCategory.ListShardMap, connectionString, options);
}
/**
* Given a key value, asynchronously obtains a SqlConnection to the shard in the mapping that contains the key value.
*
* @param key
* Input key value.
* @param connectionString
* Connection string with credential information, the DataSource and Database are obtained from the results of the lookup operation for
* key.
* @return A Task encapsulating an opened SqlConnection. All non usage-error exceptions will be reported via the returned Task
*/
public Callable<Connection> openConnectionForKeyAsync(Object key,
String connectionString) {
return openConnectionForKeyAsync(key, connectionString, ConnectionOptions.Validate);
}
/**
* Given a key value, asynchronously obtains a SqlConnection to the shard in the mapping that contains the key value.
*
* @param key
* Input key value.
* @param connectionString
* Connection string with credential information, the DataSource and Database are obtained from the results of the lookup operation for
* key.
* @param options
* Options for validation operations to perform on opened connection.
* @return A Task encapsulating an opened SqlConnection. All non usage-error exceptions will be reported via the returned Task
*/
public Callable<Connection> openConnectionForKeyAsync(Object key,
String connectionString,
ConnectionOptions options) {
return this.openConnectionForKeyAsync(key, PointMapping::new, ShardManagementErrorCategory.ListShardMap, connectionString, options);
}
/**
* Marks the given mapping offline.
*
* @param mapping
* Input point mapping.
* @return An offline mapping.
*/
public PointMapping markMappingOffline(PointMapping mapping) {
return markMappingOffline(mapping, new UUID(0L, 0L));
}
/**
* Marks the given mapping offline.
*
* @param mapping
* Input point mapping.
* @param lockOwnerId
* Lock owner id of this mapping
* @return An offline mapping.
*/
public PointMapping markMappingOffline(PointMapping mapping,
UUID lockOwnerId) {
PointMappingUpdate tempVar = new PointMappingUpdate();
tempVar.setStatus(MappingStatus.Offline);
return BaseShardMapper.setStatus(mapping, mapping.getStatus(), s -> MappingStatus.Offline, s -> tempVar, (mp,
tv,
lo) -> this.update(mapping, tempVar, lockOwnerId), lockOwnerId);
}
/**
* Marks the given mapping online.
*
* @param mapping
* Input point mapping.
* @return An online mapping.
*/
public PointMapping markMappingOnline(PointMapping mapping) {
return markMappingOnline(mapping, new UUID(0L, 0L));
}
/**
* Marks the given mapping online.
*
* @param mapping
* Input point mapping.
* @param lockOwnerId
* Lock owner id of this mapping
* @return An online mapping.
*/
public PointMapping markMappingOnline(PointMapping mapping,
UUID lockOwnerId) {
PointMappingUpdate tempVar = new PointMappingUpdate();
tempVar.setStatus(MappingStatus.Online);
return BaseShardMapper.setStatus(mapping, mapping.getStatus(), s -> MappingStatus.Online, s -> tempVar, (mp,
tv,
lo) -> this.update(mapping, tempVar, lockOwnerId), lockOwnerId);
}
/**
* Adds a point mapping.
*
* @param mapping
* Mapping being added.
* @return The added mapping object.
*/
public PointMapping add(PointMapping mapping) {
return this.add(mapping, PointMapping::new);
}
/**
* Removes a point mapping.
*
* @param mapping
* Mapping being removed.
*/
public void remove(PointMapping mapping) {
remove(mapping, new UUID(0L, 0L));
}
/**
* Removes a point mapping.
*
* @param mapping
* Mapping being removed.
* @param lockOwnerId
* Lock owner id of the mapping
*/
public void remove(PointMapping mapping,
UUID lockOwnerId) {
this.remove(mapping, PointMapping::new, lockOwnerId);
}
/**
* Looks up the key value and returns the corresponding mapping.
*
* @param key
* Input key value.
* @param lookupOptions
* Whether to use cache and/or storage for lookups.
* @return Mapping that contains the key value.
*/
public PointMapping lookup(Object key,
LookupOptions lookupOptions) {
PointMapping p = this.lookup(key, lookupOptions, PointMapping::new, ShardManagementErrorCategory.ListShardMap);
if (p == null) {
throw new ShardManagementException(ShardManagementErrorCategory.ListShardMap, ShardManagementErrorCode.MappingNotFoundForKey,
Errors._Store_ShardMapper_MappingNotFoundForKeyGlobal, this.getShardMap().getName(),
StoreOperationRequestBuilder.SP_FIND_SHARD_MAPPING_BY_KEY_GLOBAL, "Lookup");
}
return p;
}
/**
* Tries to looks up the key value and returns the corresponding mapping.
*
* @param key
* Input key value.
* @param lookupOptions
* Whether to use cache and/or storage for lookups.
* @param mapping
* Mapping that contains the key value.
* @return <c>true</c> if mapping is found, <c>false</c> otherwise.
*/
public boolean tryLookup(Object key,
LookupOptions lookupOptions,
ReferenceObjectHelper<PointMapping> mapping) {
PointMapping p = this.lookup(key, lookupOptions, PointMapping::new, ShardManagementErrorCategory.ListShardMap);
mapping.argValue = p;
return p != null;
}
/**
* Gets all the mappings that exist within given range.
*
* @param range
* Optional range value, if null, we cover everything.
* @param shard
* Optional shard parameter, if null, we cover all shards.
* @param lookupOptions
* Whether to use cache and/or storage for lookups.
* @return Read-only collection of mappings that overlap with given range.
*/
public List<PointMapping> getMappingsForRange(Range range,
Shard shard,
LookupOptions lookupOptions) {
return this.getMappingsForRange(range, shard, lookupOptions, PointMapping::new, ShardManagementErrorCategory.ListShardMap, "PointMapping");
}
/**
* Allows for update to a point mapping with the updates provided in the <paramref name="update"/> parameter.
*
* @param currentMapping
* Mapping being updated.
* @param update
* Updated properties of the Shard.
* @return New instance of mapping with updated information.
*/
public PointMapping update(PointMapping currentMapping,
PointMappingUpdate update) {
return update(currentMapping, update, new UUID(0L, 0L));
}
/**
* Allows for update to a point mapping with the updates provided in the <paramref name="update"/> parameter.
*
* @param currentMapping
* Mapping being updated.
* @param update
* Updated properties of the Shard.
* @param lockOwnerId
* Lock owner id of this mapping
* @return New instance of mapping with updated information.
*/
public PointMapping update(PointMapping currentMapping,
PointMappingUpdate update,
UUID lockOwnerId) {
return this.update(currentMapping, update, PointMapping::new, MappingStatus::getValue, i -> (MappingStatus.forValue(i)), lockOwnerId);
}
/**
* Gets the lock owner of a mapping.
*
* @param mapping
* The mapping
* @return Lock owner for the mapping.
*/
public UUID getLockOwnerForMapping(PointMapping mapping) {
return this.getLockOwnerForMapping(mapping, ShardManagementErrorCategory.ListShardMap);
}
/**
* Locks or unlocks a given mapping or all mappings.
*
* @param mapping
* Optional mapping
* @param lockOwnerId
* The lock owner id
* @param lockOwnerIdOpType
* Operation to perform on this mapping with the given lockOwnerId
*/
public void lockOrUnlockMappings(PointMapping mapping,
UUID lockOwnerId,
LockOwnerIdOpType lockOwnerIdOpType) {
this.lockOrUnlockMappings(mapping, lockOwnerId, lockOwnerIdOpType, ShardManagementErrorCategory.ListShardMap);
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше