Skip to content

Commit

Permalink
Merge pull request #109 from TeamSynergyy/feature/idgenerator
Browse files Browse the repository at this point in the history
- 분산 데이터베이스 객체 ID , 대체키 반영 #87
  • Loading branch information
rivkode committed Jun 10, 2024
2 parents b4c1bf3 + 9bf52be commit 38ffcea
Show file tree
Hide file tree
Showing 51 changed files with 460 additions and 96 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ dependencies {
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.2'
implementation 'io.jsonwebtoken:jjwt-api:0.11.2'

// uuid
implementation 'com.fasterxml.uuid:java-uuid-generator:4.0.1'

// jackson
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
implementation 'com.fasterxml.jackson.core:jackson-databind'
Expand Down
8 changes: 6 additions & 2 deletions src/main/java/com/seoultech/synergybe/domain/apply/Apply.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
public class Apply {
@Id
@Column(name = "apply_id")
private String id;
private Long id;

@Column(name = "apply_token")
private String applyToken;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
Expand All @@ -40,8 +43,9 @@ public class Apply {
private IsDeleted isDeleted = new IsDeleted(IS_DELETED_DEFAULT);

@Builder
public Apply(String id, User user, Project project) {
public Apply(Long id, String applyToken, User user, Project project) {
this.id = id;
this.applyToken = applyToken;
this.user = user;
this.project = project;
this.status = ApplyStatus.NEW;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
import com.seoultech.synergybe.domain.apply.exception.ApplyBadRequestException;
import com.seoultech.synergybe.domain.apply.exception.ApplyNotFoundException;
import com.seoultech.synergybe.domain.apply.repository.ApplyRepository;
import com.seoultech.synergybe.domain.common.idgenerator.IdGenerator;
import com.seoultech.synergybe.domain.common.idgenerator.IdPrefix;
import com.seoultech.synergybe.domain.common.generator.IdGenerator;
import com.seoultech.synergybe.domain.common.generator.IdPrefix;
import com.seoultech.synergybe.domain.common.generator.TokenGenerator;
import com.seoultech.synergybe.domain.notification.service.NotificationService;
import com.seoultech.synergybe.domain.project.domain.Project;
import com.seoultech.synergybe.domain.project.domain.service.ProjectService;
Expand All @@ -32,12 +33,14 @@ public class ApplyService {
private final UserService userService;
private final NotificationService notificationService;
private final IdGenerator idGenerator;
private final TokenGenerator tokenGenerator;

@Transactional
public GetApplyResponse createApply(String userId, String projectId) {
Project project = projectService.findProjectById(projectId);
User user = userService.getUser(userId);
String applyId = idGenerator.generateId(IdPrefix.APPLY);
Long applyId = idGenerator.generateId();
String applyToken = tokenGenerator.generateToken(IdPrefix.APPLY);

Apply apply = Apply.builder()
.id(applyId).user(user).project(project)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
public class ChatRoom {
@Id
@Column(name = "chat_room_id")
private String id;
private Long id;

@Column(name = "chat_room_token")
private String chatRoomToken;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "create_user_id", nullable = false)
Expand All @@ -27,8 +30,9 @@ public class ChatRoom {


@Builder
public ChatRoom(String id, User createUser, User attendUser, String name) {
public ChatRoom(Long id, String chatRoomToken, User createUser, User attendUser, String name) {
this.id = id;
this.chatRoomToken = chatRoomToken;
this.createUser = createUser;
this.attendUser = attendUser;
this.name = name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import com.seoultech.synergybe.domain.chat.dto.request.CreateChatRoomRequest;
import com.seoultech.synergybe.domain.chat.dto.response.GetChatRoomResponse;
import com.seoultech.synergybe.domain.chat.jpa_repository.ChatRoomRepository;
import com.seoultech.synergybe.domain.common.idgenerator.IdGenerator;
import com.seoultech.synergybe.domain.common.idgenerator.IdPrefix;
import com.seoultech.synergybe.domain.common.generator.IdGenerator;
import com.seoultech.synergybe.domain.common.generator.IdPrefix;
import com.seoultech.synergybe.domain.user.User;
import com.seoultech.synergybe.domain.user.service.UserService;
import lombok.RequiredArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ public class Comment extends BaseTime {

@Id
@Column(name = "comment_id")
private String id;
private Long id;

@Column(name = "comment_token")
private String commentToken;

@Embedded
private CommentContent comment;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import com.seoultech.synergybe.domain.comment.dto.response.GetCommentResponse;
import com.seoultech.synergybe.domain.comment.exception.CommentNotFoundException;
import com.seoultech.synergybe.domain.comment.repository.CommentRepository;
import com.seoultech.synergybe.domain.common.idgenerator.IdGenerator;
import com.seoultech.synergybe.domain.common.idgenerator.IdPrefix;
import com.seoultech.synergybe.domain.common.generator.IdGenerator;
import com.seoultech.synergybe.domain.common.generator.IdPrefix;
import com.seoultech.synergybe.domain.common.paging.ListResponse;
import com.seoultech.synergybe.domain.notification.service.NotificationService;
import com.seoultech.synergybe.domain.post.Post;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.seoultech.synergybe.domain.common.generator;

import org.springframework.stereotype.Component;

@Component
public interface IdGenerator {
Long generateId();
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.seoultech.synergybe.domain.common.idgenerator;
package com.seoultech.synergybe.domain.common.generator;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
Expand All @@ -10,7 +10,7 @@

@Slf4j
@Component
public class IdGenerator {
public class IdGeneratorRivkodeImpl {
private static final int ASCII_LOWER_CASE_START = 97;
private static final int ASCII_LOWER_CASE_END = 122;
private static final int ASCII_UPPER_CASE_START = 65;
Expand All @@ -25,7 +25,7 @@ public class IdGenerator {
private final HashMap<Integer, Integer> asciiMap = new HashMap<>();
private final HashMap<Integer, Character> timeMap = new HashMap<>();

public IdGenerator() {
public IdGeneratorRivkodeImpl() {
initializeAsciiMap();
initializeTimeMap();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package com.seoultech.synergybe.domain.common.generator;

import org.springframework.stereotype.Component;

import java.net.NetworkInterface;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.Enumeration;

/**
* Distributed Sequence Generator.
* Inspired by Twitter snowflake: https://github.com/twitter/snowflake/tree/snowflake-2010
*
* This class should be used as a Singleton.
* Make sure that you create and reuse a Single instance of Snowflake per node in your distributed system cluster.
*/
@Component
public class IdGeneratorSnowFlake implements IdGenerator {
private static final int UNUSED_BITS = 1; // Sign bit, Unused (always set to 0)
private static final int EPOCH_BITS = 41;
private static final int NODE_ID_BITS = 10;
private static final int SEQUENCE_BITS = 12;

private static final long maxNodeId = (1L << NODE_ID_BITS) - 1;
private static final long maxSequence = (1L << SEQUENCE_BITS) - 1;

// Custom Epoch (January 1, 2015 Midnight UTC = 2015-01-01T00:00:00Z)
private static final long DEFAULT_CUSTOM_EPOCH = 1420070400000L;

private final long nodeId;
private final long customEpoch;

private volatile long lastTimestamp = -1L;
private volatile long sequence = 0L;

// Create Snowflake with a nodeId and custom epoch
public IdGeneratorSnowFlake(long nodeId, long customEpoch) {
if(nodeId < 0 || nodeId > maxNodeId) {
throw new IllegalArgumentException(String.format("NodeId must be between %d and %d", 0, maxNodeId));
}
this.nodeId = nodeId;
this.customEpoch = customEpoch;
}

// Create Snowflake with a nodeId
public IdGeneratorSnowFlake(long nodeId) {
this(nodeId, DEFAULT_CUSTOM_EPOCH);
}

// Let Snowflake generate a nodeId
public IdGeneratorSnowFlake() {
this.nodeId = createNodeId();
this.customEpoch = DEFAULT_CUSTOM_EPOCH;
}

public Long generateId() {
long id = nextId();
return id;
}

public synchronized long nextId() {
long currentTimestamp = timestamp();

if(currentTimestamp < lastTimestamp) {
throw new IllegalStateException("Invalid System Clock!");
}

if (currentTimestamp == lastTimestamp) {
sequence = (sequence + 1) & maxSequence;
if(sequence == 0) {
// Sequence Exhausted, wait till next millisecond.
currentTimestamp = waitNextMillis(currentTimestamp);
}
} else {
// reset sequence to start with zero for the next millisecond
sequence = 0;
}

lastTimestamp = currentTimestamp;

long id = currentTimestamp << (NODE_ID_BITS + SEQUENCE_BITS)
| (nodeId << SEQUENCE_BITS)
| sequence;

return id;
}


// Get current timestamp in milliseconds, adjust for the custom epoch.
private long timestamp() {
return Instant.now().toEpochMilli() - customEpoch;
}

// Block and wait till next millisecond
private long waitNextMillis(long currentTimestamp) {
while (currentTimestamp == lastTimestamp) {
currentTimestamp = timestamp();
}
return currentTimestamp;
}

private long createNodeId() {
long nodeId;
try {
StringBuilder sb = new StringBuilder();
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while (networkInterfaces.hasMoreElements()) {
NetworkInterface networkInterface = networkInterfaces.nextElement();
byte[] mac = networkInterface.getHardwareAddress();
if (mac != null) {
for(byte macPort: mac) {
sb.append(String.format("%02X", macPort));
}
}
}
nodeId = sb.toString().hashCode();
} catch (Exception ex) {
nodeId = (new SecureRandom().nextInt());
}
nodeId = nodeId & maxNodeId;
return nodeId;
}

public long[] parse(long id) {
long maskNodeId = ((1L << NODE_ID_BITS) - 1) << SEQUENCE_BITS;
long maskSequence = (1L << SEQUENCE_BITS) - 1;

long timestamp = (id >> (NODE_ID_BITS + SEQUENCE_BITS)) + customEpoch;
long nodeId = (id & maskNodeId) >> SEQUENCE_BITS;
long sequence = id & maskSequence;

return new long[]{timestamp, nodeId, sequence};
}

@Override
public String toString() {
return "Snowflake Settings [EPOCH_BITS=" + EPOCH_BITS + ", NODE_ID_BITS=" + NODE_ID_BITS
+ ", SEQUENCE_BITS=" + SEQUENCE_BITS + ", CUSTOM_EPOCH=" + customEpoch
+ ", NodeId=" + nodeId + "]";
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.seoultech.synergybe.domain.common.idgenerator;
package com.seoultech.synergybe.domain.common.generator;

import lombok.Getter;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.seoultech.synergybe.domain.common.generator;

public interface TokenGenerator {
String generateToken(IdPrefix idPrefix);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.seoultech.synergybe.domain.common.generator;

import com.fasterxml.uuid.Generators;
import org.springframework.stereotype.Component;

import java.util.UUID;

@Component
public class TokenGeneratorUUID implements TokenGenerator {

public String generateToken(IdPrefix idPrefix) {
UUID originUuid = Generators.timeBasedGenerator().generate();
String[] uuidArr = originUuid.toString().split("-");
String uuidStr = uuidArr[2] + uuidArr[1] + uuidArr[0] + uuidArr[3] + uuidArr[4];
String uuid = idPrefix.getValue() + "_" + uuidStr;
return uuid;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.seoultech.synergybe.domain.common.generator;

import org.springframework.stereotype.Component;

import java.nio.ByteBuffer;
import java.util.UUID;

@Component
public class UUIDConverter {
// UUID를 BINARY(16)로 변환
public static byte[] toBinaryUUID(UUID uuid) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return bb.array();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
public class Follow extends BaseTime {
@Id
@Column(name = "follow_id")
private String id;
private Long id;

@Column(name = "follow_token")
private String followToken;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "follower_id")
Expand All @@ -29,7 +32,7 @@ public class Follow extends BaseTime {
private FollowStatus status;

@Builder
public Follow(String id, User follower, User following) {
public Follow(Long id, User follower, User following) {
this.id = id;
this.follower = follower;
this.following = following;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.seoultech.synergybe.domain.follow.service;

import com.seoultech.synergybe.domain.common.idgenerator.IdGenerator;
import com.seoultech.synergybe.domain.common.idgenerator.IdPrefix;
import com.seoultech.synergybe.domain.common.generator.IdGenerator;
import com.seoultech.synergybe.domain.common.generator.IdPrefix;
import com.seoultech.synergybe.domain.common.paging.ListResponse;
import com.seoultech.synergybe.domain.follow.Follow;
import com.seoultech.synergybe.domain.follow.FollowStatus;
Expand Down
Loading

0 comments on commit 38ffcea

Please sign in to comment.