~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/repository.py

  • Committer: Alexander Belchenko
  • Date: 2008-01-28 22:48:19 UTC
  • mto: This revision was merged to the branch mainline in revision 3230.
  • Revision ID: bialix@ukr.net-20080128224819-88buaniv5dwy9vx4
$BZR_LOG is used to contol location of .bzr.log (use /dev/null or NUL to suppress log).

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Server-side repository related request implmentations."""
18
18
 
 
19
from cStringIO import StringIO
19
20
import os
20
21
import sys
21
22
import tempfile
23
24
 
24
25
from bzrlib import errors
25
26
from bzrlib.bzrdir import BzrDir
 
27
from bzrlib.pack import ContainerSerialiser
26
28
from bzrlib.smart.request import (
27
29
    FailedSmartServerResponse,
28
30
    SmartServerRequest,
29
31
    SuccessfulSmartServerResponse,
30
32
    )
 
33
from bzrlib import revision as _mod_revision
31
34
 
32
35
 
33
36
class SmartServerRepositoryRequest(SmartServerRequest):
45
48
        """
46
49
        transport = self._backing_transport.clone(path)
47
50
        bzrdir = BzrDir.open_from_transport(transport)
48
 
        repository = bzrdir.open_repository()
49
 
        return self.do_repository_request(repository, *args)
 
51
        # Save the repository for use with do_body.
 
52
        self._repository = bzrdir.open_repository()
 
53
        return self.do_repository_request(self._repository, *args)
 
54
 
 
55
    def do_repository_request(self, repository, *args):
 
56
        """Override to provide an implementation for a verb."""
 
57
        # No-op for verbs that take bodies (None as a result indicates a body
 
58
        # is expected)
 
59
        return None
 
60
 
 
61
 
 
62
class SmartServerRepositoryGetParentMap(SmartServerRepositoryRequest):
 
63
    
 
64
    def do_repository_request(self, repository, *revision_ids):
 
65
        repository.lock_read()
 
66
        try:
 
67
            return self._do_repository_request(repository, revision_ids)
 
68
        finally:
 
69
            repository.unlock()
 
70
 
 
71
    def _do_repository_request(self, repository, revision_ids):
 
72
        """Get parent details for some revisions.
 
73
        
 
74
        All the parents for revision_ids are returned. Additionally up to 64KB
 
75
        of additional parent data found by performing a breadth first search
 
76
        from revision_ids is returned.
 
77
 
 
78
        :param repository: The repository to query in.
 
79
        :param revision_ids: The utf8 encoded revision_id to answer for.
 
80
        :return: A smart server response where the body contains an utf8
 
81
            encoded flattened list of the parents of the revisions, (the same
 
82
            format as Repository.get_revision_graph).
 
83
        """
 
84
        lines = []
 
85
        repo_graph = repository.get_graph()
 
86
        result = {}
 
87
        queried_revs = set()
 
88
        size_so_far = 0
 
89
        next_revs = revision_ids
 
90
        first_loop_done = False
 
91
        while next_revs:
 
92
            queried_revs.update(next_revs)
 
93
            parent_map = repo_graph.get_parent_map(next_revs)
 
94
            next_revs = set()
 
95
            for revision_id, parents in parent_map.iteritems():
 
96
                # adjust for the wire
 
97
                if parents == (_mod_revision.NULL_REVISION,):
 
98
                    parents = ()
 
99
                # add parents to the result
 
100
                result[revision_id] = parents
 
101
                # prepare the next query
 
102
                next_revs.update(parents)
 
103
                # Approximate the serialized cost of this revision_id.
 
104
                size_so_far += 2 + len(revision_id) + sum(map(len, parents))
 
105
                # get all the directly asked for parents, and then flesh out to
 
106
                # 64K or so.
 
107
                if first_loop_done and size_so_far > 65000:
 
108
                    next_revs = set()
 
109
                    break
 
110
            # don't query things we've already queried
 
111
            next_revs.difference_update(queried_revs)
 
112
            first_loop_done = True
 
113
 
 
114
        for revision, parents in result.items():
 
115
            lines.append(' '.join((revision, ) + tuple(parents)))
 
116
 
 
117
        return SuccessfulSmartServerResponse(('ok', ), '\n'.join(lines))
50
118
 
51
119
 
52
120
class SmartServerRepositoryGetRevisionGraph(SmartServerRepositoryRequest):
72
140
            return FailedSmartServerResponse(('nosuchrevision', revision_id), '')
73
141
 
74
142
        for revision, parents in revision_graph.items():
75
 
            lines.append(' '.join([revision,] + parents))
 
143
            lines.append(' '.join((revision, ) + tuple(parents)))
76
144
 
77
145
        return SuccessfulSmartServerResponse(('ok', ), '\n'.join(lines))
78
146
 
163
231
            return FailedSmartServerResponse(('LockContention',))
164
232
        except errors.UnlockableTransport:
165
233
            return FailedSmartServerResponse(('UnlockableTransport',))
166
 
        repository.leave_lock_in_place()
 
234
        except errors.LockFailed, e:
 
235
            return FailedSmartServerResponse(('LockFailed',
 
236
                str(e.lock), str(e.why)))
 
237
        if token is not None:
 
238
            repository.leave_lock_in_place()
167
239
        repository.unlock()
168
240
        if token is None:
169
241
            token = ''
239
311
            tarball.add(dirname, '.bzr') # recursive by default
240
312
        finally:
241
313
            tarball.close()
 
314
 
 
315
 
 
316
class SmartServerRepositoryStreamKnitDataForRevisions(SmartServerRepositoryRequest):
 
317
    """Bzr <= 1.1 streaming pull, buffers all data on server."""
 
318
 
 
319
    def do_repository_request(self, repository, *revision_ids):
 
320
        repository.lock_read()
 
321
        try:
 
322
            return self._do_repository_request(repository, revision_ids)
 
323
        finally:
 
324
            repository.unlock()
 
325
 
 
326
    def _do_repository_request(self, repository, revision_ids):
 
327
        stream = repository.get_data_stream_for_search(
 
328
            repository.revision_ids_to_search_result(set(revision_ids)))
 
329
        buffer = StringIO()
 
330
        pack = ContainerSerialiser()
 
331
        buffer.write(pack.begin())
 
332
        try:
 
333
            for name_tuple, bytes in stream:
 
334
                buffer.write(pack.bytes_record(bytes, [name_tuple]))
 
335
        except errors.RevisionNotPresent, e:
 
336
            return FailedSmartServerResponse(('NoSuchRevision', e.revision_id))
 
337
        buffer.write(pack.end())
 
338
        return SuccessfulSmartServerResponse(('ok',), buffer.getvalue())
 
339
 
 
340
 
 
341
class SmartServerRepositoryStreamRevisionsChunked(SmartServerRepositoryRequest):
 
342
    """Bzr 1.1+ streaming pull."""
 
343
 
 
344
    def do_body(self, body_bytes):
 
345
        lines = body_bytes.split('\n')
 
346
        start_keys = set(lines[0].split(' '))
 
347
        exclude_keys = set(lines[1].split(' '))
 
348
        revision_count = int(lines[2])
 
349
        repository = self._repository
 
350
        repository.lock_read()
 
351
        try:
 
352
            search = repository.get_graph()._make_breadth_first_searcher(
 
353
                start_keys)
 
354
            while True:
 
355
                try:
 
356
                    next_revs = search.next()
 
357
                except StopIteration:
 
358
                    break
 
359
                search.stop_searching_any(exclude_keys.intersection(next_revs))
 
360
            search_result = search.get_result()
 
361
            if search_result.get_recipe()[2] != revision_count:
 
362
                # we got back a different amount of data than expected, this
 
363
                # gets reported as NoSuchRevision, because less revisions
 
364
                # indicates missing revisions, and more should never happen as
 
365
                # the excludes list considers ghosts and ensures that ghost
 
366
                # filling races are not a problem.
 
367
                return FailedSmartServerResponse(('NoSuchRevision',))
 
368
            stream = repository.get_data_stream_for_search(search_result)
 
369
        except Exception:
 
370
            repository.unlock()
 
371
            raise
 
372
        return SuccessfulSmartServerResponse(('ok',),
 
373
            body_stream=self.body_stream(stream, repository))
 
374
 
 
375
    def body_stream(self, stream, repository):
 
376
        pack = ContainerSerialiser()
 
377
        yield pack.begin()
 
378
        try:
 
379
            for name_tuple, bytes in stream:
 
380
                yield pack.bytes_record(bytes, [name_tuple])
 
381
        except errors.RevisionNotPresent, e:
 
382
            # This shouldn't be able to happen, but as we don't buffer
 
383
            # everything it can in theory happen.
 
384
            yield FailedSmartServerResponse(('NoSuchRevision', e.revision_id))
 
385
        repository.unlock()
 
386
        pack.end()
 
387