/[suikacvs]/webroot/swe/lib/SWE/Object/Graph.pm
Suika

Contents of /webroot/swe/lib/SWE/Object/Graph.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (hide annotations) (download)
Mon Sep 21 09:10:40 2009 UTC (16 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.5: +46 -7 lines
++ swe/lib/SWE/DB/ChangeLog	21 Sep 2009 09:05:45 -0000
2009-09-21  Wakaba  <wakaba@suika.fam.cx>

	* Lock.pm (check_lockability): Don't allow the same level of lock
	type being locked twice to avoid deadlocks caused by same level of
	locks.

++ swe/lib/SWE/Object/ChangeLog	21 Sep 2009 09:10:06 -0000
	* Document.pm (repo, prop_untainted, untainted_prop, save_prop,
	locked): New method.  Introduced the concept of "tainted" such
	that we can access to the property in the locked code fragment
	without being afraid to update the property using old values.
	(get_or_create_graph_node): Updated to utilize |prop| family of
	method with locks.

	* Graph.pm (repo, lock, unlock): New methods.
	(add_nodes, create_node, schelling_update): Locks the database
	before the modifications.

	* Repository.pm (graph, get_document_by_id): New methods.

	* Node.pm (repo): New method.

2009-09-21  Wakaba  <wakaba@suika.fam.cx>

++ swe/lib/suikawiki/ChangeLog	21 Sep 2009 09:10:27 -0000
	* main.pl: Made the graph node view to lock the database.

2009-09-21  Wakaba  <wakaba@suika.fam.cx>

1 wakaba 1.1 package SWE::Object::Graph;
2     use strict;
3     use warnings;
4    
5     sub new ($%) {
6     my $class = shift;
7     my $self = bless {@_}, $class;
8    
9     return $self;
10     }
11    
12 wakaba 1.6 ## ------ Database ------
13    
14     sub db ($) { $_[0]->{db} }
15    
16     sub repo ($) { $_[0]->{repo} }
17    
18     sub lock () {
19     my $self = shift;
20    
21     if ($self->{lock_n}++ == 0) {
22     my $lock = $self->{lock} ||= do {
23     require SWE::DB::Lock;
24     my $lock = SWE::DB::Lock->new;
25     $lock->{file_name} = $self->db->graph_dir_name . 'graph.lock';
26     $lock->lock_type ('Graph');
27     $lock;
28     };
29    
30     $lock->lock;
31     };
32     } # lock
33    
34     sub unlock () {
35     my $self = shift;
36    
37     if (--$self->{lock_n} <= 0 and $self->{lock}) {
38     $self->{lock}->unlock;
39     }
40     } # unlock
41 wakaba 1.1
42     use constant EMPTY_NODE_RATIO => 0.2;
43     use constant INITIAL_DEGREE => 5;
44    
45     sub add_nodes ($$) {
46     my ($self, $new_doc_number) = @_;
47 wakaba 1.6
48     $self->lock;
49 wakaba 1.1
50     my $global_prop_db = $self->db->global_prop;
51    
52     my $last_node_index = ${$global_prop_db->get_data ('lastnodeindex') || \ 0};
53 wakaba 1.4 my $doc_on_node_number = ${$global_prop_db->get_data ('doconnodenumber') || \ 0};
54     $doc_on_node_number += $new_doc_number;
55     my $max_node_index = $doc_on_node_number / (1 - EMPTY_NODE_RATIO);
56     $max_node_index = $last_node_index if $max_node_index < $last_node_index;
57     $max_node_index = $last_node_index + $new_doc_number
58     if $max_node_index < $last_node_index + $new_doc_number;
59     $max_node_index = int $max_node_index;
60    
61 wakaba 1.1 if ($last_node_index < $max_node_index) {
62     my $new_edges = {};
63    
64     for my $index1 ($last_node_index + 1 .. $max_node_index) {
65     for (1 .. INITIAL_DEGREE) {
66     my $index2 = int rand $index1;
67    
68     $new_edges->{$index1}->{$index2} = 1;
69     $new_edges->{$index2}->{$index1} = 1;
70     }
71     }
72    
73     my $graph_prop_db = $self->db->graph_prop;
74     for my $index1 (keys %$new_edges) {
75     my $node = $graph_prop_db->get_data ($index1);
76     my $edges = $node->{neighbors} ||= {};
77     for my $index2 (keys %{$new_edges->{$index1}}) {
78     $edges->{$index2} = 1;
79     }
80     $graph_prop_db->set_data ($index1 => $node);
81     }
82    
83     $global_prop_db->set_data (lastnodeindex => \$max_node_index);
84     }
85 wakaba 1.4 $global_prop_db->set_data (doconnodenumber => \$doc_on_node_number);
86 wakaba 1.1
87 wakaba 1.6 $self->unlock;
88    
89 wakaba 1.1 return ($last_node_index + 1 .. $max_node_index);
90     } # add_nodes
91    
92     sub create_node ($$) {
93     my ($self, $doc_id) = @_;
94    
95 wakaba 1.6 ## In fact we don't need to lock the entire graph (though the
96     ## |add_nodes| method might require it anyway), but we don't have
97     ## way to lock a particular node only at the moment.
98     $self->lock;
99    
100 wakaba 1.1 my ($node_id) = $self->add_nodes (1);
101 wakaba 1.2
102     require SWE::Object::Node;
103 wakaba 1.6 my $node = SWE::Object::Node->new (db => $self->db, repo => $self->repo);
104 wakaba 1.2 $node->create (id => $node_id);
105     $node->prop->{ids}->{$doc_id} = 1;
106     $node->save_prop;
107    
108 wakaba 1.6 $self->unlock;
109    
110 wakaba 1.2 return $node;
111     } # create_node
112    
113     sub get_node_by_id ($$) {
114     my ($self, $node_id) = @_;
115    
116     ## TODO: cache
117 wakaba 1.1
118 wakaba 1.2 require SWE::Object::Node;
119 wakaba 1.6 my $node = SWE::Object::Node->new (db => $self->db, repo => $self->repo);
120 wakaba 1.2 $node->load (id => $node_id);
121 wakaba 1.1
122 wakaba 1.2 return $node;
123     } # get_node_by_id
124 wakaba 1.5
125     use constant RELATEDNESS_THRESHOLD => 0.9;
126    
127     sub schelling_update ($$) {
128     my ($self, $node_id) = @_;
129    
130 wakaba 1.6 $self->lock;
131    
132 wakaba 1.5 my $node = $self->get_node_by_id ($node_id);
133     my $doc_id = $node->document_id // return;
134    
135     require SWE::Object::Repository;
136     my $repo = SWE::Object::Repository->new (db => $self->db);
137    
138     my $related = 0;
139     my $n = 0;
140    
141     my $neighbor_ids = $node->neighbor_ids;
142     my $unused_nodes = [];
143     for my $n_node_id (keys %$neighbor_ids) {
144     $n++;
145     my $n_node = $self->get_node_by_id ($n_node_id);
146     my $n_doc_id = $n_node->document_id;
147     unless (defined $n_doc_id) {
148     push @$unused_nodes, $n_node;
149     next;
150     }
151     $related++ if $repo->are_related_ids ($node_id, $n_doc_id);
152     }
153    
154     my $v = 0;
155     if ($n) {
156     $v = $related / $n;
157     }
158    
159     if ($v < RELATEDNESS_THRESHOLD and @$unused_nodes) {
160     my $unused_node = $unused_nodes->[rand @$unused_nodes];
161    
162     my $id_prop_db = $self->db->id_prop;
163     my $id_prop = $id_prop_db->get_data ($doc_id);
164    
165     $id_prop->{node_id} = $unused_node->id;
166     $unused_node->prop->{ids}->{$doc_id} = 1;
167     delete $node->prop->{ids}->{$doc_id};
168    
169     $unused_node->save_prop;
170     $id_prop_db->set_data ($doc_id => $id_prop);
171     $node->save_prop;
172     }
173 wakaba 1.6
174     $self->unlock;
175 wakaba 1.5 } # schelling_update
176 wakaba 1.1
177     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24