Beispiel #1
0
func (repo *postgresRepository) listenDaemon() error {
	doneCh := registry.DoneChannel()
	pqNotificationCh := repo.listener.NotificationChannel()

	for {
		select {
		case <-doneCh:
			return nil
		case pqNotification, ok := <-pqNotificationCh:
			if !ok {
				return nil
			}

			decodedChannel, err := hex.DecodeString(pqNotification.Channel[1:]) // trim leading '_'
			if err != nil {
				return err
			}

			decodedPayload, err := hex.DecodeString(pqNotification.Extra)
			if err != nil {
				return err
			}

			repo.m.RLock()
			for ch := range repo.listenChannels[string(decodedChannel)] {
				ch <- string(decodedPayload)
			}
			repo.m.RUnlock()
		}
	}
}
Beispiel #2
0
func NewPostgresRepository(props properties.Properties) (Repository, error) {
	cs := kit.PostgresConnectionString(props)
	log.Printf("postgres connection string `%s`\n", cs)

	db, err := sql.Open("postgres", cs)
	if err != nil {
		return nil, err
	}

	repo := &postgresRepository{
		connectionString: cs,
		db:               db,

		listener:       nil,
		listenChannels: map[string]map[chan string]struct{}{},

		m:     &sync.RWMutex{},
		alive: true,
	}

	kit.SafeGo(func() error {
		<-registry.DoneChannel()
		repo.Close()
		return nil
	})

	return repo, nil
}
Beispiel #3
0
func (srv *service) Forever(setup func() error) error {
	log.Printf("start service [%v]\n", os.Getpid())

	props := properties.Clone()
	log.Println("properties:")
	for _, k := range props.Keys() {
		log.Println(" ", k, props.Get(k))
	}

	err := setup()
	if err != nil {
		return err
	}
	log.Println("service setup successful")

	sigCh := make(chan os.Signal)
	signal.Notify(sigCh, os.Kill, os.Interrupt, syscall.SIGTERM)

	err = nil
	select {
	case <-sigCh:
	case err = <-registry.ErrorChannel():
	}

	close(registry.DoneChannel())
	registry.DoneWaitGroup().Wait()

	return err
}
Beispiel #4
0
func (d *direct) Forever() {
	doneCh := registry.DoneChannel()

	var err error
	processed := true

	kit.SafeGo(func() error {
		for {
			if processed {
				select {
				case <-doneCh:
					return nil
				default:
				}

				processed, err = d.iter()
				if err != nil {
					return err
				}
			} else {
				select {
				case <-doneCh:
					return nil

				case <-d.resumeCh:
					processed, err = d.iter()
					if err != nil {
						return err
					}
				}
			}
		}

	})
}
Beispiel #5
0
func TestSanity(t *testing.T) {
	registry.DefaultServiceRegistry = registry.NewServiceRegistry()
	port := "44554"
	props := properties.Clone()
	props.Set("rest.port", port)

	handler := func(w http.ResponseWriter, r *http.Request) error {
		w.Write([]byte("done"))
		return nil
	}

	routes := PathHandlers{
		"/abc": MethodHandlers{
			"GET": handler,
		},
	}

	err := NewServer(props, routes)
	if err != nil {
		t.Fatalf("new server error %v", err.Error())
	}

	clientWG := sync.WaitGroup{}
	clientWG.Add(1)
	go func() {
		defer clientWG.Done()

		address := fmt.Sprintf("http://127.0.0.1:%v/abc", port)

		client := &http.Client{}
		req, err := http.NewRequest("GET", address, nil)
		if err != nil {
			t.Errorf("new request error %v", err.Error())
			return
		}

		resp, err := client.Do(req)
		if err != nil {
			t.Fatalf("response err %v", err.Error())
		}
		defer resp.Body.Close()

		bytes, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			t.Fatalf("response read all err %v", err.Error())
		}

		if string(bytes) != "done" {
			t.Fatalf("invalid response %v", string(bytes))
		}
	}()
	clientWG.Wait()

	close(registry.DoneChannel())
	registry.DoneWaitGroup().Wait()
}
Beispiel #6
0
// NewServer construct rest server listening specific host and port and routes requests by passed routes
func NewServer(props properties.Properties, routes PathHandlers) error {
	bind := props.Get("rest.address") + ":" + props.Get("rest.port")

	log.Println("start rest server on", bind)
	listener, err := net.Listen("tcp", bind)
	if err != nil {
		return err
	}

	s := &server{
		listener: listener,
		m:        &sync.Mutex{},
		alive:    true,
		wg:       &sync.WaitGroup{},
	}

	kit.SafeGo(func() error {
		router := mux.NewRouter()
		for path := range routes {
			mh := handlers.MethodHandler{}
			for method := range routes[path] {
				log.Printf("setup rest handler %v %v\n", method, path)
				mh[method] = s.wrapHandler(routes[path][method])
			}
			router.Path(path).Handler(mh)
		}

		handler := http.Handler(router)

		compression, err := strconv.ParseBool(props.Get("rest.compression"))
		if err == nil && compression {
			log.Println("enable compression for rest server")
			handler = handlers.CompressHandler(handler)
		} else {
			log.Println("no compression for rest server")
		}

		log.Println("rest server serves")
		err = http.Serve(listener, handler)
		if err != nil {
			return err
		}

		return nil
	})

	kit.SafeGo(func() error {
		<-registry.DoneChannel()
		s.Stop()
		return nil
	})

	return nil
}
Beispiel #7
0
func TestIdleStop(t *testing.T) {
	registry.DefaultServiceRegistry = registry.NewServiceRegistry()

	err := NewServer(properties.Clone(), PathHandlers{})
	if err != nil {
		t.Fatalf("new server error %v", err.Error())
	}

	close(registry.DoneChannel())
	registry.DoneWaitGroup().Wait()
}
Beispiel #8
0
func TestDelayShutdownOnInitialDelay(t *testing.T) {
	registry.DefaultServiceRegistry = registry.NewServiceRegistry()

	scheduler := NewFixedDelayScheduler(100*time.Millisecond, 100*time.Millisecond)

	scheduler.Schedule(func() error {
		time.Sleep(100 * time.Millisecond)
		return nil
	})

	<-time.After(40 * time.Millisecond)

	close(registry.DoneChannel())
	registry.DoneWaitGroup().Wait()
}
Beispiel #9
0
func TestDelayTaskFailure(t *testing.T) {
	registry.DefaultServiceRegistry = registry.NewServiceRegistry()

	scheduler := NewFixedDelayScheduler(0, time.Second)

	scheduler.Schedule(func() error {
		return errors.New("asd")
	})

	err := <-registry.ErrorChannel()
	close(registry.DoneChannel())
	registry.DoneWaitGroup().Wait()

	if err.Error() != "asd" {
		t.Errorf("unexpected error %v", err.Error())
	}
}
Beispiel #10
0
func NewFixedDelayScheduler(initialDelay time.Duration, delay time.Duration) Scheduler {
	scheduler := &fixedDelayScheduler{
		initialDelay: initialDelay,
		delay:        delay,

		alive:  true,
		doneCh: make(chan struct{}),
		m:      &sync.Mutex{},
		wg:     &sync.WaitGroup{},
	}

	kit.SafeGo(func() error {
		<-registry.DoneChannel()
		scheduler.Stop()
		return nil
	})

	return scheduler
}
Beispiel #11
0
func TestDelaySanity(t *testing.T) {
	registry.DefaultServiceRegistry = registry.NewServiceRegistry()

	scheduler := NewFixedDelayScheduler(0, 100*time.Millisecond)

	counter := int32(0)

	scheduler.Schedule(func() error {
		atomic.AddInt32(&counter, 1)
		time.Sleep(100 * time.Millisecond)
		return nil
	})
	<-time.After(300 * time.Millisecond)

	v := atomic.LoadInt32(&counter)
	if v != 2 {
		t.Errorf("invalid counter %v", v)
	}

	close(registry.DoneChannel())
	registry.DoneWaitGroup().Wait()
}
Beispiel #12
0
func (d *direct) listen() error {
	doneCh := registry.DoneChannel()

	newEventChannel, err := d.repo.Listen(d.newEventChannelName)
	if err != nil {
		return err
	}

	for {
		select {
		case <-doneCh:
			return nil
		case _, ok := <-newEventChannel:
			if !ok {
				return nil
			}
			select {
			case d.resumeCh <- struct{}{}:
			default:
			}
		}
	}
}
Beispiel #13
0
func TestDeflateCompression(t *testing.T) {
	registry.DefaultServiceRegistry = registry.NewServiceRegistry()
	port := "44559"
	props := properties.Clone()
	props.Set("rest.port", port)
	props.Set("rest.compression", "true")

	handler := func(w http.ResponseWriter, r *http.Request) error {
		w.Write([]byte("done"))
		return nil
	}

	routes := PathHandlers{
		"/compression": MethodHandlers{
			"GET": handler,
		},
	}

	err := NewServer(props, routes)
	if err != nil {
		t.Fatalf("new server error %v", err.Error())
	}

	clientWG := sync.WaitGroup{}
	clientWG.Add(1)
	go func() {
		defer clientWG.Done()

		address := fmt.Sprintf("http://127.0.0.1:%v/compression", port)

		client := &http.Client{}
		req, err := http.NewRequest("GET", address, nil)
		if err != nil {
			t.Errorf("new request error %v", err.Error())
			return
		}
		req.Header.Add("Accept-Encoding", "deflate,gzip")

		resp, err := client.Do(req)
		if err != nil {
			t.Fatalf("response err %v", err.Error())
		}
		defer resp.Body.Close()

		encoding := resp.Header.Get(http.CanonicalHeaderKey("Content-Encoding"))
		if encoding != "deflate" {
			t.Fatalf("invalid response encoding %v", encoding)
		}

		bytes, err := ioutil.ReadAll(flate.NewReader(resp.Body))
		if err != nil {
			t.Fatalf("response read all err %v", err.Error())
		}

		if string(bytes) != "done" {
			t.Fatalf("invalid response %v", string(bytes))
		}
	}()
	clientWG.Wait()

	close(registry.DoneChannel())
	registry.DoneWaitGroup().Wait()
}
Beispiel #14
0
func TestNilResponse(t *testing.T) {
	registry.DefaultServiceRegistry = registry.NewServiceRegistry()
	port := "44557"
	props := properties.Clone()
	props.Set("rest.port", port)

	handler := func(w http.ResponseWriter, r *http.Request) error {
		WriteResponse(w, nil, http.StatusConflict)
		return nil
	}

	routes := PathHandlers{
		"/nil": MethodHandlers{
			"GET": handler,
		},
	}

	err := NewServer(props, routes)
	if err != nil {
		t.Fatalf("new server error %v", err.Error())
	}

	clientWG := sync.WaitGroup{}
	clientWG.Add(1)
	go func() {
		defer clientWG.Done()

		address := fmt.Sprintf("http://127.0.0.1:%v/nil", port)

		client := &http.Client{}
		req, err := http.NewRequest("GET", address, nil)
		if err != nil {
			t.Fatalf("new request error %v", err.Error())
			return
		}

		resp, err := client.Do(req)
		if err != nil {
			t.Fatalf("response err %v", err.Error())
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusConflict {
			t.Fatalf("invalid response status code %v", resp.StatusCode)
		}

		var responseContent string
		err = json.NewDecoder(resp.Body).Decode(&responseContent)
		if err != nil {
			t.Fatalf("response decode error %v", err.Error())
		}

		if responseContent != http.StatusText(http.StatusConflict) {
			t.Fatalf("invalid response %v", responseContent)
		}
	}()
	clientWG.Wait()

	close(registry.DoneChannel())
	registry.DoneWaitGroup().Wait()
}
Beispiel #15
0
func TestErrorInHandler(t *testing.T) {
	registry.DefaultServiceRegistry = registry.NewServiceRegistry()
	port := "44556"
	props := properties.Clone()
	props.Set("rest.port", port)

	errorMessage := "f951a2344b8349ac222f02b4646ad504efc759e9"

	handler := func(w http.ResponseWriter, r *http.Request) error {
		return errors.New(errorMessage)
	}

	routes := PathHandlers{
		"/err": MethodHandlers{
			"GET": handler,
		},
	}

	err := NewServer(props, routes)
	if err != nil {
		t.Fatalf("new server error %v", err.Error())
	}

	clientWG := sync.WaitGroup{}
	clientWG.Add(1)
	go func() {
		defer clientWG.Done()

		address := fmt.Sprintf("http://127.0.0.1:%v/err", port)

		client := &http.Client{}
		req, err := http.NewRequest("GET", address, nil)
		if err != nil {
			t.Fatalf("new request error %v", err.Error())
			return
		}

		resp, err := client.Do(req)
		if err != nil {
			t.Fatalf("response err %v", err.Error())
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusInternalServerError {
			t.Fatalf("invalid response status code %v", resp.StatusCode)
		}

		var responseContent string
		err = json.NewDecoder(resp.Body).Decode(&responseContent)
		if err != nil {
			t.Fatalf("response decode error %v", err.Error())
		}

		if responseContent != errorMessage {
			t.Fatalf("invalid response %v", responseContent)
		}
	}()
	clientWG.Wait()

	close(registry.DoneChannel())
	registry.DoneWaitGroup().Wait()
}