mirror of
https://github.com/theonedev/onedev.git
synced 2025-12-10 00:07:37 -06:00
feat: Improve startup speed when there are many projects (OD-2509)
This commit is contained in:
parent
af58b47205
commit
b46dfb8637
@ -8159,4 +8159,26 @@ public class DataMigrator {
|
||||
}
|
||||
}
|
||||
|
||||
private void migrate207(File dataDir, Stack<Integer> versions) {
|
||||
for (File file : dataDir.listFiles()) {
|
||||
if (file.getName().startsWith("Projects.xml")) {
|
||||
VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file);
|
||||
for (Element element : dom.getRootElement().elements()) {
|
||||
element.element("lastEventDate").setName("lastActivityDate");
|
||||
}
|
||||
dom.writeToFile(file, false);
|
||||
} else if (file.getName().startsWith("ProjectLastEventDates.xml")) {
|
||||
VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file);
|
||||
for (Element element : dom.getRootElement().elements()) {
|
||||
element.setName("io.onedev.server.model.ProjectLastActivityDate");
|
||||
var commitElement = element.element("commit");
|
||||
if (commitElement != null)
|
||||
commitElement.detach();
|
||||
element.element("activity").setName("value");
|
||||
}
|
||||
dom.writeToFile(new File(dataDir, file.getName().replace("ProjectLastEventDates", "ProjectLastActivityDates")), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
package io.onedev.server.entitymanager;
|
||||
|
||||
import io.onedev.server.model.ProjectLastEventDate;
|
||||
import io.onedev.server.model.ProjectLastActivityDate;
|
||||
import io.onedev.server.persistence.dao.EntityManager;
|
||||
|
||||
public interface ProjectLastEventDateManager extends EntityManager<ProjectLastEventDate> {
|
||||
public interface ProjectLastEventDateManager extends EntityManager<ProjectLastActivityDate> {
|
||||
|
||||
void create(ProjectLastEventDate lastEventDate);
|
||||
void create(ProjectLastActivityDate lastEventDate);
|
||||
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import io.onedev.server.event.project.ProjectCreated;
|
||||
import io.onedev.server.event.project.ProjectEvent;
|
||||
import io.onedev.server.event.project.RefUpdated;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.ProjectLastEventDate;
|
||||
import io.onedev.server.model.ProjectLastActivityDate;
|
||||
import io.onedev.server.persistence.annotation.Transactional;
|
||||
import io.onedev.server.persistence.dao.BaseEntityManager;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
@ -17,7 +17,7 @@ import javax.inject.Singleton;
|
||||
import java.util.Date;
|
||||
|
||||
@Singleton
|
||||
public class DefaultProjectLastEventDateManager extends BaseEntityManager<ProjectLastEventDate> implements ProjectLastEventDateManager {
|
||||
public class DefaultProjectLastEventDateManager extends BaseEntityManager<ProjectLastActivityDate> implements ProjectLastEventDateManager {
|
||||
|
||||
@Inject
|
||||
public DefaultProjectLastEventDateManager(Dao dao) {
|
||||
@ -29,18 +29,17 @@ public class DefaultProjectLastEventDateManager extends BaseEntityManager<Projec
|
||||
public void on(ProjectEvent event) {
|
||||
Project project = event.getProject();
|
||||
if (event instanceof RefUpdated) {
|
||||
project.getLastEventDate().setActivity(new Date());
|
||||
project.getLastEventDate().setCommit(new Date());
|
||||
project.getLastActivityDate().setValue(new Date());
|
||||
} else if (!(event instanceof ProjectCreated)
|
||||
&& event.getUser() != null
|
||||
&& !event.getUser().isSystem()) {
|
||||
project.getLastEventDate().setActivity(new Date());
|
||||
project.getLastActivityDate().setValue(new Date());
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void create(ProjectLastEventDate lastEventDate) {
|
||||
public void create(ProjectLastActivityDate lastEventDate) {
|
||||
Preconditions.checkState(lastEventDate.isNew());
|
||||
dao.persist(lastEventDate);
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@ import static io.onedev.commons.utils.LockUtils.read;
|
||||
import static io.onedev.k8shelper.KubernetesHelper.BEARER;
|
||||
import static io.onedev.server.git.CommandUtils.callWithClusterCredential;
|
||||
import static io.onedev.server.git.GitUtils.getDefaultBranch;
|
||||
import static io.onedev.server.git.GitUtils.getLastCommit;
|
||||
import static io.onedev.server.git.GitUtils.getReachableCommits;
|
||||
import static io.onedev.server.git.GitUtils.isValid;
|
||||
import static io.onedev.server.git.GitUtils.setDefaultBranch;
|
||||
@ -87,7 +86,6 @@ import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.lib.StoredConfig;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
import org.hibernate.query.Query;
|
||||
@ -160,7 +158,7 @@ import io.onedev.server.model.LinkSpec;
|
||||
import io.onedev.server.model.Pack;
|
||||
import io.onedev.server.model.PackBlob;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.ProjectLastEventDate;
|
||||
import io.onedev.server.model.ProjectLastActivityDate;
|
||||
import io.onedev.server.model.PullRequest;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.UserAuthorization;
|
||||
@ -383,8 +381,8 @@ public class DefaultProjectManager extends BaseEntityManager<Project>
|
||||
}
|
||||
project.setPath(project.calcPath());
|
||||
|
||||
ProjectLastEventDate lastEventDate = new ProjectLastEventDate();
|
||||
project.setLastEventDate(lastEventDate);
|
||||
ProjectLastActivityDate lastEventDate = new ProjectLastActivityDate();
|
||||
project.setLastActivityDate(lastEventDate);
|
||||
lastEventDateManager.create(lastEventDate);
|
||||
dao.persist(project);
|
||||
|
||||
@ -519,7 +517,7 @@ public class DefaultProjectManager extends BaseEntityManager<Project>
|
||||
packBlobManager.delete(packBlob);
|
||||
|
||||
dao.remove(project);
|
||||
lastEventDateManager.delete(project.getLastEventDate());
|
||||
lastEventDateManager.delete(project.getLastActivityDate());
|
||||
|
||||
synchronized (repositoryCache) {
|
||||
Repository repository = repositoryCache.remove(project.getId());
|
||||
@ -622,7 +620,6 @@ public class DefaultProjectManager extends BaseEntityManager<Project>
|
||||
@Transactional
|
||||
@Override
|
||||
public void fork(Project from, Project to) {
|
||||
to.getLastEventDate().setCommit(from.getLastEventDate().getCommit());
|
||||
Long fromId = from.getId();
|
||||
String fromPath = from.getPath();
|
||||
Long toId = to.getId();
|
||||
@ -816,8 +813,8 @@ public class DefaultProjectManager extends BaseEntityManager<Project>
|
||||
cache.put(project.getId(), project.getFacade());
|
||||
}
|
||||
|
||||
Map<Long, ProjectLastEventDate> lastEventDates = new HashMap<>();
|
||||
for (ProjectLastEventDate lastEventDate : lastEventDateManager.query())
|
||||
Map<Long, ProjectLastActivityDate> lastEventDates = new HashMap<>();
|
||||
for (ProjectLastActivityDate lastEventDate : lastEventDateManager.query())
|
||||
lastEventDates.put(lastEventDate.getId(), lastEventDate);
|
||||
|
||||
logger.info("Checking projects...");
|
||||
@ -842,16 +839,6 @@ public class DefaultProjectManager extends BaseEntityManager<Project>
|
||||
checkGitDir(projectId);
|
||||
HookUtils.checkHooks(getGitDir(projectId));
|
||||
checkGitConfig(projectId, project.getGitPackConfig());
|
||||
|
||||
if (project.isCodeManagement()) {
|
||||
ProjectLastEventDate lastEventDate = lastEventDates.get(project.getLastEventDateId());
|
||||
RevCommit lastCommit = getLastCommit(getRepository(projectId));
|
||||
if (lastCommit != null) {
|
||||
var lastCommitDate = lastCommit.getCommitterIdent().getWhen();
|
||||
if (lastEventDate.getCommit() == null || lastEventDate.getCommit().before(lastCommitDate))
|
||||
lastEventDate.setCommit(lastCommitDate);
|
||||
}
|
||||
}
|
||||
|
||||
LinkedHashMap<String, ProjectReplica> newReplicasOfProject;
|
||||
var replica = new ProjectReplica();
|
||||
@ -877,7 +864,7 @@ public class DefaultProjectManager extends BaseEntityManager<Project>
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
updateActiveServer(projectId, newReplicasOfProject, false);
|
||||
updateActiveServer(projectId, newReplicasOfProject, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1062,16 +1049,16 @@ public class DefaultProjectManager extends BaseEntityManager<Project>
|
||||
for (var order: orders) {
|
||||
if (order.getExpression() instanceof SingularAttributePath) {
|
||||
var expr = (SingularAttributePath) order.getExpression();
|
||||
if (expr.getAttribute().getName().equals(ProjectLastEventDate.PROP_ACTIVITY)
|
||||
if (expr.getAttribute().getName().equals(ProjectLastActivityDate.PROP_VALUE)
|
||||
&& expr.getPathSource() instanceof SingularAttributePath
|
||||
&& ((SingularAttributePath) expr.getPathSource()).getAttribute().getName().equals(Project.PROP_LAST_EVENT_DATE)) {
|
||||
&& ((SingularAttributePath) expr.getPathSource()).getAttribute().getName().equals(Project.PROP_LAST_ACTIVITY_DATE)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
orders.add(builder.desc(ProjectQuery.getPath(root, Project.PROP_LAST_EVENT_DATE + "." + ProjectLastEventDate.PROP_ACTIVITY)));
|
||||
orders.add(builder.desc(ProjectQuery.getPath(root, Project.PROP_LAST_ACTIVITY_DATE + "." + ProjectLastActivityDate.PROP_VALUE)));
|
||||
|
||||
query.orderBy(orders);
|
||||
|
||||
|
||||
@ -145,7 +145,7 @@ import io.onedev.server.xodus.CommitInfoManager;
|
||||
@Table(
|
||||
indexes={
|
||||
@Index(columnList="o_parent_id"), @Index(columnList="o_forkedFrom_id"),
|
||||
@Index(columnList="o_lastEventDate_id"), @Index(columnList=PROP_NAME),
|
||||
@Index(columnList="o_lastActivityDate_id"), @Index(columnList=PROP_NAME),
|
||||
@Index(columnList=PROP_PATH)
|
||||
},
|
||||
uniqueConstraints={@UniqueConstraint(columnNames={"o_parent_id", PROP_NAME})}
|
||||
@ -187,10 +187,8 @@ public class Project extends AbstractEntity implements LabelSupport<ProjectLabel
|
||||
public static final String PROP_FORKED_FROM = "forkedFrom";
|
||||
|
||||
public static final String NAME_LAST_ACTIVITY_DATE = "Last Activity Date";
|
||||
|
||||
public static final String NAME_LAST_COMMIT_DATE = "Last Commit Date";
|
||||
|
||||
public static final String PROP_LAST_EVENT_DATE = "lastEventDate";
|
||||
public static final String PROP_LAST_ACTIVITY_DATE = "lastActivityDate";
|
||||
|
||||
public static final String NAME_LABEL = "Label";
|
||||
|
||||
@ -219,7 +217,7 @@ public class Project extends AbstractEntity implements LabelSupport<ProjectLabel
|
||||
private static final String FAKED_GITHUB_REPO_OWNER = "faked-owner-of-onedev-project";
|
||||
|
||||
public static final List<String> QUERY_FIELDS = Lists.newArrayList(
|
||||
NAME_NAME, NAME_KEY, NAME_PATH, NAME_LABEL, NAME_SERVICE_DESK_EMAIL_ADDRESS, NAME_ID, NAME_DESCRIPTION, NAME_LAST_ACTIVITY_DATE, NAME_LAST_COMMIT_DATE);
|
||||
NAME_NAME, NAME_KEY, NAME_PATH, NAME_LABEL, NAME_SERVICE_DESK_EMAIL_ADDRESS, NAME_ID, NAME_DESCRIPTION, NAME_LAST_ACTIVITY_DATE);
|
||||
|
||||
public static final Map<String, SortField<Project>> SORT_FIELDS = new LinkedHashMap<>();
|
||||
static {
|
||||
@ -228,8 +226,7 @@ public class Project extends AbstractEntity implements LabelSupport<ProjectLabel
|
||||
SORT_FIELDS.put(NAME_NAME, new SortField<>(PROP_NAME));
|
||||
SORT_FIELDS.put(NAME_KEY, new SortField<>(PROP_KEY));
|
||||
SORT_FIELDS.put(NAME_SERVICE_DESK_EMAIL_ADDRESS, new SortField<>(PROP_SERVICE_DESK_EMAIL_ADDRESS));
|
||||
SORT_FIELDS.put(NAME_LAST_ACTIVITY_DATE, new SortField<>(PROP_LAST_EVENT_DATE + "." + ProjectLastEventDate.PROP_ACTIVITY, DESCENDING));
|
||||
SORT_FIELDS.put(NAME_LAST_COMMIT_DATE, new SortField<>(PROP_LAST_EVENT_DATE + "." + ProjectLastEventDate.PROP_COMMIT, DESCENDING));
|
||||
SORT_FIELDS.put(NAME_LAST_ACTIVITY_DATE, new SortField<>(PROP_LAST_ACTIVITY_DATE + "." + ProjectLastActivityDate.PROP_VALUE, DESCENDING));
|
||||
}
|
||||
|
||||
static ThreadLocal<Stack<Project>> stack = ThreadLocal.withInitial(() -> new Stack<>());
|
||||
@ -257,7 +254,7 @@ public class Project extends AbstractEntity implements LabelSupport<ProjectLabel
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(unique=true, nullable=false)
|
||||
private ProjectLastEventDate lastEventDate;
|
||||
private ProjectLastActivityDate lastActivityDate;
|
||||
|
||||
@Column(nullable=false)
|
||||
private String name;
|
||||
@ -546,12 +543,12 @@ public class Project extends AbstractEntity implements LabelSupport<ProjectLabel
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
public ProjectLastEventDate getLastEventDate() {
|
||||
return lastEventDate;
|
||||
public ProjectLastActivityDate getLastActivityDate() {
|
||||
return lastActivityDate;
|
||||
}
|
||||
|
||||
public void setLastEventDate(ProjectLastEventDate lastEventDate) {
|
||||
this.lastEventDate = lastEventDate;
|
||||
public void setLastActivityDate(ProjectLastActivityDate lastActivityDate) {
|
||||
this.lastActivityDate = lastActivityDate;
|
||||
}
|
||||
|
||||
public Collection<PullRequest> getIncomingRequests() {
|
||||
@ -756,7 +753,7 @@ public class Project extends AbstractEntity implements LabelSupport<ProjectLabel
|
||||
|
||||
public ProjectFacade getFacade() {
|
||||
return new ProjectFacade(getId(), getName(), getKey(), getPath(), getServiceDeskEmailAddress(),
|
||||
isCodeManagement(), isIssueManagement(), getGitPackConfig(), lastEventDate.getId(),
|
||||
isCodeManagement(), isIssueManagement(), getGitPackConfig(), lastActivityDate.getId(),
|
||||
idOf(getParent()), idOf(getForkedFrom()));
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
package io.onedev.server.model;
|
||||
|
||||
import static io.onedev.server.model.ProjectLastActivityDate.PROP_VALUE;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* Maintain high dynamic data in a separate table to avoid project second-level
|
||||
* cache being invalidated frequently
|
||||
*/
|
||||
@Entity
|
||||
@Table(indexes={@Index(columnList= PROP_VALUE)})
|
||||
public class ProjectLastActivityDate extends AbstractEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String PROP_VALUE = "value";
|
||||
|
||||
@Column(nullable=false)
|
||||
private Date value = new Date();
|
||||
|
||||
public Date getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Date value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
package io.onedev.server.model;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.Table;
|
||||
import java.util.Date;
|
||||
|
||||
import static io.onedev.server.model.ProjectLastEventDate.PROP_ACTIVITY;
|
||||
import static io.onedev.server.model.ProjectLastEventDate.PROP_COMMIT;
|
||||
|
||||
/**
|
||||
* Maintain high dynamic data in a separate table to avoid project second-level
|
||||
* cache being invalidated frequently
|
||||
*/
|
||||
@Entity
|
||||
@Table(indexes={@Index(columnList= PROP_ACTIVITY), @Index(columnList= PROP_COMMIT)})
|
||||
public class ProjectLastEventDate extends AbstractEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String PROP_ACTIVITY = "activity";
|
||||
|
||||
public static final String PROP_COMMIT = "commit";
|
||||
|
||||
@Column(nullable=false)
|
||||
private Date activity = new Date();
|
||||
|
||||
private Date commit;
|
||||
|
||||
public Date getActivity() {
|
||||
return activity;
|
||||
}
|
||||
|
||||
public void setActivity(Date activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Date getCommit() {
|
||||
return commit;
|
||||
}
|
||||
|
||||
public void setCommit(@Nullable Date commit) {
|
||||
this.commit = commit;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,55 +1,23 @@
|
||||
package io.onedev.server.search.code;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Splitter;
|
||||
import io.onedev.commons.jsymbol.Symbol;
|
||||
import io.onedev.commons.jsymbol.SymbolExtractor;
|
||||
import io.onedev.commons.jsymbol.SymbolExtractorRegistry;
|
||||
import io.onedev.commons.loader.ManagedSerializedForm;
|
||||
import io.onedev.commons.utils.ExceptionUtils;
|
||||
import io.onedev.commons.utils.FileUtils;
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
import io.onedev.server.cluster.ClusterTask;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.event.Listen;
|
||||
import io.onedev.server.event.ListenerRegistry;
|
||||
import io.onedev.server.event.project.CommitIndexed;
|
||||
import io.onedev.server.event.project.RefUpdated;
|
||||
import io.onedev.server.event.system.SystemStarted;
|
||||
import io.onedev.server.git.GitUtils;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.persistence.SessionManager;
|
||||
import io.onedev.server.persistence.annotation.Sessional;
|
||||
import io.onedev.server.util.ContentDetector;
|
||||
import io.onedev.server.util.IndexResult;
|
||||
import io.onedev.server.util.concurrent.BatchWorkManager;
|
||||
import io.onedev.server.util.concurrent.BatchWorker;
|
||||
import io.onedev.server.util.concurrent.Prioritized;
|
||||
import io.onedev.commons.utils.match.Matcher;
|
||||
import io.onedev.commons.utils.match.PathMatcher;
|
||||
import io.onedev.server.util.patternset.PatternSet;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.SerializationUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.apache.lucene.document.*;
|
||||
import org.apache.lucene.document.Field.Store;
|
||||
import org.apache.lucene.index.*;
|
||||
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
|
||||
import org.apache.lucene.search.BooleanClause.Occur;
|
||||
import org.apache.lucene.search.*;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.store.FSDirectory;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.wicket.request.cycle.RequestCycle;
|
||||
import org.eclipse.jgit.lib.*;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.eclipse.jgit.treewalk.filter.TreeFilter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import static io.onedev.server.search.code.FieldConstants.BLOB_HASH;
|
||||
import static io.onedev.server.search.code.FieldConstants.BLOB_INDEX_VERSION;
|
||||
import static io.onedev.server.search.code.FieldConstants.BLOB_NAME;
|
||||
import static io.onedev.server.search.code.FieldConstants.BLOB_PATH;
|
||||
import static io.onedev.server.search.code.FieldConstants.BLOB_PRIMARY_SYMBOLS;
|
||||
import static io.onedev.server.search.code.FieldConstants.BLOB_SECONDARY_SYMBOLS;
|
||||
import static io.onedev.server.search.code.FieldConstants.BLOB_SYMBOL_LIST;
|
||||
import static io.onedev.server.search.code.FieldConstants.BLOB_TEXT;
|
||||
import static io.onedev.server.search.code.FieldConstants.COMMIT_HASH;
|
||||
import static io.onedev.server.search.code.FieldConstants.COMMIT_INDEX_VERSION;
|
||||
import static io.onedev.server.search.code.FieldConstants.LAST_COMMIT;
|
||||
import static io.onedev.server.search.code.FieldConstants.LAST_COMMIT_HASH;
|
||||
import static io.onedev.server.search.code.FieldConstants.LAST_COMMIT_INDEX_VERSION;
|
||||
import static io.onedev.server.search.code.FieldConstants.META;
|
||||
import static io.onedev.server.search.code.IndexConstants.MAX_INDEXABLE_BLOB_SIZE;
|
||||
import static io.onedev.server.search.code.IndexConstants.MAX_INDEXABLE_LINE_LEN;
|
||||
import static io.onedev.server.search.code.IndexConstants.NGRAM_SIZE;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectStreamException;
|
||||
@ -58,8 +26,70 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static io.onedev.server.search.code.FieldConstants.*;
|
||||
import static io.onedev.server.search.code.IndexConstants.*;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang.SerializationUtils;
|
||||
import org.apache.lucene.document.BinaryDocValuesField;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field.Store;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.document.StringField;
|
||||
import org.apache.lucene.document.TextField;
|
||||
import org.apache.lucene.index.DirectoryReader;
|
||||
import org.apache.lucene.index.IndexFormatTooOldException;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexWriterConfig;
|
||||
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.BooleanClause.Occur;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.ScoreMode;
|
||||
import org.apache.lucene.search.SimpleCollector;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.store.FSDirectory;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.wicket.request.cycle.RequestCycle;
|
||||
import org.eclipse.jgit.lib.AnyObjectId;
|
||||
import org.eclipse.jgit.lib.FileMode;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectLoader;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.eclipse.jgit.treewalk.filter.TreeFilter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Splitter;
|
||||
|
||||
import io.onedev.commons.jsymbol.Symbol;
|
||||
import io.onedev.commons.jsymbol.SymbolExtractor;
|
||||
import io.onedev.commons.jsymbol.SymbolExtractorRegistry;
|
||||
import io.onedev.commons.loader.ManagedSerializedForm;
|
||||
import io.onedev.commons.utils.ExceptionUtils;
|
||||
import io.onedev.commons.utils.FileUtils;
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
import io.onedev.commons.utils.match.Matcher;
|
||||
import io.onedev.commons.utils.match.PathMatcher;
|
||||
import io.onedev.server.cluster.ClusterTask;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.event.ListenerRegistry;
|
||||
import io.onedev.server.event.project.CommitIndexed;
|
||||
import io.onedev.server.git.GitUtils;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.persistence.SessionManager;
|
||||
import io.onedev.server.util.ContentDetector;
|
||||
import io.onedev.server.util.IndexResult;
|
||||
import io.onedev.server.util.concurrent.BatchWorkManager;
|
||||
import io.onedev.server.util.concurrent.BatchWorker;
|
||||
import io.onedev.server.util.concurrent.Prioritized;
|
||||
import io.onedev.server.util.patternset.PatternSet;
|
||||
|
||||
@Singleton
|
||||
public class DefaultCodeIndexManager implements CodeIndexManager, Serializable {
|
||||
@ -319,7 +349,8 @@ public class DefaultCodeIndexManager implements CodeIndexManager, Serializable {
|
||||
}
|
||||
|
||||
private IndexResult doIndex(Project project, ObjectId commit) {
|
||||
try (Directory directory = FSDirectory.open(projectManager.getIndexDir(project.getId()).toPath())) {
|
||||
var indexDir = projectManager.getIndexDir(project.getId());
|
||||
try (Directory directory = FSDirectory.open(indexDir.toPath())) {
|
||||
if (DirectoryReader.indexExists(directory)) {
|
||||
try (IndexReader reader = DirectoryReader.open(directory)) {
|
||||
IndexSearcher searcher = new IndexSearcher(reader);
|
||||
@ -327,6 +358,9 @@ public class DefaultCodeIndexManager implements CodeIndexManager, Serializable {
|
||||
return new IndexResult(0, 0);
|
||||
else
|
||||
return doIndex(project, commit, directory, searcher);
|
||||
} catch (IndexFormatTooOldException e) {
|
||||
FileUtils.cleanDir(indexDir);
|
||||
return doIndex(project, commit, directory, null);
|
||||
}
|
||||
} else {
|
||||
return doIndex(project, commit, directory, null);
|
||||
@ -364,6 +398,9 @@ public class DefaultCodeIndexManager implements CodeIndexManager, Serializable {
|
||||
try (IndexReader reader = DirectoryReader.open(directory)) {
|
||||
IndexSearcher searcher = new IndexSearcher(reader);
|
||||
return getIndexVersion().equals(getCommitIndexVersion(searcher, commitId));
|
||||
} catch (IndexFormatTooOldException e) {
|
||||
FileUtils.cleanDir(indexDir);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
@ -375,41 +412,6 @@ public class DefaultCodeIndexManager implements CodeIndexManager, Serializable {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(RefUpdated event) {
|
||||
// only index branches at back end, tags will be indexed on demand from GUI
|
||||
// as many tags might be pushed all at once when the repository is imported
|
||||
if (event.getRefName().startsWith(Constants.R_HEADS)
|
||||
&& !event.getNewCommitId().equals(ObjectId.zeroId())) {
|
||||
IndexWork work = new IndexWork(BACKEND_PRIORITY, event.getNewCommitId());
|
||||
batchWorkManager.submit(getBatchWorker(event.getProject().getId()), work);
|
||||
}
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(SystemStarted event) {
|
||||
for (var file: projectManager.getProjectsDir().listFiles()) {
|
||||
if (!NumberUtils.isDigits(file.getName()))
|
||||
continue;
|
||||
var projectId = Long.valueOf(file.getName());
|
||||
var indexDir = projectManager.getIndexDir(projectId);
|
||||
if (indexDir.exists()) {
|
||||
try (var directory = FSDirectory.open(indexDir.toPath())) {
|
||||
if (DirectoryReader.indexExists(directory)) {
|
||||
try (var reader = DirectoryReader.open(directory)) {
|
||||
} catch (IndexFormatTooOldException e) {
|
||||
FileUtils.cleanDir(indexDir);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void indexAsync(Long projectId, ObjectId commitId) {
|
||||
|
||||
@ -1,62 +0,0 @@
|
||||
package io.onedev.server.search.entity.project;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.From;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.ProjectLastEventDate;
|
||||
import io.onedev.server.search.entity.EntityQuery;
|
||||
import io.onedev.server.util.ProjectScope;
|
||||
import io.onedev.server.util.criteria.Criteria;
|
||||
|
||||
public class CommitDateCriteria extends Criteria<Project> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final int operator;
|
||||
|
||||
private final String value;
|
||||
|
||||
private final Date date;
|
||||
|
||||
public CommitDateCriteria(String value, int operator) {
|
||||
date = EntityQuery.getDateValue(value);
|
||||
this.operator = operator;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate getPredicate(@Nullable ProjectScope projectScope, CriteriaQuery<?> query, From<Project, Project> from, CriteriaBuilder builder) {
|
||||
Path<Date> attribute = ProjectQuery.getPath(from, Project.PROP_LAST_EVENT_DATE + "." + ProjectLastEventDate.PROP_COMMIT);
|
||||
if (operator == ProjectQueryLexer.IsUntil)
|
||||
return builder.lessThan(attribute, date);
|
||||
else
|
||||
return builder.greaterThan(attribute, date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Project project) {
|
||||
if (project.getLastEventDate().getCommit() != null) {
|
||||
if (operator == ProjectQueryLexer.IsUntil)
|
||||
return project.getLastEventDate().getCommit().before(date);
|
||||
else
|
||||
return project.getLastEventDate().getCommit().after(date);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringWithoutParens() {
|
||||
return Criteria.quote(Project.NAME_LAST_COMMIT_DATE) + " "
|
||||
+ ProjectQuery.getRuleName(operator) + " "
|
||||
+ Criteria.quote(value);
|
||||
}
|
||||
|
||||
}
|
||||
@ -10,7 +10,7 @@ import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.ProjectLastEventDate;
|
||||
import io.onedev.server.model.ProjectLastActivityDate;
|
||||
import io.onedev.server.search.entity.EntityQuery;
|
||||
import io.onedev.server.util.ProjectScope;
|
||||
import io.onedev.server.util.criteria.Criteria;
|
||||
@ -41,7 +41,7 @@ public class LastActivityDateCriteria extends Criteria<Project> {
|
||||
|
||||
@Override
|
||||
public Predicate getPredicate(@Nullable ProjectScope projectScope, CriteriaQuery<?> query, From<Project, Project> from, CriteriaBuilder builder) {
|
||||
Path<Date> attribute = ProjectQuery.getPath(from, Project.PROP_LAST_EVENT_DATE + "." + ProjectLastEventDate.PROP_ACTIVITY);
|
||||
Path<Date> attribute = ProjectQuery.getPath(from, Project.PROP_LAST_ACTIVITY_DATE + "." + ProjectLastActivityDate.PROP_VALUE);
|
||||
if (operator == ProjectQueryLexer.IsUntil)
|
||||
return builder.lessThan(attribute, date);
|
||||
else
|
||||
@ -51,9 +51,9 @@ public class LastActivityDateCriteria extends Criteria<Project> {
|
||||
@Override
|
||||
public boolean matches(Project project) {
|
||||
if (operator == ProjectQueryLexer.IsUntil)
|
||||
return project.getLastEventDate().getActivity().before(date);
|
||||
return project.getLastActivityDate().getValue().before(date);
|
||||
else
|
||||
return project.getLastEventDate().getActivity().after(date);
|
||||
return project.getLastActivityDate().getValue().after(date);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -182,10 +182,7 @@ public class ProjectQuery extends EntityQuery<Project> {
|
||||
break;
|
||||
case IsUntil:
|
||||
case IsSince:
|
||||
if (fieldName.equals(Project.NAME_LAST_ACTIVITY_DATE))
|
||||
criterias.add(new LastActivityDateCriteria(value, operator));
|
||||
else
|
||||
criterias.add(new CommitDateCriteria(value, operator));
|
||||
criterias.add(new LastActivityDateCriteria(value, operator));
|
||||
break;
|
||||
default:
|
||||
throw new ExplicitException("Unexpected operator " + getRuleName(operator));
|
||||
@ -272,7 +269,7 @@ public class ProjectQuery extends EntityQuery<Project> {
|
||||
case IsUntil:
|
||||
case IsSince:
|
||||
if (!fieldName.equals(Project.NAME_LAST_ACTIVITY_DATE)
|
||||
&& !fieldName.equals(Project.NAME_LAST_COMMIT_DATE)) {
|
||||
&& !fieldName.equals(Project.NAME_LAST_ACTIVITY_DATE)) {
|
||||
throw newOperatorException(fieldName, operator);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -87,7 +87,7 @@ public class ProjectQueryBehavior extends ANTLRAssistBehavior {
|
||||
String fieldName = ProjectQuery.getValue(fieldElements.get(0).getMatchedText());
|
||||
try {
|
||||
ProjectQuery.checkField(fieldName, operator);
|
||||
if (fieldName.equals(Project.NAME_LAST_ACTIVITY_DATE) || fieldName.equals(Project.NAME_LAST_COMMIT_DATE)) {
|
||||
if (fieldName.equals(Project.NAME_LAST_ACTIVITY_DATE)) {
|
||||
List<InputSuggestion> suggestions = SuggestionUtils.suggest(DateUtils.RELAX_DATE_EXAMPLES, matchWith);
|
||||
return !suggestions.isEmpty()? suggestions: null;
|
||||
} else if (fieldName.equals(Project.NAME_NAME)) {
|
||||
|
||||
@ -23,7 +23,6 @@ import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
@ -31,6 +30,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
@ -57,8 +57,6 @@ import org.slf4j.LoggerFactory;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.hazelcast.map.EntryProcessor;
|
||||
import com.hazelcast.map.IMap;
|
||||
|
||||
import io.onedev.commons.loader.ManagedSerializedForm;
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
@ -81,7 +79,6 @@ import io.onedev.server.event.project.ActiveServerChanged;
|
||||
import io.onedev.server.event.project.RefUpdated;
|
||||
import io.onedev.server.event.project.issue.IssueCommitsAttached;
|
||||
import io.onedev.server.event.system.SystemStarted;
|
||||
import io.onedev.server.event.system.SystemStarting;
|
||||
import io.onedev.server.git.GitContribution;
|
||||
import io.onedev.server.git.GitContributor;
|
||||
import io.onedev.server.git.GitUtils;
|
||||
@ -211,9 +208,7 @@ public class DefaultCommitInfoManager extends AbstractEnvironmentManager
|
||||
|
||||
private final Map<Long, Integer> totalCommitCountCache = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<Long, List<NameAndEmail>> usersCache = new ConcurrentHashMap<>();
|
||||
|
||||
private volatile IMap<String, Set<Long>> contributedProjects;
|
||||
private final Map<Long, Pair<Set<NameAndEmail>, Set<String>>> usersCache = new ConcurrentHashMap<>();
|
||||
|
||||
@Inject
|
||||
public DefaultCommitInfoManager(ProjectManager projectManager,
|
||||
@ -257,6 +252,19 @@ public class DefaultCommitInfoManager extends AbstractEnvironmentManager
|
||||
Repository repository = projectManager.getRepository(project.getId());
|
||||
|
||||
Pair<byte[], ObjectId> result = env.computeInTransaction(txn -> {
|
||||
var users = usersCache.get(project.getId());
|
||||
if (users == null) {
|
||||
byte[] userBytes = readBytes(defaultStore, txn, USERS_KEY);
|
||||
if (userBytes != null) {
|
||||
Set<NameAndEmail> nameAndEmails = SerializationUtils.deserialize(userBytes);
|
||||
users = new Pair<>(nameAndEmails,
|
||||
nameAndEmails.stream().map(NameAndEmail::getEmailAddress).collect(Collectors.toSet()));
|
||||
} else {
|
||||
users = new Pair<>(new HashSet<>(), new HashSet<>());
|
||||
}
|
||||
usersCache.put(project.getId(), users);
|
||||
}
|
||||
|
||||
ByteIterable commitKey = new CommitByteIterable(commitId);
|
||||
byte[] commitBytes = readBytes(commitsStore, txn, commitKey);
|
||||
|
||||
@ -288,12 +296,7 @@ public class DefaultCommitInfoManager extends AbstractEnvironmentManager
|
||||
|
||||
Map<Long, Integer> commitCountCache = new HashMap<>();
|
||||
|
||||
Set<NameAndEmail> users;
|
||||
byte[] userBytes = readBytes(defaultStore, txn, USERS_KEY);
|
||||
if (userBytes != null)
|
||||
users = SerializationUtils.deserialize(userBytes);
|
||||
else
|
||||
users = new HashSet<>();
|
||||
var users = usersCache.get(project.getId());
|
||||
var usersChanged = new AtomicBoolean(false);
|
||||
|
||||
new ElementPumper<LogCommit>() {
|
||||
@ -384,14 +387,16 @@ public class DefaultCommitInfoManager extends AbstractEnvironmentManager
|
||||
}
|
||||
|
||||
if (currentCommit.getCommitter() != null) {
|
||||
if (users.add(new NameAndEmail(currentCommit.getCommitter())))
|
||||
if (users.getLeft().add(new NameAndEmail(currentCommit.getCommitter())))
|
||||
usersChanged.set(true);
|
||||
users.getRight().add(currentCommit.getCommitter().getEmailAddress());
|
||||
}
|
||||
|
||||
if (currentCommit.getAuthor() != null) {
|
||||
NameAndEmail nameAndEmail = new NameAndEmail(currentCommit.getAuthor());
|
||||
if (users.add(nameAndEmail))
|
||||
if (users.getLeft().add(nameAndEmail))
|
||||
usersChanged.set(true);
|
||||
users.getRight().add(nameAndEmail.getEmailAddress());
|
||||
|
||||
ByteIterable authorKey = new ArrayByteIterable(SerializationUtils.serialize(nameAndEmail));
|
||||
int userIndex = readInt(userToIndexStore, txn, authorKey, -1);
|
||||
@ -475,10 +480,8 @@ public class DefaultCommitInfoManager extends AbstractEnvironmentManager
|
||||
writeInt(defaultStore, txn, NEXT_PATH_INDEX_KEY, nextIndex.path);
|
||||
|
||||
if (usersChanged.get()) {
|
||||
userBytes = SerializationUtils.serialize((Serializable) users);
|
||||
var userBytes = SerializationUtils.serialize((Serializable) users.getLeft());
|
||||
defaultStore.put(txn, USERS_KEY, new ArrayByteIterable(userBytes));
|
||||
usersCache.remove(project.getId());
|
||||
updateContributedProjects(project.getId(), users);
|
||||
}
|
||||
|
||||
for (Map.Entry<Long, Integer> entry : commitCountCache.entrySet())
|
||||
@ -497,26 +500,6 @@ public class DefaultCommitInfoManager extends AbstractEnvironmentManager
|
||||
logger.debug("Collected commit information (project: {}, ref: {})", project.getPath(), refName);
|
||||
}
|
||||
|
||||
private void updateContributedProjects(Long projectId, Set<NameAndEmail> users) {
|
||||
Set<String> emailAddresses = users.stream()
|
||||
.map(NameAndEmail::getEmailAddress)
|
||||
.collect(toSet());
|
||||
|
||||
contributedProjects.executeOnKeys(emailAddresses, new EntryProcessor<String, Set<Long>, Object>() {
|
||||
@Override
|
||||
public Object process(IMap.Entry<String, Set<Long>> entry) {
|
||||
Set<Long> contributedProjectsOfEmail = entry.getValue();
|
||||
if (contributedProjectsOfEmail == null) {
|
||||
contributedProjectsOfEmail = new HashSet<>();
|
||||
}
|
||||
if (contributedProjectsOfEmail.add(projectId)) {
|
||||
entry.setValue(contributedProjectsOfEmail);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void collectContributions(Project project, ObjectId commitId) {
|
||||
Environment env = getEnv(project.getId().toString());
|
||||
Store defaultStore = getStore(env, DEFAULT_STORE);
|
||||
@ -908,25 +891,14 @@ public class DefaultCommitInfoManager extends AbstractEnvironmentManager
|
||||
|
||||
@Override
|
||||
public List<NameAndEmail> call() {
|
||||
List<NameAndEmail> users = usersCache.get(projectId);
|
||||
if (users == null) {
|
||||
Environment env = getEnv(projectId.toString());
|
||||
Store store = getStore(env, DEFAULT_STORE);
|
||||
|
||||
users = env.computeInReadonlyTransaction(txn -> {
|
||||
byte[] bytes = readBytes(store, txn, USERS_KEY);
|
||||
if (bytes != null) {
|
||||
List<NameAndEmail> innerUsers =
|
||||
new ArrayList<>(SerializationUtils.deserialize(bytes));
|
||||
Collections.sort(innerUsers);
|
||||
return innerUsers;
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
});
|
||||
usersCache.put(projectId, users);
|
||||
var users = usersCache.get(projectId);
|
||||
if (users != null) {
|
||||
var sortedUsers = new ArrayList<>(users.getLeft());
|
||||
Collections.sort(sortedUsers);
|
||||
return sortedUsers;
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
});
|
||||
@ -1112,8 +1084,37 @@ public class DefaultCommitInfoManager extends AbstractEnvironmentManager
|
||||
sessionManager.run(() -> {
|
||||
Project project = projectManager.load(projectId);
|
||||
List<CollectingWork> collectingWorks = new ArrayList<>();
|
||||
for (Object work : works)
|
||||
collectingWorks.add((CollectingWork) work);
|
||||
for (Object work : works) {
|
||||
if (work instanceof CollectingWork) {
|
||||
collectingWorks.add((CollectingWork) work);
|
||||
} else if (work instanceof CheckingWork) {
|
||||
try (RevWalk revWalk = new RevWalk(projectManager.getRepository(projectId))) {
|
||||
Collection<Ref> refs = new ArrayList<>();
|
||||
refs.addAll(projectManager.getRepository(projectId).getRefDatabase()
|
||||
.getRefsByPrefix(Constants.R_HEADS));
|
||||
refs.addAll(projectManager.getRepository(projectId).getRefDatabase()
|
||||
.getRefsByPrefix(Constants.R_TAGS));
|
||||
|
||||
for (Ref ref : refs) {
|
||||
RevObject revObj;
|
||||
try {
|
||||
revObj = revWalk.peel(revWalk.parseAny(ref.getObjectId()));
|
||||
} catch (MissingObjectException e) {
|
||||
var message = String.format("%s (project id: %d, ref: %s)", e.getMessage(),
|
||||
projectId, ref.getName());
|
||||
throw new ExplicitException(message);
|
||||
}
|
||||
if (revObj instanceof RevCommit) {
|
||||
RevCommit commit = (RevCommit) revObj;
|
||||
collectingWorks.add(new CollectingWork(CHECK_PRIORITY, commit.copy(),
|
||||
commit.getCommitTime(), ref.getName()));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
collectingWorks.sort(new CommitTimeComparator());
|
||||
|
||||
for (CollectingWork work : collectingWorks)
|
||||
@ -1124,65 +1125,12 @@ public class DefaultCommitInfoManager extends AbstractEnvironmentManager
|
||||
};
|
||||
}
|
||||
|
||||
private boolean collect(Long projectId, int priority) {
|
||||
List<CollectingWork> works = new ArrayList<>();
|
||||
try (RevWalk revWalk = new RevWalk(projectManager.getRepository(projectId))) {
|
||||
Collection<Ref> refs = new ArrayList<>();
|
||||
refs.addAll(projectManager.getRepository(projectId).getRefDatabase().getRefsByPrefix(Constants.R_HEADS));
|
||||
refs.addAll(projectManager.getRepository(projectId).getRefDatabase().getRefsByPrefix(Constants.R_TAGS));
|
||||
|
||||
for (Ref ref : refs) {
|
||||
RevObject revObj;
|
||||
try {
|
||||
revObj = revWalk.peel(revWalk.parseAny(ref.getObjectId()));
|
||||
} catch (MissingObjectException e) {
|
||||
var message = String.format("%s (project id: %d, ref: %s)", e.getMessage(), projectId, ref.getName());
|
||||
throw new ExplicitException(message);
|
||||
}
|
||||
if (revObj instanceof RevCommit) {
|
||||
RevCommit commit = (RevCommit) revObj;
|
||||
works.add(new CollectingWork(priority, commit.copy(),
|
||||
commit.getCommitTime(), ref.getName()));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (!works.isEmpty()) {
|
||||
works.sort(new CommitTimeComparator());
|
||||
|
||||
for (CollectingWork work : works)
|
||||
batchWorkManager.submit(getBatchWorker(projectId), work);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(SystemStarting event) {
|
||||
contributedProjects = clusterManager.getHazelcastInstance().getMap("contributedProjects");
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(SystemStarted event) {
|
||||
logger.info("Caching code contribution info...");
|
||||
for (var projectId: projectManager.getActiveIds()) {
|
||||
checkVersion(getEnvDir(projectId.toString()));
|
||||
if (collect(projectId, CHECK_PRIORITY)) {
|
||||
Environment env = getEnv(projectId.toString());
|
||||
Store store = getStore(env, DEFAULT_STORE);
|
||||
|
||||
env.computeInReadonlyTransaction(txn -> {
|
||||
byte[] bytes = readBytes(store, txn, USERS_KEY);
|
||||
if (bytes != null)
|
||||
updateContributedProjects(projectId, SerializationUtils.deserialize(bytes));
|
||||
return null;
|
||||
});
|
||||
}
|
||||
batchWorkManager.submit(getBatchWorker(projectId), new CheckingWork(CHECK_PRIORITY));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1191,7 +1139,7 @@ public class DefaultCommitInfoManager extends AbstractEnvironmentManager
|
||||
public void on(ActiveServerChanged event) {
|
||||
for (var projectId: event.getProjectIds()) {
|
||||
checkVersion(getEnvDir(projectId.toString()));
|
||||
collect(projectId, CHECK_PRIORITY);
|
||||
batchWorkManager.submit(getBatchWorker(projectId), new CheckingWork(CHECK_PRIORITY));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1258,6 +1206,14 @@ public class DefaultCommitInfoManager extends AbstractEnvironmentManager
|
||||
});
|
||||
}
|
||||
|
||||
static class CheckingWork extends Prioritized {
|
||||
|
||||
public CheckingWork(int priority) {
|
||||
super(priority);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CollectingWork extends Prioritized {
|
||||
|
||||
private final String refName;
|
||||
@ -1564,53 +1520,54 @@ public class DefaultCommitInfoManager extends AbstractEnvironmentManager
|
||||
@Sessional
|
||||
@Override
|
||||
public Map<Long, Map<ObjectId, Long>> getUserCommits(User user, Date fromDate, Date toDate) {
|
||||
var emailAddresses = user.getEmailAddresses().stream()
|
||||
.filter(it -> it.isVerified())
|
||||
.map(it -> it.getValue())
|
||||
.collect(toSet());
|
||||
|
||||
var userCommits = new HashMap<Long, Map<ObjectId, Long>>();
|
||||
if (contributedProjects != null) {
|
||||
var emailAddresses = user.getEmailAddresses().stream()
|
||||
.filter(it -> it.isVerified())
|
||||
.map(it -> it.getValue())
|
||||
.collect(toSet());
|
||||
var cache = projectManager.cloneCache();
|
||||
var projectIds = new HashSet<Long>();
|
||||
contributedProjects.getAll(emailAddresses).values().stream()
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(projectIds::addAll);
|
||||
for (var projectId: projectIds) {
|
||||
var project = cache.get(projectId);
|
||||
if (project != null && project.isCodeManagement() && project.getForkedFromId() == null) {
|
||||
var activeServer = projectManager.getActiveServer(projectId, false);
|
||||
if (activeServer != null) {
|
||||
userCommits.put(projectId, clusterManager.runOnServer(activeServer, new ClusterTask<>() {
|
||||
Map<String, Map<Long, Map<ObjectId, Long>>> result = clusterManager.runOnAllServers(new ClusterTask<>() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Map<ObjectId, Long> call() {
|
||||
Environment env = getEnv(projectId.toString());
|
||||
Store userCommitsStore = getStore(env, USER_COMMITS_STORE);
|
||||
|
||||
return env.computeInReadonlyTransaction(new TransactionalComputable<Map<ObjectId, Long>>() {
|
||||
|
||||
@Override
|
||||
public Map<ObjectId, Long> compute(Transaction txn) {
|
||||
var userCommits = new HashMap<ObjectId, Long>();
|
||||
for (var emailAddress: emailAddresses) {
|
||||
deserializeUserCommits(readBytes(userCommitsStore, txn, new StringByteIterable(emailAddress))).entrySet().forEach(it -> {
|
||||
if (it.getValue() >= fromDate.getTime() && it.getValue() <= toDate.getTime())
|
||||
userCommits.put(it.getKey(), it.getValue());
|
||||
});
|
||||
}
|
||||
return userCommits;
|
||||
@Override
|
||||
public Map<Long, Map<ObjectId, Long>> call() {
|
||||
var localServer = clusterManager.getLocalServerAddress();
|
||||
var userCommits = new HashMap<Long, Map<ObjectId, Long>>();
|
||||
for (var entry: usersCache.entrySet()) {
|
||||
var projectId = entry.getKey();
|
||||
if (localServer.equals(projectManager.getActiveServer(projectId, false))
|
||||
&& emailAddresses.stream().anyMatch(entry.getValue().getRight()::contains)) {
|
||||
var project = projectManager.findFacade(projectId);
|
||||
if (project != null && project.isCodeManagement() && project.getForkedFromId() == null) {
|
||||
Environment env = getEnv(projectId.toString());
|
||||
Store userCommitsStore = getStore(env, USER_COMMITS_STORE);
|
||||
|
||||
userCommits.put(projectId, env.computeInReadonlyTransaction(new TransactionalComputable<Map<ObjectId, Long>>() {
|
||||
|
||||
@Override
|
||||
public Map<ObjectId, Long> compute(Transaction txn) {
|
||||
var userCommits = new HashMap<ObjectId, Long>();
|
||||
for (var emailAddress : emailAddresses) {
|
||||
deserializeUserCommits(
|
||||
readBytes(userCommitsStore, txn, new StringByteIterable(emailAddress)))
|
||||
.entrySet().forEach(it -> {
|
||||
if (it.getValue() >= fromDate.getTime()
|
||||
&& it.getValue() <= toDate.getTime())
|
||||
userCommits.put(it.getKey(), it.getValue());
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}));
|
||||
return userCommits;
|
||||
}
|
||||
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
return userCommits;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
for (var entry: result.entrySet())
|
||||
userCommits.putAll(entry.getValue());
|
||||
return userCommits;
|
||||
}
|
||||
|
||||
|
||||
@ -167,9 +167,9 @@ public class ProductServletConfigurator implements ServletConfigurator {
|
||||
context.addServlet(new ServletHolder(jerseyServlet), "/~api/*");
|
||||
context.addServlet(new ServletHolder(serverServlet), "/~server");
|
||||
|
||||
var mcpServletHolder = new ServletHolder(mcpServerServlet);
|
||||
context.addServlet(mcpServletHolder, "/~mcp");
|
||||
context.addServlet(mcpServletHolder, "/~mcp/*");
|
||||
//var mcpServletHolder = new ServletHolder(mcpServerServlet);
|
||||
//context.addServlet(mcpServletHolder, "/~mcp");
|
||||
//context.addServlet(mcpServletHolder, "/~mcp/*");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user