Skip to content
This repository was archived by the owner on Nov 9, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion deploy/lib/server_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,9 @@ def bootstrap
nr = 1
end

raise ExitException.new("Increase nr-replicas, minimum is 1") if nr < 1
raise ExitException.new("Forest replication and failover requires at least 3 nodes to function properly. This cluster has only #{r.body.to_i} node(s).") if r.body.to_i < 3

raise ExitException.new("Increase nr-replicas - minimum is 1, or disable forest replication. Replicas specified #{nr}") if nr < 1
raise ExitException.new("Adding #{nr} replicas to internals requires at least a #{nr + 1} node cluster") if r.body.to_i <= nr

logger.info "Bootstrapping replicas for #{@properties['ml.system-dbs']} on #{@hostname}..."
Expand Down Expand Up @@ -899,6 +901,11 @@ def bootstrap
raise ExitException.new("... Bootstrap FAILED")
return false
else
if r.body.match("WARNING")
r.body.split(/\n/).each do |li|
puts li if (li[/^WARNING/])
end
end
if r.body.match("(note: restart required)")
logger.warn "************************************"
logger.warn "*** RESTART OF MARKLOGIC IS REQUIRED"
Expand Down
164 changes: 107 additions & 57 deletions deploy/lib/xquery/setup.xqy
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ declare variable $system-users := ("nobody", "infostudio-admin", "healthcheck");
declare variable $system-roles as xs:string+ :=
setup:read-config-file("security.xml")/sec:security/sec:roles/sec:role/@name;

(: Return a set of messages to the user - add to the "messages" sequence :)
declare variable $messages :=
map:map() !
(
map:put( ., "messages", () ),
.
);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably use map:new and map:entry here, particularly since it uses the ! map operator. It works though. But apparently the same trick was used directly below before..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generally use the ! operator when creating a map, since it is much more performant than map:new and map:entry. It obviously matters little when creating a small map, but for larger maps the performance can be significantly worse (especially when a module is being called repeatedly).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, hadn't considered performance, nor was I expecting so much difference. My consideration was simply that map:new/map:entry gives code that is easier to understand.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind. I might clean up in future. Or never..

(: Used to adjust which host gets the next internal forest replica when the number of :)
(: replicas is less than (#hosts - 1) :)
declare variable $internal-forests :=
Expand Down Expand Up @@ -712,7 +720,8 @@ declare function setup:do-setup($import-config as element(configuration)+, $opti
) else (),
if ($restart-needed) then
"note: restart required"
else ()
else (),
fn:string-join( map:get( $messages, "messages" ), "&#xa;" )
)
}
catch($ex)
Expand Down Expand Up @@ -1513,9 +1522,7 @@ declare function setup:create-forests-from-config(
$forest-name,
$data-directory,
$host-id,
if (fn:count($hosts) gt 1) then
setup:reassign-replicas($replicas, $hosts, $hostnr, $forest-name, (), $is-internal )
else ()
setup:reassign-replicas($replicas, $hosts, $hostnr, $forest-name, (), $is-internal )
)
};

Expand Down Expand Up @@ -1565,9 +1572,7 @@ declare function setup:create-forests-from-count(
$new-forest-name,
$data-directory,
$host,
if (fn:count($hosts) gt 1) then
setup:reassign-replicas($replicas, $hosts, $hostnr, $forest-name, $forestnr, $is-internal )
else ()
setup:reassign-replicas($replicas, $hosts, $hostnr, $forest-name, $forestnr, $is-internal )
)
};

