comparison mercurial/localrepo.py @ 39700:b10d145837bc

localrepo: extract resolving of opener options to standalone functions Requirements and config options are converted into a dict which is available to the store vfs to consult. This is how storage options are communicated from the repo layer to the storage layer. Currently, we do that option resolution in a private method on the repo instance. And there is a single method doing that resolution. Opener options are logically specific to the storage backend they apply to. And, opener options may wish to influence how the repo object/type is constructed. So it makes sense to have more granular storage option resolution that occurs before the repo object is instantiated. This commit extracts the code for resolving opener options into new module-level functions. These functions are run before the repo instance is constructed. As part of the code move, we split the option resolution into generic and revlog-specific options. After this commit, we no longer add revlog-specific options to repos that don't have a revlog requirement. Some of these opener options and associated config options might make sense on alternate storage backends. We can always reuse config options and opener option names for other backends. But we shouldn't be passing opener options to storage backends that won't recognize them. I haven't done it here, but after this commit it should be possible for store backends to validate the set of opener options it receives. Because localrepository.openerreqs is no longer used after this commit, it has been removed. I'm not super thrilled about the code outside of localrepo that is adding requirements and updating opener options. We'll probably want to create a more formal API for that use case that constructs a new repo instance and poisons the old repo object. But this was a pre-existing issue and can be dealt with later. I have little doubt it will cause me troubles as I continue to refactor how repository objects are instantiated. .. api:: ``localrepository.openerreqs`` has been removed. Override ``localrepo.resolvestorevfsoptions()`` to add custom opener options. .. api:: ``localrepository._applyopenerreqs()`` has been removed. Use ``localrepo.resolvestorevfsoptions()`` to add custom opener options. Differential Revision: https://phab.mercurial-scm.org/D4576
author Gregory Szorc <gregory.szorc@gmail.com>
date Wed, 12 Sep 2018 15:59:26 -0700
parents 6f26417b71bb
children 543f26ece6cf
comparison
equal deleted inserted replaced
39699:6f26417b71bb 39700:b10d145837bc
479 # The store has changed over time and the exact layout is dictated by 479 # The store has changed over time and the exact layout is dictated by
480 # requirements. The store interface abstracts differences across all 480 # requirements. The store interface abstracts differences across all
481 # of them. 481 # of them.
482 store = makestore(requirements, storebasepath, 482 store = makestore(requirements, storebasepath,
483 lambda base: vfsmod.vfs(base, cacheaudited=True)) 483 lambda base: vfsmod.vfs(base, cacheaudited=True))
484
485 hgvfs.createmode = store.createmode 484 hgvfs.createmode = store.createmode
485
486 storevfs = store.vfs
487 storevfs.options = resolvestorevfsoptions(ui, requirements)
486 488
487 # The cache vfs is used to manage cache files. 489 # The cache vfs is used to manage cache files.
488 cachevfs = vfsmod.vfs(cachepath, cacheaudited=True) 490 cachevfs = vfsmod.vfs(cachepath, cacheaudited=True)
489 cachevfs.createmode = store.createmode 491 cachevfs.createmode = store.createmode
490 492
575 b'dotencode' in requirements) 577 b'dotencode' in requirements)
576 578
577 return storemod.encodedstore(path, vfstype) 579 return storemod.encodedstore(path, vfstype)
578 580
579 return storemod.basicstore(path, vfstype) 581 return storemod.basicstore(path, vfstype)
582
583 def resolvestorevfsoptions(ui, requirements):
584 """Resolve the options to pass to the store vfs opener.
585
586 The returned dict is used to influence behavior of the storage layer.
587 """
588 options = {}
589
590 if b'treemanifest' in requirements:
591 options[b'treemanifest'] = True
592
593 # experimental config: format.manifestcachesize
594 manifestcachesize = ui.configint(b'format', b'manifestcachesize')
595 if manifestcachesize is not None:
596 options[b'manifestcachesize'] = manifestcachesize
597
598 # In the absence of another requirement superseding a revlog-related
599 # requirement, we have to assume the repo is using revlog version 0.
600 # This revlog format is super old and we don't bother trying to parse
601 # opener options for it because those options wouldn't do anything
602 # meaningful on such old repos.
603 if b'revlogv1' in requirements or REVLOGV2_REQUIREMENT in requirements:
604 options.update(resolverevlogstorevfsoptions(ui, requirements))
605
606 return options
607
608 def resolverevlogstorevfsoptions(ui, requirements):
609 """Resolve opener options specific to revlogs."""
610
611 options = {}
612
613 if b'revlogv1' in requirements:
614 options[b'revlogv1'] = True
615 if REVLOGV2_REQUIREMENT in requirements:
616 options[b'revlogv2'] = True
617
618 if b'generaldelta' in requirements:
619 options[b'generaldelta'] = True
620
621 # experimental config: format.chunkcachesize
622 chunkcachesize = ui.configint(b'format', b'chunkcachesize')
623 if chunkcachesize is not None:
624 options[b'chunkcachesize'] = chunkcachesize
625
626 deltabothparents = ui.configbool(b'storage',
627 b'revlog.optimize-delta-parent-choice')
628 options[b'deltabothparents'] = deltabothparents
629
630 options[b'lazydeltabase'] = not scmutil.gddeltaconfig(ui)
631
632 chainspan = ui.configbytes(b'experimental', b'maxdeltachainspan')
633 if 0 <= chainspan:
634 options[b'maxdeltachainspan'] = chainspan
635
636 mmapindexthreshold = ui.configbytes(b'experimental',
637 b'mmapindexthreshold')
638 if mmapindexthreshold is not None:
639 options[b'mmapindexthreshold'] = mmapindexthreshold
640
641 withsparseread = ui.configbool(b'experimental', b'sparse-read')
642 srdensitythres = float(ui.config(b'experimental',
643 b'sparse-read.density-threshold'))
644 srmingapsize = ui.configbytes(b'experimental',
645 b'sparse-read.min-gap-size')
646 options[b'with-sparse-read'] = withsparseread
647 options[b'sparse-read-density-threshold'] = srdensitythres
648 options[b'sparse-read-min-gap-size'] = srmingapsize
649
650 sparserevlog = SPARSEREVLOG_REQUIREMENT in requirements
651 options[b'sparse-revlog'] = sparserevlog
652 if sparserevlog:
653 options[b'generaldelta'] = True
654
655 maxchainlen = None
656 if sparserevlog:
657 maxchainlen = revlogconst.SPARSE_REVLOG_MAX_CHAIN_LENGTH
658 # experimental config: format.maxchainlen
659 maxchainlen = ui.configint(b'format', b'maxchainlen', maxchainlen)
660 if maxchainlen is not None:
661 options[b'maxchainlen'] = maxchainlen
662
663 for r in requirements:
664 if r.startswith(b'exp-compression-'):
665 options[b'compengine'] = r[len(b'exp-compression-'):]
666
667 return options
580 668
581 @interfaceutil.implementer(repository.completelocalrepository) 669 @interfaceutil.implementer(repository.completelocalrepository)
582 class localrepository(object): 670 class localrepository(object):
583 671
584 # obsolete experimental requirements: 672 # obsolete experimental requirements:
600 'relshared', 688 'relshared',
601 'dotencode', 689 'dotencode',
602 'exp-sparse', 690 'exp-sparse',
603 'internal-phase' 691 'internal-phase'
604 } 692 }
605 openerreqs = {
606 'revlogv1',
607 'generaldelta',
608 'treemanifest',
609 }
610 693
611 # list of prefix for file which can be written without 'wlock' 694 # list of prefix for file which can be written without 'wlock'
612 # Extensions should extend this list when needed 695 # Extensions should extend this list when needed
613 _wlockfreeprefix = { 696 _wlockfreeprefix = {
614 # We migh consider requiring 'wlock' for the next 697 # We migh consider requiring 'wlock' for the next
710 self.ui.configbool('devel', 'check-locks')): 793 self.ui.configbool('devel', 'check-locks')):
711 if util.safehasattr(self.svfs, 'vfs'): # this is filtervfs 794 if util.safehasattr(self.svfs, 'vfs'): # this is filtervfs
712 self.svfs.vfs.audit = self._getsvfsward(self.svfs.vfs.audit) 795 self.svfs.vfs.audit = self._getsvfsward(self.svfs.vfs.audit)
713 else: # standard vfs 796 else: # standard vfs
714 self.svfs.audit = self._getsvfsward(self.svfs.audit) 797 self.svfs.audit = self._getsvfsward(self.svfs.audit)
715 self._applyopenerreqs()
716 798
717 self._dirstatevalidatewarned = False 799 self._dirstatevalidatewarned = False
718 800
719 self._branchcaches = {} 801 self._branchcaches = {}
720 self._revbranchcache = None 802 self._revbranchcache = None
814 caps = set(caps) 896 caps = set(caps)
815 capsblob = bundle2.encodecaps(bundle2.getrepocaps(self, 897 capsblob = bundle2.encodecaps(bundle2.getrepocaps(self,
816 role='client')) 898 role='client'))
817 caps.add('bundle2=' + urlreq.quote(capsblob)) 899 caps.add('bundle2=' + urlreq.quote(capsblob))
818 return caps 900 return caps
819
820 def _applyopenerreqs(self):
821 self.svfs.options = {r: True for r in self.requirements
822 if r in self.openerreqs}
823 # experimental config: format.chunkcachesize
824 chunkcachesize = self.ui.configint('format', 'chunkcachesize')
825 if chunkcachesize is not None:
826 self.svfs.options['chunkcachesize'] = chunkcachesize
827 # experimental config: format.manifestcachesize
828 manifestcachesize = self.ui.configint('format', 'manifestcachesize')
829 if manifestcachesize is not None:
830 self.svfs.options['manifestcachesize'] = manifestcachesize
831 deltabothparents = self.ui.configbool('storage',
832 'revlog.optimize-delta-parent-choice')
833 self.svfs.options['deltabothparents'] = deltabothparents
834 self.svfs.options['lazydeltabase'] = not scmutil.gddeltaconfig(self.ui)
835 chainspan = self.ui.configbytes('experimental', 'maxdeltachainspan')
836 if 0 <= chainspan:
837 self.svfs.options['maxdeltachainspan'] = chainspan
838 mmapindexthreshold = self.ui.configbytes('experimental',
839 'mmapindexthreshold')
840 if mmapindexthreshold is not None:
841 self.svfs.options['mmapindexthreshold'] = mmapindexthreshold
842 withsparseread = self.ui.configbool('experimental', 'sparse-read')
843 srdensitythres = float(self.ui.config('experimental',
844 'sparse-read.density-threshold'))
845 srmingapsize = self.ui.configbytes('experimental',
846 'sparse-read.min-gap-size')
847 self.svfs.options['with-sparse-read'] = withsparseread
848 self.svfs.options['sparse-read-density-threshold'] = srdensitythres
849 self.svfs.options['sparse-read-min-gap-size'] = srmingapsize
850 sparserevlog = SPARSEREVLOG_REQUIREMENT in self.requirements
851 self.svfs.options['sparse-revlog'] = sparserevlog
852 if sparserevlog:
853 self.svfs.options['generaldelta'] = True
854 maxchainlen = None
855 if sparserevlog:
856 maxchainlen = revlogconst.SPARSE_REVLOG_MAX_CHAIN_LENGTH
857 # experimental config: format.maxchainlen
858 maxchainlen = self.ui.configint('format', 'maxchainlen', maxchainlen)
859 if maxchainlen is not None:
860 self.svfs.options['maxchainlen'] = maxchainlen
861
862 for r in self.requirements:
863 if r.startswith('exp-compression-'):
864 self.svfs.options['compengine'] = r[len('exp-compression-'):]
865
866 # TODO move "revlogv2" to openerreqs once finalized.
867 if REVLOGV2_REQUIREMENT in self.requirements:
868 self.svfs.options['revlogv2'] = True
869 901
870 def _writerequirements(self): 902 def _writerequirements(self):
871 scmutil.writerequires(self.vfs, self.requirements) 903 scmutil.writerequires(self.vfs, self.requirements)
872 904
873 # Don't cache auditor/nofsauditor, or you'll end up with reference cycle: 905 # Don't cache auditor/nofsauditor, or you'll end up with reference cycle: