Code Monkey home page Code Monkey logo

segment-leaf's Introduction

segment-leaf

Rewrite https://github.com/Meituan-Dianping/Leaf with Groovy, add unit tests.

Main Classes

  1. IDGenerator.groovy -> get increase id.
  2. OneBiz.groovy -> dao model and methods.

Unit tests using Spock

IDGeneratorTest

package org.segment.leaf

import groovy.sql.Sql
import groovy.util.logging.Slf4j
import org.h2.jdbcx.JdbcDataSource
import org.junit.Before
import spock.lang.Specification

import javax.sql.DataSource
import java.util.concurrent.CountDownLatch
import java.util.concurrent.atomic.AtomicInteger

@Slf4j
class IDGeneratorTest extends Specification {
    DataSource dataSource

    @Before
    void before() {
        dataSource = new JdbcDataSource()
        dataSource.url = 'jdbc:h2:~/test-segment-leaf'
        dataSource.user = 'sa'
        dataSource.password = ''

        def sql = new Sql(dataSource)

        String ddl = '''
CREATE TABLE if not exists leaf_alloc (
  biz_tag varchar(128)  NOT NULL DEFAULT '',
  max_id bigint(20) NOT NULL DEFAULT '1',
  step int(11) NOT NULL,
  description varchar(256)  DEFAULT NULL,
  update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (biz_tag)
)
'''
        sql.execute(ddl)
        sql.executeUpdate('delete from leaf_alloc')
        // begin step = 100 so it can expand
        String addSql = '''
insert into leaf_alloc(biz_tag, max_id, step, description) values('leaf-segment-test', 1, 100, 'Test leaf Segment Mode Get Id')
'''
        sql.executeUpdate(addSql)
    }

    void testGet() {
        given:
        def id = new IDGenerator()
        id.dataSource = dataSource
        id.init()
        and:
        List<Result> list = []
        final int loopTimes = 200
        loopTimes.times {
            list << id.get('leaf-segment-test')
            // mock do business
            long ms = 10 + new Random().nextInt(10)
            Thread.sleep(ms)
        }
        log.warn 'first result: ' + list[0].id
        log.warn 'middle result: ' + list[(loopTimes / 2) as int].id
        log.warn 'last result: ' + list[-1].id
        expect:
        list.size() == loopTimes
        list.every { it.status == Result.Status.SUCCESS }
        cleanup:
        id.close()
    }

    void testGetMultiThread() {
        given:
        def id = new IDGenerator()
        id.name = 'id generator'
        id.dataSource = dataSource
        id.init()

        Conf.instance.put('wait_ms_when_next_segment_not_ready', '100')
        and:
        // set step bigger as segment may be not ready for many times
        def sql = new Sql(dataSource)
        sql.executeUpdate('update leaf_alloc set step = 2000')

        final int threadNumber = 100
        final int loopTimes = 1000
        def set = Collections.synchronizedSortedSet(new TreeSet<Long>())
        def latch = new CountDownLatch(threadNumber)
        AtomicInteger failedNumber = new AtomicInteger(0)
        threadNumber.times {
            Thread.start {
                try {
                    loopTimes.times {
                        def idResult = id.get('leaf-segment-test').id
                        if (idResult < 0) {
                            failedNumber.incrementAndGet()
                        }
                        set << idResult
                    }
                } finally {
                    latch.countDown()
                }
            }
        }
        latch.await()
        def namePre = id.name.padRight(20, ' ') + 'first result: '
        log.warn namePre + set[0]
        log.warn namePre + 'middle result: ' + set[loopTimes * threadNumber / 2 as int]
        log.warn namePre + 'last result: ' + set[-1]
        log.warn namePre + 'failed number: ' + failedNumber
        expect:
        // get -3 many times
        set.size() <= loopTimes * threadNumber
        // first is -3
        set.size() + failedNumber - 1 == loopTimes * threadNumber
        // no exception
        set.every { it > 0 || it == -3 }
        cleanup:
        id.close()
        log.warn namePre + ' test done'
    }

