File Coverage

blib/lib/AtteanX/Model/SPARQLCache/LDF.pm
Criterion Covered Total %
statement 20 20 100.0
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 27 27 100.0


line stmt bran cond sub pod time code
1             package AtteanX::Model::SPARQLCache::LDF;
2              
3              
4 10     10   191734 use v5.14;
  10         25  
5 10     10   40 use warnings;
  10         12  
  10         217  
6              
7 10     10   34 use Moo;
  10         14  
  10         50  
8 10     10   8680 use Types::Standard qw(InstanceOf);
  10         15  
  10         60  
9 10     10   3736 use Class::Method::Modifiers;
  10         16  
  10         573  
10 10     10   41 use List::MoreUtils qw(any);
  10         12  
  10         73  
11 10     10   4593 use namespace::clean;
  10         13  
  10         52  
12              
13             extends 'AtteanX::Model::SPARQLCache';
14             with 'MooX::Log::Any';
15              
16             has 'ldf_store' => (is => 'ro',
17             isa => InstanceOf['AtteanX::Store::LDF'],
18             required => 1);
19              
20             has 'publisher' => (is => 'ro',
21             isa => InstanceOf['Redis'],
22             predicate => 'has_publisher'
23             );
24              
25             around 'cost_for_plan' => sub {
26             my $orig = shift;
27             my @params = @_;
28             my $self = shift;
29             my $plan = shift;
30             my $planner = shift;
31             my @passthroughs = qw/Attean::Plan::Iterator Attean::Plan::Quad/;
32             my $cost = $orig->(@params);
33             if ($self->log->is_debug) {
34             my $logcost = $cost || 'not defined';
35             $self->log->debug('Cost for original plan \'' . ref($plan) . "' was $logcost.");
36             }
37             if ($plan->isa('AtteanX::Plan::LDF::Triple')) {
38             $cost = $self->ldf_store->cost_for_plan($plan);
39             return $cost;
40             }
41             if ($cost && any { $plan->isa($_) } @passthroughs) {
42             # In here, we just pass the plans that probably do not need
43             # balancing against others
44             $self->log->debug("Use original's cost for '" . ref($plan) . "'");
45             return $cost;
46             }
47             # This is where the plans that needs to be balanced against LDFs go
48             if ($plan->isa('AtteanX::Plan::SPARQLBGP')) {
49             if ($cost <= 1000 && (scalar(@{ $plan->children }) == 1)) {
50             $self->log->trace("Set cost for single BGP SPARQL plan");
51             $cost = 1001;
52             } else {
53             $cost = ($cost + 1) * 5;
54             }
55             return $cost;
56             }
57             if ($plan->does('Attean::API::Plan::Join')) {
58             # Then, penalize the plan by the number of LDFs
59             my $countldfs = scalar $plan->subpatterns_of_type('AtteanX::Plan::LDF::Triple');
60             return unless ($countldfs);
61             unless ($cost) {
62             my @children = @{ $plan->children };
63             if ($plan->isa('Attean::Plan::NestedLoopJoin')) {
64             my $lcost = $planner->cost_for_plan($children[0], $self);
65             my $rcost = $planner->cost_for_plan($children[1], $self);
66             if ($lcost == 0) {
67             $cost = $rcost;
68             } elsif ($rcost == 0) {
69             $cost = $lcost;
70             } else {
71             $cost = $lcost * $rcost;
72             }
73             $cost++ if ($rcost > $lcost);
74             } elsif ($plan->isa('Attean::Plan::HashJoin')) {
75             my $lcost = $planner->cost_for_plan($children[0], $self);
76             my $rcost = $planner->cost_for_plan($children[1], $self);
77             $cost = ($lcost + $rcost);
78             $cost++ if ($rcost > $lcost);
79             }
80             }
81             unless ($plan->children_are_variable_connected) {
82             $cost *= 10 if $plan->isa('Attean::Plan::NestedLoopJoin');
83             $cost *= 100 if $plan->isa('Attean::Plan::HashJoin');
84             }
85              
86             $cost *= $countldfs; # TODO: This is assuming that it is better to join remotely
87             }
88              
89             # Now, penalize plan if any SPARQLBGP has a common variable with a LDFTriple
90             my %bgpvars;
91             my %ldfvars;
92             my $shared = 0;
93             $plan->walk(prefix => sub {
94             my $node = shift;
95             if ($node->isa('AtteanX::Plan::SPARQLBGP')) {
96             map { $bgpvars{$_} = 1 } @{$node->in_scope_variables};
97             }
98             elsif ($node->isa('AtteanX::Plan::LDF::Triple')) {
99             map { $ldfvars{$_} = 1 } @{$node->in_scope_variables};
100             # TODO: A single loop should be sufficient
101             }
102             foreach my $lid (keys(%ldfvars)) {
103             if ($bgpvars{$lid}) {
104             $shared = $lid;
105             last;
106             # TODO: Jump out of the walk here
107             }
108             }
109             });
110             if ($shared) {
111             $self->log->debug("Penalizing for SPARQL and LDF common variable '?$shared'.");
112             $cost += 1000;
113             }
114             # $cost *= 10; # TODO: Just multiply by a factor for now...
115            
116             return $cost;
117             };
118              
119              
120              
121             1;