Expand Down Expand Up @@ -1619,65 +1624,95 @@ declare function setup:reassign-replicas(
$is-internal as xs:boolean
) as element(as:assignment)*
{
(: Ensure a forest number exists - if not specified, assume "1" :)
let $final-forestnr := ( $forestnr, 1 ) [1]

(: Set of hosts valid for replicas - no replicas on the same host as the primary forest, so remove that host from the set. :)
let $rep-hosts := fn:remove( $hosts, $hostnr )
(: Only process if there are any replicas to consider :)
if( fn:count( $replicas ) > 0 ) then

for $replica at $pos in $replicas

(: If a limit on the number of replicas was specified, then use it. :)
let $nr-replicas := ( $replica/as:forest-name/@nr-replicas, "1" )[1]

(: If number of replicas specified as "MAX", then use all hosts :)
let $num-forced-replicas as xs:int :=
if ($nr-replicas = "MAX" or $nr-replicas = "max" ) then
fn:count( $rep-hosts )
else xs:int( $nr-replicas )

(: Adjust the replicas for internal forests so they don't get all jammed onto a single server :)
let $adjuster as xs:int :=
if ($is-internal ) then
let $return := map:get( $internal-forests, "internal-forest-adjust" )
(: Sanity check for replica count requests :)
if( fn:count( $hosts ) < 3 ) then
let $notif := fn:concat( "Forest replication and failover requires at least 3 nodes to function properly.",
" This cluster only has ", fn:count( $hosts ), " node(s).",
" Defaulting to using no replicas." )
return
(
map:put( $internal-forests, "internal-forest-adjust", $return + $num-forced-replicas ),
$return
setup:return-message( $notif, "warning" ),
xdmp:log( $notif, "warning" )
)
else 0

(: Loop over the forced number of replicas :)
for $replicanr in (1 to $num-forced-replicas)
else
(: Ensure a forest number exists - if not specified, assume "1" :)
let $final-forestnr := ( $forestnr, 1 ) [1]

(: get the replica name - get from the config if specified, or build it :)
let $base-replica-name as xs:string := ($replica/as:forest-name[fn:string-length(fn:string(.)) > 0], fn:concat($forest-name, '-replica'))[1]
(: Set of hosts valid for replicas - no replicas on the same host as the primary forest, so remove that host from the set. :)
let $rep-hosts := fn:remove( $hosts, $hostnr )

for $replica at $pos in $replicas

(: Determine which host to apply the replica to. This is the actual host NAME based on the count of the hosts that :)
(: are available to replicate this forest, which does not include the same host that is hosting the primary. :)
let $replica-host-index := ($hostnr + (($final-forestnr - 1) * $num-forced-replicas + 1) + $replicanr + $pos + $adjuster - 4) mod count($rep-hosts) + 1
let $replica-host-name := ($replica/as:host-name[fn:string-length(fn:string(.)) > 0], xdmp:host-name($rep-hosts[$replica-host-index]))[1]
(: If a limit on the number of replicas was specified, then use it. :)
let $nr-replicas := ( $replica/as:forest-name/@nr-replicas, "1" )[1]

(: The *index* of the host must match the index of the host across ALL hosts, not just replica hosts. :)
let $replica-real-index := if( $replica-host-index >= $hostnr ) then $replica-host-index + 1 else $replica-host-index
(: If number of replicas specified as "MAX", then use all hosts :)
let $max-allowed-replicas := fn:count( $rep-hosts )
let $num-forced-replicas as xs:int :=
if ($nr-replicas = "MAX" or $nr-replicas = "max" ) then
$max-allowed-replicas
else xs:int( $nr-replicas )

(: Generate the new replica name based on name, forest counter, primary host, and replica host :)
let $replica-name := setup:gen-forest-name( $base-replica-name, $hostnr, $forestnr, $replica-real-index )
(: Verify that the host count can support the replica count request :)
let $_ :=
if ($num-forced-replicas < 1) then
fn:error(
xs:QName("INVALID_NR_REPLICAS"),
fn:concat("Increase nr-replicas - minimum is 1, or disable forest replication. Replicas specified ", $nr-replicas ) )
else if ($num-forced-replicas > $max-allowed-replicas) then
fn:error(
xs:QName("EXCESS_NR_REPLICAS"),
fn:concat("Adding ", $num-forced-replicas, " replicas to ", $forest-name, " requires at least a ", (fn:count($hosts) + 1), " node cluster.") )
else ()

(: Adjust the replicas for internal forests so they don't get all jammed onto a single server :)
let $adjuster as xs:int :=
if ($is-internal ) then
let $return := map:get( $internal-forests, "internal-forest-adjust" )
return
(
map:put( $internal-forests, "internal-forest-adjust", $return + $num-forced-replicas ),
$return
)
else 0

(: Loop over the forced number of replicas :)
for $replicanr in (1 to $num-forced-replicas)

(: get the replica name - get from the config if specified, or build it :)
let $base-replica-name as xs:string := ($replica/as:forest-name[fn:string-length(fn:string(.)) > 0], fn:concat($forest-name, '-replica'))[1]

(: Determine which host to apply the replica to. This is the actual host NAME based on the count of the hosts that :)
(: are available to replicate this forest, which does not include the same host that is hosting the primary. :)
let $replica-host-index := ($hostnr + (($final-forestnr - 1) * $num-forced-replicas + 1) + $replicanr + $pos + $adjuster - 4) mod count($rep-hosts) + 1
let $replica-host-name := ($replica/as:host-name[fn:string-length(fn:string(.)) > 0], xdmp:host-name($rep-hosts[$replica-host-index]))[1]

(: The *index* of the host must match the index of the host across ALL hosts, not just replica hosts. :)
let $replica-real-index := if( $replica-host-index >= $hostnr ) then $replica-host-index + 1 else $replica-host-index

(: Generate the new replica name based on name, forest counter, primary host, and replica host :)
let $replica-name := setup:gen-forest-name( $base-replica-name, $hostnr, $forestnr, $replica-real-index )

let $_ :=
if( map:contains( $delete-map, $replica-name ) ) then
(: If it exists already, remove this replica from the list of those to delete :)
map:delete( $delete-map, $replica-name )
else
(: This is a new replica - will need to wait on replication before attempting delete :)
map:put( $replicating-map, $replica-name, fn:true() )

return element { fn:node-name($replica) } {
$replica/@*,
<as:forest-name>{$replica-name}</as:forest-name>,
<as:host-name>{$replica-host-name}</as:host-name>,
$replica/node() except ($replica/as:forest-name, $replica/as:host-name)
}
let $_ :=
if( map:contains( $delete-map, $replica-name ) ) then
(: If it exists already, remove this replica from the list of those to delete :)
map:delete( $delete-map, $replica-name )
else
(: This is a new replica - will need to wait on replication before attempting delete :)
map:put( $replicating-map, $replica-name, fn:true() )

return element { fn:node-name($replica) } {
$replica/@*,
<as:forest-name>{$replica-name}</as:forest-name>,
<as:host-name>{$replica-host-name}</as:host-name>,
$replica/node() except ($replica/as:forest-name, $replica/as:host-name)
}
else
(: No replicas to process :)
()
};

declare function setup:validate-forests-from-count(
Expand Down Expand Up @@ -6361,6 +6396,21 @@ declare function setup:get-server($server-name as xs:string, $group-id as xs:uns
xdmp:group-servers($group-id)[xdmp:server-name(.) = $server-name]
};

declare function setup:return-message( $message as xs:string ) as empty-sequence()
{
setup:return-message( $message, () )
};
declare function setup:return-message( $message as xs:string, $level as xs:string? ) as empty-sequence()
{
let $prefix :=
if( fn:exists( $level ) ) then
fn:concat( fn:upper-case( $level ), ": " )
else ()

return
map:put( $messages, "messages", ( map:get( $messages, "messages" ), fn:concat( $prefix, $message ) ) )
};

(:
: Force update mode. This is so that we can create an SSL certificate template
: and then tell an app server to use it.
Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
October-2017-dev
October-2017-dev