    void testGetMultiThreadWithMultiInstance() {
        given:
        def id1 = new IDGenerator()
        id1.name = 'id generator 1'
        def id2 = new IDGenerator()
        id2.name = 'id generator 2'
        def id3 = new IDGenerator()
        id3.name = 'id generator 3'

        id1.dataSource = dataSource
        id2.dataSource = dataSource
        id3.dataSource = dataSource
        id1.init()
        id2.init()
        id3.init()

        Conf.instance.put('wait_ms_when_next_segment_not_ready', '100')
        and:
        // set step bigger as segment may be not ready for many times
        def sql = new Sql(dataSource)
        sql.executeUpdate('update leaf_alloc set step = 10000')

        final int threadNumber = 100
        final int loopTimes = 1000
        def set1 = Collections.synchronizedSortedSet(new TreeSet<Long>())
        def set2 = Collections.synchronizedSortedSet(new TreeSet<Long>())
        def set3 = Collections.synchronizedSortedSet(new TreeSet<Long>())
        def latch = new CountDownLatch(threadNumber)
        AtomicInteger failedNumber1 = new AtomicInteger(0)
        AtomicInteger failedNumber2 = new AtomicInteger(0)
        AtomicInteger failedNumber3 = new AtomicInteger(0)
        threadNumber.times {
            Thread.start {
                try {
                    loopTimes.times {
                        def idResult1 = id1.get('leaf-segment-test').id
                        def idResult2 = id2.get('leaf-segment-test').id
                        def idResult3 = id3.get('leaf-segment-test').id
                        if (idResult1 < 0) {
                            failedNumber1.incrementAndGet()
                        }
                        if (idResult2 < 0) {
                            failedNumber2.incrementAndGet()
                        }
                        if (idResult3 < 0) {
                            failedNumber3.incrementAndGet()
                        }
                        set1 << idResult1
                        set2 << idResult2
                        set3 << idResult3
                    }
                } finally {
                    latch.countDown()
                }
            }
        }
        latch.await()
        def namePre1 = id1.name.padRight(20, ' ')
        def namePre2 = id2.name.padRight(20, ' ')
        def namePre3 = id3.name.padRight(20, ' ')

        log.warn namePre1 + 'first result: ' + set1[0]
        log.warn namePre1 + 'middle result: ' + set1[loopTimes * threadNumber / 2 as int]
        log.warn namePre1 + 'last result: ' + set1[-1]
        log.warn namePre1 + 'failed number: ' + failedNumber1

        log.warn namePre2 + 'first result: ' + set2[0]
        log.warn namePre2 + 'middle result: ' + set2[loopTimes * threadNumber / 2 as int]
        log.warn namePre2 + 'last result: ' + set2[-1]
        log.warn namePre2 + 'failed number: ' + failedNumber2

        log.warn namePre3 + 'first result: ' + set3[0]
        log.warn namePre3 + 'middle result: ' + set3[loopTimes * threadNumber / 2 as int]
        log.warn namePre3 + 'last result: ' + set3[-1]
        log.warn namePre3 + 'failed number: ' + failedNumber3

        expect:
        // get -3 many times
        set1.size() <= loopTimes * threadNumber
        set2.size() <= loopTimes * threadNumber
        set3.size() <= loopTimes * threadNumber
        // first is -3 if there is failed get
        set1.size() + failedNumber1 - (failedNumber1.get() > 0 ? 1 : 0) == loopTimes * threadNumber
        set2.size() + failedNumber2 - (failedNumber2.get() > 0 ? 1 : 0) == loopTimes * threadNumber
        set3.size() + failedNumber3 - (failedNumber3.get() > 0 ? 1 : 0) == loopTimes * threadNumber
        // no exception
        set1.every { it > 0 || it == -3 }
        set2.every { it > 0 || it == -3 }
        set3.every { it > 0 || it == -3 }

        set1.remove(-3L) != null
        set2.remove(-3L) != null
        set3.remove(-3L) != null

        !set1.any { it in set2 }
        !set1.any { it in set3 }

        !set2.any { it in set1 }
        !set2.any { it in set3 }

        !set3.any { it in set1 }
        !set3.any { it in set2 }

        cleanup:
        id1.close()
        id2.close()
        id3.close()
        log.warn namePre1 + ' test done'
        log.warn namePre2 + ' test done'
        log.warn namePre3 + ' test done'
    }
}

segment-leaf's People

Contributors

segment11 avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.